From 7c239227d84ede36fded70e979997a0dbd971d2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Delacotte?= Date: Thu, 6 Mar 2025 11:01:43 +0100 Subject: [PATCH] first commit --- BB_RTR_dca.py | 1049 +++++++++++++ BinHV45.py | 71 + BreakEven.py | 67 + DevilStra.json | 29 + DevilStra.py | 721 +++++++++ DevilStra.txt | 76 + DevilStra2.json | 29 + DevilStra2.py | 658 ++++++++ Diamond.py | 155 ++ Diamond.txt | 77 + Empty.py | 123 ++ EnCirculation.txt | 20 + FractalAtr.py | 328 ++++ FractalAtr2.py | 433 ++++++ Genetic.py | 362 +++++ GodStra.py | 171 +++ GodStra.txt | 73 + GodStraHo.py | 200 +++ GodStraJD.py | 668 ++++++++ GodStraJD1.py | 858 +++++++++++ GodStraJD2.py | 848 ++++++++++ GodStraJD3.py | 863 +++++++++++ GodStraJD3_1.json | 51 + GodStraJD3_1.py | 1033 +++++++++++++ GodStraJD3_2.json | 84 + GodStraJD3_2.jsonOLD | 84 + GodStraJD3_2.py | 1078 +++++++++++++ GodStraJD3_3.py | 1111 ++++++++++++++ GodStraJD3_4.json | 41 + GodStraJD3_4.jsonBest | 48 + GodStraJD3_4.jsonOld | 48 + GodStraJD3_4.py | 985 ++++++++++++ GodStraJD3_4_1.json | 54 + GodStraJD3_4_1.py | 1121 ++++++++++++++ GodStraJD3_5.json | 26 + GodStraJD3_5.py | 618 ++++++++ GodStraJD3_5_1.json | 41 + GodStraJD3_5_1.jsonDECREASE | 32 + GodStraJD3_5_1.py | 577 +++++++ GodStraJD3_5_2.json | 30 + GodStraJD3_5_2.jsonBest | 30 + GodStraJD3_5_2.py | 628 ++++++++ GodStraJD3_5_3.json | 29 + GodStraJD3_5_3.py | 609 ++++++++ GodStraJD3_6.json | 48 + GodStraJD3_6.py | 956 ++++++++++++ GodStraJD3_6_53.json | 48 + GodStraJD3_6_53.jsonOLD | 48 + GodStraJD3_6_53.py | 973 ++++++++++++ GodStraJD3_6_53_1.json | 51 + GodStraJD3_6_53_1.py | 920 +++++++++++ GodStraJD3_6_53_2.json | 29 + GodStraJD3_6_53_2.py | 520 +++++++ GodStraJD3_7.json | 33 + GodStraJD3_7.py | 636 ++++++++ GodStraJD3_7_1.json | 31 + GodStraJD3_7_1.py | 540 +++++++ GodStraJD3_7_2.json | 32 + GodStraJD3_7_2.py | 606 ++++++++ GodStraJD3_7_3.json | 34 + GodStraJD3_7_3.py | 641 ++++++++ GodStraJD3_7_4.json | 45 + GodStraJD3_7_4.py | 804 ++++++++++ GodStraJD3_7_5.json | 48 + GodStraJD3_7_5.py | 764 +++++++++ GodStraJD3_7_5_1.json | 57 + GodStraJD3_7_5_1.py | 823 ++++++++++ GodStraJD3_7_5_10.json | 72 + GodStraJD3_7_5_10.py | 962 ++++++++++++ GodStraJD3_7_5_2.json | 51 + GodStraJD3_7_5_2.py | 781 ++++++++++ GodStraJD3_7_5_3.json | 51 + GodStraJD3_7_5_3.json1 | 51 + GodStraJD3_7_5_3.py | 808 ++++++++++ GodStraJD3_7_5_4.json | 57 + GodStraJD3_7_5_4.py | 827 ++++++++++ GodStraJD3_7_5_5.json | 63 + GodStraJD3_7_5_5.py | 875 +++++++++++ GodStraJD3_7_5_6.json | 48 + GodStraJD3_7_5_6.py | 898 +++++++++++ GodStraJD3_7_5_7.json | 63 + GodStraJD3_7_5_7.py | 878 +++++++++++ GodStraJD3_7_5_8.json | 46 + GodStraJD3_7_5_8.py | 889 +++++++++++ GodStraJD3_7_5_9.json | 52 + GodStraJD3_7_5_9.py | 861 +++++++++++ GodStraJD3_7_5_9_1.json | 67 + GodStraJD3_7_5_9_1.py | 863 +++++++++++ GodStraJD3_7_5_9_2.json | 97 ++ GodStraJD3_7_5_9_2.py | 811 ++++++++++ GodStraJD3_7_6.json | 63 + GodStraJD3_7_6.py | 787 ++++++++++ GodStraJD3_7_6_2.json | 63 + GodStraJD3_7_6_2.py | 788 ++++++++++ GodStraJD3_8.json | 54 + GodStraJD3_8.py | 1043 +++++++++++++ GodStraJD3_9.json | 48 + GodStraJD3_9.jsonBest | 48 + GodStraJD3_9.jsonOld | 47 + GodStraJD3_9.jsonOld2 | 48 + GodStraJD3_9.py | 1197 +++++++++++++++ GodStraJD4.py | 848 ++++++++++ GodStraJD5.py | 828 ++++++++++ GodStraJD_P.py | 673 ++++++++ GodStraJD_Test.json | 51 + GodStraNew.json | 51 + GodStraNew.py | 639 ++++++++ GodStraNew.txt | 72 + Heracles.py | 127 ++ Heracles.txt | 87 ++ Heracles_2.json | 33 + Heracles_2.py | 274 ++++ HourBasedStrategy.py | 105 ++ HourBasedStrategy.txt | 87 ++ InformativeSample.py | 131 ++ Ishimoku_1.json | 70 + Ishimoku_1.py | 554 +++++++ Ishimoku_2.json | 70 + Ishimoku_2.py | 554 +++++++ Ishimoku_3.json | 70 + Ishimoku_3.py | 554 +++++++ Ishimoku_4.json | 70 + Ishimoku_4.py | 554 +++++++ Ishimoku_5.json | 70 + Ishimoku_5.py | 554 +++++++ Ishimoku_6.json | 77 + Ishimoku_6.py | 728 +++++++++ Ishimoku_6Best.json | 81 + Ishimoku_7.json | 70 + Ishimoku_7.py | 752 +++++++++ MultiMa.py | 88 ++ MyStrategy.py | 362 +++++ NotAnotherSMAOffsetStrategy.py | 224 +++ NotAnotherSMAOffsetStrategyHO.json | 37 + NotAnotherSMAOffsetStrategyHO.py | 301 ++++ NotAnotherSMAOffsetStrategyX1.py | 299 ++++ NotAnotherSMAOffsetStrategy_uzi2.py | 269 ++++ Pierrick_20211201.py | 132 ++ PremiereStrategie.py | 552 +++++++ RalliV1.py | 292 ++++ RalliV1_disable56.py | 292 ++++ Reco_1.py | 129 ++ SMAOG.py | 109 ++ SMAOffsetProtectOptV1Mod2.py | 191 +++ Scalp.py | 75 + SecondeStrategie.py | 554 +++++++ Solipsis5.json | 49 + Solipsis5.py | 588 +++++++ StJD01.py | 506 ++++++ StJD02.py | 525 +++++++ Strategy001.py | 121 ++ Strategy001_custom_sell.py | 141 ++ Strategy002.py | 135 ++ Strategy003.py | 152 ++ Strategy004.py | 154 ++ Strategy005.json | 36 + Strategy005.py | 185 +++ StrategyJD.json | 29 + StrategyJD.jsonPositive | 29 + StrategyJD.py | 282 ++++ StrategyJD_1.json | 31 + StrategyJD_1.py | 287 ++++ StrategyJD_2.json | 30 + StrategyJD_2.py | 340 ++++ StrategyJD_3.json | 33 + StrategyJD_3.py | 336 ++++ StrategyJD_4.json | 31 + StrategyJD_4.py | 349 +++++ StrategyJD_5.json | 33 + StrategyJD_5.py | 303 ++++ StrategyJD_5_2.json | 33 + StrategyJD_5_2.py | 303 ++++ StrategyJD_5_3.json | 44 + StrategyJD_5_3.py | 346 +++++ StrategyJD_5_4.json | 59 + StrategyJD_5_4.jsonNostoploss | 41 + StrategyJD_5_4.py | 326 ++++ StrategyJD_5_5.json | 59 + StrategyJD_5_5.py | 314 ++++ StrategyJD_5_6.json | 53 + StrategyJD_5_6.py | 331 ++++ StrategyJD_5_7.json | 49 + StrategyJD_5_7.py | 295 ++++ StrategyJD_5_8.json | 45 + StrategyJD_5_8.py | 290 ++++ StrategyJD_5_9.json | 43 + StrategyJD_5_9.py | 301 ++++ StrategyPierrick.py | 139 ++ StrategyPierrick2.jsonOLD | 28 + StrategyPierrick2.py | 272 ++++ StrategyPierrick22.py | 156 ++ StrategyPierrick23.py | 159 ++ StrategyPierrick3.py | 180 +++ StrategyPierrick31.py | 196 +++ StrategyPierrick32.py | 177 +++ StrategyPierrick4.py | 166 ++ StrategyPierrick41.py | 207 +++ StrategyPierrick411.json | 34 + ...egyPierrick411.jsonMaxDrawDownHyperOptLoss | 33 + StrategyPierrick411.jsonOLD2 | 33 + StrategyPierrick411.jsonOld | 27 + ...tegyPierrick411.jsonOnlyProfitHyperOptLoss | 33 + StrategyPierrick411.jsonSharpeHyperOptLoss | 33 + ...egyPierrick411.jsonSharpeHyperOptLossDaily | 33 + ...yPierrick411.jsonShortTradeDurHyperOptLoss | 33 + StrategyPierrick411.jsonSortinoHyperOptLoss | 33 + ...gyPierrick411.jsonSortinoHyperOptLossDaily | 33 + StrategyPierrick411.py | 228 +++ StrategyPierrick4111.json | 42 + StrategyPierrick4111.jsonOLD2 | 39 + StrategyPierrick4111.py | 327 ++++ StrategyPierrick4112.json | 34 + StrategyPierrick4112.py | 288 ++++ StrategyPierrick4113.json | 34 + StrategyPierrick4113.py | 281 ++++ StrategyPierrick4114.json | 34 + StrategyPierrick4114.py | 264 ++++ StrategyPierrick4115.json | 34 + StrategyPierrick4115.jsonOLD | 34 + StrategyPierrick4115.py | 282 ++++ StrategyPierrick4116.json | 33 + StrategyPierrick4116.jsonOLD | 36 + StrategyPierrick4116.py | 255 +++ StrategyPierrick4117.json | 34 + StrategyPierrick4117.py | 319 ++++ StrategyPierrick4118.json | 31 + ...egyPierrick4118.jsonOnlyProfitHyperOptLoss | 31 + ...Pierrick4118.jsonStrategyPierrick4118.json | 31 + StrategyPierrick4118.py | 250 +++ StrategyPierrick4119.json | 34 + StrategyPierrick4119.py | 281 ++++ StrategyPierrick411_02.json | 28 + StrategyPierrick411_02.py | 246 +++ StrategyPierrick411_03.py | 228 +++ StrategyPierrick412.json | 38 + StrategyPierrick412.jsonOLD | 37 + StrategyPierrick412.py | 282 ++++ StrategyPierrick4120.json | 34 + StrategyPierrick4120.py | 281 ++++ StrategyPierrick4121.jsonOLD | 34 + StrategyPierrick4121.py | 286 ++++ StrategyPierrick41211.py | 281 ++++ StrategyPierrick41212.py | 338 ++++ StrategyPierrick41213.py | 347 +++++ StrategyPierrick41214.py | 359 +++++ StrategyPierrick41215.py | 311 ++++ StrategyPierrick41216.py | 323 ++++ StrategyPierrick41217.py | 350 +++++ StrategyPierrick41218.py | 349 +++++ StrategyPierrick41219.py | 378 +++++ StrategyPierrick4122.json | 31 + StrategyPierrick4122.py | 289 ++++ StrategyPierrick41220.py | 410 +++++ StrategyPierrick41221.py | 453 ++++++ StrategyPierrick41222.py | 312 ++++ StrategyPierrick41223.py | 355 +++++ StrategyPierrick413.json | 37 + StrategyPierrick413.py | 267 ++++ StrategyPierrick415.py | 231 +++ StrategyPierrick4151.py | 231 +++ StrategyPierrick416.py | 236 +++ StrategyPierrick42.py | 189 +++ StrategyPierrick43.py | 189 +++ StrategyPierrick44.py | 229 +++ StrategyPierrick5.py | 222 +++ StrategyPierrick51.py | 222 +++ StrategyPierrick52.py | 222 +++ Supertrend.py | 177 +++ Swing-High-To-Sky.py | 110 ++ TheForce.py | 188 +++ TheForce_1.py | 188 +++ Zeus.json | 34 + Zeus.py | 216 +++ Zeus.txt | 70 + Zeus_10.json | 81 + Zeus_10.py | 710 +++++++++ Zeus_2.json | 54 + Zeus_2.py | 353 +++++ Zeus_2_1.json | 54 + Zeus_2_1.py | 356 +++++ Zeus_2_2.json | 56 + Zeus_2_2.py | 368 +++++ Zeus_3.json | 54 + Zeus_3.py | 346 +++++ Zeus_4.json | 78 + Zeus_4.jsonFirst | 85 + Zeus_4.py | 512 +++++++ Zeus_5.json | 105 ++ Zeus_5.py | 1121 ++++++++++++++ Zeus_5_1.json | 114 ++ Zeus_5_1.py | 1072 +++++++++++++ Zeus_5_2.ALGOjson | 114 ++ Zeus_5_2.BTCjson | 114 ++ Zeus_5_2.json | 114 ++ Zeus_5_2.py | 1104 +++++++++++++ Zeus_5_3.json | 105 ++ Zeus_5_3.py | 1144 ++++++++++++++ Zeus_6.json | 114 ++ Zeus_6.json2 | 111 ++ Zeus_6.py | 1080 +++++++++++++ Zeus_6.py2 | 1089 +++++++++++++ Zeus_7.json | 82 + Zeus_7.py | 723 +++++++++ Zeus_8.json | 114 ++ Zeus_8.py | 966 ++++++++++++ Zeus_8_1.json | 114 ++ Zeus_8_1.py | 1013 ++++++++++++ Zeus_8_2.json | 123 ++ Zeus_8_2.py | 1006 ++++++++++++ Zeus_8_3.json | 121 ++ Zeus_8_3.py | 1022 ++++++++++++ Zeus_8_3_1.json | 114 ++ Zeus_8_3_1.py | 1044 +++++++++++++ Zeus_8_3_1_1.json | 114 ++ Zeus_8_3_1_1.py | 998 ++++++++++++ Zeus_8_3_2.json | 114 ++ Zeus_8_3_2.py | 947 ++++++++++++ Zeus_8_3_2_1.json | 81 + Zeus_8_3_2_1.py | 1010 ++++++++++++ Zeus_8_3_2_B_1.json | 87 ++ Zeus_8_3_2_B_1.py | 1108 +++++++++++++ Zeus_8_3_2_B_3.json | 75 + Zeus_8_3_2_B_3.py | 1023 ++++++++++++ Zeus_8_3_2_B_4.json | 83 + Zeus_8_3_2_B_4.jsonKeep | 80 + Zeus_8_3_2_B_4.py | 1080 +++++++++++++ Zeus_8_3_2_B_4_2.json | 83 + Zeus_8_3_2_B_4_2.py | 1365 +++++++++++++++++ Zeus_8_3_2_B_4_2.txt | 15 + Zeus_8_3_2_B_4_2.txtold | 11 + Zeus_8_3_3.json | 117 ++ Zeus_8_3_3.py | 1014 ++++++++++++ Zeus_8_3_3_1.json | 117 ++ Zeus_8_3_3_1.py | 984 ++++++++++++ Zeus_8_3_3_2.json | 35 + Zeus_8_3_3_2.py | 441 ++++++ Zeus_8_3_3_3.json | 79 + Zeus_8_3_3_3.py | 756 +++++++++ Zeus_8_4h.json | 40 + Zeus_8_4h.jsonProfit | 44 + Zeus_8_4h.py | 409 +++++ Zeus_8d.json | 36 + Zeus_8d.jsonstoploss | 36 + Zeus_8d.py | 437 ++++++ Zeus_8d_1.py | 472 ++++++ Zeus_8d_2.json | 38 + Zeus_8d_2.py | 277 ++++ Zeus_9.json | 81 + Zeus_9.py | 707 +++++++++ Zeus_AI.json | 116 ++ Zeus_AI.py | 1017 ++++++++++++ __pycache__/BB_RTR_dca.cpython-39.pyc | Bin 0 -> 24407 bytes __pycache__/BinHV45.cpython-39.pyc | Bin 0 -> 2356 bytes __pycache__/BreakEven.cpython-39.pyc | Bin 0 -> 1964 bytes __pycache__/DevilStra.cpython-39.pyc | Bin 0 -> 11330 bytes __pycache__/DevilStra2.cpython-39.pyc | Bin 0 -> 10358 bytes __pycache__/Diamond.cpython-39.pyc | Bin 0 -> 2765 bytes __pycache__/Empty.cpython-39.pyc | Bin 0 -> 2240 bytes __pycache__/FractalAtr.cpython-39.pyc | Bin 0 -> 9884 bytes __pycache__/FractalAtr2.cpython-39.pyc | Bin 0 -> 11967 bytes __pycache__/Genetic.cpython-39.pyc | Bin 0 -> 8215 bytes __pycache__/GodStra.cpython-39.pyc | Bin 0 -> 4442 bytes __pycache__/GodStraHo.cpython-39.pyc | Bin 0 -> 6171 bytes __pycache__/GodStraJD.cpython-39.pyc | Bin 0 -> 10056 bytes __pycache__/GodStraJD1.cpython-39.pyc | Bin 0 -> 14525 bytes __pycache__/GodStraJD2.cpython-39.pyc | Bin 0 -> 14525 bytes __pycache__/GodStraJD3.cpython-39.pyc | Bin 0 -> 14654 bytes __pycache__/GodStraJD3_1.cpython-39.pyc | Bin 0 -> 15938 bytes __pycache__/GodStraJD3_2.cpython-39.pyc | Bin 0 -> 16524 bytes __pycache__/GodStraJD3_3.cpython-39.pyc | Bin 0 -> 16508 bytes __pycache__/GodStraJD3_4.cpython-39.pyc | Bin 0 -> 13034 bytes __pycache__/GodStraJD3_4_1.cpython-39.pyc | Bin 0 -> 16325 bytes __pycache__/GodStraJD3_5.cpython-39.pyc | Bin 0 -> 8642 bytes __pycache__/GodStraJD3_5_1.cpython-39.pyc | Bin 0 -> 9640 bytes __pycache__/GodStraJD3_5_2.cpython-39.pyc | Bin 0 -> 9743 bytes __pycache__/GodStraJD3_5_3.cpython-39.pyc | Bin 0 -> 9300 bytes __pycache__/GodStraJD3_6.cpython-39.pyc | Bin 0 -> 11066 bytes __pycache__/GodStraJD3_6_53.cpython-39.pyc | Bin 0 -> 13611 bytes __pycache__/GodStraJD3_6_53_1.cpython-39.pyc | Bin 0 -> 13100 bytes __pycache__/GodStraJD3_6_53_2.cpython-39.pyc | Bin 0 -> 8704 bytes __pycache__/GodStraJD3_7.cpython-39.pyc | Bin 0 -> 8558 bytes __pycache__/GodStraJD3_7_1.cpython-39.pyc | Bin 0 -> 9303 bytes __pycache__/GodStraJD3_7_2.cpython-39.pyc | Bin 0 -> 10425 bytes __pycache__/GodStraJD3_7_3.cpython-39.pyc | Bin 0 -> 10595 bytes __pycache__/GodStraJD3_7_4.cpython-39.pyc | Bin 0 -> 12558 bytes __pycache__/GodStraJD3_7_5.cpython-39.pyc | Bin 0 -> 12895 bytes __pycache__/GodStraJD3_7_5_1.cpython-39.pyc | Bin 0 -> 13851 bytes __pycache__/GodStraJD3_7_5_10.cpython-39.pyc | Bin 0 -> 16163 bytes __pycache__/GodStraJD3_7_5_2.cpython-39.pyc | Bin 0 -> 13070 bytes __pycache__/GodStraJD3_7_5_3.cpython-39.pyc | Bin 0 -> 13288 bytes __pycache__/GodStraJD3_7_5_4.cpython-39.pyc | Bin 0 -> 14043 bytes __pycache__/GodStraJD3_7_5_5.cpython-39.pyc | Bin 0 -> 13805 bytes __pycache__/GodStraJD3_7_5_6.cpython-39.pyc | Bin 0 -> 13469 bytes __pycache__/GodStraJD3_7_5_7.cpython-39.pyc | Bin 0 -> 13792 bytes __pycache__/GodStraJD3_7_5_8.cpython-39.pyc | Bin 0 -> 12850 bytes __pycache__/GodStraJD3_7_5_9.cpython-39.pyc | Bin 0 -> 13271 bytes __pycache__/GodStraJD3_7_5_9_1.cpython-39.pyc | Bin 0 -> 14033 bytes __pycache__/GodStraJD3_7_5_9_2.cpython-39.pyc | Bin 0 -> 16981 bytes __pycache__/GodStraJD3_7_6.cpython-39.pyc | Bin 0 -> 12606 bytes __pycache__/GodStraJD3_7_6_2.cpython-39.pyc | Bin 0 -> 12670 bytes __pycache__/GodStraJD3_8.cpython-39.pyc | Bin 0 -> 14637 bytes __pycache__/GodStraJD3_9.cpython-39.pyc | Bin 0 -> 15062 bytes __pycache__/GodStraJD4.cpython-39.pyc | Bin 0 -> 14525 bytes __pycache__/GodStraJD5.cpython-39.pyc | Bin 0 -> 14445 bytes __pycache__/GodStraJD_P.cpython-39.pyc | Bin 0 -> 10131 bytes __pycache__/GodStraNew.cpython-39.pyc | Bin 0 -> 9753 bytes __pycache__/Heracles.cpython-39.pyc | Bin 0 -> 2735 bytes __pycache__/Heracles_2.cpython-39.pyc | Bin 0 -> 6144 bytes __pycache__/HourBasedStrategy.cpython-39.pyc | Bin 0 -> 1613 bytes __pycache__/InformativeSample.cpython-39.pyc | Bin 0 -> 3868 bytes __pycache__/Ishimoku_1.cpython-39.pyc | Bin 0 -> 11319 bytes __pycache__/Ishimoku_2.cpython-39.pyc | Bin 0 -> 11194 bytes __pycache__/Ishimoku_3.cpython-39.pyc | Bin 0 -> 11242 bytes __pycache__/Ishimoku_4.cpython-39.pyc | Bin 0 -> 11289 bytes __pycache__/Ishimoku_5.cpython-39.pyc | Bin 0 -> 10750 bytes __pycache__/Ishimoku_6.cpython-39.pyc | Bin 0 -> 14294 bytes __pycache__/Ishimoku_7.cpython-39.pyc | Bin 0 -> 12970 bytes __pycache__/MultiMa.cpython-39.pyc | Bin 0 -> 2389 bytes __pycache__/MyStrategy.cpython-39.pyc | Bin 0 -> 8230 bytes ...NotAnotherSMAOffsetStrategy.cpython-39.pyc | Bin 0 -> 5688 bytes ...tAnotherSMAOffsetStrategyHO.cpython-39.pyc | Bin 0 -> 6071 bytes ...tAnotherSMAOffsetStrategyX1.cpython-39.pyc | Bin 0 -> 6483 bytes ...otherSMAOffsetStrategy_uzi2.cpython-39.pyc | Bin 0 -> 6683 bytes __pycache__/Pierrick_20211201.cpython-39.pyc | Bin 0 -> 2459 bytes __pycache__/PremiereStrategie.cpython-39.pyc | Bin 0 -> 9571 bytes __pycache__/RalliV1.cpython-39.pyc | Bin 0 -> 7039 bytes __pycache__/RalliV1_disable56.cpython-39.pyc | Bin 0 -> 6729 bytes __pycache__/Reco_1.cpython-39.pyc | Bin 0 -> 2792 bytes __pycache__/SMAOG.cpython-39.pyc | Bin 0 -> 3851 bytes .../SMAOffsetProtectOptV1Mod2.cpython-39.pyc | Bin 0 -> 4941 bytes __pycache__/Scalp.cpython-39.pyc | Bin 0 -> 2270 bytes __pycache__/SecondeStrategie.cpython-39.pyc | Bin 0 -> 9826 bytes __pycache__/Solipsis5.cpython-39.pyc | Bin 0 -> 15191 bytes __pycache__/StJD01.cpython-39.pyc | Bin 0 -> 8881 bytes __pycache__/StJD02.cpython-39.pyc | Bin 0 -> 9947 bytes __pycache__/Strategy001.cpython-39.pyc | Bin 0 -> 3562 bytes .../Strategy001_custom_sell.cpython-39.pyc | Bin 0 -> 4339 bytes __pycache__/Strategy002.cpython-39.pyc | Bin 0 -> 3716 bytes __pycache__/Strategy003.cpython-39.pyc | Bin 0 -> 4085 bytes __pycache__/Strategy004.cpython-39.pyc | Bin 0 -> 3986 bytes __pycache__/Strategy005.cpython-39.pyc | Bin 0 -> 4902 bytes __pycache__/StrategyJD.cpython-39.pyc | Bin 0 -> 3736 bytes __pycache__/StrategyJD_1.cpython-39.pyc | Bin 0 -> 4340 bytes __pycache__/StrategyJD_2.cpython-39.pyc | Bin 0 -> 4344 bytes __pycache__/StrategyJD_3.cpython-39.pyc | Bin 0 -> 4949 bytes __pycache__/StrategyJD_4.cpython-39.pyc | Bin 0 -> 5808 bytes __pycache__/StrategyJD_5.cpython-39.pyc | Bin 0 -> 4996 bytes __pycache__/StrategyJD_5_2.cpython-39.pyc | Bin 0 -> 5014 bytes __pycache__/StrategyJD_5_3.cpython-39.pyc | Bin 0 -> 6608 bytes __pycache__/StrategyJD_5_4.cpython-39.pyc | Bin 0 -> 8073 bytes __pycache__/StrategyJD_5_5.cpython-39.pyc | Bin 0 -> 7645 bytes __pycache__/StrategyJD_5_6.cpython-39.pyc | Bin 0 -> 7055 bytes __pycache__/StrategyJD_5_7.cpython-39.pyc | Bin 0 -> 6935 bytes __pycache__/StrategyJD_5_8.cpython-39.pyc | Bin 0 -> 6579 bytes __pycache__/StrategyJD_5_9.cpython-39.pyc | Bin 0 -> 6568 bytes __pycache__/StrategyPierrick.cpython-39.pyc | Bin 0 -> 2533 bytes __pycache__/StrategyPierrick2.cpython-39.pyc | Bin 0 -> 3836 bytes __pycache__/StrategyPierrick22.cpython-39.pyc | Bin 0 -> 2885 bytes __pycache__/StrategyPierrick23.cpython-39.pyc | Bin 0 -> 2940 bytes __pycache__/StrategyPierrick3.cpython-39.pyc | Bin 0 -> 3164 bytes __pycache__/StrategyPierrick31.cpython-39.pyc | Bin 0 -> 3360 bytes __pycache__/StrategyPierrick32.cpython-39.pyc | Bin 0 -> 3209 bytes __pycache__/StrategyPierrick4.cpython-39.pyc | Bin 0 -> 3013 bytes __pycache__/StrategyPierrick41.cpython-39.pyc | Bin 0 -> 3504 bytes .../StrategyPierrick411.cpython-39.pyc | Bin 0 -> 3998 bytes .../StrategyPierrick4111.cpython-39.pyc | Bin 0 -> 6078 bytes .../StrategyPierrick4112.cpython-39.pyc | Bin 0 -> 5113 bytes .../StrategyPierrick4113.cpython-39.pyc | Bin 0 -> 4986 bytes .../StrategyPierrick4114.cpython-39.pyc | Bin 0 -> 4823 bytes .../StrategyPierrick4115.cpython-39.pyc | Bin 0 -> 5185 bytes .../StrategyPierrick4116.cpython-39.pyc | Bin 0 -> 5335 bytes .../StrategyPierrick4117.cpython-39.pyc | Bin 0 -> 5002 bytes .../StrategyPierrick4118.cpython-39.pyc | Bin 0 -> 4493 bytes .../StrategyPierrick4119.cpython-39.pyc | Bin 0 -> 5335 bytes .../StrategyPierrick411_02.cpython-39.pyc | Bin 0 -> 4141 bytes .../StrategyPierrick411_03.cpython-39.pyc | Bin 0 -> 4005 bytes .../StrategyPierrick412.cpython-39.pyc | Bin 0 -> 5214 bytes .../StrategyPierrick4120.cpython-39.pyc | Bin 0 -> 4645 bytes .../StrategyPierrick4121.cpython-39.pyc | Bin 0 -> 4695 bytes .../StrategyPierrick41211.cpython-39.pyc | Bin 0 -> 5030 bytes .../StrategyPierrick41212.cpython-39.pyc | Bin 0 -> 5718 bytes .../StrategyPierrick41213.cpython-39.pyc | Bin 0 -> 5644 bytes .../StrategyPierrick41214.cpython-39.pyc | Bin 0 -> 6079 bytes .../StrategyPierrick41215.cpython-39.pyc | Bin 0 -> 5986 bytes .../StrategyPierrick41216.cpython-39.pyc | Bin 0 -> 5915 bytes .../StrategyPierrick41217.cpython-39.pyc | Bin 0 -> 6443 bytes .../StrategyPierrick41218.cpython-39.pyc | Bin 0 -> 6574 bytes .../StrategyPierrick41219.cpython-39.pyc | Bin 0 -> 6562 bytes .../StrategyPierrick4122.cpython-39.pyc | Bin 0 -> 5258 bytes .../StrategyPierrick41220.cpython-39.pyc | Bin 0 -> 7645 bytes .../StrategyPierrick41221.cpython-39.pyc | Bin 0 -> 7799 bytes .../StrategyPierrick41222.cpython-39.pyc | Bin 0 -> 5871 bytes .../StrategyPierrick41223.cpython-39.pyc | Bin 0 -> 6520 bytes .../StrategyPierrick413.cpython-39.pyc | Bin 0 -> 4992 bytes .../StrategyPierrick415.cpython-39.pyc | Bin 0 -> 3575 bytes .../StrategyPierrick4151.cpython-39.pyc | Bin 0 -> 3581 bytes .../StrategyPierrick416.cpython-39.pyc | Bin 0 -> 3672 bytes __pycache__/StrategyPierrick42.cpython-39.pyc | Bin 0 -> 3504 bytes __pycache__/StrategyPierrick43.cpython-39.pyc | Bin 0 -> 3504 bytes __pycache__/StrategyPierrick44.cpython-39.pyc | Bin 0 -> 3786 bytes __pycache__/StrategyPierrick5.cpython-39.pyc | Bin 0 -> 3665 bytes __pycache__/StrategyPierrick51.cpython-39.pyc | Bin 0 -> 3671 bytes __pycache__/StrategyPierrick52.cpython-39.pyc | Bin 0 -> 3671 bytes __pycache__/Supertrend.cpython-39.pyc | Bin 0 -> 5492 bytes __pycache__/Swing-High-To-Sky.cpython-39.pyc | Bin 0 -> 2929 bytes __pycache__/TheForce.cpython-39.pyc | Bin 0 -> 4285 bytes __pycache__/TheForce_1.cpython-39.pyc | Bin 0 -> 4296 bytes __pycache__/Zeus.cpython-39.pyc | Bin 0 -> 4530 bytes __pycache__/Zeus_10.cpython-39.pyc | Bin 0 -> 13836 bytes __pycache__/Zeus_2.cpython-39.pyc | Bin 0 -> 8091 bytes __pycache__/Zeus_2_1.cpython-39.pyc | Bin 0 -> 8151 bytes __pycache__/Zeus_2_2.cpython-39.pyc | Bin 0 -> 8371 bytes __pycache__/Zeus_3.cpython-39.pyc | Bin 0 -> 8159 bytes __pycache__/Zeus_4.cpython-39.pyc | Bin 0 -> 11003 bytes __pycache__/Zeus_5.cpython-39.pyc | Bin 0 -> 22255 bytes __pycache__/Zeus_5_1.cpython-39.pyc | Bin 0 -> 21883 bytes __pycache__/Zeus_5_2.cpython-39.pyc | Bin 0 -> 22232 bytes __pycache__/Zeus_5_3.cpython-39.pyc | Bin 0 -> 22463 bytes __pycache__/Zeus_6.cpython-39.pyc | Bin 0 -> 22118 bytes __pycache__/Zeus_7.cpython-39.pyc | Bin 0 -> 15076 bytes __pycache__/Zeus_8.cpython-39.pyc | Bin 0 -> 20068 bytes __pycache__/Zeus_8_1.cpython-39.pyc | Bin 0 -> 21187 bytes __pycache__/Zeus_8_2.cpython-39.pyc | Bin 0 -> 21399 bytes __pycache__/Zeus_8_3.cpython-39.pyc | Bin 0 -> 21448 bytes __pycache__/Zeus_8_3_1.cpython-39.pyc | Bin 0 -> 21922 bytes __pycache__/Zeus_8_3_1_1.cpython-39.pyc | Bin 0 -> 20830 bytes __pycache__/Zeus_8_3_2.cpython-39.pyc | Bin 0 -> 19465 bytes __pycache__/Zeus_8_3_2_1.cpython-39.pyc | Bin 0 -> 17081 bytes __pycache__/Zeus_8_3_2_B_1.cpython-39.pyc | Bin 0 -> 20310 bytes __pycache__/Zeus_8_3_2_B_3.cpython-39.pyc | Bin 0 -> 19607 bytes __pycache__/Zeus_8_3_2_B_4.cpython-39.pyc | Bin 0 -> 19441 bytes __pycache__/Zeus_8_3_2_B_4_2.cpython-39.pyc | Bin 0 -> 29727 bytes __pycache__/Zeus_8_3_3.cpython-39.pyc | Bin 0 -> 20922 bytes __pycache__/Zeus_8_3_3_1.cpython-39.pyc | Bin 0 -> 20903 bytes __pycache__/Zeus_8_3_3_2.cpython-39.pyc | Bin 0 -> 7965 bytes __pycache__/Zeus_8_3_3_3.cpython-39.pyc | Bin 0 -> 11358 bytes __pycache__/Zeus_8_4h.cpython-39.pyc | Bin 0 -> 6448 bytes __pycache__/Zeus_8d.cpython-39.pyc | Bin 0 -> 7163 bytes __pycache__/Zeus_8d_1.cpython-39.pyc | Bin 0 -> 6369 bytes __pycache__/Zeus_8d_2.cpython-39.pyc | Bin 0 -> 4538 bytes __pycache__/Zeus_9.cpython-39.pyc | Bin 0 -> 13584 bytes __pycache__/Zeus_AI.cpython-39.pyc | Bin 0 -> 20953 bytes __pycache__/custom_indicators.cpython-39.pyc | Bin 0 -> 6681 bytes .../custom_stoploss_with_psar.cpython-39.pyc | Bin 0 -> 2869 bytes .../fixed_riskreward_loss.cpython-39.pyc | Bin 0 -> 3588 bytes __pycache__/hlhb.cpython-39.pyc | Bin 0 -> 2727 bytes __pycache__/jeroen_test.cpython-39.pyc | Bin 0 -> 3411 bytes __pycache__/mabStra.cpython-39.pyc | Bin 0 -> 2406 bytes __pycache__/wtc.cpython-39.pyc | Bin 0 -> 2852 bytes custom_indicators.py | 222 +++ custom_stoploss_with_psar.py | 91 ++ fixed_riskreward_loss.py | 120 ++ hlhb.py | 128 ++ jeroen_test.py | 164 ++ mabStra.py | 97 ++ wtc.json | 39 + wtc.py | 156 ++ wtc.txt | 84 + 558 files changed, 108235 insertions(+) create mode 100644 BB_RTR_dca.py create mode 100644 BinHV45.py create mode 100644 BreakEven.py create mode 100644 DevilStra.json create mode 100644 DevilStra.py create mode 100644 DevilStra.txt create mode 100644 DevilStra2.json create mode 100644 DevilStra2.py create mode 100644 Diamond.py create mode 100644 Diamond.txt create mode 100644 Empty.py create mode 100644 EnCirculation.txt create mode 100644 FractalAtr.py create mode 100644 FractalAtr2.py create mode 100644 Genetic.py create mode 100644 GodStra.py create mode 100644 GodStra.txt create mode 100644 GodStraHo.py create mode 100644 GodStraJD.py create mode 100644 GodStraJD1.py create mode 100644 GodStraJD2.py create mode 100644 GodStraJD3.py create mode 100644 GodStraJD3_1.json create mode 100644 GodStraJD3_1.py create mode 100644 GodStraJD3_2.json create mode 100644 GodStraJD3_2.jsonOLD create mode 100644 GodStraJD3_2.py create mode 100644 GodStraJD3_3.py create mode 100644 GodStraJD3_4.json create mode 100644 GodStraJD3_4.jsonBest create mode 100644 GodStraJD3_4.jsonOld create mode 100644 GodStraJD3_4.py create mode 100644 GodStraJD3_4_1.json create mode 100644 GodStraJD3_4_1.py create mode 100644 GodStraJD3_5.json create mode 100644 GodStraJD3_5.py create mode 100644 GodStraJD3_5_1.json create mode 100644 GodStraJD3_5_1.jsonDECREASE create mode 100644 GodStraJD3_5_1.py create mode 100644 GodStraJD3_5_2.json create mode 100644 GodStraJD3_5_2.jsonBest create mode 100644 GodStraJD3_5_2.py create mode 100644 GodStraJD3_5_3.json create mode 100644 GodStraJD3_5_3.py create mode 100644 GodStraJD3_6.json create mode 100644 GodStraJD3_6.py create mode 100644 GodStraJD3_6_53.json create mode 100644 GodStraJD3_6_53.jsonOLD create mode 100644 GodStraJD3_6_53.py create mode 100644 GodStraJD3_6_53_1.json create mode 100644 GodStraJD3_6_53_1.py create mode 100644 GodStraJD3_6_53_2.json create mode 100644 GodStraJD3_6_53_2.py create mode 100644 GodStraJD3_7.json create mode 100644 GodStraJD3_7.py create mode 100644 GodStraJD3_7_1.json create mode 100644 GodStraJD3_7_1.py create mode 100644 GodStraJD3_7_2.json create mode 100644 GodStraJD3_7_2.py create mode 100644 GodStraJD3_7_3.json create mode 100644 GodStraJD3_7_3.py create mode 100644 GodStraJD3_7_4.json create mode 100644 GodStraJD3_7_4.py create mode 100644 GodStraJD3_7_5.json create mode 100644 GodStraJD3_7_5.py create mode 100644 GodStraJD3_7_5_1.json create mode 100644 GodStraJD3_7_5_1.py create mode 100644 GodStraJD3_7_5_10.json create mode 100644 GodStraJD3_7_5_10.py create mode 100644 GodStraJD3_7_5_2.json create mode 100644 GodStraJD3_7_5_2.py create mode 100644 GodStraJD3_7_5_3.json create mode 100644 GodStraJD3_7_5_3.json1 create mode 100644 GodStraJD3_7_5_3.py create mode 100644 GodStraJD3_7_5_4.json create mode 100644 GodStraJD3_7_5_4.py create mode 100644 GodStraJD3_7_5_5.json create mode 100644 GodStraJD3_7_5_5.py create mode 100644 GodStraJD3_7_5_6.json create mode 100644 GodStraJD3_7_5_6.py create mode 100644 GodStraJD3_7_5_7.json create mode 100644 GodStraJD3_7_5_7.py create mode 100644 GodStraJD3_7_5_8.json create mode 100644 GodStraJD3_7_5_8.py create mode 100644 GodStraJD3_7_5_9.json create mode 100644 GodStraJD3_7_5_9.py create mode 100644 GodStraJD3_7_5_9_1.json create mode 100644 GodStraJD3_7_5_9_1.py create mode 100644 GodStraJD3_7_5_9_2.json create mode 100644 GodStraJD3_7_5_9_2.py create mode 100644 GodStraJD3_7_6.json create mode 100644 GodStraJD3_7_6.py create mode 100644 GodStraJD3_7_6_2.json create mode 100644 GodStraJD3_7_6_2.py create mode 100644 GodStraJD3_8.json create mode 100644 GodStraJD3_8.py create mode 100644 GodStraJD3_9.json create mode 100644 GodStraJD3_9.jsonBest create mode 100644 GodStraJD3_9.jsonOld create mode 100644 GodStraJD3_9.jsonOld2 create mode 100644 GodStraJD3_9.py create mode 100644 GodStraJD4.py create mode 100644 GodStraJD5.py create mode 100644 GodStraJD_P.py create mode 100644 GodStraJD_Test.json create mode 100644 GodStraNew.json create mode 100644 GodStraNew.py create mode 100644 GodStraNew.txt create mode 100644 Heracles.py create mode 100644 Heracles.txt create mode 100644 Heracles_2.json create mode 100644 Heracles_2.py create mode 100644 HourBasedStrategy.py create mode 100644 HourBasedStrategy.txt create mode 100644 InformativeSample.py create mode 100644 Ishimoku_1.json create mode 100644 Ishimoku_1.py create mode 100644 Ishimoku_2.json create mode 100644 Ishimoku_2.py create mode 100644 Ishimoku_3.json create mode 100644 Ishimoku_3.py create mode 100644 Ishimoku_4.json create mode 100644 Ishimoku_4.py create mode 100644 Ishimoku_5.json create mode 100644 Ishimoku_5.py create mode 100644 Ishimoku_6.json create mode 100644 Ishimoku_6.py create mode 100644 Ishimoku_6Best.json create mode 100644 Ishimoku_7.json create mode 100644 Ishimoku_7.py create mode 100644 MultiMa.py create mode 100644 MyStrategy.py create mode 100644 NotAnotherSMAOffsetStrategy.py create mode 100644 NotAnotherSMAOffsetStrategyHO.json create mode 100644 NotAnotherSMAOffsetStrategyHO.py create mode 100644 NotAnotherSMAOffsetStrategyX1.py create mode 100644 NotAnotherSMAOffsetStrategy_uzi2.py create mode 100644 Pierrick_20211201.py create mode 100644 PremiereStrategie.py create mode 100644 RalliV1.py create mode 100644 RalliV1_disable56.py create mode 100644 Reco_1.py create mode 100644 SMAOG.py create mode 100644 SMAOffsetProtectOptV1Mod2.py create mode 100644 Scalp.py create mode 100644 SecondeStrategie.py create mode 100644 Solipsis5.json create mode 100644 Solipsis5.py create mode 100644 StJD01.py create mode 100644 StJD02.py create mode 100644 Strategy001.py create mode 100644 Strategy001_custom_sell.py create mode 100644 Strategy002.py create mode 100644 Strategy003.py create mode 100644 Strategy004.py create mode 100644 Strategy005.json create mode 100644 Strategy005.py create mode 100644 StrategyJD.json create mode 100644 StrategyJD.jsonPositive create mode 100644 StrategyJD.py create mode 100644 StrategyJD_1.json create mode 100644 StrategyJD_1.py create mode 100644 StrategyJD_2.json create mode 100644 StrategyJD_2.py create mode 100644 StrategyJD_3.json create mode 100644 StrategyJD_3.py create mode 100644 StrategyJD_4.json create mode 100644 StrategyJD_4.py create mode 100644 StrategyJD_5.json create mode 100644 StrategyJD_5.py create mode 100644 StrategyJD_5_2.json create mode 100644 StrategyJD_5_2.py create mode 100644 StrategyJD_5_3.json create mode 100644 StrategyJD_5_3.py create mode 100644 StrategyJD_5_4.json create mode 100644 StrategyJD_5_4.jsonNostoploss create mode 100644 StrategyJD_5_4.py create mode 100644 StrategyJD_5_5.json create mode 100644 StrategyJD_5_5.py create mode 100644 StrategyJD_5_6.json create mode 100644 StrategyJD_5_6.py create mode 100644 StrategyJD_5_7.json create mode 100644 StrategyJD_5_7.py create mode 100644 StrategyJD_5_8.json create mode 100644 StrategyJD_5_8.py create mode 100644 StrategyJD_5_9.json create mode 100644 StrategyJD_5_9.py create mode 100644 StrategyPierrick.py create mode 100644 StrategyPierrick2.jsonOLD create mode 100644 StrategyPierrick2.py create mode 100644 StrategyPierrick22.py create mode 100644 StrategyPierrick23.py create mode 100644 StrategyPierrick3.py create mode 100644 StrategyPierrick31.py create mode 100644 StrategyPierrick32.py create mode 100644 StrategyPierrick4.py create mode 100644 StrategyPierrick41.py create mode 100644 StrategyPierrick411.json create mode 100644 StrategyPierrick411.jsonMaxDrawDownHyperOptLoss create mode 100644 StrategyPierrick411.jsonOLD2 create mode 100644 StrategyPierrick411.jsonOld create mode 100644 StrategyPierrick411.jsonOnlyProfitHyperOptLoss create mode 100644 StrategyPierrick411.jsonSharpeHyperOptLoss create mode 100644 StrategyPierrick411.jsonSharpeHyperOptLossDaily create mode 100644 StrategyPierrick411.jsonShortTradeDurHyperOptLoss create mode 100644 StrategyPierrick411.jsonSortinoHyperOptLoss create mode 100644 StrategyPierrick411.jsonSortinoHyperOptLossDaily create mode 100644 StrategyPierrick411.py create mode 100644 StrategyPierrick4111.json create mode 100644 StrategyPierrick4111.jsonOLD2 create mode 100644 StrategyPierrick4111.py create mode 100644 StrategyPierrick4112.json create mode 100644 StrategyPierrick4112.py create mode 100644 StrategyPierrick4113.json create mode 100644 StrategyPierrick4113.py create mode 100644 StrategyPierrick4114.json create mode 100644 StrategyPierrick4114.py create mode 100644 StrategyPierrick4115.json create mode 100644 StrategyPierrick4115.jsonOLD create mode 100644 StrategyPierrick4115.py create mode 100644 StrategyPierrick4116.json create mode 100644 StrategyPierrick4116.jsonOLD create mode 100644 StrategyPierrick4116.py create mode 100644 StrategyPierrick4117.json create mode 100644 StrategyPierrick4117.py create mode 100644 StrategyPierrick4118.json create mode 100644 StrategyPierrick4118.jsonOnlyProfitHyperOptLoss create mode 100644 StrategyPierrick4118.jsonStrategyPierrick4118.json create mode 100644 StrategyPierrick4118.py create mode 100644 StrategyPierrick4119.json create mode 100644 StrategyPierrick4119.py create mode 100644 StrategyPierrick411_02.json create mode 100644 StrategyPierrick411_02.py create mode 100644 StrategyPierrick411_03.py create mode 100644 StrategyPierrick412.json create mode 100644 StrategyPierrick412.jsonOLD create mode 100644 StrategyPierrick412.py create mode 100644 StrategyPierrick4120.json create mode 100644 StrategyPierrick4120.py create mode 100644 StrategyPierrick4121.jsonOLD create mode 100644 StrategyPierrick4121.py create mode 100644 StrategyPierrick41211.py create mode 100644 StrategyPierrick41212.py create mode 100644 StrategyPierrick41213.py create mode 100644 StrategyPierrick41214.py create mode 100644 StrategyPierrick41215.py create mode 100644 StrategyPierrick41216.py create mode 100644 StrategyPierrick41217.py create mode 100644 StrategyPierrick41218.py create mode 100644 StrategyPierrick41219.py create mode 100644 StrategyPierrick4122.json create mode 100644 StrategyPierrick4122.py create mode 100644 StrategyPierrick41220.py create mode 100644 StrategyPierrick41221.py create mode 100644 StrategyPierrick41222.py create mode 100644 StrategyPierrick41223.py create mode 100644 StrategyPierrick413.json create mode 100644 StrategyPierrick413.py create mode 100644 StrategyPierrick415.py create mode 100644 StrategyPierrick4151.py create mode 100644 StrategyPierrick416.py create mode 100644 StrategyPierrick42.py create mode 100644 StrategyPierrick43.py create mode 100644 StrategyPierrick44.py create mode 100644 StrategyPierrick5.py create mode 100644 StrategyPierrick51.py create mode 100644 StrategyPierrick52.py create mode 100644 Supertrend.py create mode 100644 Swing-High-To-Sky.py create mode 100644 TheForce.py create mode 100644 TheForce_1.py create mode 100644 Zeus.json create mode 100644 Zeus.py create mode 100644 Zeus.txt create mode 100644 Zeus_10.json create mode 100644 Zeus_10.py create mode 100644 Zeus_2.json create mode 100644 Zeus_2.py create mode 100644 Zeus_2_1.json create mode 100644 Zeus_2_1.py create mode 100644 Zeus_2_2.json create mode 100644 Zeus_2_2.py create mode 100644 Zeus_3.json create mode 100644 Zeus_3.py create mode 100644 Zeus_4.json create mode 100644 Zeus_4.jsonFirst create mode 100644 Zeus_4.py create mode 100644 Zeus_5.json create mode 100644 Zeus_5.py create mode 100644 Zeus_5_1.json create mode 100644 Zeus_5_1.py create mode 100644 Zeus_5_2.ALGOjson create mode 100644 Zeus_5_2.BTCjson create mode 100644 Zeus_5_2.json create mode 100644 Zeus_5_2.py create mode 100644 Zeus_5_3.json create mode 100644 Zeus_5_3.py create mode 100644 Zeus_6.json create mode 100644 Zeus_6.json2 create mode 100644 Zeus_6.py create mode 100644 Zeus_6.py2 create mode 100644 Zeus_7.json create mode 100644 Zeus_7.py create mode 100644 Zeus_8.json create mode 100644 Zeus_8.py create mode 100644 Zeus_8_1.json create mode 100644 Zeus_8_1.py create mode 100644 Zeus_8_2.json create mode 100644 Zeus_8_2.py create mode 100644 Zeus_8_3.json create mode 100644 Zeus_8_3.py create mode 100644 Zeus_8_3_1.json create mode 100644 Zeus_8_3_1.py create mode 100644 Zeus_8_3_1_1.json create mode 100644 Zeus_8_3_1_1.py create mode 100644 Zeus_8_3_2.json create mode 100644 Zeus_8_3_2.py create mode 100644 Zeus_8_3_2_1.json create mode 100644 Zeus_8_3_2_1.py create mode 100644 Zeus_8_3_2_B_1.json create mode 100644 Zeus_8_3_2_B_1.py create mode 100644 Zeus_8_3_2_B_3.json create mode 100644 Zeus_8_3_2_B_3.py create mode 100644 Zeus_8_3_2_B_4.json create mode 100644 Zeus_8_3_2_B_4.jsonKeep create mode 100644 Zeus_8_3_2_B_4.py create mode 100644 Zeus_8_3_2_B_4_2.json create mode 100644 Zeus_8_3_2_B_4_2.py create mode 100644 Zeus_8_3_2_B_4_2.txt create mode 100644 Zeus_8_3_2_B_4_2.txtold create mode 100644 Zeus_8_3_3.json create mode 100644 Zeus_8_3_3.py create mode 100644 Zeus_8_3_3_1.json create mode 100644 Zeus_8_3_3_1.py create mode 100644 Zeus_8_3_3_2.json create mode 100644 Zeus_8_3_3_2.py create mode 100644 Zeus_8_3_3_3.json create mode 100644 Zeus_8_3_3_3.py create mode 100644 Zeus_8_4h.json create mode 100644 Zeus_8_4h.jsonProfit create mode 100644 Zeus_8_4h.py create mode 100644 Zeus_8d.json create mode 100644 Zeus_8d.jsonstoploss create mode 100644 Zeus_8d.py create mode 100644 Zeus_8d_1.py create mode 100644 Zeus_8d_2.json create mode 100644 Zeus_8d_2.py create mode 100644 Zeus_9.json create mode 100644 Zeus_9.py create mode 100644 Zeus_AI.json create mode 100644 Zeus_AI.py create mode 100644 __pycache__/BB_RTR_dca.cpython-39.pyc create mode 100644 __pycache__/BinHV45.cpython-39.pyc create mode 100644 __pycache__/BreakEven.cpython-39.pyc create mode 100644 __pycache__/DevilStra.cpython-39.pyc create mode 100644 __pycache__/DevilStra2.cpython-39.pyc create mode 100644 __pycache__/Diamond.cpython-39.pyc create mode 100644 __pycache__/Empty.cpython-39.pyc create mode 100644 __pycache__/FractalAtr.cpython-39.pyc create mode 100644 __pycache__/FractalAtr2.cpython-39.pyc create mode 100644 __pycache__/Genetic.cpython-39.pyc create mode 100644 __pycache__/GodStra.cpython-39.pyc create mode 100644 __pycache__/GodStraHo.cpython-39.pyc create mode 100644 __pycache__/GodStraJD.cpython-39.pyc create mode 100644 __pycache__/GodStraJD1.cpython-39.pyc create mode 100644 __pycache__/GodStraJD2.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_1.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_2.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_3.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_4.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_4_1.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_5.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_5_1.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_5_2.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_5_3.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_6.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_6_53.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_6_53_1.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_6_53_2.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_7.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_7_1.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_7_2.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_7_3.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_7_4.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_7_5.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_7_5_1.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_7_5_10.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_7_5_2.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_7_5_3.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_7_5_4.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_7_5_5.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_7_5_6.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_7_5_7.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_7_5_8.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_7_5_9.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_7_5_9_1.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_7_5_9_2.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_7_6.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_7_6_2.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_8.cpython-39.pyc create mode 100644 __pycache__/GodStraJD3_9.cpython-39.pyc create mode 100644 __pycache__/GodStraJD4.cpython-39.pyc create mode 100644 __pycache__/GodStraJD5.cpython-39.pyc create mode 100644 __pycache__/GodStraJD_P.cpython-39.pyc create mode 100644 __pycache__/GodStraNew.cpython-39.pyc create mode 100644 __pycache__/Heracles.cpython-39.pyc create mode 100644 __pycache__/Heracles_2.cpython-39.pyc create mode 100644 __pycache__/HourBasedStrategy.cpython-39.pyc create mode 100644 __pycache__/InformativeSample.cpython-39.pyc create mode 100644 __pycache__/Ishimoku_1.cpython-39.pyc create mode 100644 __pycache__/Ishimoku_2.cpython-39.pyc create mode 100644 __pycache__/Ishimoku_3.cpython-39.pyc create mode 100644 __pycache__/Ishimoku_4.cpython-39.pyc create mode 100644 __pycache__/Ishimoku_5.cpython-39.pyc create mode 100644 __pycache__/Ishimoku_6.cpython-39.pyc create mode 100644 __pycache__/Ishimoku_7.cpython-39.pyc create mode 100644 __pycache__/MultiMa.cpython-39.pyc create mode 100644 __pycache__/MyStrategy.cpython-39.pyc create mode 100644 __pycache__/NotAnotherSMAOffsetStrategy.cpython-39.pyc create mode 100644 __pycache__/NotAnotherSMAOffsetStrategyHO.cpython-39.pyc create mode 100644 __pycache__/NotAnotherSMAOffsetStrategyX1.cpython-39.pyc create mode 100644 __pycache__/NotAnotherSMAOffsetStrategy_uzi2.cpython-39.pyc create mode 100644 __pycache__/Pierrick_20211201.cpython-39.pyc create mode 100644 __pycache__/PremiereStrategie.cpython-39.pyc create mode 100644 __pycache__/RalliV1.cpython-39.pyc create mode 100644 __pycache__/RalliV1_disable56.cpython-39.pyc create mode 100644 __pycache__/Reco_1.cpython-39.pyc create mode 100644 __pycache__/SMAOG.cpython-39.pyc create mode 100644 __pycache__/SMAOffsetProtectOptV1Mod2.cpython-39.pyc create mode 100644 __pycache__/Scalp.cpython-39.pyc create mode 100644 __pycache__/SecondeStrategie.cpython-39.pyc create mode 100644 __pycache__/Solipsis5.cpython-39.pyc create mode 100644 __pycache__/StJD01.cpython-39.pyc create mode 100644 __pycache__/StJD02.cpython-39.pyc create mode 100644 __pycache__/Strategy001.cpython-39.pyc create mode 100644 __pycache__/Strategy001_custom_sell.cpython-39.pyc create mode 100644 __pycache__/Strategy002.cpython-39.pyc create mode 100644 __pycache__/Strategy003.cpython-39.pyc create mode 100644 __pycache__/Strategy004.cpython-39.pyc create mode 100644 __pycache__/Strategy005.cpython-39.pyc create mode 100644 __pycache__/StrategyJD.cpython-39.pyc create mode 100644 __pycache__/StrategyJD_1.cpython-39.pyc create mode 100644 __pycache__/StrategyJD_2.cpython-39.pyc create mode 100644 __pycache__/StrategyJD_3.cpython-39.pyc create mode 100644 __pycache__/StrategyJD_4.cpython-39.pyc create mode 100644 __pycache__/StrategyJD_5.cpython-39.pyc create mode 100644 __pycache__/StrategyJD_5_2.cpython-39.pyc create mode 100644 __pycache__/StrategyJD_5_3.cpython-39.pyc create mode 100644 __pycache__/StrategyJD_5_4.cpython-39.pyc create mode 100644 __pycache__/StrategyJD_5_5.cpython-39.pyc create mode 100644 __pycache__/StrategyJD_5_6.cpython-39.pyc create mode 100644 __pycache__/StrategyJD_5_7.cpython-39.pyc create mode 100644 __pycache__/StrategyJD_5_8.cpython-39.pyc create mode 100644 __pycache__/StrategyJD_5_9.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick2.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick22.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick23.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick3.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick31.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick32.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick4.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick41.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick411.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick4111.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick4112.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick4113.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick4114.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick4115.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick4116.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick4117.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick4118.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick4119.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick411_02.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick411_03.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick412.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick4120.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick4121.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick41211.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick41212.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick41213.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick41214.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick41215.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick41216.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick41217.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick41218.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick41219.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick4122.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick41220.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick41221.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick41222.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick41223.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick413.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick415.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick4151.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick416.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick42.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick43.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick44.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick5.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick51.cpython-39.pyc create mode 100644 __pycache__/StrategyPierrick52.cpython-39.pyc create mode 100644 __pycache__/Supertrend.cpython-39.pyc create mode 100644 __pycache__/Swing-High-To-Sky.cpython-39.pyc create mode 100644 __pycache__/TheForce.cpython-39.pyc create mode 100644 __pycache__/TheForce_1.cpython-39.pyc create mode 100644 __pycache__/Zeus.cpython-39.pyc create mode 100644 __pycache__/Zeus_10.cpython-39.pyc create mode 100644 __pycache__/Zeus_2.cpython-39.pyc create mode 100644 __pycache__/Zeus_2_1.cpython-39.pyc create mode 100644 __pycache__/Zeus_2_2.cpython-39.pyc create mode 100644 __pycache__/Zeus_3.cpython-39.pyc create mode 100644 __pycache__/Zeus_4.cpython-39.pyc create mode 100644 __pycache__/Zeus_5.cpython-39.pyc create mode 100644 __pycache__/Zeus_5_1.cpython-39.pyc create mode 100644 __pycache__/Zeus_5_2.cpython-39.pyc create mode 100644 __pycache__/Zeus_5_3.cpython-39.pyc create mode 100644 __pycache__/Zeus_6.cpython-39.pyc create mode 100644 __pycache__/Zeus_7.cpython-39.pyc create mode 100644 __pycache__/Zeus_8.cpython-39.pyc create mode 100644 __pycache__/Zeus_8_1.cpython-39.pyc create mode 100644 __pycache__/Zeus_8_2.cpython-39.pyc create mode 100644 __pycache__/Zeus_8_3.cpython-39.pyc create mode 100644 __pycache__/Zeus_8_3_1.cpython-39.pyc create mode 100644 __pycache__/Zeus_8_3_1_1.cpython-39.pyc create mode 100644 __pycache__/Zeus_8_3_2.cpython-39.pyc create mode 100644 __pycache__/Zeus_8_3_2_1.cpython-39.pyc create mode 100644 __pycache__/Zeus_8_3_2_B_1.cpython-39.pyc create mode 100644 __pycache__/Zeus_8_3_2_B_3.cpython-39.pyc create mode 100644 __pycache__/Zeus_8_3_2_B_4.cpython-39.pyc create mode 100644 __pycache__/Zeus_8_3_2_B_4_2.cpython-39.pyc create mode 100644 __pycache__/Zeus_8_3_3.cpython-39.pyc create mode 100644 __pycache__/Zeus_8_3_3_1.cpython-39.pyc create mode 100644 __pycache__/Zeus_8_3_3_2.cpython-39.pyc create mode 100644 __pycache__/Zeus_8_3_3_3.cpython-39.pyc create mode 100644 __pycache__/Zeus_8_4h.cpython-39.pyc create mode 100644 __pycache__/Zeus_8d.cpython-39.pyc create mode 100644 __pycache__/Zeus_8d_1.cpython-39.pyc create mode 100644 __pycache__/Zeus_8d_2.cpython-39.pyc create mode 100644 __pycache__/Zeus_9.cpython-39.pyc create mode 100644 __pycache__/Zeus_AI.cpython-39.pyc create mode 100644 __pycache__/custom_indicators.cpython-39.pyc create mode 100644 __pycache__/custom_stoploss_with_psar.cpython-39.pyc create mode 100644 __pycache__/fixed_riskreward_loss.cpython-39.pyc create mode 100644 __pycache__/hlhb.cpython-39.pyc create mode 100644 __pycache__/jeroen_test.cpython-39.pyc create mode 100644 __pycache__/mabStra.cpython-39.pyc create mode 100644 __pycache__/wtc.cpython-39.pyc create mode 100644 custom_indicators.py create mode 100644 custom_stoploss_with_psar.py create mode 100644 fixed_riskreward_loss.py create mode 100644 hlhb.py create mode 100644 jeroen_test.py create mode 100644 mabStra.py create mode 100644 wtc.json create mode 100644 wtc.py create mode 100644 wtc.txt diff --git a/BB_RTR_dca.py b/BB_RTR_dca.py new file mode 100644 index 0000000..594591e --- /dev/null +++ b/BB_RTR_dca.py @@ -0,0 +1,1049 @@ +# --- Do not remove these libs --- +import freqtrade.vendor.qtpylib.indicators as qtpylib +import logging +import math +import numpy as np +import talib.abstract as ta +import pandas_ta as pta + +from freqtrade.persistence import Trade +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame, Series, DatetimeIndex, merge +from datetime import datetime, timedelta +from freqtrade.strategy import merge_informative_pair, CategoricalParameter, DecimalParameter, IntParameter, stoploss_from_open +from functools import reduce +from technical.indicators import RMI, zema + +logger = logging.getLogger(__name__) + +# -------------------------------- +def ha_typical_price(bars): + res = (bars['ha_high'] + bars['ha_low'] + bars['ha_close']) / 3. + return Series(index=bars.index, data=res) + +def EWO(dataframe, ema_length=5, ema2_length=35): + df = dataframe.copy() + ema1 = ta.EMA(df, timeperiod=ema_length) + ema2 = ta.EMA(df, timeperiod=ema2_length) + emadif = (ema1 - ema2) / df['low'] * 100 + return emadif + +# Volume Weighted Moving Average +def vwma(dataframe: DataFrame, length: int = 10): + """Indicator: Volume Weighted Moving Average (VWMA)""" + # Calculate Result + pv = dataframe['close'] * dataframe['volume'] + vwma = Series(ta.SMA(pv, timeperiod=length) / ta.SMA(dataframe['volume'], timeperiod=length)) + return vwma + +# Modified Elder Ray Index +def moderi(dataframe: DataFrame, len_slow_ma: int = 32) -> Series: + slow_ma = Series(ta.EMA(vwma(dataframe, length=len_slow_ma), timeperiod=len_slow_ma)) + return slow_ma >= slow_ma.shift(1) # we just need true & false for ERI trend + +# Williams %R +def williams_r(dataframe: DataFrame, period: int = 14) -> Series: + """Williams %R, or just %R, is a technical analysis oscillator showing the current closing price in relation to the high and low + of the past N days (for a given N). It was developed by a publisher and promoter of trading materials, Larry Williams. + Its purpose is to tell whether a stock or commodity market is trading near the high or the low, or somewhere in between, + of its recent trading range. + The oscillator is on a negative scale, from −100 (lowest) up to 0 (highest). + """ + + highest_high = dataframe["high"].rolling(center=False, window=period).max() + lowest_low = dataframe["low"].rolling(center=False, window=period).min() + + WR = Series( + (highest_high - dataframe["close"]) / (highest_high - lowest_low), + name=f"{period} Williams %R", + ) + + return WR * -100 + +# VWAP bands +def VWAPB(dataframe, window_size=20, num_of_std=1): + df = dataframe.copy() + df['vwap'] = qtpylib.rolling_vwap(df,window=window_size) + rolling_std = df['vwap'].rolling(window=window_size).std() + df['vwap_low'] = df['vwap'] - (rolling_std * num_of_std) + df['vwap_high'] = df['vwap'] + (rolling_std * num_of_std) + return df['vwap_low'], df['vwap'], df['vwap_high'] + +def top_percent_change(dataframe: DataFrame, length: int) -> float: + """ + Percentage change of the current close from the range maximum Open price + + :param dataframe: DataFrame The original OHLC dataframe + :param length: int The length to look back + """ + if length == 0: + return (dataframe['open'] - dataframe['close']) / dataframe['close'] + else: + return (dataframe['open'].rolling(length).max() - dataframe['close']) / dataframe['close'] + +class BB_RTR(IStrategy): + ''' + BB_RPB_TSL_RNG with conditions from true_lambo and dca + + (1) Improve 7_33 x_201 + ''' + + ########################################################################## + + # Hyperopt result area + + # buy space + buy_params = { + ## + "buy_pump_1_factor": 1.096, + "buy_pump_2_factor": 1.125, + ## + "buy_threshold": 0.003, + "buy_bb_factor": 0.999, + "buy_bb_delta": 0.025, + "buy_bb_width": 0.095, + ## + "buy_cci": -116, + "buy_cci_length": 25, + "buy_rmi": 49, + "buy_rmi_length": 17, + "buy_srsi_fk": 32, + ## + "buy_closedelta": 12.148, + "buy_ema_diff": 0.022, + ## + "buy_adx": 20, + "buy_fastd": 20, + "buy_fastk": 22, + "buy_ema_cofi": 0.98, + "buy_ewo_high": 4.179, + ## + "buy_ema_high": 0.968, + "buy_ema_low": 0.935, + "buy_ewo": -5.001, + "buy_rsi": 23, + "buy_rsi_fast": 44, + ## + "buy_ema_high_2": 1.087, + "buy_ema_low_2": 0.970, + ## + "buy_no_trend_cti_4": -0.597, + "buy_no_trend_factor_4": 0.024, + "buy_no_trend_r14_4": -44.062, + ## + "buy_V_bb_width_5": 0.063, + "buy_V_cti_5": -0.086, + "buy_V_mfi_5": 38.158, + "buy_V_r14_5": -41.493, + ## + "buy_vwap_closedelta": 26.941, + "buy_vwap_closedelta_2": 20.099, + "buy_vwap_closedelta_3": 27.654, + ## + "buy_vwap_cti": -0.087, + "buy_vwap_cti_2": -0.748, + "buy_vwap_cti_3": -0.2, + ## + "buy_vwap_width": 1.308, + "buy_vwap_width_2": 3.212, + "buy_vwap_width_3": 0.49, + ## + } + + # sell space + sell_params = { + "pHSL": -0.998, # Disable ? + "pPF_1": 0.019, + "pPF_2": 0.065, + "pSL_1": 0.019, + "pSL_2": 0.062, + ## + "high_offset_2": 0.997, + ## + "sell_cti_r_cti": 0.844, + "sell_cti_r_r": -19.99, + ## + "sell_u_e_2_cmf": -0.0, + "sell_u_e_2_ema_close_delta": 0.016, + "sell_u_e_2_rsi": 10, + ## + "sell_deadfish_profit": -0.063, + "sell_deadfish_bb_factor": 0.954, + "sell_deadfish_bb_width": 0.043, + "sell_deadfish_volume_factor": 2.37, + ## + "sell_cmf_div_1_cmf": 0.442, + "sell_cmf_div_1_profit": 0.02, + } + + # ROI + minimal_roi = { + "0": 0.10, + } + + # Optimal timeframe for the strategy + timeframe = '5m' + inf_1h = '1h' + + # Disabled + stoploss = -0.998 + + # Options + use_custom_stoploss = True + use_sell_signal = True + process_only_new_candles = True + startup_candle_count: int = 400 + + ############################################################################ + + ## Buy params + + is_optimize_dip = False + buy_rmi = IntParameter(30, 50, default=35, optimize= is_optimize_dip) + buy_cci = IntParameter(-135, -90, default=-133, optimize= is_optimize_dip) + buy_srsi_fk = IntParameter(30, 50, default=25, optimize= is_optimize_dip) + buy_cci_length = IntParameter(25, 45, default=25, optimize = is_optimize_dip) + buy_rmi_length = IntParameter(8, 20, default=8, optimize = is_optimize_dip) + + is_optimize_break = False + buy_bb_width = DecimalParameter(0.05, 0.2, default=0.15, optimize = is_optimize_break) + buy_bb_delta = DecimalParameter(0.025, 0.08, default=0.04, optimize = is_optimize_break) + + is_optimize_local_dip = False + buy_ema_diff = DecimalParameter(0.022, 0.027, default=0.025, optimize = is_optimize_local_dip) + buy_bb_factor = DecimalParameter(0.990, 0.999, default=0.995, optimize = False) + buy_closedelta = DecimalParameter(12.0, 18.0, default=15.0, optimize = is_optimize_local_dip) + + is_optimize_ewo = False + buy_rsi_fast = IntParameter(35, 50, default=45, optimize = False) + buy_rsi = IntParameter(15, 30, default=35, optimize = False) + buy_ewo = DecimalParameter(-6.0, 5, default=-5.585, optimize = is_optimize_ewo) + buy_ema_low = DecimalParameter(0.9, 0.99, default=0.942 , optimize = is_optimize_ewo) + buy_ema_high = DecimalParameter(0.95, 1.2, default=1.084 , optimize = is_optimize_ewo) + + is_optimize_ewo_2 = False + buy_ema_low_2 = DecimalParameter(0.96, 0.978, default=0.96 , optimize = is_optimize_ewo_2) + buy_ema_high_2 = DecimalParameter(1.05, 1.2, default=1.09 , optimize = is_optimize_ewo_2) + + is_optimize_cofi = False + buy_ema_cofi = DecimalParameter(0.96, 0.98, default=0.97 , optimize = is_optimize_cofi) + buy_fastk = IntParameter(20, 30, default=20, optimize = is_optimize_cofi) + buy_fastd = IntParameter(20, 30, default=20, optimize = is_optimize_cofi) + buy_adx = IntParameter(20, 30, default=30, optimize = is_optimize_cofi) + buy_ewo_high = DecimalParameter(2, 12, default=3.553, optimize = is_optimize_cofi) + + is_optimize_vwap = False + buy_vwap_width = DecimalParameter(0.05, 10.0, default=0.80 , optimize = is_optimize_vwap) + buy_vwap_closedelta = DecimalParameter(10.0, 30.0, default=15.0, optimize = is_optimize_vwap) + buy_vwap_cti = DecimalParameter(-0.9, -0.0, default=-0.6 , optimize = is_optimize_vwap) + + is_optimize_vwap_2 = False + buy_vwap_width_2 = DecimalParameter(0.05, 10.0, default=0.80 , optimize = is_optimize_vwap_2) + buy_vwap_closedelta_2 = DecimalParameter(10.0, 30.0, default=15.0, optimize = is_optimize_vwap_2) + buy_vwap_cti_2 = DecimalParameter(-0.9, -0.0, default=-0.6 , optimize = is_optimize_vwap_2) + + is_optimize_vwap_3 = True + buy_vwap_width_3 = DecimalParameter(0.05, 10.0, default=0.80 , optimize = is_optimize_vwap_3) + buy_vwap_closedelta_3 = DecimalParameter(10.0, 30.0, default=15.0, optimize = is_optimize_vwap_3) + buy_vwap_cti_3 = DecimalParameter(-0.9, -0.0, default=-0.6 , optimize = is_optimize_vwap_3) + + is_optimize_no_trend_4 = False + buy_no_trend_factor_4 = DecimalParameter(0.01, 0.05, default=0.030 , optimize = is_optimize_no_trend_4) + buy_no_trend_cti_4 = DecimalParameter(-0.9, -0.0, default=-0.6 , optimize = is_optimize_no_trend_4) + buy_no_trend_r14_4 = DecimalParameter(-100, -44, default=-80 , optimize = is_optimize_no_trend_4) + + is_optimize_V_5 = False + buy_V_bb_width_5 = DecimalParameter(0.01, 0.1, default=0.01 , optimize = is_optimize_V_5) + buy_V_cti_5 = DecimalParameter(-0.95, -0.0, default=-0.6 , optimize = is_optimize_V_5) + buy_V_r14_5 = DecimalParameter(-100, 0, default=-60 , optimize = is_optimize_V_5) + buy_V_mfi_5 = DecimalParameter(10, 40, default=30 , optimize = is_optimize_V_5) + + is_optimize_gumbo = False + buy_gumbo_ema = DecimalParameter(0.9, 1.2, default=0.97 , optimize = is_optimize_gumbo) + buy_gumbo_ewo_low = DecimalParameter(-12.0, 5, default=-5.585, optimize = is_optimize_gumbo) + buy_gumbo_cti = DecimalParameter(-0.9, -0.0, default=-0.5 , optimize = is_optimize_gumbo) + buy_gumbo_r14 = DecimalParameter(-100, -44, default=-60 , optimize = is_optimize_gumbo) + + is_optimize_gumbo_protection = False + buy_gumbo_tpct_0 = DecimalParameter(0.0, 0.25, default=0.131, decimals=2, optimize = is_optimize_gumbo_protection) + buy_gumbo_tpct_3 = DecimalParameter(0.0, 0.25, default=0.131, decimals=2, optimize = is_optimize_gumbo_protection) + buy_gumbo_tpct_9 = DecimalParameter(0.0, 0.25, default=0.131, decimals=2, optimize = is_optimize_gumbo_protection) + + # Buy params toggle + buy_is_dip_enabled = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) + buy_is_break_enabled = CategoricalParameter([True, False], default=True, space='buy', optimize=False, load=True) + + is_optimize_pump_1 = False + buy_pump_1_factor = DecimalParameter(1.0, 1.25, default= 1.1 , optimize = is_optimize_pump_1) + + is_optimize_pump_2 = False + buy_pump_2_factor = DecimalParameter(1.0, 1.20, default= 1.1 , optimize = is_optimize_pump_2) + + ## Sell params + is_optimize_sell_offset = False + high_offset_2 = DecimalParameter(0.99, 1.5, default=sell_params['high_offset_2'], space='sell', optimize=is_optimize_sell_offset) + + is_optimize_sell_u_e_2 = False + sell_u_e_2_cmf = DecimalParameter(-0.4, 0.0, default=0.0, optimize = is_optimize_sell_u_e_2) + sell_u_e_2_ema_close_delta = DecimalParameter(0.001, 0.027, default= 0.024, optimize = is_optimize_sell_u_e_2) + sell_u_e_2_rsi = IntParameter(10, 30, default=24, optimize = is_optimize_sell_u_e_2) + + is_optimize_deadfish = False + sell_deadfish_bb_width = DecimalParameter(0.010, 0.025, default=0.05 , optimize = is_optimize_deadfish) + sell_deadfish_profit = DecimalParameter(-0.10, -0.05, default=-0.05 , optimize = is_optimize_deadfish) + sell_deadfish_bb_factor = DecimalParameter(0.90, 1.20, default=1.0 , optimize = is_optimize_deadfish) + sell_deadfish_volume_factor = DecimalParameter(1.5, 3, default=1.5 , optimize = is_optimize_deadfish) + + is_optimize_cti_r = False + sell_cti_r_cti = DecimalParameter(0.55, 1, default=0.5 , optimize = is_optimize_cti_r) + sell_cti_r_r = DecimalParameter(-15, 0, default=-20 , optimize = is_optimize_cti_r) + + is_optimize_cmf_div = True + sell_cmf_div_1_profit = DecimalParameter(0.005, 0.02, default=0.005 , optimize = is_optimize_cmf_div) + sell_cmf_div_1_cmf = DecimalParameter(0.0, 0.5, default=0.0 , optimize = is_optimize_cmf_div) + sell_cmf_div_2_profit = DecimalParameter(0.005, 0.02, default=0.005 , optimize = is_optimize_cmf_div) + sell_cmf_div_2_cmf = DecimalParameter(0.0, 0.5, default=0.0 , optimize = is_optimize_cmf_div) + + ## Trailing params + + # hard stoploss profit + is_optimize_trailing = False + pHSL = DecimalParameter(-0.200, -0.040, default=-0.08, decimals=3, space='sell', load=True, optimize=is_optimize_trailing) + # profit threshold 1, trigger point, SL_1 is used + pPF_1 = DecimalParameter(0.008, 0.020, default=0.016, decimals=3, space='sell', load=True, optimize=is_optimize_trailing) + pSL_1 = DecimalParameter(0.008, 0.020, default=0.011, decimals=3, space='sell', load=True, optimize=is_optimize_trailing) + + # profit threshold 2, SL_2 is used + pPF_2 = DecimalParameter(0.040, 0.100, default=0.080, decimals=3, space='sell', load=True, optimize=is_optimize_trailing) + pSL_2 = DecimalParameter(0.020, 0.070, default=0.040, decimals=3, space='sell', load=True, optimize=is_optimize_trailing) + + ############################################################################ + + def informative_pairs(self): + + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1h') for pair in pairs] + + return informative_pairs + + ############################################################################ + + ## Custom Trailing stoploss ( credit to Perkmeister for this custom stoploss to help the strategy ride a green candle ) + def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, + current_rate: float, current_profit: float, **kwargs) -> float: + + # hard stoploss profit + HSL = self.pHSL.value + PF_1 = self.pPF_1.value + SL_1 = self.pSL_1.value + PF_2 = self.pPF_2.value + SL_2 = self.pSL_2.value + + # For profits between PF_1 and PF_2 the stoploss (sl_profit) used is linearly interpolated + # between the values of SL_1 and SL_2. For all profits above PL_2 the sl_profit value + # rises linearly with current profit, for profits below PF_1 the hard stoploss profit is used. + + if (current_profit > PF_2): + sl_profit = SL_2 + (current_profit - PF_2) + elif (current_profit > PF_1): + sl_profit = SL_1 + ((current_profit - PF_1) * (SL_2 - SL_1) / (PF_2 - PF_1)) + else: + sl_profit = HSL + + # Only for hyperopt invalid return + if (sl_profit >= current_profit): + return -0.99 + + return stoploss_from_open(sl_profit, current_profit) + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + + last_candle = dataframe.iloc[-1] + previous_candle_1 = dataframe.iloc[-2] + previous_candle_2 = dataframe.iloc[-3] + + max_profit = ((trade.max_rate - trade.open_rate) / trade.open_rate) + max_loss = ((trade.open_rate - trade.min_rate) / trade.min_rate) + + buy_tag = 'empty' + if hasattr(trade, 'buy_tag') and trade.buy_tag is not None: + buy_tag = trade.buy_tag + buy_tags = buy_tag.split() + + # sell cti_r + if 0.012 > current_profit >= 0.0 : + if (last_candle['cti'] > self.sell_cti_r_cti.value) and (last_candle['r_14'] > self.sell_cti_r_r.value): + return f"sell_profit_cti_r_0_1( {buy_tag})" + + # main sell + if current_profit > 0.02: + if (last_candle['momdiv_sell_1h'] == True): + return f"signal_profit_q_momdiv_1h( {buy_tag})" + if (last_candle['momdiv_sell'] == True): + return f"signal_profit_q_momdiv( {buy_tag})" + if (last_candle['momdiv_coh'] == True): + return f"signal_profit_q_momdiv_coh( {buy_tag})" + if (last_candle['cti_40_1h'] > 0.844) and (last_candle['r_84_1h'] > -20): + return f"signal_profit_cti_r( {buy_tag})" + + # sell quick + if (0.06 > current_profit > 0.02) and (last_candle['rsi'] > 80.0): + return f"signal_profit_q_1( {buy_tag})" + + if (0.06 > current_profit > 0.02) and (last_candle['cti'] > 0.95): + return f"signal_profit_q_2( {buy_tag})" + + # sell recover + if (max_loss > 0.06) and (0.05 > current_profit > 0.01) and (last_candle['rsi'] < 46): + return f"signal_profit_r_1( {buy_tag})" + + # sell offset + if ( + (current_profit > 0.005) + and (last_candle['close'] > last_candle['sma_9']) + and (last_candle['close'] > last_candle['ema_24'] * self.high_offset_2.value) + and (last_candle['rsi'] > 50) + and (last_candle['rsi_fast'] > last_candle['rsi_slow']) + ): + return f"sell_offset( {buy_tag})" + + # sell vwap dump + if ( + (current_profit > 0.005) + and (last_candle['ema_vwap_diff_50'] > 0.0) + and (last_candle['ema_vwap_diff_50'] < 0.012) + ): + return f"sell_vwap_dump( {buy_tag})" + + # sell cmf div + if ( + (current_profit > 0.005) + and (last_candle['cmf'] > 0) + and (last_candle['cmf_div_slow'] == 1) + ): + return f"sell_cmf_div( {buy_tag})" + + # stoploss + if ( + (current_profit < -0.025) + and (last_candle['close'] < last_candle['ema_200']) + and (last_candle['cmf'] < self.sell_u_e_2_cmf.value) + and (((last_candle['ema_200'] - last_candle['close']) / last_candle['close']) < self.sell_u_e_2_ema_close_delta.value) + and last_candle['rsi'] > previous_candle_1['rsi'] + and (last_candle['rsi'] > (last_candle['rsi_1h'] + self.sell_u_e_2_rsi.value)) + ): + return f"sell_stoploss_u_e_2( {buy_tag})" + + # stoploss - deadfish + if ( (current_profit < self.sell_deadfish_profit.value) + and (last_candle['close'] < last_candle['ema_200']) + and (last_candle['bb_width'] < self.sell_deadfish_bb_width.value) + and (last_candle['close'] > last_candle['bb_middleband2'] * self.sell_deadfish_bb_factor.value) + and (last_candle['volume_mean_12'] < last_candle['volume_mean_24'] * self.sell_deadfish_volume_factor.value) + and (last_candle['cmf'] < 0.0) + ): + return f"sell_stoploss_deadfish( {buy_tag})" + + ############################################################################ + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + assert self.dp, "DataProvider is required for multiple timeframes." + + # Bollinger bands (hyperopt hard to implement) + bollinger2 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) + dataframe['bb_lowerband2'] = bollinger2['lower'] + dataframe['bb_middleband2'] = bollinger2['mid'] + dataframe['bb_upperband2'] = bollinger2['upper'] + + bollinger3 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=3) + dataframe['bb_lowerband3'] = bollinger3['lower'] + dataframe['bb_middleband3'] = bollinger3['mid'] + dataframe['bb_upperband3'] = bollinger3['upper'] + + ### Other checks + + dataframe['bb_width'] = ((dataframe['bb_upperband2'] - dataframe['bb_lowerband2']) / dataframe['bb_middleband2']) + dataframe['bb_delta'] = ((dataframe['bb_lowerband2'] - dataframe['bb_lowerband3']) / dataframe['bb_lowerband2']) + dataframe['bb_bottom_cross'] = qtpylib.crossed_below(dataframe['close'], dataframe['bb_lowerband3']).astype('int') + + # CCI hyperopt + for val in self.buy_cci_length.range: + dataframe[f'cci_length_{val}'] = ta.CCI(dataframe, val) + + dataframe['cci'] = ta.CCI(dataframe, 26) + dataframe['cci_long'] = ta.CCI(dataframe, 170) + + # RMI hyperopt + for val in self.buy_rmi_length.range: + dataframe[f'rmi_length_{val}'] = RMI(dataframe, length=val, mom=4) + #dataframe['rmi'] = RMI(dataframe, length=8, mom=4) + + # SRSI hyperopt ? + stoch = ta.STOCHRSI(dataframe, 15, 20, 2, 2) + dataframe['srsi_fk'] = stoch['fastk'] + dataframe['srsi_fd'] = stoch['fastd'] + + # BinH + dataframe['closedelta'] = (dataframe['close'] - dataframe['close'].shift()).abs() + + # SMA + dataframe['sma_15'] = ta.SMA(dataframe, timeperiod=15) + dataframe['sma_30'] = ta.SMA(dataframe, timeperiod=30) + + # CTI + dataframe['cti'] = pta.cti(dataframe["close"], length=20) + + # CMF + dataframe['cmf'] = chaikin_money_flow(dataframe, 20) + + # MFI + dataframe['mfi'] = ta.MFI(dataframe) + + # EMA + dataframe['ema_8'] = ta.EMA(dataframe, timeperiod=8) + dataframe['ema_12'] = ta.EMA(dataframe, timeperiod=12) + dataframe['ema_13'] = ta.EMA(dataframe, timeperiod=13) + dataframe['ema_16'] = ta.EMA(dataframe, timeperiod=16) + dataframe['ema_24'] = ta.EMA(dataframe, timeperiod=24) + dataframe['ema_26'] = ta.EMA(dataframe, timeperiod=26) + dataframe['ema_50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['hma_50'] = qtpylib.hull_moving_average(dataframe['close'], window=50) + dataframe['ema_100'] = ta.EMA(dataframe, timeperiod=100) + dataframe['ema_200'] = ta.EMA(dataframe, timeperiod=200) + + # SMA + dataframe['sma_9'] = ta.SMA(dataframe, timeperiod=9) + dataframe['sma_15'] = ta.SMA(dataframe, timeperiod=15) + dataframe['sma_21'] = ta.SMA(dataframe, timeperiod=21) + + # VWAP + vwap_low, vwap, vwap_high = VWAPB(dataframe, 20, 1) + dataframe['vwap_upperband'] = vwap_high + dataframe['vwap_middleband'] = vwap + dataframe['vwap_lowerband'] = vwap_low + dataframe['vwap_width'] = ( (dataframe['vwap_upperband'] - dataframe['vwap_lowerband']) / dataframe['vwap_middleband'] ) * 100 + + # RSI + dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) + dataframe['rsi_fast'] = ta.RSI(dataframe, timeperiod=4) + dataframe['rsi_slow'] = ta.RSI(dataframe, timeperiod=20) + dataframe['rsi_84'] = ta.RSI(dataframe, timeperiod=84) + dataframe['rsi_112'] = ta.RSI(dataframe, timeperiod=112) + + # Elliot + dataframe['EWO'] = EWO(dataframe, 50, 200) + + # Cofi + stoch_fast = ta.STOCHF(dataframe, 5, 3, 0, 3, 0) + dataframe['fastd'] = stoch_fast['fastd'] + dataframe['fastk'] = stoch_fast['fastk'] + dataframe['adx'] = ta.ADX(dataframe) + + # Heiken Ashi + heikinashi = qtpylib.heikinashi(dataframe) + dataframe['ha_open'] = heikinashi['open'] + dataframe['ha_close'] = heikinashi['close'] + dataframe['ha_high'] = heikinashi['high'] + dataframe['ha_low'] = heikinashi['low'] + + ## BB 40 + bollinger2_40 = qtpylib.bollinger_bands(ha_typical_price(dataframe), window=40, stds=2) + dataframe['bb_lowerband2_40'] = bollinger2_40['lower'] + dataframe['bb_middleband2_40'] = bollinger2_40['mid'] + dataframe['bb_upperband2_40'] = bollinger2_40['upper'] + + # ClucHA + dataframe['bb_delta_cluc'] = (dataframe['bb_middleband2_40'] - dataframe['bb_lowerband2_40']).abs() + dataframe['ha_closedelta'] = (dataframe['ha_close'] - dataframe['ha_close'].shift()).abs() + dataframe['tail'] = (dataframe['ha_close'] - dataframe['ha_low']).abs() + dataframe['ema_slow'] = ta.EMA(dataframe['ha_close'], timeperiod=50) + dataframe['rocr'] = ta.ROCR(dataframe['ha_close'], timeperiod=28) + + # Williams %R + dataframe['r_14'] = williams_r(dataframe, period=14) + + # Volume + dataframe['volume_mean_4'] = dataframe['volume'].rolling(4).mean().shift(1) + dataframe['volume_mean_12'] = dataframe['volume'].rolling(12).mean().shift(1) + dataframe['volume_mean_24'] = dataframe['volume'].rolling(24).mean().shift(1) + + # Diff + dataframe['ema_vwap_diff_50'] = ( ( dataframe['ema_50'] - dataframe['vwap_lowerband'] ) / dataframe['ema_50'] ) + + # Dip Protection + dataframe['tpct_change_1'] = top_percent_change(dataframe, 1) + dataframe['tpct_change_2'] = top_percent_change(dataframe, 2) + dataframe['tpct_change_4'] = top_percent_change(dataframe, 4) + + # MOMDIV + mom = momdiv(dataframe) + dataframe['momdiv_buy'] = mom['momdiv_buy'] + dataframe['momdiv_sell'] = mom['momdiv_sell'] + dataframe['momdiv_coh'] = mom['momdiv_coh'] + dataframe['momdiv_col'] = mom['momdiv_col'] + + # cmf div + dataframe['cmf_div_fast'] = ( ( dataframe['cmf'].rolling(12).max() >= dataframe['cmf'] * 1.025 ) ) + dataframe['cmf_div_slow'] = ( ( dataframe['cmf'].rolling(20).max() >= dataframe['cmf'] * 1.025 ) ) + + # Modified Elder Ray Index + dataframe['moderi_96'] = moderi(dataframe, 96) + + ############################################################################ + + # 1h tf + inf_tf = '1h' + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=inf_tf) + + # Heikin Ashi + inf_heikinashi = qtpylib.heikinashi(informative) + informative['ha_close'] = inf_heikinashi['close'] + informative['rocr'] = ta.ROCR(informative['ha_close'], timeperiod=168) + + # Bollinger bands + bollinger2 = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband2'] = bollinger2['lower'] + informative['bb_middleband2'] = bollinger2['mid'] + informative['bb_upperband2'] = bollinger2['upper'] + informative['bb_width'] = ((informative['bb_upperband2'] - informative['bb_lowerband2']) / informative['bb_middleband2']) + + # RSI + informative['rsi'] = ta.RSI(informative, timeperiod=14) + informative['rsi_28'] = ta.RSI(informative, timeperiod=28) + informative['rsi_42'] = ta.RSI(informative, timeperiod=42) + + # EMA + informative['ema_20'] = ta.EMA(informative, timeperiod=20) + informative['ema_26'] = ta.EMA(informative, timeperiod=26) + informative['ema_200'] = ta.EMA(informative, timeperiod=200) + + # Williams %R + informative['r_84'] = williams_r(informative, period=84) + + # CTI + informative['cti_40'] = pta.cti(informative["close"], length=40) + + # MOMDIV + mom = momdiv(informative) + informative['momdiv_buy'] = mom['momdiv_buy'] + informative['momdiv_sell'] = mom['momdiv_sell'] + informative['momdiv_coh'] = mom['momdiv_coh'] + informative['momdiv_col'] = mom['momdiv_col'] + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, inf_tf, ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = [] + dataframe.loc[:, 'buy_tag'] = '' + + ############################################################################ + + # Utils + + is_pump_1 = ( (dataframe['close'].rolling(48).max() >= (dataframe['close'] * self.buy_pump_1_factor.value )) ) + + pump_protection_strict = ( + (dataframe['close'].rolling(48).max() >= (dataframe['close'] * 1.125 )) & + ( (dataframe['close'].rolling(288).max() >= (dataframe['close'] * 1.225 )) ) + ) + + pump_protection_loose = ( + (dataframe['close'].rolling(48).max() >= (dataframe['close'] * 1.05 )) & + ( (dataframe['close'].rolling(288).max() >= (dataframe['close'] * 1.125 )) ) + ) + + is_pump_4 = ( + (dataframe['close'].rolling(48).max() >= (dataframe['close'] * 1.075 )) & + ( (dataframe['close'].rolling(288).max() >= (dataframe['close'] * 1.17 )) ) + ) + + is_crash_1 = ( + (dataframe['tpct_change_1'] < 0.08) & + (dataframe['tpct_change_2'] < 0.08) + ) + + is_crash_2 = ( + (dataframe['tpct_change_1'] < 0.06) & + (dataframe['tpct_change_2'] < 0.06) + ) + + is_crash_3 = ( + (dataframe['tpct_change_1'] < 0.055) & + (dataframe['tpct_change_2'] < 0.055) + ) + + rsi_check = ( + (dataframe['rsi_84'] < 60) & + (dataframe['rsi_112'] < 60) + ) + + min_EWO_check = ( (dataframe['EWO'] > -5.585) ) + + max_EWO_check = ( (dataframe['EWO'] < 10.6) ) + + ############################################################################ + + if self.buy_is_dip_enabled.value: + + is_dip = ( + (dataframe[f'rmi_length_{self.buy_rmi_length.value}'] < self.buy_rmi.value) & + (dataframe[f'cci_length_{self.buy_cci_length.value}'] <= self.buy_cci.value) & + (dataframe['srsi_fk'] < self.buy_srsi_fk.value) + ) + + if self.buy_is_break_enabled.value: + + is_break = ( + + ( (dataframe['bb_delta'] > self.buy_bb_delta.value) #"buy_bb_delta": 0.025 0.036 + & #"buy_bb_width": 0.095 0.133 + (dataframe['bb_width'] > self.buy_bb_width.value) + ) + & + (dataframe['closedelta'] > dataframe['close'] * self.buy_closedelta.value / 1000 ) & # from BinH + (dataframe['close'] < dataframe['bb_lowerband3'] * self.buy_bb_factor.value) & + (is_crash_1) + ) + + is_local_uptrend = ( # from NFI next gen + + (dataframe['ema_26'] > dataframe['ema_12']) & + (dataframe['ema_26'] - dataframe['ema_12'] > dataframe['open'] * self.buy_ema_diff.value) & + (dataframe['ema_26'].shift() - dataframe['ema_12'].shift() > dataframe['open'] / 100) & + (dataframe['close'] < dataframe['bb_lowerband2'] * self.buy_bb_factor.value) & + (dataframe['closedelta'] > dataframe['close'] * self.buy_closedelta.value / 1000 ) & + (dataframe['EWO'] < 4) & + (dataframe['EWO'] > -2.5) + ) + + is_ewo = ( # from SMA offset + (dataframe['rsi_fast'] < self.buy_rsi_fast.value) & + (dataframe['close'] < dataframe['ema_8'] * self.buy_ema_low.value) & + (dataframe['EWO'] > self.buy_ewo.value) & + (dataframe['close'] < dataframe['ema_16'] * self.buy_ema_high.value) & + (dataframe['rsi'] < self.buy_rsi.value) + ) + + is_ewo_2 = ( + (dataframe['rsi_fast'] < self.buy_rsi_fast.value) & + (dataframe['close'] < dataframe['ema_8'] * self.buy_ema_low_2.value) & + (dataframe['EWO'] > self.buy_ewo_high.value) & + (dataframe['close'] < dataframe['ema_16'] * self.buy_ema_high_2.value) & + (dataframe['rsi'] < self.buy_rsi.value) & + (rsi_check) + ) + + is_vwap = ( + (dataframe['close'] < dataframe['vwap_lowerband']) & + (dataframe['vwap_width'] > self.buy_vwap_width.value) & + (dataframe['closedelta'] > dataframe['close'] * self.buy_vwap_closedelta.value / 1000 ) & + (dataframe['cti'] < self.buy_vwap_cti.value) & + (dataframe['EWO'] > 8) & + (rsi_check) & + (pump_protection_strict) + ) + + is_vwap_2 = ( + (dataframe['close'] < dataframe['vwap_lowerband']) & + (dataframe['vwap_width'] > self.buy_vwap_width_2.value) & + (dataframe['closedelta'] > dataframe['close'] * self.buy_vwap_closedelta_2.value / 1000 ) & + (dataframe['cti'] < self.buy_vwap_cti_2.value) & + (dataframe['EWO'] > 4) & + (dataframe['EWO'] < 8) & + (rsi_check) & + (pump_protection_strict) + ) + + is_vwap_3 = ( + (dataframe['close'] < dataframe['vwap_lowerband']) & + (dataframe['vwap_width'] > self.buy_vwap_width_3.value) & + (dataframe['closedelta'] > dataframe['close'] * self.buy_vwap_closedelta_3.value / 1000 ) & + (dataframe['cti'] < self.buy_vwap_cti_3.value) & + (dataframe['EWO'] < 4) & + (dataframe['EWO'] > -2.5) & + (dataframe['rsi_28_1h'] < 46) & + (rsi_check) + & + (pump_protection_loose) + ) + + is_VWAP = ( + (dataframe['close'] < dataframe['vwap_lowerband']) & + (dataframe['tpct_change_1'] > 0.04) & + (dataframe['cti'] < -0.8) & + (dataframe['rsi'] < 35) & + (rsi_check) + ) + + is_no_trend_4 = ( + (dataframe['ema_26'] > dataframe['ema_12']) & + (dataframe['ema_26'] - dataframe['ema_12'] > dataframe['open'] * self.buy_no_trend_factor_4.value) & + (dataframe['ema_26'].shift() - dataframe['ema_12'].shift() > dataframe['open'] / 100) & + (dataframe['cti'] < self.buy_no_trend_cti_4.value) & + (dataframe['r_14'] < self.buy_no_trend_r14_4.value) & + (dataframe['EWO'] < -4) & + (min_EWO_check) & + (rsi_check) + ) + + is_V_5 = ( + (dataframe['bb_width'] > self.buy_V_bb_width_5.value) & + (dataframe['cti'] < self.buy_V_cti_5.value) & + (dataframe['r_14'] < self.buy_V_r14_5.value) & + (dataframe['mfi'] < self.buy_V_mfi_5.value) & + # Really Bear, don't engage until dump over + (dataframe['ema_vwap_diff_50'] > 0.215) & + (dataframe['EWO'] < -10) & + (rsi_check) + ) + + is_insta = ( + (dataframe['bb_width_1h'] > 0.13) & + (dataframe['r_14'] < -50) & + (dataframe['r_84_1h'] < -69) & + (dataframe['cti'] < -0.84) & + (dataframe['cti_40_1h'] < -0.73) + & + ( (dataframe['close'].rolling(48).max() >= (dataframe['close'] * 1.1 )) ) + ) + + # NFI quick mode + + is_nfi_32 = ( + (dataframe['rsi_slow'] < dataframe['rsi_slow'].shift(1)) & + (dataframe['rsi_fast'] < 46) & + (dataframe['rsi'] > 19) & + (dataframe['close'] < dataframe['sma_15'] * 0.942) & + (dataframe['cti'] < -0.86) + ) + + is_nfi_33 = ( + (dataframe['close'] < (dataframe['ema_13'] * 0.978)) & + (dataframe['EWO'] > 8) & + (dataframe['cti'] < -0.88) & + (dataframe['rsi'] < 32) & + (dataframe['r_14'] < -98.0) & + (dataframe['volume'] < (dataframe['volume_mean_4'] * 2.5)) + ) + + is_nfix_39 = ( + (dataframe['ema_200'] > (dataframe['ema_200'].shift(12) * 1.01)) & + (dataframe['ema_200'] > (dataframe['ema_200'].shift(48) * 1.07)) & + (dataframe['bb_lowerband2_40'].shift().gt(0)) & + (dataframe['bb_delta_cluc'].gt(dataframe['close'] * 0.056)) & + (dataframe['closedelta'].gt(dataframe['close'] * 0.01)) & + (dataframe['tail'].lt(dataframe['bb_delta_cluc'] * 0.5)) & + (dataframe['close'].lt(dataframe['bb_lowerband2_40'].shift())) & + (dataframe['close'].le(dataframe['close'].shift())) & + (dataframe['close'] > dataframe['ema_50'] * 0.912) + ) + + is_nfix_201 = ( + (dataframe['rsi_slow'] < dataframe['rsi_slow'].shift()) & + (dataframe['rsi_fast'] < 30.0) & + (dataframe['ema_20_1h'] > dataframe['ema_26_1h']) & + (dataframe['close'] < dataframe['sma_15'] * 0.953) & + (dataframe['cti'] < -0.82) & + (dataframe['cci'] < -210.0) + & + (is_pump_1) & + (rsi_check) + ) + + is_nfi7_33 = ( + (dataframe['moderi_96']) & + (dataframe['cti'] < -0.88) & + (dataframe['close'] < (dataframe['ema_13'] * 0.988)) & + (dataframe['EWO'] > 6.4) & + (dataframe['rsi'] < 32.0) & + (dataframe['volume'] < (dataframe['volume_mean_4'] * 2.0)) + & + (pump_protection_loose) & + (rsi_check) + ) + + is_nfi_sma_3 = ( + (dataframe['bb_lowerband2_40'].shift() > 0) & + (dataframe['bb_delta_cluc'] > dataframe['close'] * 0.059) & + (dataframe['ha_closedelta'] > dataframe['close'] * 0.023) & + (dataframe['tail'] < dataframe['bb_delta_cluc'] * 0.418) & + (dataframe['close'] < dataframe['bb_lowerband2_40'].shift()) & + (dataframe['close'] < dataframe['close'].shift()) + ) + + is_BB_checked = is_dip & is_break + + ## condition append + conditions.append(is_BB_checked) # P + dataframe.loc[is_BB_checked, 'buy_tag'] += 'bb ' + + conditions.append(is_local_uptrend) + dataframe.loc[is_local_uptrend, 'buy_tag'] += 'local_uptrend ' + + conditions.append(is_ewo) + dataframe.loc[is_ewo, 'buy_tag'] += 'ewo ' + + conditions.append(is_ewo_2) + dataframe.loc[is_ewo_2, 'buy_tag'] += 'ewo2 ' + + conditions.append(is_no_trend_4) + dataframe.loc[is_no_trend_4, 'buy_tag'] += 'no_trend_4 ' + + conditions.append(is_vwap) + dataframe.loc[is_vwap, 'buy_tag'] += 'vwap ' + + conditions.append(is_vwap_2) + dataframe.loc[is_vwap_2, 'buy_tag'] += 'vwap_2 ' + + conditions.append(is_vwap_3) + dataframe.loc[is_vwap_3, 'buy_tag'] += 'vwap_3 ' + + conditions.append(is_VWAP) + dataframe.loc[is_VWAP, 'buy_tag'] += 'VWAP ' + + conditions.append(is_insta) + dataframe.loc[is_insta, 'buy_tag'] += 'insta ' + + # NFI + conditions.append(is_nfi_32) + dataframe.loc[is_nfi_32, 'buy_tag'] += 'nfi_32 ' + + conditions.append(is_nfi_33) + dataframe.loc[is_nfi_33, 'buy_tag'] += 'nfi_33 ' + + conditions.append(is_nfix_39) + dataframe.loc[is_nfix_39, 'buy_tag'] += 'x_39 ' + + conditions.append(is_nfix_201) + dataframe.loc[is_nfix_201, 'buy_tag'] += 'x_201 ' + + conditions.append(is_nfi7_33) + dataframe.loc[is_nfi7_33, 'buy_tag'] += '7_33 ' + + conditions.append(is_nfi_sma_3) + dataframe.loc[is_nfi_sma_3, 'buy_tag'] += 'sma_3 ' + + # Very Bear + conditions.append(is_V_5) + dataframe.loc[is_V_5, 'buy_tag'] += 'V_5 ' + + if conditions: + dataframe.loc[reduce(lambda x, y: x | y, conditions), 'buy' ] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ (dataframe['volume'] > 0), 'sell' ] = 0 + + return dataframe + +# Chaikin Money Flow +def chaikin_money_flow(dataframe, n=20, fillna=False) -> Series: + """Chaikin Money Flow (CMF) + It measures the amount of Money Flow Volume over a specific period. + http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:chaikin_money_flow_cmf + Args: + dataframe(pandas.Dataframe): dataframe containing ohlcv + n(int): n period. + fillna(bool): if fill nan values. + Returns: + pandas.Series: New feature generated. + """ + mfv = ((dataframe['close'] - dataframe['low']) - (dataframe['high'] - dataframe['close'])) / (dataframe['high'] - dataframe['low']) + mfv = mfv.fillna(0.0) # float division by zero + mfv *= dataframe['volume'] + cmf = (mfv.rolling(n, min_periods=0).sum() + / dataframe['volume'].rolling(n, min_periods=0).sum()) + if fillna: + cmf = cmf.replace([np.inf, -np.inf], np.nan).fillna(0) + return Series(cmf, name='cmf') + +# Mom DIV +def momdiv(dataframe: DataFrame, mom_length: int = 10, bb_length: int = 20, bb_dev: float = 2.0, lookback: int = 30) -> DataFrame: + mom: Series = ta.MOM(dataframe, timeperiod=mom_length) + upperband, middleband, lowerband = ta.BBANDS(mom, timeperiod=bb_length, nbdevup=bb_dev, nbdevdn=bb_dev, matype=0) + buy = qtpylib.crossed_below(mom, lowerband) + sell = qtpylib.crossed_above(mom, upperband) + hh = dataframe['high'].rolling(lookback).max() + ll = dataframe['low'].rolling(lookback).min() + coh = dataframe['high'] >= hh + col = dataframe['low'] <= ll + df = DataFrame({ + "momdiv_mom": mom, + "momdiv_upperb": upperband, + "momdiv_lowerb": lowerband, + "momdiv_buy": buy, + "momdiv_sell": sell, + "momdiv_coh": coh, + "momdiv_col": col, + }, index=dataframe['close'].index) + return df + +class BB_RTR_dca (BB_RTR): + + # DCA options + position_adjustment_enable = True + + initial_safety_order_trigger = -0.08 + max_safety_orders = 2 + safety_order_step_scale = 0.5 #SS + safety_order_volume_scale = 1.6 #OS + + # Auto compound calculation + max_dca_multiplier = (1 + max_safety_orders) + if (max_safety_orders > 0): + if (safety_order_volume_scale > 1): + max_dca_multiplier = (2 + (safety_order_volume_scale * (math.pow(safety_order_volume_scale, (max_safety_orders - 1)) - 1) / (safety_order_volume_scale - 1))) + elif (safety_order_volume_scale < 1): + max_dca_multiplier = (2 + (safety_order_volume_scale * (1 - math.pow(safety_order_volume_scale, (max_safety_orders - 1))) / (1 - safety_order_volume_scale))) + + # Let unlimited stakes leave funds open for DCA orders + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + if self.config['stake_amount'] == 'unlimited': + return proposed_stake / self.max_dca_multiplier + + return proposed_stake + + # DCA + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + + if current_profit > self.initial_safety_order_trigger: + return None + + count_of_buys = trade.nr_of_successful_buys + + if 1 <= count_of_buys <= self.max_safety_orders: + safety_order_trigger = (abs(self.initial_safety_order_trigger) * count_of_buys) + if (self.safety_order_step_scale > 1): + safety_order_trigger = abs(self.initial_safety_order_trigger) + (abs(self.initial_safety_order_trigger) * self.safety_order_step_scale * (math.pow(self.safety_order_step_scale,(count_of_buys - 1)) - 1) / (self.safety_order_step_scale - 1)) + elif (self.safety_order_step_scale < 1): + safety_order_trigger = abs(self.initial_safety_order_trigger) + (abs(self.initial_safety_order_trigger) * self.safety_order_step_scale * (1 - math.pow(self.safety_order_step_scale,(count_of_buys - 1))) / (1 - self.safety_order_step_scale)) + + if current_profit <= (-1 * abs(safety_order_trigger)): + try: + stake_amount = self.wallets.get_trade_stake_amount(trade.pair, None) + # This calculates base order size + stake_amount = stake_amount / self.max_dca_multiplier + # This then calculates current safety order size + stake_amount = stake_amount * math.pow(self.safety_order_volume_scale, (count_of_buys - 1)) + amount = stake_amount / current_rate + logger.info(f"Initiating safety order buy #{count_of_buys} for {trade.pair} with stake amount of {stake_amount} which equals {amount}") + return stake_amount + except Exception as exception: + logger.info(f'Error occured while trying to get stake amount for {trade.pair}: {str(exception)}') + return None + + return None diff --git a/BinHV45.py b/BinHV45.py new file mode 100644 index 0000000..c007a7c --- /dev/null +++ b/BinHV45.py @@ -0,0 +1,71 @@ +# --- Do not remove these libs --- +from freqtrade.strategy import IStrategy +from freqtrade.strategy import IntParameter +from pandas import DataFrame +import numpy as np +# -------------------------------- + +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +def bollinger_bands(stock_price, window_size, num_of_std): + rolling_mean = stock_price.rolling(window=window_size).mean() + rolling_std = stock_price.rolling(window=window_size).std() + lower_band = rolling_mean - (rolling_std * num_of_std) + + return rolling_mean, lower_band + + +class BinHV45(IStrategy): + INTERFACE_VERSION = 2 + + minimal_roi = { + "0": 0.0125 + } + + stoploss = -0.05 + timeframe = '1m' + + buy_bbdelta = IntParameter(low=1, high=15, default=30, space='buy', optimize=True) + buy_closedelta = IntParameter(low=15, high=20, default=30, space='buy', optimize=True) + buy_tail = IntParameter(low=20, high=30, default=30, space='buy', optimize=True) + + # Hyperopt parameters + buy_params = { + "buy_bbdelta": 7, + "buy_closedelta": 17, + "buy_tail": 25, + } + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + bollinger = qtpylib.bollinger_bands(dataframe['close'], window=40, stds=2) + + dataframe['upper'] = bollinger['upper'] + dataframe['mid'] = bollinger['mid'] + dataframe['lower'] = bollinger['lower'] + dataframe['bbdelta'] = (dataframe['mid'] - dataframe['lower']).abs() + dataframe['pricedelta'] = (dataframe['open'] - dataframe['close']).abs() + dataframe['closedelta'] = (dataframe['close'] - dataframe['close'].shift()).abs() + dataframe['tail'] = (dataframe['close'] - dataframe['low']).abs() + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + dataframe['lower'].shift().gt(0) & + dataframe['bbdelta'].gt(dataframe['close'] * self.buy_bbdelta.value / 1000) & + dataframe['closedelta'].gt(dataframe['close'] * self.buy_closedelta.value / 1000) & + dataframe['tail'].lt(dataframe['bbdelta'] * self.buy_tail.value / 1000) & + dataframe['close'].lt(dataframe['lower'].shift()) & + dataframe['close'].le(dataframe['close'].shift()) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + no sell signal + """ + dataframe.loc[:, 'sell'] = 0 + return dataframe diff --git a/BreakEven.py b/BreakEven.py new file mode 100644 index 0000000..b405890 --- /dev/null +++ b/BreakEven.py @@ -0,0 +1,67 @@ +# --- Do not remove these libs --- +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame +# -------------------------------- + + +class BreakEven(IStrategy): + """ + author@: lenik + + Sometimes I want to close the bot ASAP, but not have the positions floating around. + + I can "/stopbuy" and wait for the positions to get closed by the bot rules, which is + waiting for some profit, etc -- this usually takes too long... + + What I would prefer is to close everything that is over 0% profit to avoid the losses. + + Here's a simple strategy with empty buy/sell signals and "minimal_roi = { 0 : 0 }" that + sells everything already at profit and wait until the positions at loss will come to break + even point (or the small profit you provide in ROI table). + + You may restart the bot with the new strategy as a command-line parameter. + + Another way would be to specify the original strategy in the config file, then change to + this one and simply "/reload_config" from the Telegram bot. + + """ + + # This attribute will be overridden if the config file contains "minimal_roi" + minimal_roi = { + "0": 0.01, # at least 1% at first + "10": 0 # after 10min, everything goes + } + + # This is more radical version that sells everything above the profit level +# minimal_roi = { +# "0": 0 +# } + + # And this is basically "/forcesell all", that sells no matter what profit +# minimal_roi = { +# "0": -1 +# } + + # Optimal stoploss designed for the strategy + stoploss = -0.05 + + # Optimal timeframe for the strategy + timeframe = '5m' + + # don't generate any buy or sell signals, everything is handled by ROI and stop_loss + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + ), + 'buy'] = 0 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + ), + 'sell'] = 0 + return dataframe diff --git a/DevilStra.json b/DevilStra.json new file mode 100644 index 0000000..774499a --- /dev/null +++ b/DevilStra.json @@ -0,0 +1,29 @@ +{ + "strategy_name": "DevilStra", + "params": { + "roi": { + "0": 0.574, + "1757": 0.158, + "3804": 0.089, + "6585": 0 + }, + "stoploss": { + "stoploss": -0.28 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": null, + "trailing_stop_positive_offset": 0.0, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_spell": "Gu,Ra,Si,Gu,Pa,De,Si,La,Si,De,Cu,Lu,Pa,Si,De,La,Gu,La,Cu,Cu,Zi,Si,Gu,Lu,Pa,Lu,Ra,La,Si,Si,Ra,Lu,Pa,Ra,De,Zi,Ra,Si,De,La,La,Lu,Gu,Lu,Cu,Ra,Cu,Cu,La,Lu,De,Gu,Si,Cu,Cu,La,Si,Ra,Cu,Zi,La,Gu,De,Zi,De,Gu,Pa,Si,Cu,Lu,Gu,Si,Gu,Lu,Ra,De,Lu,Pa,Pa,Si,Zi,Pa,Cu,Ra,Gu,Ra,De,Cu,Lu,De,Pa,Lu,De,Lu,Pa,Gu,Cu,De,La,Ra,Pa,Gu,Lu,Zi,La,Ra,Cu,Si,De,Ra,La,Zi,Lu,Gu,De,Si,Ra,Pa,Si,De,De,La,La,Lu,Cu,La,Lu,Pa,Pa,Pa,De,Lu,La,Zi,Si,Pa,Pa,La,Si,Cu,Ra,Ra,Gu,Zi,Si,Si,La,La,Lu,La,La,Lu,Pa,Lu,Pa,Cu,Lu,Lu,Si,Zi,Lu,Si,Gu,La,La,La,Pa,Ra,Ra,Cu,De,De,Si,De,De,Ra,Pa,Lu,Zi,Cu,Zi,Gu,Zi,Pa,Si,Gu,Zi,La,Ra,La,Zi,Si,Zi,Si,La,La,Zi,Pa,La,Pa,La,Lu,Pa,Si,Zi,Gu,Zi,De,Zi,Ra,Pa,Cu,De,Cu,Ra,Gu,Gu,Zi,Gu,Zi,Cu,Lu,Gu,Zi,Cu,Pa,Gu,Si,Zi,La,Ra,Lu,Pa,Gu,Si,Zi,La,Ra,Pa,Ra,Cu,Cu,Zi,Cu,Gu,De,Lu,De,De,Ra,Cu,Gu,De,Ra,Si,Pa,La,Si,La,Zi,Lu,Zi,Cu,Zi,La,De,Lu,Cu,Zi" + }, + "sell": { + "sell_spell": "La,Pa,De,De,La,Si,Si,La,La,La,Si,Pa,Pa,Lu,De,Cu,Cu,Gu,Lu,Ra,Lu,Si,Ra,De,La,Cu,La,La,Gu,La,De,Ra,Ra,Ra,Gu,Lu,Si,Si,Zi,Zi,La,Pa,Pa,Zi,Cu,Gu,Gu,Pa,Gu,Cu,Si,Ra,Ra,La,Gu,De,Si,La,Ra,Pa,Si,Lu,Pa,De,Zi,De,Lu,Si,Gu,De,Lu,De,Ra,Ra,Zi,De,Cu,Zi,Gu,Pa,Ra,De,Pa,De,Pa,Ra,Si,Si,Zi,Cu,Lu,Zi,Ra,De,Ra,Zi,Zi,Pa,Lu,Zi,Cu,Pa,Gu,Pa,Cu,De,Zi,De,De,Pa,Pa,Zi,Lu,Ra,Pa,Ra,Lu,Zi,Gu,Zi,Si,Lu,Ra,Ra,Zi,Lu,Pa,Lu,Si,Pa,Pa,Pa,Si,Zi,La,La,Lu,De,Zi,Gu,Ra,Ra,Ra,Zi,Pa,Zi,Cu,Lu,Gu,Cu,De,Lu,Gu,Lu,Gu,Si,Pa,Pa,Si,La,Gu,Ra,Pa,Si,Si,Si,Cu,Cu,Cu,Si,De,Lu,Gu,Gu,Lu,De,Ra,Gu,Gu,Gu,Cu,La,De,Cu,Zi,Pa,Si,De,Pa,Pa,Pa,La,De,Gu,Zi,La,De,Cu,La,Pa,Ra,Si,Si,Zi,Cu,Ra,Pa,Gu,Pa,Ra,Zi,De,Zi,Gu,Gu,Pa,Cu,Lu,Gu,De,Si,Pa,La,Cu,Zi,Gu,De,Gu,La,Cu,Gu,De,Cu,Cu,Gu,Ra,Lu,Zi,De,La,Ra,Pa,Pa,Si,La,Lu,La,De,De,Ra,De,La,La,Pa,Cu,Lu,Pa,Ra,Pa,Pa,Cu,Zi,Gu,Cu,Gu,La,Si,Ra,Pa" + }, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-04 11:36:44.627014+00:00" +} \ No newline at end of file diff --git a/DevilStra.py b/DevilStra.py new file mode 100644 index 0000000..c96782a --- /dev/null +++ b/DevilStra.py @@ -0,0 +1,721 @@ +# DevilStra Strategy +# 𝔇𝔢𝔳𝔦𝔩 𝔦𝔰 𝔞𝔩𝔴𝔞𝔶𝔰 𝔰𝔱𝔯𝔬𝔫𝔤𝔢𝔯 𝔱𝔥𝔞𝔫 𝔊𝔬𝔡. +# 𝔅𝔲𝔱 𝔱𝔥𝔢 𝔬𝔫𝔩𝔶 𝔬𝔫𝔢 𝔴𝔥𝔬 𝔥𝔞𝔰 𝔱𝔥𝔢 𝔞𝔟𝔦𝔩𝔦𝔱𝔶 +# 𝔗𝔬 𝔠𝔯𝔢𝔞𝔱𝔢 𝔫𝔢𝔴 𝔠𝔯𝔢𝔞𝔱𝔲𝔯𝔢𝔰 𝔦𝔰 𝔊𝔬𝔡. +# 𝔄𝔫𝔡 𝔱𝔥𝔢 𝔇𝔢𝔳𝔦𝔩 𝔪𝔞𝔨𝔢𝔰 𝔭𝔬𝔴𝔢𝔯𝔣𝔲𝔩 𝔰𝔭𝔢𝔩𝔩𝔰 +# 𝔉𝔯𝔬𝔪 𝔱𝔥𝔦𝔰 𝔰𝔪𝔞𝔩𝔩 𝔠𝔯𝔢𝔞𝔱𝔲𝔯𝔢𝔰 (𝔩𝔦𝔨𝔢 𝔣𝔯𝔬𝔤𝔰, 𝔢𝔱𝔠.) +# 𝔚𝔦𝔱𝔥 𝔣𝔯𝔞𝔤𝔪𝔢𝔫𝔱𝔞𝔱𝔦𝔬𝔫 𝔞𝔫𝔡 𝔪𝔦𝔵𝔦𝔫𝔤 𝔱𝔥𝔢𝔪. +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# * IMPORTANT: You Need An "STATIC" Pairlist On Your Config.json ! +# * IMPORTANT: First set PAIR_LIST_LENGHT={pair_whitelist size} +# * And re-hyperopt the Sell strategy And paste result in exact +# * place(lines 535~564) + +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell -s 𝕯𝖊𝖛𝖎𝖑𝕾𝖙𝖗𝖆 + +# --- Do not remove these libs --- +import numpy as np +from functools import reduce +import freqtrade.vendor.qtpylib.indicators as qtpylib +import talib.abstract as ta +import random +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame + +# ########################## SETTINGS ############################## +# pairlist lenght(use exact count of pairs you used in whitelist size+1): +PAIR_LIST_LENGHT = 269 +# you can find exact value of this inside GodStraNew +TREND_CHECK_CANDLES = 4 +# Set the pain range of devil(2~9999) +PAIN_RANGE = 1000 +# Add "GodStraNew" Generated Results As spells inside SPELLS. +# Set them unic phonemes like 'Zi' 'Gu' or 'Lu'! +# * Use below replacement on GodStraNew results to +# * Change God Generated Creatures to Spells: +# +-----------------------------+----------------------+ +# | GodStraNew Hyperopt Results | DevilStra Spells | +# +-----------------------------+----------------------+ +# | | "phonem" : { | +# | buy_params = { | "buy_params" : { | +# | ... | ... | +# | } | }, | +# | sell_params = { | "sell_params" : { | +# | ... | ... | +# | } | } | +# | | }, | +# +-----------------------------+----------------------+ +SPELLS = { + "Zi": { + "buy_params": { + "buy_crossed_indicator0": "BOP-4", + "buy_crossed_indicator1": "MACD-0-50", + "buy_crossed_indicator2": "DEMA-52", + "buy_indicator0": "MINUS_DI-50", + "buy_indicator1": "HT_TRENDMODE-50", + "buy_indicator2": "CORREL-128", + "buy_operator0": "/>R", + "buy_operator1": "CA", + "buy_operator2": "CDT", + "buy_real_num0": 0.1763, + "buy_real_num1": 0.6891, + "buy_real_num2": 0.0509, + }, + "sell_params": { + "sell_crossed_indicator0": "WCLPRICE-52", + "sell_crossed_indicator1": "AROONOSC-15", + "sell_crossed_indicator2": "CDLRISEFALL3METHODS-52", + "sell_indicator0": "COS-50", + "sell_indicator1": "CDLCLOSINGMARUBOZU-30", + "sell_indicator2": "CDL2CROWS-130", + "sell_operator0": "DT", + "sell_operator1": ">R", + "sell_operator2": "/>R", + "sell_real_num0": 0.0678, + "sell_real_num1": 0.8698, + "sell_real_num2": 0.3917, + } + }, + "Gu": { + "buy_params": { + "buy_crossed_indicator0": "SMA-20", + "buy_crossed_indicator1": "CDLLADDERBOTTOM-20", + "buy_crossed_indicator2": "OBV-50", + "buy_indicator0": "MAMA-1-50", + "buy_indicator1": "SUM-40", + "buy_indicator2": "VAR-30", + "buy_operator0": "R", + "sell_operator2": "CUT", + "sell_real_num0": 0.2707, + "sell_real_num1": 0.7987, + "sell_real_num2": 0.6891, + } + }, + "Lu": { + "buy_params": { + "buy_crossed_indicator0": "HT_SINE-0-28", + "buy_crossed_indicator1": "ADD-130", + "buy_crossed_indicator2": "ADD-12", + "buy_indicator0": "ADD-28", + "buy_indicator1": "AVGPRICE-15", + "buy_indicator2": "AVGPRICE-12", + "buy_operator0": "DT", + "buy_operator1": "D", + "buy_operator2": "C", + "buy_real_num0": 0.3676, + "buy_real_num1": 0.4284, + "buy_real_num2": 0.372, + }, + "sell_params": { + "sell_crossed_indicator0": "HT_SINE-0-5", + "sell_crossed_indicator1": "HT_SINE-0-4", + "sell_crossed_indicator2": "HT_SINE-0-28", + "sell_indicator0": "ADD-30", + "sell_indicator1": "AVGPRICE-28", + "sell_indicator2": "ADD-50", + "sell_operator0": "CUT", + "sell_operator1": "DT", + "sell_operator2": "=R", + "sell_real_num0": 0.3205, + "sell_real_num1": 0.2055, + "sell_real_num2": 0.8467, + } + }, + "La": { + "buy_params": { + "buy_crossed_indicator0": "WMA-14", + "buy_crossed_indicator1": "MAMA-1-14", + "buy_crossed_indicator2": "CDLHIKKAKE-14", + "buy_indicator0": "T3-14", + "buy_indicator1": "BETA-14", + "buy_indicator2": "HT_PHASOR-1-14", + "buy_operator0": "/>R", + "buy_operator1": ">", + "buy_operator2": ">R", + "buy_real_num0": 0.0551, + "buy_real_num1": 0.3469, + "buy_real_num2": 0.3871, + }, + "sell_params": { + "sell_crossed_indicator0": "HT_TRENDLINE-14", + "sell_crossed_indicator1": "LINEARREG-14", + "sell_crossed_indicator2": "STOCHRSI-1-14", + "sell_indicator0": "CDLDARKCLOUDCOVER-14", + "sell_indicator1": "AD-14", + "sell_indicator2": "CDLSTALLEDPATTERN-14", + "sell_operator0": "/=R", + "sell_operator1": "COT", + "sell_operator2": "OT", + "sell_real_num0": 0.3992, + "sell_real_num1": 0.7747, + "sell_real_num2": 0.7415, + } + }, + "Si": { + "buy_params": { + "buy_crossed_indicator0": "MACDEXT-2-14", + "buy_crossed_indicator1": "CORREL-14", + "buy_crossed_indicator2": "CMO-14", + "buy_indicator0": "MA-14", + "buy_indicator1": "ADXR-14", + "buy_indicator2": "CDLMARUBOZU-14", + "buy_operator0": "<", + "buy_operator1": "/", + "buy_operator2": "CA", + "buy_real_num0": 0.2208, + "buy_real_num1": 0.1371, + "buy_real_num2": 0.6389, + }, + "sell_params": { + "sell_crossed_indicator0": "MACDEXT-0-15", + "sell_crossed_indicator1": "BBANDS-2-15", + "sell_crossed_indicator2": "DEMA-15", + "sell_indicator0": "ULTOSC-15", + "sell_indicator1": "MIDPOINT-12", + "sell_indicator2": "PLUS_DI-12", + "sell_operator0": "<", + "sell_operator1": "DT", + "sell_operator2": "COT", + "sell_real_num0": 0.278, + "sell_real_num1": 0.0643, + "sell_real_num2": 0.7065, + } + }, + "De": { + "buy_params": { + "buy_crossed_indicator0": "HT_DCPERIOD-12", + "buy_crossed_indicator1": "HT_PHASOR-0-12", + "buy_crossed_indicator2": "MACDFIX-1-15", + "buy_indicator0": "CMO-12", + "buy_indicator1": "TRIMA-12", + "buy_indicator2": "MACDEXT-0-15", + "buy_operator0": "<", + "buy_operator1": "D", + "buy_operator2": "<", + "buy_real_num0": 0.3924, + "buy_real_num1": 0.5546, + "buy_real_num2": 0.7648, + }, + "sell_params": { + "sell_crossed_indicator0": "MACDFIX-1-15", + "sell_crossed_indicator1": "MACD-1-15", + "sell_crossed_indicator2": "WMA-15", + "sell_indicator0": "ROC-15", + "sell_indicator1": "MACD-2-15", + "sell_indicator2": "CCI-60", + "sell_operator0": "CA", + "sell_operator1": " 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator( + dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class DevilStra(IStrategy): + # #################### RESULT PASTE PLACE #################### + # 16/16: 108 trades. 75/18/15 Wins/Draws/Losses. Avg profit 7.77%. Median profit 8.89%. Total profit 0.08404983 BTC ( 84.05Σ%). Avg duration 3 days, 6:49:00 min. Objective: -11.22849 + + # Buy hyperspace params: + buy_params = { + "buy_spell": "Zi,Lu,Ra,Ra,La,Si,Pa,Si,Cu,La,De,Lu,De,La,Zi,Zi,Zi,Zi,Zi,Lu,Lu,Lu,Si,La,Ra,Pa,La,Zi,Zi,Gu,Ra,De,Gu,Zi,Ra,Ra,Ra,Cu,Pa,De,De,La,Lu,Lu,Lu,La,Zi,Cu,Ra,Gu,Pa,La,Zi,Zi,Si,Lu,Ra,Cu,Cu,Pa,Si,Gu,De,De,Lu,Gu,Zi,Pa,Lu,Pa,Ra,Gu,Cu,La,Pa,Lu,Zi,La,Zi,Gu,Zi,De,Cu,Ra,Lu,Ra,Gu,Si,Ra,La,La,Lu,Gu,Zi,Si,La,Pa,Pa,Cu,Cu,Zi,Gu,Pa,Zi,Pa,Cu,Lu,Pa,Si,De,Gu,Lu,Lu,Cu,Ra,Si,Pa,Gu,Si,Cu,Pa,Zi,Pa,Zi,Gu,Lu,Ra,Pa,Ra,De,Ra,Pa,Zi,La,Pa,De,Pa,Cu,Gu,De,Lu,La,Ra,Zi,Si,Zi,Zi,Cu,Cu,De,Pa,Pa,Zi,De,Ra,La,Lu,De,Lu,Gu,Cu,Cu,La,De,Gu,Lu,Ra,Pa,Lu,Cu,Pa,Pa,De,Si,Zi,Cu,De,De,De,Lu,Si,Zi,Gu,Si,Si,Ra,Pa,Si,La,La,Lu,Lu,De,Gu,Gu,Zi,Ra,La,Lu,Lu,La,Si,Zi,Si,Zi,Si,Lu,Cu,Zi,Lu,De,La,Ra,Ra,Lu,De,Pa,Zi,Gu,Cu,Zi,Pa,De,Si,Lu,De,Cu,De,Zi,Ra,Gu,De,Si,Lu,Lu,Ra,De,Gu,Cu,Gu,La,De,Lu,Lu,Si,Cu,Lu,Zi,Lu,Cu,Gu,Lu,Lu,Ra,Si,Ra,Pa,Lu,De,Ra,Zi,Gu,Gu,Zi,Lu,Cu,Cu,Cu,Lu", + } + + # Sell hyperspace params: + sell_params = { + "sell_spell": "La,Pa,De,De,La,Si,Si,La,La,La,Si,Pa,Pa,Lu,De,Cu,Cu,Gu,Lu,Ra,Lu,Si,Ra,De,La,Cu,La,La,Gu,La,De,Ra,Ra,Ra,Gu,Lu,Si,Si,Zi,Zi,La,Pa,Pa,Zi,Cu,Gu,Gu,Pa,Gu,Cu,Si,Ra,Ra,La,Gu,De,Si,La,Ra,Pa,Si,Lu,Pa,De,Zi,De,Lu,Si,Gu,De,Lu,De,Ra,Ra,Zi,De,Cu,Zi,Gu,Pa,Ra,De,Pa,De,Pa,Ra,Si,Si,Zi,Cu,Lu,Zi,Ra,De,Ra,Zi,Zi,Pa,Lu,Zi,Cu,Pa,Gu,Pa,Cu,De,Zi,De,De,Pa,Pa,Zi,Lu,Ra,Pa,Ra,Lu,Zi,Gu,Zi,Si,Lu,Ra,Ra,Zi,Lu,Pa,Lu,Si,Pa,Pa,Pa,Si,Zi,La,La,Lu,De,Zi,Gu,Ra,Ra,Ra,Zi,Pa,Zi,Cu,Lu,Gu,Cu,De,Lu,Gu,Lu,Gu,Si,Pa,Pa,Si,La,Gu,Ra,Pa,Si,Si,Si,Cu,Cu,Cu,Si,De,Lu,Gu,Gu,Lu,De,Ra,Gu,Gu,Gu,Cu,La,De,Cu,Zi,Pa,Si,De,Pa,Pa,Pa,La,De,Gu,Zi,La,De,Cu,La,Pa,Ra,Si,Si,Zi,Cu,Ra,Pa,Gu,Pa,Ra,Zi,De,Zi,Gu,Gu,Pa,Cu,Lu,Gu,De,Si,Pa,La,Cu,Zi,Gu,De,Gu,La,Cu,Gu,De,Cu,Cu,Gu,Ra,Lu,Zi,De,La,Ra,Pa,Pa,Si,La,Lu,La,De,De,Ra,De,La,La,Pa,Cu,Lu,Pa,Ra,Pa,Pa,Cu,Zi,Gu,Cu,Gu,La,Si,Ra,Pa", + } + + # ROI table: + minimal_roi = { + "0": 0.574, + "1757": 0.158, + "3804": 0.089, + "6585": 0 + } + + # Stoploss: + stoploss = -0.28 + # #################### END OF RESULT PLACE #################### + + # 𝖂𝖔𝖗𝖘𝖙, 𝖀𝖓𝖎𝖉𝖊𝖆𝖑, 𝕾𝖚𝖇𝖔𝖕𝖙𝖎𝖒𝖆𝖑, 𝕸𝖆𝖑𝖆𝖕𝖗𝖔𝖕𝖔𝖘 𝕬𝖓𝖉 𝕯𝖎𝖘𝖒𝖆𝖑 𝖙𝖎𝖒𝖊𝖋𝖗𝖆𝖒𝖊 𝖋𝖔𝖗 𝖙𝖍𝖎𝖘 𝖘𝖙𝖗𝖆𝖙𝖊𝖌𝖞: + timeframe = '4h' + + spell_pot = [ + ",".join( + tuple( + random.choices( + list(SPELLS.keys()), + # TODO: k will be change to len(pairlist) + k=PAIR_LIST_LENGHT + ) + ) + )for i in range(PAIN_RANGE) + ] + + buy_spell = CategoricalParameter( + spell_pot, default=spell_pot[0], space='buy') + sell_spell = CategoricalParameter( + spell_pot, default=spell_pot[0], space='sell') + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + pairs = self.dp.current_whitelist() + pairs_len = len(pairs) + pair_index = pairs.index(metadata['pair']) + + buy_spells = self.buy_spell.value.split(",") + buy_spells_len = len(buy_spells) + + if pairs_len > buy_spells_len: + print( + f"First set PAIR_LIST_LENGHT={pairs_len + 1} And re-hyperopt the") + print("Buy strategy And paste result in exact place(lines 535~564)") + print("IMPORTANT: You Need An 'STATIC' Pairlist On Your Config.json !!!") + exit() + + buy_params_index = buy_spells[pair_index] + + params = spell_finder(buy_params_index, 'buy') + conditions = list() + # TODO: Its not dry code! + buy_indicator = params['buy_indicator0'] + buy_crossed_indicator = params['buy_crossed_indicator0'] + buy_operator = params['buy_operator0'] + buy_real_num = params['buy_real_num0'] + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + # backup + buy_indicator = params['buy_indicator1'] + buy_crossed_indicator = params['buy_crossed_indicator1'] + buy_operator = params['buy_operator1'] + buy_real_num = params['buy_real_num1'] + + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + buy_indicator = params['buy_indicator2'] + buy_crossed_indicator = params['buy_crossed_indicator2'] + buy_operator = params['buy_operator2'] + buy_real_num = params['buy_real_num2'] + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'buy']=1 + + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + pairs = self.dp.current_whitelist() + pairs_len = len(pairs) + pair_index = pairs.index(metadata['pair']) + + sell_spells = self.sell_spell.value.split(",") + sell_spells_len = len(sell_spells) + + if pairs_len > sell_spells_len: + print( + f"First set PAIR_LIST_LENGHT={pairs_len + 1} And re-hyperopt the") + print("Sell strategy And paste result in exact place(lines 535~564)") + print("IMPORTANT: You Need An 'STATIC' Pairlist On Your Config.json !!!") + exit() + + sell_params_index = sell_spells[pair_index] + + params = spell_finder(sell_params_index, 'sell') + + conditions = list() + # TODO: Its not dry code! + sell_indicator = params['sell_indicator0'] + sell_crossed_indicator = params['sell_crossed_indicator0'] + sell_operator = params['sell_operator0'] + sell_real_num = params['sell_real_num0'] + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = params['sell_indicator1'] + sell_crossed_indicator = params['sell_crossed_indicator1'] + sell_operator = params['sell_operator1'] + sell_real_num = params['sell_real_num1'] + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = params['sell_indicator2'] + sell_crossed_indicator = params['sell_crossed_indicator2'] + sell_operator = params['sell_operator2'] + sell_real_num = params['sell_real_num2'] + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'sell']=1 + return dataframe diff --git a/DevilStra.txt b/DevilStra.txt new file mode 100644 index 0000000..a05b101 --- /dev/null +++ b/DevilStra.txt @@ -0,0 +1,76 @@ +Result for strategy DevilStra +============================================================ BACKTESTING REPORT =========================================================== +| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | Avg Duration | Win Draw Loss Win% | +|-----------+--------+----------------+----------------+-------------------+----------------+-------------------+-------------------------| +| SAND/USDT | 8 | 231.37 | 1850.99 | 1852.839 | 185.28 | 39 days, 21:07:00 | 6 1 1 75.0 | +| SOL/USDT | 135 | 4.65 | 628.37 | 628.998 | 62.90 | 2 days, 6:16:00 | 83 7 45 61.5 | +| AVAX/USDT | 84 | 5.75 | 483.08 | 483.565 | 48.36 | 3 days, 15:04:00 | 63 6 15 75.0 | +| GALA/USDT | 13 | 14.36 | 186.68 | 186.864 | 18.69 | 4 days, 22:32:00 | 9 0 4 69.2 | +| ETH/USDT | 43 | 3.56 | 153.26 | 153.409 | 15.34 | 4 days, 11:59:00 | 32 6 5 74.4 | +| BNB/USDT | 31 | 4.56 | 141.48 | 141.626 | 14.16 | 4 days, 15:44:00 | 18 10 3 58.1 | +| ROSE/USDT | 48 | 2.89 | 138.94 | 139.075 | 13.91 | 3 days, 22:48:00 | 25 15 8 52.1 | +| XRP/USDT | 50 | 1.64 | 82.23 | 82.309 | 8.23 | 3 days, 16:22:00 | 27 8 15 54.0 | +| EGLD/USDT | 44 | 0.08 | 3.35 | 3.355 | 0.34 | 4 days, 7:08:00 | 27 6 11 61.4 | +| ZEC/USDT | 0 | 0.00 | 0.00 | 0.000 | 0.00 | 0:00 | 0 0 0 0 | +| ADA/USDT | 0 | 0.00 | 0.00 | 0.000 | 0.00 | 0:00 | 0 0 0 0 | +| IOTX/USDT | 0 | 0.00 | 0.00 | 0.000 | 0.00 | 0:00 | 0 0 0 0 | +| CELR/USDT | 0 | 0.00 | 0.00 | 0.000 | 0.00 | 0:00 | 0 0 0 0 | +| TRX/USDT | 0 | 0.00 | 0.00 | 0.000 | 0.00 | 0:00 | 0 0 0 0 | +| BTC/USDT | 0 | 0.00 | 0.00 | 0.000 | 0.00 | 0:00 | 0 0 0 0 | +| TOTAL | 456 | 8.04 | 3668.37 | 3672.038 | 367.20 | 4 days, 3:40:00 | 290 59 107 63.6 | +=========================================================== BUY TAG STATS =========================================================== +| TAG | Buys | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | Avg Duration | Win Draw Loss Win% | +|-------+--------+----------------+----------------+-------------------+----------------+-----------------+-------------------------| +| TOTAL | 456 | 8.04 | 3668.37 | 3672.038 | 367.20 | 4 days, 3:40:00 | 290 59 107 63.6 | +===================================================== SELL REASON STATS ===================================================== +| Sell Reason | Sells | Win Draws Loss Win% | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | +|---------------+---------+--------------------------+----------------+----------------+-------------------+----------------| +| roi | 316 | 257 59 0 100 | 16.93 | 5351.22 | 5356.57 | 356.75 | +| sell_signal | 81 | 32 0 49 39.5 | -1.14 | -92.04 | -92.136 | -6.14 | +| stop_loss | 56 | 0 0 56 0 | -28.14 | -1576.06 | -1577.63 | -105.07 | +| force_sell | 3 | 1 0 2 33.3 | -4.92 | -14.75 | -14.763 | -0.98 | +========================================================= LEFT OPEN TRADES REPORT ========================================================= +| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | Avg Duration | Win Draw Loss Win% | +|-----------+--------+----------------+----------------+-------------------+----------------+-------------------+-------------------------| +| EGLD/USDT | 1 | 8.35 | 8.35 | 8.353 | 0.84 | 1 day, 12:00:00 | 1 0 0 100 | +| SOL/USDT | 1 | -10.94 | -10.94 | -10.952 | -1.10 | 8 days, 12:30:00 | 0 0 1 0 | +| XRP/USDT | 1 | -12.15 | -12.15 | -12.165 | -1.22 | 10 days, 12:20:00 | 0 0 1 0 | +| TOTAL | 3 | -4.92 | -14.75 | -14.763 | -1.48 | 6 days, 20:17:00 | 1 0 2 33.3 | +=============== SUMMARY METRICS ================ +| Metric | Value | +|------------------------+---------------------| +| Backtesting from | 2021-01-01 00:00:00 | +| Backtesting to | 2021-11-20 00:00:00 | +| Max open trades | 15 | +| | | +| Total/Daily Avg Trades | 456 / 1.41 | +| Starting balance | 1000.000 USDT | +| Final balance | 4672.038 USDT | +| Absolute profit | 3672.038 USDT | +| Total profit % | 367.20% | +| Trades per day | 1.41 | +| Avg. daily profit % | 1.14% | +| Avg. stake amount | 100.000 USDT | +| Total trade volume | 45600.000 USDT | +| | | +| Best Pair | SAND/USDT 1850.99% | +| Worst Pair | ZEC/USDT 0.00% | +| Best trade | SAND/USDT 1576.03% | +| Worst trade | ETH/USDT -28.14% | +| Best day | 1577.608 USDT | +| Worst day | -197.204 USDT | +| Days win/draw/lose | 170 / 98 / 55 | +| Avg. Duration Winners | 3 days, 15:35:00 | +| Avg. Duration Loser | 3 days, 18:33:00 | +| Rejected Buy signals | 0 | +| | | +| Min balance | 1015.800 USDT | +| Max balance | 4686.801 USDT | +| Drawdown | 362.40% | +| Drawdown | 362.761 USDT | +| Drawdown high | 1116.607 USDT | +| Drawdown low | 753.846 USDT | +| Drawdown Start | 2021-05-12 21:55:00 | +| Drawdown End | 2021-06-22 12:45:00 | +| Market change | 2716.79% | +================================================ diff --git a/DevilStra2.json b/DevilStra2.json new file mode 100644 index 0000000..cf4dad0 --- /dev/null +++ b/DevilStra2.json @@ -0,0 +1,29 @@ +{ + "strategy_name": "DevilStra2", + "params": { + "roi": { + "0": 0.574, + "1757": 0.158, + "3804": 0.089, + "6585": 0 + }, + "stoploss": { + "stoploss": -0.28 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": null, + "trailing_stop_positive_offset": 0.0, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_spell": "De,De,Si,Zi,Si,Ra,Lu,Gu,Ra,Pa,Ra,La,La,Ra,Ra,Zi,La,Gu,De,Ra,Zi,Cu,Cu,La,Lu,Si,Pa,Zi,Zi,Ra,Cu,De,Zi,Zi,Si,Gu,La,Zi,Zi,Gu,Zi,La,Pa,Pa,Gu,Gu,Pa,De,Pa,Lu,La,La,Gu,Ra,Lu,Zi,Pa,Lu,Gu,De,Si,Cu,Gu,La,Zi,Gu,Cu,Si,Pa,La,La,Lu,Cu,Gu,Si,Lu,Ra,De,Si,Lu,Si,La,Si,Cu,De,Lu,Pa,Gu,Pa,Si,Gu,Lu,Lu,Cu,Lu,De,Cu,Pa,De,Pa,Si,Si,Lu,Pa,La,Gu,Pa,Si,Cu,Gu,Zi,La,Lu,Lu,Zi,De,La,La,Zi,La,Gu,Pa,Cu,Pa,Gu,Pa,Gu,Gu,Ra,Lu,Lu,Zi,De,Ra,Lu,La,Lu,De,Si,Lu,Ra,Si,Si,La,Pa,Lu,Zi,Zi,Cu,Cu,Ra,Si,De,Ra,Ra,Lu,La,Zi,De,Pa,Pa,Si,Zi,Gu,De,Gu,Ra,Cu,Pa,Lu,De,Pa,La,Lu,Cu,Ra,Pa,Cu,Cu,Ra,Gu,Zi,La,Si,Zi,La,Si,De,Cu,Ra,Si,Gu,Si,De,Si,Ra,Pa,Gu,Gu,Pa,Si,Zi,Si,Cu,La,Si,De,Cu,Lu,De,Zi,Si,Pa,Gu,Cu,Si,Pa,Gu,De,La,Ra,Pa,Ra,La,Ra,Ra,La,Pa,Ra,La,Cu,De,Lu,Ra,Si,Ra,Cu,Pa,Si,Zi,Gu,De,Cu,De,Lu,Pa,Zi,La,La,La,La,Lu,La,Lu,Cu,Zi,Zi,Zi,La,Si,Ra,De,Lu,Ra,Si,Ra,De,Pa,Lu" + }, + "sell": { + "sell_spell": "La,Pa,De,De,La,Si,Si,La,La,La,Si,Pa,Pa,Lu,De,Cu,Cu,Gu,Lu,Ra,Lu,Si,Ra,De,La,Cu,La,La,Gu,La,De,Ra,Ra,Ra,Gu,Lu,Si,Si,Zi,Zi,La,Pa,Pa,Zi,Cu,Gu,Gu,Pa,Gu,Cu,Si,Ra,Ra,La,Gu,De,Si,La,Ra,Pa,Si,Lu,Pa,De,Zi,De,Lu,Si,Gu,De,Lu,De,Ra,Ra,Zi,De,Cu,Zi,Gu,Pa,Ra,De,Pa,De,Pa,Ra,Si,Si,Zi,Cu,Lu,Zi,Ra,De,Ra,Zi,Zi,Pa,Lu,Zi,Cu,Pa,Gu,Pa,Cu,De,Zi,De,De,Pa,Pa,Zi,Lu,Ra,Pa,Ra,Lu,Zi,Gu,Zi,Si,Lu,Ra,Ra,Zi,Lu,Pa,Lu,Si,Pa,Pa,Pa,Si,Zi,La,La,Lu,De,Zi,Gu,Ra,Ra,Ra,Zi,Pa,Zi,Cu,Lu,Gu,Cu,De,Lu,Gu,Lu,Gu,Si,Pa,Pa,Si,La,Gu,Ra,Pa,Si,Si,Si,Cu,Cu,Cu,Si,De,Lu,Gu,Gu,Lu,De,Ra,Gu,Gu,Gu,Cu,La,De,Cu,Zi,Pa,Si,De,Pa,Pa,Pa,La,De,Gu,Zi,La,De,Cu,La,Pa,Ra,Si,Si,Zi,Cu,Ra,Pa,Gu,Pa,Ra,Zi,De,Zi,Gu,Gu,Pa,Cu,Lu,Gu,De,Si,Pa,La,Cu,Zi,Gu,De,Gu,La,Cu,Gu,De,Cu,Cu,Gu,Ra,Lu,Zi,De,La,Ra,Pa,Pa,Si,La,Lu,La,De,De,Ra,De,La,La,Pa,Cu,Lu,Pa,Ra,Pa,Pa,Cu,Zi,Gu,Cu,Gu,La,Si,Ra,Pa" + }, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-04 13:36:54.394335+00:00" +} \ No newline at end of file diff --git a/DevilStra2.py b/DevilStra2.py new file mode 100644 index 0000000..81e2714 --- /dev/null +++ b/DevilStra2.py @@ -0,0 +1,658 @@ +# DevilStra Strategy +# 𝔇𝔢𝔳𝔦𝔩 𝔦𝔰 𝔞𝔩𝔴𝔞𝔶𝔰 𝔰𝔱𝔯𝔬𝔫𝔤𝔢𝔯 𝔱𝔥𝔞𝔫 𝔊𝔬𝔡. +# 𝔅𝔲𝔱 𝔱𝔥𝔢 𝔬𝔫𝔩𝔶 𝔬𝔫𝔢 𝔴𝔥𝔬 𝔥𝔞𝔰 𝔱𝔥𝔢 𝔞𝔟𝔦𝔩𝔦𝔱𝔶 +# 𝔗𝔬 𝔠𝔯𝔢𝔞𝔱𝔢 𝔫𝔢𝔴 𝔠𝔯𝔢𝔞𝔱𝔲𝔯𝔢𝔰 𝔦𝔰 𝔊𝔬𝔡. +# 𝔄𝔫𝔡 𝔱𝔥𝔢 𝔇𝔢𝔳𝔦𝔩 𝔪𝔞𝔨𝔢𝔰 𝔭𝔬𝔴𝔢𝔯𝔣𝔲𝔩 𝔰𝔭𝔢𝔩𝔩𝔰 +# 𝔉𝔯𝔬𝔪 𝔱𝔥𝔦𝔰 𝔰𝔪𝔞𝔩𝔩 𝔠𝔯𝔢𝔞𝔱𝔲𝔯𝔢𝔰 (𝔩𝔦𝔨𝔢 𝔣𝔯𝔬𝔤𝔰, 𝔢𝔱𝔠.) +# 𝔚𝔦𝔱𝔥 𝔣𝔯𝔞𝔤𝔪𝔢𝔫𝔱𝔞𝔱𝔦𝔬𝔫 𝔞𝔫𝔡 𝔪𝔦𝔵𝔦𝔫𝔤 𝔱𝔥𝔢𝔪. +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# * IMPORTANT: You Need An "STATIC" Pairlist On Your Config.json ! +# * IMPORTANT: First set PAIR_LIST_LENGHT={pair_whitelist size} +# * And re-hyperopt the Sell strategy And paste result in exact +# * place(lines 535~564) + +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell -s 𝕯𝖊𝖛𝖎𝖑𝕾𝖙𝖗𝖆 + +# --- Do not remove these libs --- +import numpy as np +from functools import reduce +import freqtrade.vendor.qtpylib.indicators as qtpylib +import talib.abstract as ta +import random +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame + +# ########################## SETTINGS ############################## +# pairlist lenght(use exact count of pairs you used in whitelist size+1): +PAIR_LIST_LENGHT = 269 +# you can find exact value of this inside GodStraNew +TREND_CHECK_CANDLES = 4 +# Set the pain range of devil(2~9999) +PAIN_RANGE = 1000 +# Add "GodStraNew" Generated Results As spells inside SPELLS. +# Set them unic phonemes like 'Zi' 'Gu' or 'Lu'! +# * Use below replacement on GodStraNew results to +# * Change God Generated Creatures to Spells: +# +-----------------------------+----------------------+ +# | GodStraNew Hyperopt Results | DevilStra Spells | +# +-----------------------------+----------------------+ +# | | "phonem" : { | +# | buy_params = { | "buy_params" : { | +# | ... | ... | +# | } | }, | +# | sell_params = { | "sell_params" : { | +# | ... | ... | +# | } | } | +# | | }, | +# +-----------------------------+----------------------+ +SPELLS = { + "Zi": { + "buy_params": { + "buy_crossed_indicator0": "BOP-4", + "buy_crossed_indicator1": "MACD-0-50", + "buy_crossed_indicator2": "DEMA-52", + "buy_indicator0": "MINUS_DI-50", + "buy_indicator1": "HT_TRENDMODE-50", + "buy_indicator2": "CORREL-128", + "buy_operator0": "/>R", + "buy_operator1": "CA", + "buy_operator2": "CDT", + "buy_real_num0": 0.1763, + "buy_real_num1": 0.6891, + "buy_real_num2": 0.0509, + }, + "sell_params": { + "sell_crossed_indicator0": "WCLPRICE-52", + "sell_crossed_indicator1": "AROONOSC-15", + "sell_crossed_indicator2": "CDLRISEFALL3METHODS-52", + "sell_indicator0": "COS-50", + "sell_indicator1": "CDLCLOSINGMARUBOZU-30", + "sell_indicator2": "CDL2CROWS-130", + "sell_operator0": "DT", + "sell_operator1": ">R", + "sell_operator2": "/>R", + "sell_real_num0": 0.0678, + "sell_real_num1": 0.8698, + "sell_real_num2": 0.3917, + } + }, + "Gu": { + "buy_params": { + "buy_crossed_indicator0": "SMA-20", + "buy_crossed_indicator1": "CDLLADDERBOTTOM-20", + "buy_crossed_indicator2": "OBV-50", + "buy_indicator0": "MAMA-1-50", + "buy_indicator1": "SUM-40", + "buy_indicator2": "VAR-30", + "buy_operator0": "R", + "sell_operator2": "CUT", + "sell_real_num0": 0.2707, + "sell_real_num1": 0.7987, + "sell_real_num2": 0.6891, + } + }, + "Lu": { + "buy_params": { + "buy_crossed_indicator0": "HT_SINE-0-28", + "buy_crossed_indicator1": "ADD-130", + "buy_crossed_indicator2": "ADD-12", + "buy_indicator0": "ADD-28", + "buy_indicator1": "AVGPRICE-15", + "buy_indicator2": "AVGPRICE-12", + "buy_operator0": "DT", + "buy_operator1": "D", + "buy_operator2": "C", + "buy_real_num0": 0.3676, + "buy_real_num1": 0.4284, + "buy_real_num2": 0.372, + }, + "sell_params": { + "sell_crossed_indicator0": "HT_SINE-0-5", + "sell_crossed_indicator1": "HT_SINE-0-4", + "sell_crossed_indicator2": "HT_SINE-0-28", + "sell_indicator0": "ADD-30", + "sell_indicator1": "AVGPRICE-28", + "sell_indicator2": "ADD-50", + "sell_operator0": "CUT", + "sell_operator1": "DT", + "sell_operator2": "=R", + "sell_real_num0": 0.3205, + "sell_real_num1": 0.2055, + "sell_real_num2": 0.8467, + } + }, + "La": { + "buy_params": { + "buy_crossed_indicator0": "WMA-14", + "buy_crossed_indicator1": "MAMA-1-14", + "buy_crossed_indicator2": "CDLHIKKAKE-14", + "buy_indicator0": "T3-14", + "buy_indicator1": "BETA-14", + "buy_indicator2": "HT_PHASOR-1-14", + "buy_operator0": "/>R", + "buy_operator1": ">", + "buy_operator2": ">R", + "buy_real_num0": 0.0551, + "buy_real_num1": 0.3469, + "buy_real_num2": 0.3871, + }, + "sell_params": { + "sell_crossed_indicator0": "HT_TRENDLINE-14", + "sell_crossed_indicator1": "LINEARREG-14", + "sell_crossed_indicator2": "STOCHRSI-1-14", + "sell_indicator0": "CDLDARKCLOUDCOVER-14", + "sell_indicator1": "AD-14", + "sell_indicator2": "CDLSTALLEDPATTERN-14", + "sell_operator0": "/=R", + "sell_operator1": "COT", + "sell_operator2": "OT", + "sell_real_num0": 0.3992, + "sell_real_num1": 0.7747, + "sell_real_num2": 0.7415, + } + }, + "Si": { + "buy_params": { + "buy_crossed_indicator0": "MACDEXT-2-14", + "buy_crossed_indicator1": "CORREL-14", + "buy_crossed_indicator2": "CMO-14", + "buy_indicator0": "MA-14", + "buy_indicator1": "ADXR-14", + "buy_indicator2": "CDLMARUBOZU-14", + "buy_operator0": "<", + "buy_operator1": "/", + "buy_operator2": "CA", + "buy_real_num0": 0.2208, + "buy_real_num1": 0.1371, + "buy_real_num2": 0.6389, + }, + "sell_params": { + "sell_crossed_indicator0": "MACDEXT-0-15", + "sell_crossed_indicator1": "BBANDS-2-15", + "sell_crossed_indicator2": "DEMA-15", + "sell_indicator0": "ULTOSC-15", + "sell_indicator1": "MIDPOINT-12", + "sell_indicator2": "PLUS_DI-12", + "sell_operator0": "<", + "sell_operator1": "DT", + "sell_operator2": "COT", + "sell_real_num0": 0.278, + "sell_real_num1": 0.0643, + "sell_real_num2": 0.7065, + } + }, + "De": { + "buy_params": { + "buy_crossed_indicator0": "HT_DCPERIOD-12", + "buy_crossed_indicator1": "HT_PHASOR-0-12", + "buy_crossed_indicator2": "MACDFIX-1-15", + "buy_indicator0": "CMO-12", + "buy_indicator1": "TRIMA-12", + "buy_indicator2": "MACDEXT-0-15", + "buy_operator0": "<", + "buy_operator1": "D", + "buy_operator2": "<", + "buy_real_num0": 0.3924, + "buy_real_num1": 0.5546, + "buy_real_num2": 0.7648, + }, + "sell_params": { + "sell_crossed_indicator0": "MACDFIX-1-15", + "sell_crossed_indicator1": "MACD-1-15", + "sell_crossed_indicator2": "WMA-15", + "sell_indicator0": "ROC-15", + "sell_indicator1": "MACD-2-15", + "sell_indicator2": "CCI-60", + "sell_operator0": "CA", + "sell_operator1": " 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator( + dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class DevilStra2(IStrategy): + # #################### RESULT PASTE PLACE #################### + # 16/16: 108 trades. 75/18/15 Wins/Draws/Losses. Avg profit 7.77%. Median profit 8.89%. Total profit 0.08404983 BTC ( 84.05Σ%). Avg duration 3 days, 6:49:00 min. Objective: -11.22849 + + # Buy hyperspace params: + buy_params = { + "buy_spell": "Zi,Lu,Ra,Ra,La,Si,Pa,Si,Cu,La,De,Lu,De,La,Zi,Zi,Zi,Zi,Zi,Lu,Lu,Lu,Si,La,Ra,Pa,La,Zi,Zi,Gu,Ra,De,Gu,Zi,Ra,Ra,Ra,Cu,Pa,De,De,La,Lu,Lu,Lu,La,Zi,Cu,Ra,Gu,Pa,La,Zi,Zi,Si,Lu,Ra,Cu,Cu,Pa,Si,Gu,De,De,Lu,Gu,Zi,Pa,Lu,Pa,Ra,Gu,Cu,La,Pa,Lu,Zi,La,Zi,Gu,Zi,De,Cu,Ra,Lu,Ra,Gu,Si,Ra,La,La,Lu,Gu,Zi,Si,La,Pa,Pa,Cu,Cu,Zi,Gu,Pa,Zi,Pa,Cu,Lu,Pa,Si,De,Gu,Lu,Lu,Cu,Ra,Si,Pa,Gu,Si,Cu,Pa,Zi,Pa,Zi,Gu,Lu,Ra,Pa,Ra,De,Ra,Pa,Zi,La,Pa,De,Pa,Cu,Gu,De,Lu,La,Ra,Zi,Si,Zi,Zi,Cu,Cu,De,Pa,Pa,Zi,De,Ra,La,Lu,De,Lu,Gu,Cu,Cu,La,De,Gu,Lu,Ra,Pa,Lu,Cu,Pa,Pa,De,Si,Zi,Cu,De,De,De,Lu,Si,Zi,Gu,Si,Si,Ra,Pa,Si,La,La,Lu,Lu,De,Gu,Gu,Zi,Ra,La,Lu,Lu,La,Si,Zi,Si,Zi,Si,Lu,Cu,Zi,Lu,De,La,Ra,Ra,Lu,De,Pa,Zi,Gu,Cu,Zi,Pa,De,Si,Lu,De,Cu,De,Zi,Ra,Gu,De,Si,Lu,Lu,Ra,De,Gu,Cu,Gu,La,De,Lu,Lu,Si,Cu,Lu,Zi,Lu,Cu,Gu,Lu,Lu,Ra,Si,Ra,Pa,Lu,De,Ra,Zi,Gu,Gu,Zi,Lu,Cu,Cu,Cu,Lu", + } + + # Sell hyperspace params: + sell_params = { + "sell_spell": "La,Pa,De,De,La,Si,Si,La,La,La,Si,Pa,Pa,Lu,De,Cu,Cu,Gu,Lu,Ra,Lu,Si,Ra,De,La,Cu,La,La,Gu,La,De,Ra,Ra,Ra,Gu,Lu,Si,Si,Zi,Zi,La,Pa,Pa,Zi,Cu,Gu,Gu,Pa,Gu,Cu,Si,Ra,Ra,La,Gu,De,Si,La,Ra,Pa,Si,Lu,Pa,De,Zi,De,Lu,Si,Gu,De,Lu,De,Ra,Ra,Zi,De,Cu,Zi,Gu,Pa,Ra,De,Pa,De,Pa,Ra,Si,Si,Zi,Cu,Lu,Zi,Ra,De,Ra,Zi,Zi,Pa,Lu,Zi,Cu,Pa,Gu,Pa,Cu,De,Zi,De,De,Pa,Pa,Zi,Lu,Ra,Pa,Ra,Lu,Zi,Gu,Zi,Si,Lu,Ra,Ra,Zi,Lu,Pa,Lu,Si,Pa,Pa,Pa,Si,Zi,La,La,Lu,De,Zi,Gu,Ra,Ra,Ra,Zi,Pa,Zi,Cu,Lu,Gu,Cu,De,Lu,Gu,Lu,Gu,Si,Pa,Pa,Si,La,Gu,Ra,Pa,Si,Si,Si,Cu,Cu,Cu,Si,De,Lu,Gu,Gu,Lu,De,Ra,Gu,Gu,Gu,Cu,La,De,Cu,Zi,Pa,Si,De,Pa,Pa,Pa,La,De,Gu,Zi,La,De,Cu,La,Pa,Ra,Si,Si,Zi,Cu,Ra,Pa,Gu,Pa,Ra,Zi,De,Zi,Gu,Gu,Pa,Cu,Lu,Gu,De,Si,Pa,La,Cu,Zi,Gu,De,Gu,La,Cu,Gu,De,Cu,Cu,Gu,Ra,Lu,Zi,De,La,Ra,Pa,Pa,Si,La,Lu,La,De,De,Ra,De,La,La,Pa,Cu,Lu,Pa,Ra,Pa,Pa,Cu,Zi,Gu,Cu,Gu,La,Si,Ra,Pa", + } + + # ROI table: + minimal_roi = { + "0": 0.574, + "1757": 0.158, + "3804": 0.089, + "6585": 0 + } + + # Stoploss: + stoploss = -0.28 + # #################### END OF RESULT PLACE #################### + + # 𝖂𝖔𝖗𝖘𝖙, 𝖀𝖓𝖎𝖉𝖊𝖆𝖑, 𝕾𝖚𝖇𝖔𝖕𝖙𝖎𝖒𝖆𝖑, 𝕸𝖆𝖑𝖆𝖕𝖗𝖔𝖕𝖔𝖘 𝕬𝖓𝖉 𝕯𝖎𝖘𝖒𝖆𝖑 𝖙𝖎𝖒𝖊𝖋𝖗𝖆𝖒𝖊 𝖋𝖔𝖗 𝖙𝖍𝖎𝖘 𝖘𝖙𝖗𝖆𝖙𝖊𝖌𝖞: + timeframe = '4h' + + spell_pot = [ + ",".join( + tuple( + random.choices( + list(SPELLS.keys()), + # TODO: k will be change to len(pairlist) + k=PAIR_LIST_LENGHT + ) + ) + )for i in range(PAIN_RANGE) + ] + + buy_spell = CategoricalParameter( + spell_pot, default=spell_pot[0], space='buy') + sell_spell = CategoricalParameter( + spell_pot, default=spell_pot[0], space='sell') + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + pairs = self.dp.current_whitelist() + pairs_len = len(pairs) + pair_index = pairs.index(metadata['pair']) + + buy_spells = self.buy_spell.value.split(",") + buy_spells_len = len(buy_spells) + + if pairs_len > buy_spells_len: + print( + f"First set PAIR_LIST_LENGHT={pairs_len + 1} And re-hyperopt the") + print("Buy strategy And paste result in exact place(lines 535~564)") + print("IMPORTANT: You Need An 'STATIC' Pairlist On Your Config.json !!!") + exit() + + buy_params_index = buy_spells[pair_index] + + params = spell_finder(buy_params_index, 'buy') + conditions = list() + # TODO: Its not dry code! + buy_indicator = params['buy_indicator0'] + buy_crossed_indicator = params['buy_crossed_indicator0'] + buy_operator = params['buy_operator0'] + buy_real_num = params['buy_real_num0'] + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + # backup + buy_indicator = params['buy_indicator1'] + buy_crossed_indicator = params['buy_crossed_indicator1'] + buy_operator = params['buy_operator1'] + buy_real_num = params['buy_real_num1'] + + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + buy_indicator = params['buy_indicator2'] + buy_crossed_indicator = params['buy_crossed_indicator2'] + buy_operator = params['buy_operator2'] + buy_real_num = params['buy_real_num2'] + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'buy']=1 + + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe diff --git a/Diamond.py b/Diamond.py new file mode 100644 index 0000000..fddff21 --- /dev/null +++ b/Diamond.py @@ -0,0 +1,155 @@ +# 𝐼𝓉 𝒾𝓈 𝒟𝒾𝒶𝓂𝑜𝓃𝒹 𝒮𝓉𝓇𝒶𝓉𝑒𝑔𝓎. +# 𝒯𝒽𝒶𝓉 𝓉𝒶𝓀𝑒𝓈 𝒽𝑒𝓇 𝑜𝓌𝓃 𝓇𝒾𝑔𝒽𝓉𝓈 𝓁𝒾𝓀𝑒 𝒜𝒻𝑔𝒽𝒶𝓃𝒾𝓈𝓉𝒶𝓃 𝓌𝑜𝓂𝑒𝓃 +# 𝒯𝒽𝑜𝓈𝑒 𝓌𝒽𝑜 𝓈𝓉𝒾𝓁𝓁 𝓅𝓇𝑜𝓊𝒹 𝒶𝓃𝒹 𝒽𝑜𝓅𝑒𝒻𝓊𝓁. +# 𝒯𝒽𝑜𝓈𝑒 𝓌𝒽𝑜 𝓉𝒽𝑒 𝓂𝑜𝓈𝓉 𝒷𝑒𝒶𝓊𝓉𝒾𝒻𝓊𝓁 𝒸𝓇𝑒𝒶𝓉𝓊𝓇𝑒𝓈 𝒾𝓃 𝓉𝒽𝑒 𝒹𝑒𝓅𝓉𝒽𝓈 𝑜𝒻 𝓉𝒽𝑒 𝒹𝒶𝓇𝓀𝑒𝓈𝓉. +# 𝒯𝒽𝑜𝓈𝑒 𝓌𝒽𝑜 𝓈𝒽𝒾𝓃𝑒 𝓁𝒾𝓀𝑒 𝒹𝒾𝒶𝓂𝑜𝓃𝒹𝓈 𝒷𝓊𝓇𝒾𝑒𝒹 𝒾𝓃 𝓉𝒽𝑒 𝒽𝑒𝒶𝓇𝓉 𝑜𝒻 𝓉𝒽𝑒 𝒹𝑒𝓈𝑒𝓇𝓉 ... +# 𝒲𝒽𝓎 𝓃𝑜𝓉 𝒽𝑒𝓁𝓅 𝓌𝒽𝑒𝓃 𝓌𝑒 𝒸𝒶𝓃? +# 𝐼𝒻 𝓌𝑒 𝒷𝑒𝓁𝒾𝑒𝓋𝑒 𝓉𝒽𝑒𝓇𝑒 𝒾𝓈 𝓃𝑜 𝓂𝒶𝓃 𝓁𝑒𝒻𝓉 𝓌𝒾𝓉𝒽 𝓉𝒽𝑒𝓂 +# (𝒲𝒽𝒾𝒸𝒽 𝒾𝓈 𝓅𝓇𝑜𝒷𝒶𝒷𝓁𝓎 𝓉𝒽𝑒 𝓅𝓇𝑜𝒹𝓊𝒸𝓉 𝑜𝒻 𝓉𝒽𝑒 𝓉𝒽𝑜𝓊𝑔𝒽𝓉 𝑜𝒻 𝓅𝒶𝒾𝓃𝓁𝑒𝓈𝓈 𝒸𝑜𝓇𝓅𝓈𝑒𝓈) +# 𝒲𝒽𝑒𝓇𝑒 𝒽𝒶𝓈 𝑜𝓊𝓇 𝒽𝓊𝓂𝒶𝓃𝒾𝓉𝓎 𝑔𝑜𝓃𝑒? +# 𝒲𝒽𝑒𝓇𝑒 𝒽𝒶𝓈 𝒽𝓊𝓂𝒶𝓃𝒾𝓉𝓎 𝑔𝑜𝓃𝑒? +# 𝒲𝒽𝓎 𝓃𝑜𝓉 𝒽𝑒𝓁𝓅 𝓌𝒽𝑒𝓃 𝓌𝑒 𝒸𝒶𝓃? +# 𝓁𝑒𝓉𝓈 𝓅𝒾𝓅 𝓊𝓃𝒾𝓃𝓈𝓉𝒶𝓁𝓁 𝓉𝒶-𝓁𝒾𝒷 𝑜𝓃 𝒜𝒻𝑔𝒽𝒶𝓃𝒾𝓈𝓉𝒶𝓃 + +# IMPORTANT: Diamond strategy is designed to be pure and +# cuz of that it have not any indicator population. idea is that +# It is just use the pure dataframe ohlcv data for calculation +# of buy/sell signals, But you can add your indicators and add +# your key names inside catagorical hyperoptable params and +# than you be able to hyperopt them as well. +# thanks to: @Kroissan, @drakes00 And @xmatthias for his patience and helps +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# * freqtrade backtesting --strategy Diamond + +# freqtrade hyperopt --hyperopt-loss ShortTradeDurHyperOptLoss --spaces buy sell roi trailing stoploss --strategy Diamond -j 2 -e 10 +# * 3/10: 76 trades. 51/18/7 Wins/Draws/Losses. Avg profit 1.92%. Median profit 2.40%. Total profit 0.04808472 BTC ( 48.08%). Avg duration 5:06:00 min. Objective: 1.75299 +# freqtrade hyperopt --hyperopt-loss OnlyProfitHyperOptLoss --spaces buy sell roi trailing stoploss --strategy Diamond -j 2 -e 10 +# * 10/10: 76 trades. 39/34/3 Wins/Draws/Losses. Avg profit 0.61%. Median profit 0.05%. Total profit 0.01528359 BTC ( 15.28%). Avg duration 17:32:00 min. Objective: -0.01528 +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi trailing stoploss --strategy Diamond -j 2 -e 10 +# * 4/10: 15 trades. 10/2/3 Wins/Draws/Losses. Avg profit 1.52%. Median profit 7.99%. Total profit 0.00754274 BTC ( 7.54%). Avg duration 1 day, 0:04:00 min. Objective: -0.90653 +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces buy sell roi trailing stoploss --strategy Diamond -j 2 -e 10 +# * 7/10: 130 trades. 68/54/8 Wins/Draws/Losses. Avg profit 0.71%. Median profit 0.06%. Total profit 0.03050369 BTC ( 30.50%). Avg duration 10:07:00 min. Objective: -11.08185 +# freqtrade hyperopt --hyperopt-loss SortinoHyperOptLoss --spaces buy sell roi trailing stoploss --strategy Diamond -j 2 -e 10 +# * 2/10: 10 trades. 7/0/3 Wins/Draws/Losses. Avg profit 5.50%. Median profit 7.05%. Total profit 0.01817970 BTC ( 18.18%). Avg duration 0:27:00 min. Objective: -11.72450 +# freqtrade hyperopt --hyperopt-loss SortinoHyperOptLossDaily --spaces buy sell roi trailing stoploss --strategy Diamond -j 2 -e 10 +# | * Best | 3/10 | 165 | 98 63 4 | 1.00% | 0.05453885 BTC (54.54%) | 0 days 08:02:00 | 0.00442974 BTC (13.41%) | -41.371 | +# | * Best | 7/10 | 101 | 56 42 3 | 0.73% | 0.02444518 BTC (24.45%) | 0 days 13:08:00 | 0.00107122 BTC (3.24%) | -66.7687 | +# * 7/10: 101 trades. 56/42/3 Wins/Draws/Losses. Avg profit 0.73%. Median profit 0.13%. Total profit 0.02444518 BTC ( 24.45%). Avg duration 13:08:00 min. Objective: -66.76866 +# freqtrade hyperopt --hyperopt-loss OnlyProfitHyperOptLoss --spaces buy sell roi trailing stoploss --strategy Diamond -j 2 -e 10 +# * 7/10: 117 trades. 74/41/2 Wins/Draws/Losses. Avg profit 1.91%. Median profit 1.50%. Total profit 0.07370921 BTC ( 73.71%). Avg duration 9:26:00 min. Objective: -0.07371 + +# --- Do not remove these libs --- +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame +# -------------------------------- + +# Add your lib to import here +import talib.abstract as ta +from functools import reduce +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class Diamond(IStrategy): + # ###################### RESULT PLACE ###################### + # Config: 5 x UNLIMITED STOCK costume pair list, + # hyperopt : 5000 x SortinoHyperOptLossDaily, + # 34/5000: 297 trades. 136/156/5 Wins/Draws/Losses. Avg profit 0.49%. Median profit 0.00%. Total profit 45.84477237 USDT ( 33.96Σ%). Avg duration 11:54:00 min. Objective: -46.50379 + + # Buy hyperspace params: + buy_params = { + "buy_fast_key": "high", + "buy_horizontal_push": 7, + "buy_slow_key": "volume", + "buy_vertical_push": 0.942, + } + + # Sell hyperspace params: + sell_params = { + "sell_fast_key": "high", + "sell_horizontal_push": 10, + "sell_slow_key": "low", + "sell_vertical_push": 1.184, + } + + # ROI table: + minimal_roi = { + "0": 0.242, + "13": 0.044, + "51": 0.02, + "170": 0 + } + + # Stoploss: + stoploss = -0.271 + + # Trailing stop: + trailing_stop = True + trailing_stop_positive = 0.011 + trailing_stop_positive_offset = 0.054 + trailing_only_offset_is_reached = False + # timeframe + timeframe = '5m' + # #################### END OF RESULT PLACE #################### + + buy_vertical_push = DecimalParameter(0.5, 1.5, decimals=3, default=1, space='buy') + buy_horizontal_push = IntParameter(0, 10, default=0, space='buy') + buy_fast_key = CategoricalParameter(['open', 'high', 'low', 'close', 'volume', + # you can not enable this lines befour you + # populate an indicator for them and set + # the same key name for it + # 'ma_fast', 'ma_slow', {...} + ], default='ma_fast', space='buy') + buy_slow_key = CategoricalParameter(['open', 'high', 'low', 'close', 'volume', + # 'ma_fast', 'ma_slow', {...} + ], default='ma_slow', space='buy') + + sell_vertical_push = DecimalParameter(0.5, 1.5, decimals=3, default=1, space='sell') + sell_horizontal_push = IntParameter(0, 10, default=0, space='sell') + sell_fast_key = CategoricalParameter(['open', 'high', 'low', 'close', 'volume', + # 'ma_fast', 'ma_slow', {...} + ], default='ma_fast', space='sell') + sell_slow_key = CategoricalParameter(['open', 'high', 'low', 'close', 'volume', + # 'ma_fast', 'ma_slow', {...} + ], default='ma_slow', space='sell') + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # you can add new indicators and enable them inside + # hyperoptable categorical params on the top + # dataframe['ma_fast'] = ta.SMA(dataframe, timeperiod=9) + # dataframe['ma_slow'] = ta.SMA(dataframe, timeperiod=18) + # dataframe['{...}'] = ta.{...}(dataframe, timeperiod={...}) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + conditions.append( + qtpylib.crossed_above + ( + dataframe[self.buy_fast_key.value].shift(self.buy_horizontal_push.value), + dataframe[self.buy_slow_key.value] * self.buy_vertical_push.value + ) + ) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'buy']=1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + conditions.append( + qtpylib.crossed_below + ( + dataframe[self.sell_fast_key.value].shift(self.sell_horizontal_push.value), + dataframe[self.sell_slow_key.value] * self.sell_vertical_push.value + ) + ) + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'sell']=1 + return dataframe diff --git a/Diamond.txt b/Diamond.txt new file mode 100644 index 0000000..6084e4e --- /dev/null +++ b/Diamond.txt @@ -0,0 +1,77 @@ +Result for strategy Diamond +========================================================== BACKTESTING REPORT ========================================================== +| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | Avg Duration | Win Draw Loss Win% | +|-----------+--------+----------------+----------------+-------------------+----------------+----------------+-------------------------| +| CELR/USDT | 40 | 1.20 | 47.95 | 47.999 | 4.80 | 6:14:00 | 25 15 0 100 | +| ETH/USDT | 432 | 0.09 | 39.39 | 39.431 | 3.94 | 14:42:00 | 197 226 9 45.6 | +| ROSE/USDT | 60 | 0.64 | 38.12 | 38.154 | 3.82 | 7:32:00 | 29 31 0 100 | +| SAND/USDT | 14 | 1.41 | 19.70 | 19.717 | 1.97 | 3:29:00 | 11 3 0 100 | +| ADA/USDT | 7 | 0.86 | 5.99 | 6.000 | 0.60 | 9:27:00 | 3 4 0 100 | +| AVAX/USDT | 7 | 0.86 | 5.99 | 6.000 | 0.60 | 3:30:00 | 3 4 0 100 | +| XRP/USDT | 7 | 0.86 | 5.99 | 6.000 | 0.60 | 2:55:00 | 3 4 0 100 | +| SOL/USDT | 4 | 0.86 | 3.43 | 3.435 | 0.34 | 3:36:00 | 2 2 0 100 | +| IOTX/USDT | 78 | 0.03 | 2.58 | 2.582 | 0.26 | 8:35:00 | 37 39 2 47.4 | +| BNB/USDT | 7 | 0.32 | 2.26 | 2.262 | 0.23 | 6:11:00 | 2 5 0 100 | +| GALA/USDT | 0 | 0.00 | 0.00 | 0.000 | 0.00 | 0:00 | 0 0 0 0 | +| TRX/USDT | 2 | 0.00 | 0.00 | 0.000 | 0.00 | 12:42:00 | 0 2 0 0 | +| BTC/USDT | 0 | 0.00 | 0.00 | 0.000 | 0.00 | 0:00 | 0 0 0 0 | +| EGLD/USDT | 300 | -0.13 | -39.93 | -39.973 | -4.00 | 14:28:00 | 134 156 10 44.7 | +| ZEC/USDT | 277 | -0.25 | -69.80 | -69.865 | -6.99 | 15:57:00 | 137 130 10 49.5 | +| TOTAL | 1235 | 0.05 | 61.68 | 61.742 | 6.17 | 13:32:00 | 583 621 31 47.2 | +========================================================== BUY TAG STATS =========================================================== +| TAG | Buys | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | Avg Duration | Win Draw Loss Win% | +|-------+--------+----------------+----------------+-------------------+----------------+----------------+-------------------------| +| TOTAL | 1235 | 0.05 | 61.68 | 61.742 | 6.17 | 13:32:00 | 583 621 31 47.2 | +======================================================= SELL REASON STATS ======================================================== +| Sell Reason | Sells | Win Draws Loss Win% | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | +|--------------------+---------+--------------------------+----------------+----------------+-------------------+----------------| +| roi | 1202 | 581 621 0 100 | 0.63 | 759.35 | 760.111 | 50.62 | +| trailing_stop_loss | 24 | 2 0 22 8.3 | -23.86 | -572.56 | -573.132 | -38.17 | +| sell_signal | 4 | 0 0 4 0 | -14.02 | -56.06 | -56.117 | -3.74 | +| force_sell | 3 | 0 0 3 0 | -4.85 | -14.56 | -14.574 | -0.97 | +| stop_loss | 2 | 0 0 2 0 | -27.25 | -54.49 | -54.546 | -3.63 | +======================================================== LEFT OPEN TRADES REPORT ========================================================= +| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | Avg Duration | Win Draw Loss Win% | +|-----------+--------+----------------+----------------+-------------------+----------------+------------------+-------------------------| +| ZEC/USDT | 1 | -1.32 | -1.32 | -1.320 | -0.13 | 2:40:00 | 0 0 1 0 | +| EGLD/USDT | 1 | -3.25 | -3.25 | -3.251 | -0.33 | 1:20:00 | 0 0 1 0 | +| ETH/USDT | 1 | -9.99 | -9.99 | -10.003 | -1.00 | 4 days, 10:50:00 | 0 0 1 0 | +| TOTAL | 3 | -4.85 | -14.56 | -14.574 | -1.46 | 1 day, 12:57:00 | 0 0 3 0 | +=============== SUMMARY METRICS ================ +| Metric | Value | +|------------------------+---------------------| +| Backtesting from | 2021-01-01 00:00:00 | +| Backtesting to | 2021-11-20 00:00:00 | +| Max open trades | 15 | +| | | +| Total/Daily Avg Trades | 1235 / 3.82 | +| Starting balance | 1000.000 USDT | +| Final balance | 1061.742 USDT | +| Absolute profit | 61.742 USDT | +| Total profit % | 6.17% | +| Trades per day | 3.82 | +| Avg. daily profit % | 0.02% | +| Avg. stake amount | 100.000 USDT | +| Total trade volume | 123500.000 USDT | +| | | +| Best Pair | CELR/USDT 47.95% | +| Worst Pair | ZEC/USDT -69.80% | +| Best trade | EGLD/USDT 9.32% | +| Worst trade | IOTX/USDT -27.25% | +| Best day | 18.000 USDT | +| Worst day | -55.225 USDT | +| Days win/draw/lose | 201 / 100 / 23 | +| Avg. Duration Winners | 2:30:00 | +| Avg. Duration Loser | 4 days, 7:03:00 | +| Rejected Buy signals | 0 | +| | | +| Min balance | 940.516 USDT | +| Max balance | 1218.619 USDT | +| Drawdown | 277.83% | +| Drawdown | 278.103 USDT | +| Drawdown high | 218.619 USDT | +| Drawdown low | -59.484 USDT | +| Drawdown Start | 2021-05-12 06:00:00 | +| Drawdown End | 2021-07-20 03:05:00 | +| Market change | 2716.79% | +================================================ diff --git a/Empty.py b/Empty.py new file mode 100644 index 0000000..7153a92 --- /dev/null +++ b/Empty.py @@ -0,0 +1,123 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + +# This class is a sample. Feel free to customize it. +class Empty(IStrategy): + + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # ROI table: + minimal_roi = { + #"0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 #0.015 + trailing_only_offset_is_reached = True + + #max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "Aaron": { + 'aroonup': {'color': 'blue'}, + 'aroondown': {'color': 'red'} + } + } + } + + def informative_pairs(self): + + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # MACD + #macd = ta.MACD(dataframe) + #dataframe['macd'] = macd['macd'] + #dataframe['macdsignal'] = macd['macdsignal'] + #dataframe['macdhist'] = macd['macdhist'] + + # RSI + #dataframe['rsi'] = ta.RSI(dataframe) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + # ( + # (dataframe['close'] < dataframe['bb_lowerband']) + # & (dataframe['bb_width'] >= 0.065) + # #& (dataframe['rsi'] < 45) + # & (dataframe['volume'] > 0) + # ) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + + ), + 'sell'] = 1 + return dataframe + diff --git a/EnCirculation.txt b/EnCirculation.txt new file mode 100644 index 0000000..a70ce89 --- /dev/null +++ b/EnCirculation.txt @@ -0,0 +1,20 @@ +132,670,764,300 DOGE +19,215,937 BTC +71,683,856 LTC +138,063,286 ETC +18,204,913 XMR +19,238,231 BCH +15,772,013 ZEC +50,298,735,565 XRP +34,424,965,305 ADA +8,734,317,475 MATIC +1,139,368,061 DOT +362,910,497 SOL +549,063,278,876,302 SHIB +92,159,192,741 TRX +300,677,692 AVAX +18,204,913 XMR +1,344,947,069 STX +13,572,466,120 ZIL + +https://coinmarketcap.com/ diff --git a/FractalAtr.py b/FractalAtr.py new file mode 100644 index 0000000..5d84461 --- /dev/null +++ b/FractalAtr.py @@ -0,0 +1,328 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# flake8: noqa: F401 +# isort: skip_file +# --- Do not remove these libs --- +import logging +import numpy as np +import pandas as pd +from pandas import DataFrame +from datetime import datetime, timedelta +from typing import Optional, Union, Tuple +from freqtrade.exchange import timeframe_to_prev_date +from freqtrade.persistence import Trade +from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, stoploss_from_open, + IntParameter, IStrategy, merge_informative_pair, informative, stoploss_from_absolute) + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import pandas_ta as pta +from technical import qtpylib + +class FractalAtr(IStrategy): + + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 3 + + # Optimal timeframe for the strategy. + timeframe = '1m' + + # Can this strategy go short? + can_short: bool = False + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi". + minimal_roi = { + "0": 0.5 + } + + # Optimal stoploss designed for the strategy. + # This attribute will be overridden if the config file contains "stoploss". + stoploss = -1 + + position_adjustment_enable = True + use_custom_stoploss = True + + # Trailing stoploss + trailing_stop = False + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = True + + # These values can be overridden in the config. + use_exit_signal = True + exit_profit_only = False + ignore_roi_if_entry_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Strategy parameters + buy_rsi = IntParameter(10, 40, default=30, space="buy") + sell_rsi = IntParameter(60, 90, default=70, space="sell") + + # trailing stoploss hyperopt parameters + # hard stoploss profit + + pHSL = DecimalParameter(-0.200, -0.040, default=-0.99, decimals=3, space='sell', optimize=False, load=True) + # profit threshold 1, trigger point, SL_1 is used + pPF_1 = DecimalParameter(0.008, 0.020, default=0.022, decimals=3, space='sell', optimize=True, load=True) + pSL_1 = DecimalParameter(0.008, 0.020, default=0.021, decimals=3, space='sell', optimize=True, load=True) + + # profit threshold 2, SL_2 is used + pPF_2 = DecimalParameter(0.040, 0.100, default=0.080, decimals=3, space='sell', optimize=True, load=True) + pSL_2 = DecimalParameter(0.020, 0.070, default=0.040, decimals=3, space='sell', optimize=True, load=True) + + # Optional order type mapping. + order_types = { + 'entry': 'limit', + 'exit': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'entry': 'GTC', + 'exit': 'GTC' + } + locked_pairs = {} + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + if True | (len(dataframe) < 1): + return None + last_candle = dataframe.iloc[-1].squeeze() + filled_buys = trade.select_filled_orders('buy') + count_of_buys = len(filled_buys) + + condition = (last_candle['enter_long'] == 1) # & (last_candle['close'] <= last_candle['close_1h']) + + # self.protection_nb_buy_lost.value + if (0 < count_of_buys <= 2) & (current_profit < - 0.1) & (condition): + try: + stake_amount = self.config['stake_amount'] + print("Adjust " + trade.pair + " " + str(current_time) + " " + str(current_profit) + " " + str(count_of_buys) + " " + + str(stake_amount)) + return stake_amount + except Exception as exception: + print(exception) + return None + return None + + def calculateScore(self, last_candle): + score = 1 #- (last_candle['percent10'] / 0.01 + return score + + @informative('1h') + def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + self.will_frac(dataframe) + dataframe['bear'] = dataframe['high'].where(dataframe['fractal_sup'] == True, 0) + dataframe['bear'] = dataframe['bear'].replace(to_replace=0, method="ffill") + dataframe['bull'] = dataframe['low'].where(dataframe['fractal_inf'] == True, 0) + dataframe['bull'] = dataframe['bull'].replace(to_replace=0, method="ffill") + dataframe['max72'] = ta.MAX(dataframe['close'], timeperiod=72) + dataframe['min72'] = ta.MIN(dataframe['close'], timeperiod=72) + dataframe['mid72'] = (dataframe['min72'] + (dataframe['max72'] - dataframe['min72']) / 2) + dataframe['width_72'] = (dataframe['max72'] - dataframe['min72']) / dataframe['max72'] + + dataframe['percent'] = dataframe['close'].pct_change() + dataframe['percent5'] = dataframe['close'].pct_change(5) + return dataframe + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + self.will_frac(dataframe) + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['atr'] = ta.ATR(dataframe) + dataframe['bear'] = dataframe['high'].where(dataframe['fractal_sup'] == True, 0) + dataframe['bear'] = dataframe['bear'].replace(to_replace=0, method="ffill") + dataframe['bull'] = dataframe['low'].where(dataframe['fractal_inf'] == True, 0) + dataframe['bull'] = dataframe['bull'].replace(to_replace=0, method="ffill") + dataframe["percent"] = dataframe["close"].pct_change() + dataframe["percent3"] = dataframe["close"].pct_change(3) + dataframe["percent10"] = dataframe["close"].pct_change(10) + + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['max200'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['atr_relatif'] = ta.ATR(dataframe) / dataframe["min200"] + dataframe["close50"] = dataframe["close"].rolling(50).mean() + dataframe["close3"] = dataframe["close"].rolling(3).mean() + dataframe['rsi_mean'] = dataframe['rsi'].rolling(3).mean() + dataframe['atr_relatif_mean'] = dataframe['atr_relatif'].rolling(3).mean() + dataframe['width_200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['max200'] + dataframe['width_close'] = (dataframe['close'] - dataframe['min200']) / dataframe['close'] + dataframe['200_close'] = dataframe['width_close'] / dataframe['width_200'] * 100 + dataframe['72h_close'] = (dataframe['close'] - dataframe['min72_1h']) / dataframe['min72_1h'] * 100 + + return dataframe + + def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (qtpylib.crossed_above(dataframe['close'], dataframe['bull']) | + qtpylib.crossed_above(dataframe['close'].shift(1), dataframe['bull'].shift(1)) | + qtpylib.crossed_above(dataframe['close'].shift(2), dataframe['bull'].shift(2)) + ) & + ((dataframe['rsi'] < 20) | (dataframe['atr_relatif'] > 0.008)) & + # ((dataframe['200_close'] < 25) | (dataframe['width_200'] < 0.10)) & + (dataframe['close3'] < dataframe['close50']) & # Make sure Volume is not 0 + (dataframe['volume'] > 0) # Make sure Volume is not 0 + + ), ['buy', 'enter_tag']] = (1, 'buy_fractal') + + return dataframe + + def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # #(dataframe['volume'] > 0) # Make sure Volume is not 0 + # ), + # 'exit_long'] = 1 + return dataframe + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + last_candle_12 = dataframe.iloc[-13].squeeze() + + allow_to_buy = True #(not self.stop_all) #& (not self.all_down) + + if pair in self.locked_pairs: + trade = self.locked_pairs[pair] + if (last_candle['close3'] >= last_candle['mid72_1h']): + print(pair + ' is locked ' + str(current_time) + " rate=" + str(rate) + " locked_rate=" + str( + trade.open_rate) + ' close3=' + str(last_candle['close3']) + ' mid72=' + str(last_candle['mid72_1h']) + + str(trade)) + allow_to_buy = False + # else: + # print(pair + ' unlocked ' + str(current_time) + " rate=" + str(rate) + " locked_rate=" + str( + # self.locked_pairs[pair])) + # del self.locked_pairs[pair] + + if allow_to_buy: + print('Buy ' + entry_tag + ' ' + str(current_time) + ' ' + pair + " dispo=" + str( + round(self.wallets.get_available_stake_amount())) + " score=" + str(self.calculateScore(last_candle))) + return allow_to_buy + + def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, rate: float, + time_in_force: str, + exit_reason: str, current_time, **kwargs, ) -> bool: + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + + string = "" + buy_signal = self.getTradeCandle(dataframe, trade) + if not buy_signal.empty: + buy_signal_candle = buy_signal.iloc[-1] + string = str(buy_signal_candle['date']) + ' open=' + str(buy_signal_candle['open']) \ + + " score=" + str(self.calculateScore(last_candle)) + + print('Sell trade ' + exit_reason + ' ' + str(current_time) + ' ' + pair + " dispo=" + str( + round(self.wallets.get_available_stake_amount())) #"+ str(amount) + ' ' + str(rate) + + " open_rate=" + str(trade.open_rate) + " rate=" + str(rate) + " profit" + str(trade.calc_profit(rate, amount)) + + " " + string) + + return True + + def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs) -> 'Optional[Union[str, bool]]': + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + if (last_candle['percent'] > 0) | (last_candle['percent3'] > -0.002): + return None + + days = (current_time - trade.open_date_utc).days + hours = (current_time - trade.open_date_utc).seconds / 3600 + minutes = (current_time - trade.open_date_utc).seconds / 60 + + # if (days >= 1) & (current_profit < -0.02): + # return 'too_old' + + self.testLockedTrade(trade, current_profit, current_rate, current_time, pair) + # if pair in self.locked_pairs: + # print(pair + " stoploss") + # return "force_stoploss" + factor = 10 + exit_atr = (trade.open_rate + (last_candle['atr'] * factor)) + if (current_rate > exit_atr): + return "exit_atr" + + def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, + current_rate: float, current_profit: float, **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + if (last_candle['percent'] > 0) | (last_candle['percent3'] > -0.002): + return -1 + + # print(pair + " " + str(current_time) + " rate=" + str(current_profit)) + self.testLockedTrade(trade, current_profit, current_rate, current_time, pair) + + # hard stoploss profit + HSL = self.pHSL.value + PF_1 = self.pPF_1.value + SL_1 = self.pSL_1.value + PF_2 = self.pPF_2.value + SL_2 = self.pSL_2.value + + # For profits between PF_1 and PF_2 the stoploss (sl_profit) used is linearly interpolated + # between the values of SL_1 and SL_2. For all profits above PL_2 the sl_profit value + # rises linearly with current profit, for profits below PF_1 the hard stoploss profit is used. + + if current_profit > PF_2: + sl_profit = SL_2 + (current_profit - PF_2) + elif (current_profit > PF_1): + sl_profit = SL_1 + ((current_profit - PF_1) * (SL_2 - SL_1) / (PF_2 - PF_1)) + else: + sl_profit = HSL + + return stoploss_from_open(sl_profit, current_profit) + + # def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, + # current_rate: float, current_profit: float, **kwargs) -> float: + # dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + # candle = dataframe.iloc[-1].squeeze() + # factor = 10 + # return stoploss_from_absolute(current_rate - (candle['atr'] * factor), current_rate, is_short=trade.is_short) + def getTradeCandle(self, dataframe, trade: 'Trade'): + trade_open_date = timeframe_to_prev_date(self.timeframe, trade.open_date_utc) + buy_signal = dataframe.loc[dataframe['date'] <= trade_open_date] + return buy_signal + + def testLockedTrade(self, trade: 'Trade', current_profit, current_rate, current_time, pair): + if current_profit <= -0.1: + if pair in self.locked_pairs: + trade = self.locked_pairs[pair] + if current_rate > trade.open_rate: + print(pair + ' unlocked ' + str(current_time) + " rate=" + str(current_rate) + " locked_rate=" + str( + trade.open_rate) + ' profit=' + str(current_profit)) + del self.locked_pairs[pair] + # else: + # self.locked_pairs[pair] = current_rate + else: + self.locked_pairs[pair] = trade + # self.lock_pair(pair, until=current_time + timedelta(hours=24)) + + print(pair + " locked at " + str(current_time) + " rate=" + str(current_rate) + " locked_rate=" + str( + trade.open_rate) + ' profit=' + str(current_profit)) + + def will_frac(self, df: pd.DataFrame, period: int = 2) -> Tuple[pd.Series, pd.Series]: + """ + Indicate bearish and bullish fractal patterns using shifted Series. + :param df: OHLC data + :param period: number of lower (or higher) points on each side of a high (or low) + :return: tuple of boolean Series (bearish, bullish) where True marks a fractal pattern + """ + periods = [p for p in range(-period, period + 1) if p != 0] # default [-2, -1, 1, 2] + highs = [df['high'] > df['high'].shift(p) for p in periods] + bears = pd.Series(np.logical_and.reduce(highs), index=df.index) + lows = [df['low'] < df['low'].shift(p) for p in periods] + bulls = pd.Series(np.logical_and.reduce(lows), index=df.index) + df['fractal_sup'] = bears + df['fractal_inf'] = bulls + return df \ No newline at end of file diff --git a/FractalAtr2.py b/FractalAtr2.py new file mode 100644 index 0000000..eafbde1 --- /dev/null +++ b/FractalAtr2.py @@ -0,0 +1,433 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# flake8: noqa: F401 +# isort: skip_file +# --- Do not remove these libs --- +import logging +import numpy as np +import pandas as pd +from pandas import DataFrame +from datetime import datetime, timedelta +from typing import Optional, Union, Tuple +from freqtrade.exchange import timeframe_to_prev_date +from freqtrade.persistence import Trade +from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, stoploss_from_open, + IntParameter, IStrategy, merge_informative_pair, informative, stoploss_from_absolute) + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import pandas_ta as pta +from technical import qtpylib + +class FractalAtr2(IStrategy): + + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 3 + + # Optimal timeframe for the strategy. + timeframe = '1m' + + # Can this strategy go short? + can_short: bool = False + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi". + minimal_roi = { + "0": 0.5 + } + + # Optimal stoploss designed for the strategy. + # This attribute will be overridden if the config file contains "stoploss". + stoploss = -1 + + position_adjustment_enable = True + use_custom_stoploss = True + + # Trailing stoploss + trailing_stop = False + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = True + + # These values can be overridden in the config. + use_exit_signal = True + exit_profit_only = False + ignore_roi_if_entry_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Strategy parameters + buy_rsi = IntParameter(10, 40, default=30, space="buy") + sell_rsi = IntParameter(60, 90, default=70, space="sell") + + # trailing stoploss hyperopt parameters + # hard stoploss profit + + pHSL = DecimalParameter(-0.200, -0.040, default=-0.99, decimals=3, space='sell', optimize=False, load=True) + # profit threshold 1, trigger point, SL_1 is used + pPF_1 = DecimalParameter(0.008, 0.020, default=0.022, decimals=3, space='sell', optimize=True, load=True) + pSL_1 = DecimalParameter(0.008, 0.020, default=0.021, decimals=3, space='sell', optimize=True, load=True) + + # profit threshold 2, SL_2 is used + pPF_2 = DecimalParameter(0.040, 0.100, default=0.080, decimals=3, space='sell', optimize=True, load=True) + pSL_2 = DecimalParameter(0.020, 0.070, default=0.040, decimals=3, space='sell', optimize=True, load=True) + + # Optional order type mapping. + order_types = { + 'entry': 'limit', + 'exit': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'entry': 'GTC', + 'exit': 'GTC' + } + locked_pairs = {} + max_profit_pairs = {} + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + if True | (len(dataframe) < 1): + return None + last_candle = dataframe.iloc[-1].squeeze() + filled_buys = trade.select_filled_orders('buy') + count_of_buys = len(filled_buys) + + condition = (last_candle['enter_long'] == 1) # & (last_candle['close'] <= last_candle['close_1h']) + + # self.protection_nb_buy_lost.value + if (0 < count_of_buys <= 2) & (current_profit < - 0.1) & (condition): + try: + stake_amount = self.config['stake_amount'] + print("Adjust " + trade.pair + " " + str(current_time) + " " + str(current_profit) + " " + str(count_of_buys) + " " + + str(stake_amount)) + return stake_amount + except Exception as exception: + print(exception) + return None + return None + + def calculateScore(self, last_candle): + score = 1 #- (last_candle['percent10'] / 0.01 + return score + + @informative('1d') + def populate_indicators_1d(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['atr'] = ta.ATR(dataframe) + dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_ecart"] = ((dataframe["bb_upperband"] - dataframe["bb_lowerband"])) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + return dataframe + + @informative('1h') + def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + self.will_frac(dataframe) + dataframe['bear'] = dataframe['high'].where(dataframe['fractal_sup'] == True, 0) + dataframe['bear'] = dataframe['bear'].replace(to_replace=0, method="ffill") + dataframe['bull'] = dataframe['low'].where(dataframe['fractal_inf'] == True, 0) + dataframe['bull'] = dataframe['bull'].replace(to_replace=0, method="ffill") + dataframe['max72'] = ta.MAX(dataframe['close'], timeperiod=72) + dataframe['min72'] = ta.MIN(dataframe['close'], timeperiod=72) + dataframe['mid72'] = (dataframe['min72'] + (dataframe['max72'] - dataframe['min72']) / 2) + dataframe['width_72'] = (dataframe['max72'] - dataframe['min72']) / dataframe['max72'] + + dataframe['percent'] = dataframe['close'].pct_change() + dataframe['percent5'] = dataframe['close'].pct_change(5) + return dataframe + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + self.will_frac(dataframe) + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['atr'] = ta.ATR(dataframe) + dataframe['bear'] = dataframe['high'].where(dataframe['fractal_sup'] == True, 0) + dataframe['bear'] = dataframe['bear'].replace(to_replace=0, method="ffill") + dataframe['bull'] = dataframe['low'].where(dataframe['fractal_inf'] == True, 0) + dataframe['bull'] = dataframe['bull'].replace(to_replace=0, method="ffill") + dataframe["percent"] = dataframe["close"].pct_change() + dataframe["percent3"] = dataframe["close"].pct_change(3) + dataframe["percent10"] = dataframe["close"].pct_change(10) + dataframe["diff_close1"] = dataframe["close"] - dataframe["close"].shift(1) + dataframe["diff_close3"] = dataframe["close"] - dataframe["close"].shift(3) + dataframe["diff_close5"] = dataframe["close"] - dataframe["close"].shift(5) + dataframe["diff_close10"] = dataframe["close"] - dataframe["close"].shift(10) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['max50'] = ta.MAX(dataframe['close'], timeperiod=50) + + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_3'] = dataframe['min200'].rolling(3).mean() + + dataframe['max200'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['atr_relatif'] = ta.ATR(dataframe) / dataframe["min200"] + dataframe["close50"] = dataframe["close"].rolling(50).mean() + dataframe["close3"] = dataframe["close"].rolling(3).mean() + dataframe['rsi_mean'] = dataframe['rsi'].rolling(3).mean() + dataframe['atr_relatif_mean'] = dataframe['atr_relatif'].rolling(3).mean() + dataframe['width_200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['max200'] + dataframe['width_close'] = (dataframe['close'] - dataframe['min200']) / dataframe['close'] + dataframe['200_close'] = dataframe['width_close'] / dataframe['width_200'] * 100 + dataframe['72h_close'] = (dataframe['close'] - dataframe['min72_1h']) / dataframe['min72_1h'] * 100 + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_ecart"] = ((dataframe["bb_upperband"] - dataframe["bb_lowerband"])) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + return dataframe + + def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (qtpylib.crossed_above(dataframe['close'], dataframe['bull']) | + qtpylib.crossed_above(dataframe['close'].shift(1), dataframe['bull'].shift(1)) | + qtpylib.crossed_above(dataframe['close'].shift(2), dataframe['bull'].shift(2)) | + (dataframe['low'] <= dataframe['min200']) | + (dataframe['low'] <= dataframe['bb_lowerband_1d']) + ) & + # ((dataframe['rsi'] < 60) | (dataframe['atr_relatif'] > 0.008)) & + # ((dataframe['200_close'] < 25) | (dataframe['width_200'] < 0.10)) & + (dataframe['open'] < dataframe['bb_upperband_1d']) & + (dataframe['bb_width'] >= 0.015) & + (dataframe['width_close'] <= 0.015) & + (dataframe['open'] < dataframe['close3']) & + (dataframe['open'] < dataframe['close50']) & + (dataframe['fractal_inf_1h'] == 1) & + #((dataframe['fractal_inf_1h'] == 1) | (dataframe['low'] <= dataframe['bb_lowerband_1d'])) & + # (dataframe['percent10'] < -0.005) & + (dataframe['volume'] > 0) # Make sure Volume is not 0 + + ), ['buy', 'enter_tag']] = (1, 'buy_fractal') + + # dataframe.loc[ + # ( + # # ((dataframe['200_close'] < 25) | (dataframe['width_200'] < 0.10)) & + # # (dataframe['close3'] <= dataframe['width_200']) & + # (dataframe['min200'].shift(5) == dataframe['min200']) & + # (dataframe['min72_1h'] > dataframe['min200']) & + # (dataframe['width_200'] > 0.035) & + # (dataframe['width_close'] == 0) & + # (dataframe['volume'] > 0) # Make sure Volume is not 0 + # + # ), ['buy', 'enter_tag']] = (1, 'buy_min72') + + return dataframe + + def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + ((dataframe['200_close'].shift(3) > 95) | (dataframe['200_close'].shift(2) > 95) + | (dataframe['200_close'].shift(1) > 95) | (dataframe['200_close'] > 95) + | (dataframe['percent'] < -0.005) + # | (dataframe['high'] >= dataframe['max50']) + # | ((dataframe['percent3'] < 0) & (dataframe['close3'].shift(1) >= dataframe['close3'])) + ) + & + (dataframe['percent'] < 0) & + (dataframe['volume'] > 0) # Make sure Volume is not 0 + ), + 'exit_long'] = 1 + return dataframe + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + last_candle_12 = dataframe.iloc[-13].squeeze() + + allow_to_buy = True #(not self.stop_all) #& (not self.all_down) + + if pair in self.locked_pairs: + trade = self.locked_pairs[pair] + if (last_candle['close3'] >= last_candle['mid72_1h']): + print(pair + ' is locked ' + str(current_time) + " rate=" + str(rate) + " locked_rate=" + str( + trade.open_rate) + ' close3=' + str(last_candle['close3']) + ' mid72=' + str(last_candle['mid72_1h']) + + str(trade)) + allow_to_buy = False + # else: + # print(pair + ' unlocked ' + str(current_time) + " rate=" + str(rate) + " locked_rate=" + str( + # self.locked_pairs[pair])) + # del self.locked_pairs[pair] + + if allow_to_buy: + print('Buy ' + entry_tag + ' ' + str(current_time) + ' ' + pair + " dispo=" + str( + round(self.wallets.get_available_stake_amount())) + " score=" + str(self.calculateScore(last_candle))) + return allow_to_buy + + def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, rate: float, + time_in_force: str, + exit_reason: str, current_time, **kwargs, ) -> bool: + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + + days = (current_time - trade.open_date_utc).days + hours = (current_time - trade.open_date_utc).seconds / 3600 + minutes = (current_time - trade.open_date_utc).seconds / 60 + + allow_to_sell = False #(minutes > 30) + profit = trade.calc_profit(rate, amount) + if not (pair in self.max_profit_pairs): + self.max_profit_pairs[pair] = profit + self.max_profit_pairs[pair] = max(self.max_profit_pairs[pair], profit) + + if (self.max_profit_pairs[pair] > 0.5): + allow_decrease = 0.2 * self.max_profit_pairs[pair] + print("Max profit=" + str(self.max_profit_pairs[pair]) + ' ' + str(profit) + ' ' + str(allow_decrease)) + if self.max_profit_pairs[pair] - profit < allow_decrease: + allow_to_sell = False + else: + allow_to_sell = True + # if last_candle['percent'] <= + + if (profit < - 1.5): + print("Stop loss profit=" + str(self.max_profit_pairs[pair]) + ' ' + str(profit)) + allow_to_sell = True + + string = "" + buy_signal = self.getTradeCandle(dataframe, trade) + if not buy_signal.empty: + buy_signal_candle = buy_signal.iloc[-1] + string = str(buy_signal_candle['date']) + ' open=' + str(buy_signal_candle['open']) \ + + " score=" + str(self.calculateScore(last_candle)) + + if allow_to_sell: + print('Sell trade ' + exit_reason + ' ' + str(current_time) + ' ' + pair + " dispo=" + str( + round(self.wallets.get_available_stake_amount())) #"+ str(amount) + ' ' + str(rate) + + " open_rate=" + str(trade.open_rate) + " rate=" + str(rate) + " profit=" + str(trade.calc_profit(rate, amount)) + + " " + string) + del self.max_profit_pairs[pair] + else: + print('Cancel Sell trade ' + exit_reason + ' ' + str(current_time) + ' ' + pair) + return allow_to_sell + + def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs) -> 'Optional[Union[str, bool]]': + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + if (last_candle['percent'] > 0) | (last_candle['percent3'] > -0.002): + return None + + days = (current_time - trade.open_date_utc).days + hours = (current_time - trade.open_date_utc).seconds / 3600 + minutes = (current_time - trade.open_date_utc).seconds / 60 + + if not (pair in self.max_profit_pairs): + self.max_profit_pairs[pair] = current_profit + self.max_profit_pairs[pair] = max(self.max_profit_pairs[pair], current_profit) + + if (hours >= 2) & (self.max_profit_pairs[pair] < 0.002) & (self.max_profit_pairs[pair] > - 0.002): + return "no_change" + + # if (days >= 1) & (current_profit < -0.02): + # return 'too_old' + + self.testLockedTrade(trade, current_profit, current_rate, current_time, pair) + # if pair in self.locked_pairs: + # print(pair + " stoploss") + # return "force_stoploss" + factor = 10 + exit_atr = (trade.open_rate + (last_candle['atr'] * factor)) + if (current_rate > exit_atr): + return "exit_atr" + + def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, + current_rate: float, current_profit: float, **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + if (last_candle['percent'] > 0) | (last_candle['percent3'] > -0.002): + return -1 + + # print(pair + " " + str(current_time) + " rate=" + str(current_profit)) + self.testLockedTrade(trade, current_profit, current_rate, current_time, pair) + + # hard stoploss profit + HSL = self.pHSL.value + PF_1 = self.pPF_1.value + SL_1 = self.pSL_1.value + PF_2 = self.pPF_2.value + SL_2 = self.pSL_2.value + + # For profits between PF_1 and PF_2 the stoploss (sl_profit) used is linearly interpolated + # between the values of SL_1 and SL_2. For all profits above PL_2 the sl_profit value + # rises linearly with current profit, for profits below PF_1 the hard stoploss profit is used. + + if current_profit > PF_2: + sl_profit = SL_2 + (current_profit - PF_2) + elif (current_profit > PF_1): + sl_profit = SL_1 + ((current_profit - PF_1) * (SL_2 - SL_1) / (PF_2 - PF_1)) + else: + sl_profit = HSL + + return stoploss_from_open(sl_profit, current_profit) + + # def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, + # current_rate: float, current_profit: float, **kwargs) -> float: + # dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + # candle = dataframe.iloc[-1].squeeze() + # factor = 10 + # return stoploss_from_absolute(current_rate - (candle['atr'] * factor), current_rate, is_short=trade.is_short) + def getTradeCandle(self, dataframe, trade: 'Trade'): + trade_open_date = timeframe_to_prev_date(self.timeframe, trade.open_date_utc) + buy_signal = dataframe.loc[dataframe['date'] <= trade_open_date] + return buy_signal + + def testLockedTrade(self, trade: 'Trade', current_profit, current_rate, current_time, pair): + return "" + if current_profit <= -0.1: + if pair in self.locked_pairs: + trade = self.locked_pairs[pair] + if current_rate > trade.open_rate: + print(pair + ' unlocked ' + str(current_time) + " rate=" + str(current_rate) + " locked_rate=" + str( + trade.open_rate) + ' profit=' + str(current_profit)) + del self.locked_pairs[pair] + # else: + # self.locked_pairs[pair] = current_rate + else: + self.locked_pairs[pair] = trade + # self.lock_pair(pair, until=current_time + timedelta(hours=24)) + + print(pair + " locked at " + str(current_time) + " rate=" + str(current_rate) + " locked_rate=" + str( + trade.open_rate) + ' profit=' + str(current_profit)) + + def will_frac(self, df: pd.DataFrame, period: int = 2) -> Tuple[pd.Series, pd.Series]: + """ + Indicate bearish and bullish fractal patterns using shifted Series. + :param df: OHLC data + :param period: number of lower (or higher) points on each side of a high (or low) + :return: tuple of boolean Series (bearish, bullish) where True marks a fractal pattern + """ + periods = [p for p in range(-period, period + 1) if p != 0] # default [-2, -1, 1, 2] + highs = [df['high'] > df['high'].shift(p) for p in periods] + bears = pd.Series(np.logical_and.reduce(highs), index=df.index) + lows = [df['low'] < df['low'].shift(p) for p in periods] + bulls = pd.Series(np.logical_and.reduce(lows), index=df.index) + df['fractal_sup'] = bears + df['fractal_inf'] = bulls + return df diff --git a/Genetic.py b/Genetic.py new file mode 100644 index 0000000..b71fd1c --- /dev/null +++ b/Genetic.py @@ -0,0 +1,362 @@ +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame +# -------------------------------- + +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +import numpy as np # noqa + + +def activate(x): + return np.tanh(x) # tanh + +params = { + '0-0-0-w': -0.53814, + '0-0-bias': -0.96407, + '1-0-0-w': -0.49249, + '10-0-0-w': 0.08845, + '11-0-0-w': -0.14317, + '12-0-0-w': 0.00923, + '13-0-0-w': 0.30464, + '14-0-0-w': -0.35835, + '15-0-0-w': -0.49712, + '16-0-0-w': 0.76135, + '17-0-0-w': -0.75257, + '18-0-0-w': -0.04622, + '19-0-0-w': 0.10012, + '2-0-0-w': -0.23534, + '20-0-0-w': -0.04553, + '21-0-0-w': -0.35334, + '22-0-0-w': 0.17952, + '23-0-0-w': 0.44446, + '24-0-0-w': -0.15875, + '25-0-0-w': 0.97565, + '26-0-0-w': -0.89948, + '27-0-0-w': 0.61777, + '28-0-0-w': -0.60204, + '29-0-0-w': -0.85229, + '3-0-0-w': 0.47262, + '30-0-0-w': -0.52791, + '31-0-0-w': 0.98494, + '4-0-0-w': -0.54942, + '5-0-0-w': 0.40523, + '6-0-0-w': 0.4723, + '7-0-0-w': 0.63297, + '8-0-0-w': 0.07159, + '9-0-0-w': -0.86791, + 'adx-bias': -0.48719, + 'ao-bias': -0.87518, + 'aroonosc-bias': -0.56096, + 'bb_percent-bias': -0.98703, + 'bb_width-bias': -0.73742, + 'cci-bias': 0.47039, + 'end-0-w': -0.81658, + 'end-bias': 0.74656, + 'fastd-bias': -0.2793, + 'fisher_rsi_norm-bias': -0.36065, + 'kc_percent-bias': 0.76707, + 'kc_width-bias': 0.5489, + 'macd-bias': 0.55448, + 'macdhist-bias': -0.83133, + 'macdsignal-bias': 0.30828, + 'mfi-bias': -0.13097, + 'roc-bias': -0.78885, + 'rsi-bias': 0.9856, + 'sar-bias': 0.43812, + 'sma10-bias': -0.39019, + 'sma100-bias': 0.03558, + 'sma21-bias': 0.07457, + 'sma3-bias': 0.93633, + 'sma5-bias': -0.93329, + 'sma50-bias': -0.60637, + 'tema10-bias': -0.45946, + 'tema100-bias': 0.1662, + 'tema21-bias': 0.68466, + 'tema3-bias': 0.25368, + 'tema5-bias': -0.88818, + 'tema50-bias': 0.3019, + 'uo-bias': 0.71019, + 'wbb_percent-bias': -0.55964, + 'wbb_width-bias': 0.23523, + + 's-0-0-0-w': 0.85409, + 's-0-0-bias': -0.04613, + 's-1-0-0-w': -0.14997, + 's-10-0-0-w': -0.67008, + 's-11-0-0-w': -0.40221, + 's-12-0-0-w': 0.64553, + 's-13-0-0-w': 0.22838, + 's-14-0-0-w': 0.99977, + 's-15-0-0-w': 0.89363, + 's-16-0-0-w': -0.88212, + 's-17-0-0-w': -0.71813, + 's-18-0-0-w': 0.41602, + 's-19-0-0-w': -0.48389, + 's-2-0-0-w': 0.09649, + 's-20-0-0-w': 0.64273, + 's-21-0-0-w': -0.31671, + 's-22-0-0-w': 0.9663, + 's-23-0-0-w': 0.00229, + 's-24-0-0-w': 0.96244, + 's-25-0-0-w': -0.24513, + 's-26-0-0-w': 0.52312, + 's-27-0-0-w': 0.44742, + 's-28-0-0-w': -0.03916, + 's-29-0-0-w': 0.88882, + 's-3-0-0-w': -0.32112, + 's-30-0-0-w': -0.70886, + 's-31-0-0-w': -0.42672, + 's-4-0-0-w': -0.55265, + 's-5-0-0-w': 0.56105, + 's-6-0-0-w': 0.47436, + 's-7-0-0-w': 0.58136, + 's-8-0-0-w': -0.48308, + 's-9-0-0-w': -0.16024, + 's-adx-bias': -0.4091, + 's-ao-bias': 0.76889, + 's-aroonosc-bias': 0.16228, + 's-bb_percent-bias': 0.19407, + 's-bb_width-bias': 0.11795, + 's-cci-bias': 0.8379, + 's-end-0-w': -0.14648, + 's-end-bias': -0.85697, + 's-fastd-bias': -0.00581, + 's-fisher_rsi_norm-bias': -0.05253, + 's-kc_percent-bias': -0.3562, + 's-kc_width-bias': 0.67451, + 's-macd-bias': -0.17742, + 's-macdhist-bias': -0.58328, + 's-macdsignal-bias': -0.79847, + 's-mfi-bias': -0.48236, + 's-roc-bias': -0.5914, + 's-rsi-bias': -0.9618, + 's-sar-bias': 0.57033, + 's-sma10-bias': 0.14349, + 's-sma100-bias': 0.02401, + 's-sma21-bias': 0.78191, + 's-sma3-bias': 0.72279, + 's-sma5-bias': -0.19383, + 's-sma50-bias': 0.63697, + 's-tema10-bias': 0.96837, + 's-tema100-bias': 0.77171, + 's-tema21-bias': 0.67279, + 's-tema3-bias': -0.24583, + 's-tema5-bias': -0.08997, + 's-tema50-bias': 0.65532, + 's-uo-bias': 0.67701, + 's-wbb_percent-bias': -0.658, + 's-wbb_width-bias': -0.71056 +} + +network_shape = [1] + +class Genetic(IStrategy): + # ROI table: + minimal_roi = { + "0": 0.21029, + "11": 0.05876, + "57": 0.02191, + "281": 0 + } + + # Stoploss: + stoploss = -0.07693 + + # Optimal ticker interval for the strategy + ticker_interval = '2h' + + # Trailing stop: + trailing_only_offset_is_reached = False + trailing_stop = True + trailing_stop_positive = 0.01019 + trailing_stop_positive_offset = 0.01164 + + # run "populate_indicators" only for new candle + process_only_new_candles = True + + # Experimental settings (configuration will overide these if set) + use_sell_signal = True + sell_profit_only = True + ignore_roi_if_buy_signal = True + + startup_candle_count = 100 + + 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. + """ + + # Momentum Indicators + # ------------------------------------ + + # ADX + dataframe['adx'] = ta.ADX(dataframe) / 100 + + # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # # Minus Directional Indicator / Movement + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + dataframe['aroonosc'] = ta.AROONOSC(dataframe) / 100 + + # # Awesome Oscillator + dataframe['ao'] = ((qtpylib.awesome_oscillator(dataframe) > 0).astype(int) - 0.5) * 2 + + # # Keltner Channel + keltner = qtpylib.keltner_channel(dataframe) + dataframe["kc_upperband"] = keltner["upper"] + dataframe["kc_lowerband"] = keltner["lower"] + dataframe["kc_middleband"] = keltner["mid"] + dataframe["kc_percent"] = ( + (dataframe["close"] - dataframe["kc_lowerband"]) / + (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) + ) + dataframe["kc_width"] = ( + (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) / dataframe["kc_middleband"] + ) + + # # Ultimate Oscillator + dataframe['uo'] = ta.ULTOSC(dataframe) / 100 + + # # Commodity Channel Index: values [Oversold:-100, Overbought:100] + dataframe['cci'] = ta.CCI(dataframe) / 200 + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) / 100 + + # # Inverse Fisher transform on RSI: values [-1.0, 1.0] (https://goo.gl/2JGGoy) + rsi = 0.1 * (dataframe['rsi'] * 100 - 50) + fisher_rsi = (np.exp(2 * rsi) - 1) / (np.exp(2 * rsi) + 1) + + # # Inverse Fisher transform on RSI normalized: values [0.0, 100.0] (https://goo.gl/2JGGoy) + dataframe['fisher_rsi_norm'] = 50 * (fisher_rsi + 1) / 100 + + # # Stochastic Slow + # stoch = ta.STOCH(dataframe) + # dataframe['slowd'] = stoch['slowd'] + # dataframe['slowk'] = stoch['slowk'] + + # Stochastic Fast + stoch_fast = ta.STOCHF(dataframe) + dataframe['fastd'] = stoch_fast['fastd'] / 100 + + # # Stochastic RSI + # stoch_rsi = ta.STOCHRSI(dataframe) + # dataframe['fastd_rsi'] = stoch_rsi['fastd'] + # dataframe['fastk_rsi'] = stoch_rsi['fastk'] + + # MACD + macd = ta.MACD(dataframe) + dataframe['macd'] = macd['macd'] + dataframe['macdsignal'] = macd['macdsignal'] + dataframe['macdhist'] = macd['macdhist'] + + # MFI + dataframe['mfi'] = ta.MFI(dataframe) / 100 + + # # ROC + dataframe['roc'] = ta.ROC(dataframe) / 100 + + # Overlap Studies + # ------------------------------------ + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + # # SMA - Simple Moving Average + dataframe['sma3'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=3) - 1 + dataframe['sma5'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=5) - 1 + dataframe['sma10'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=10) - 1 + dataframe['sma21'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=21) - 1 + dataframe['sma50'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=50) - 1 + dataframe['sma100'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=100) - 1 + + # Parabolic SAR + dataframe['sar'] = dataframe['close'] / ta.SAR(dataframe) - 1 + + # TEMA - Triple Exponential Moving Average + dataframe['tema3'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=3) - 1 + dataframe['tema5'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=5) - 1 + dataframe['tema10'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=10) - 1 + dataframe['tema21'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=21) - 1 + dataframe['tema50'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=50) - 1 + dataframe['tema100'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=100) - 1 + + 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 + :return: DataFrame with buy column + """ + indicators = ['aroonosc', 'ao', 'uo', 'cci', 'rsi', 'fisher_rsi_norm', 'sar', + 'sma3', 'sma5', 'sma10', 'sma21', 'sma50', 'sma100', + 'tema3', 'tema5', 'tema10', 'tema21', 'tema50', 'tema100', + 'fastd', 'adx', 'bb_percent', 'bb_width', 'macd', 'macdsignal', 'macdhist', 'mfi', + 'wbb_percent', 'wbb_width', 'roc', 'kc_percent', 'kc_width'] + inputs = [] + for indicator in indicators: + inputs.append(dataframe[indicator] + params[indicator + '-bias']) + + for index, layer_size in enumerate(network_shape): + outputs = [] + for n in range(layer_size): + weight = 0 + for i, input in enumerate(inputs): + weight += params['{}-{}-{}-w'.format(i, index, n)] * input + weight += params['{}-{}-bias'.format(index, n)] + outputs.append(activate(weight)) + inputs = outputs + + weight = 0 + for i, input in enumerate(inputs): + weight += params['end-{}-w'.format(i)] * input + weight += params['end-bias'] + + dataframe.loc[activate(weight) > 0, 'buy'] = 1 + + # Check that the candle had volume + dataframe.loc[dataframe['volume'] <= 0, 'buy'] = 0 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe \ No newline at end of file diff --git a/GodStra.py b/GodStra.py new file mode 100644 index 0000000..ec4717b --- /dev/null +++ b/GodStra.py @@ -0,0 +1,171 @@ +# GodStra Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT:Add to your pairlists inside config.json (Under StaticPairList): +# { +# "method": "AgeFilter", +# "min_days_listed": 30 +# }, +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# IMPORTANT: Use Smallest "max_open_trades" for getting best results inside config.json + +# --- Do not remove these libs --- +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame +# -------------------------------- + +# Add your lib to import here +# import talib.abstract as ta +from ta import add_all_ta_features +from ta.utils import dropna +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np + + +class GodStra(IStrategy): + # 5/66: 9 trades. 8/0/1 Wins/Draws/Losses. Avg profit 21.83%. Median profit 35.52%. Total profit 1060.11476586 USDT ( 196.50Σ%). Avg duration 3440.0 min. Objective: -7.06960 + # +--------+---------+----------+------------------+--------------+-------------------------------+----------------+-------------+ + # | Best | Epoch | Trades | Win Draw Loss | Avg profit | Profit | Avg duration | Objective | + # |--------+---------+----------+------------------+--------------+-------------------------------+----------------+-------------| + # | * Best | 1/500 | 11 | 2 1 8 | 5.22% | 280.74230393 USDT (57.40%) | 2,421.8 m | -2.85206 | + # | * Best | 2/500 | 10 | 7 0 3 | 18.76% | 983.46414442 USDT (187.58%) | 360.0 m | -4.32665 | + # | * Best | 5/500 | 9 | 8 0 1 | 21.83% | 1,060.11476586 USDT (196.50%) | 3,440.0 m | -7.0696 | + + # Buy hyperspace params: + buy_params = { + 'buy-cross-0': 'volatility_kcc', + 'buy-indicator-0': 'trend_ichimoku_base', + 'buy-int-0': 42, + 'buy-oper-0': ' DataFrame: + # Add all ta features + dataframe = dropna(dataframe) + dataframe = add_all_ta_features( + dataframe, open="open", high="high", low="low", close="close", volume="volume", + fillna=True) + # dataframe.to_csv("df.csv", index=True) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = list() + # /5: Cuz We have 5 Group of variables inside buy_param + for i in range(self.dna_size(self.buy_params)): + + OPR = self.buy_params[f'buy-oper-{i}'] + IND = self.buy_params[f'buy-indicator-{i}'] + CRS = self.buy_params[f'buy-cross-{i}'] + INT = self.buy_params[f'buy-int-{i}'] + REAL = self.buy_params[f'buy-real-{i}'] + DFIND = dataframe[IND] + DFCRS = dataframe[CRS] + + if OPR == ">": + conditions.append(DFIND > DFCRS) + elif OPR == "=": + conditions.append(np.isclose(DFIND, DFCRS)) + elif OPR == "<": + conditions.append(DFIND < DFCRS) + elif OPR == "CA": + conditions.append(qtpylib.crossed_above(DFIND, DFCRS)) + elif OPR == "CB": + conditions.append(qtpylib.crossed_below(DFIND, DFCRS)) + elif OPR == ">I": + conditions.append(DFIND > INT) + elif OPR == "=I": + conditions.append(DFIND == INT) + elif OPR == "R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == " DataFrame: + conditions = list() + for i in range(self.dna_size(self.sell_params)): + OPR = self.sell_params[f'sell-oper-{i}'] + IND = self.sell_params[f'sell-indicator-{i}'] + CRS = self.sell_params[f'sell-cross-{i}'] + INT = self.sell_params[f'sell-int-{i}'] + REAL = self.sell_params[f'sell-real-{i}'] + DFIND = dataframe[IND] + DFCRS = dataframe[CRS] + + if OPR == ">": + conditions.append(DFIND > DFCRS) + elif OPR == "=": + conditions.append(np.isclose(DFIND, DFCRS)) + elif OPR == "<": + conditions.append(DFIND < DFCRS) + elif OPR == "CA": + conditions.append(qtpylib.crossed_above(DFIND, DFCRS)) + elif OPR == "CB": + conditions.append(qtpylib.crossed_below(DFIND, DFCRS)) + elif OPR == ">I": + conditions.append(DFIND > INT) + elif OPR == "=I": + conditions.append(DFIND == INT) + elif OPR == "R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == " List[Dimension]: + """ + Define your Hyperopt space for searching buy strategy parameters. + """ + gene = list() + + for i in range(DNA_SIZE): + gene.append(Categorical(GodGenes, name=f'buy-indicator-{i}')) + gene.append(Categorical(GodGenes, name=f'buy-cross-{i}')) + gene.append(Integer(-1, 101, name=f'buy-int-{i}')) + gene.append(Real(-1.1, 1.1, name=f'buy-real-{i}')) + # Operations + # CA: Crossed Above, CB: Crossed Below, + # I: Integer, R: Real, D: Disabled + gene.append(Categorical(["D", ">", "<", "=", "CA", "CB", + ">I", "=I", "R", "=R", " 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 + for i in range(DNA_SIZE): + + OPR = params[f'buy-oper-{i}'] + IND = params[f'buy-indicator-{i}'] + CRS = params[f'buy-cross-{i}'] + INT = params[f'buy-int-{i}'] + REAL = params[f'buy-real-{i}'] + DFIND = dataframe[IND] + DFCRS = dataframe[CRS] + + if OPR == ">": + conditions.append(DFIND > DFCRS) + elif OPR == "=": + conditions.append(np.isclose(DFIND, DFCRS)) + elif OPR == "<": + conditions.append(DFIND < DFCRS) + elif OPR == "CA": + conditions.append(qtpylib.crossed_above(DFIND, DFCRS)) + elif OPR == "CB": + conditions.append(qtpylib.crossed_below(DFIND, DFCRS)) + elif OPR == ">I": + conditions.append(DFIND > INT) + elif OPR == "=I": + conditions.append(DFIND == INT) + elif OPR == "R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == " List[Dimension]: + """ + Define your Hyperopt space for searching sell strategy parameters. + """ + gene = list() + + for i in range(DNA_SIZE): + gene.append(Categorical(GodGenes, name=f'sell-indicator-{i}')) + gene.append(Categorical(GodGenes, name=f'sell-cross-{i}')) + gene.append(Integer(-1, 101, name=f'sell-int-{i}')) + gene.append(Real(-0.01, 1.01, name=f'sell-real-{i}')) + # Operations + # CA: Crossed Above, CB: Crossed Below, + # I: Integer, R: Real, D: Disabled + gene.append(Categorical(["D", ">", "<", "=", "CA", "CB", + ">I", "=I", "R", "=R", " Callable: + """ + Define the sell strategy parameters to be used by Hyperopt. + """ + def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Sell strategy Hyperopt will build and use. + """ + conditions = [] + + # GUARDS AND TRENDS + for i in range(DNA_SIZE): + + OPR = params[f'sell-oper-{i}'] + IND = params[f'sell-indicator-{i}'] + CRS = params[f'sell-cross-{i}'] + INT = params[f'sell-int-{i}'] + REAL = params[f'sell-real-{i}'] + DFIND = dataframe[IND] + DFCRS = dataframe[CRS] + + if OPR == ">": + conditions.append(DFIND > DFCRS) + elif OPR == "=": + conditions.append(np.isclose(DFIND, DFCRS)) + elif OPR == "<": + conditions.append(DFIND < DFCRS) + elif OPR == "CA": + conditions.append(qtpylib.crossed_above(DFIND, DFCRS)) + elif OPR == "CB": + conditions.append(qtpylib.crossed_below(DFIND, DFCRS)) + elif OPR == ">I": + conditions.append(DFIND > INT) + elif OPR == "=I": + conditions.append(DFIND == INT) + elif OPR == "R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator( + dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 0.598, + "644": 0.166, + "3269": 0.115, + "7289": 0 + } + + # Stoploss: + stoploss = -0.128 + # Buy hypers + timeframe = '4h' + + # Trailing stoploss + trailing_stop = True + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + buy_crossed_indicator0 = CategoricalParameter( + god_genes_with_timeperiod, default="ADD-20", space='buy') + buy_crossed_indicator1 = CategoricalParameter( + god_genes_with_timeperiod, default="ASIN-6", space='buy') + buy_crossed_indicator2 = CategoricalParameter( + god_genes_with_timeperiod, default="CDLEVENINGSTAR-50", space='buy') + + buy_indicator0 = CategoricalParameter( + god_genes_with_timeperiod, default="SMA-100", space='buy') + buy_indicator1 = CategoricalParameter( + god_genes_with_timeperiod, default="WILLR-50", space='buy') + buy_indicator2 = CategoricalParameter( + god_genes_with_timeperiod, default="CDLHANGINGMAN-20", space='buy') + + buy_operator0 = CategoricalParameter(operators, default="/ DataFrame: + ''' + It's good to calculate all indicators in all time periods here and so optimize the strategy. + But this strategy can take much time to generate anything that may not use in his optimization. + I just calculate the specific indicators in specific time period inside buy and sell strategy populator methods if needed. + Also, this method (populate_indicators) just calculates default value of hyperoptable params + so using this method have not big benefits instade of calculating useable things inside buy and sell trand populators + ''' + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + # TODO: Its not dry code! + buy_indicator = self.buy_indicator0.value + buy_crossed_indicator = self.buy_crossed_indicator0.value + buy_operator = self.buy_operator0.value + buy_real_num = self.buy_real_num0.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + # backup + buy_indicator = self.buy_indicator1.value + buy_crossed_indicator = self.buy_crossed_indicator1.value + buy_operator = self.buy_operator1.value + buy_real_num = self.buy_real_num1.value + + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + buy_indicator = self.buy_indicator2.value + buy_crossed_indicator = self.buy_crossed_indicator2.value + buy_operator = self.buy_operator2.value + buy_real_num = self.buy_real_num2.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['bb_lowerband']) + & (dataframe['bb_width'] >= 0.15) + & (dataframe['volume'] > 0) + ) | + reduce(lambda x, y: x & y, conditions), + 'buy']=1 + + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + # TODO: Its not dry code! + sell_indicator = self.sell_indicator0.value + sell_crossed_indicator = self.sell_crossed_indicator0.value + sell_operator = self.sell_operator0.value + sell_real_num = self.sell_real_num0.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator1.value + sell_crossed_indicator = self.sell_crossed_indicator1.value + sell_operator = self.sell_operator1.value + sell_real_num = self.sell_real_num1.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator2.value + sell_crossed_indicator = self.sell_crossed_indicator2.value + sell_operator = self.sell_operator2.value + sell_real_num = self.sell_real_num2.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'sell']=1 + return dataframe diff --git a/GodStraJD1.py b/GodStraJD1.py new file mode 100644 index 0000000..5bfe5dc --- /dev/null +++ b/GodStraJD1.py @@ -0,0 +1,858 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from freqtrade import data +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle +# TODO: this gene is removed 'MAVP' cuz or error on periods +all_god_genes = { + 'Overlap Studies': { + 'BBANDS-0', # Bollinger Bands + 'BBANDS-1', # Bollinger Bands + 'BBANDS-2', # Bollinger Bands + 'DEMA', # Double Exponential Moving Average + 'EMA', # Exponential Moving Average + 'HT_TRENDLINE', # Hilbert Transform - Instantaneous Trendline + 'KAMA', # Kaufman Adaptive Moving Average + 'MA', # Moving average + 'MAMA-0', # MESA Adaptive Moving Average + 'MAMA-1', # MESA Adaptive Moving Average + # TODO: Fix this + # 'MAVP', # Moving average with variable period + 'MIDPOINT', # MidPoint over period + 'MIDPRICE', # Midpoint Price over period + 'SAR', # Parabolic SAR + 'SAREXT', # Parabolic SAR - Extended + 'SMA', # Simple Moving Average + 'T3', # Triple Exponential Moving Average (T3) + 'TEMA', # Triple Exponential Moving Average + 'TRIMA', # Triangular Moving Average + 'WMA', # Weighted Moving Average + }, + 'Momentum Indicators': { + 'ADX', # Average Directional Movement Index + 'ADXR', # Average Directional Movement Index Rating + 'APO', # Absolute Price Oscillator + 'AROON-0', # Aroon + 'AROON-1', # Aroon + 'AROONOSC', # Aroon Oscillator + 'BOP', # Balance Of Power + 'CCI', # Commodity Channel Index + 'CMO', # Chande Momentum Oscillator + 'DX', # Directional Movement Index + 'MACD-0', # Moving Average Convergence/Divergence + 'MACD-1', # Moving Average Convergence/Divergence + 'MACD-2', # Moving Average Convergence/Divergence + 'MACDEXT-0', # MACD with controllable MA type + 'MACDEXT-1', # MACD with controllable MA type + 'MACDEXT-2', # MACD with controllable MA type + 'MACDFIX-0', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-1', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-2', # Moving Average Convergence/Divergence Fix 12/26 + 'MFI', # Money Flow Index + 'MINUS_DI', # Minus Directional Indicator + 'MINUS_DM', # Minus Directional Movement + 'MOM', # Momentum + 'PLUS_DI', # Plus Directional Indicator + 'PLUS_DM', # Plus Directional Movement + 'PPO', # Percentage Price Oscillator + 'ROC', # Rate of change : ((price/prevPrice)-1)*100 + # Rate of change Percentage: (price-prevPrice)/prevPrice + 'ROCP', + 'ROCR', # Rate of change ratio: (price/prevPrice) + # Rate of change ratio 100 scale: (price/prevPrice)*100 + 'ROCR100', + 'RSI', # Relative Strength Index + 'STOCH-0', # Stochastic + 'STOCH-1', # Stochastic + 'STOCHF-0', # Stochastic Fast + 'STOCHF-1', # Stochastic Fast + 'STOCHRSI-0', # Stochastic Relative Strength Index + 'STOCHRSI-1', # Stochastic Relative Strength Index + # 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA + 'TRIX', + 'ULTOSC', # Ultimate Oscillator + 'WILLR', # Williams' %R + }, + 'Volume Indicators': { + 'AD', # Chaikin A/D Line + 'ADOSC', # Chaikin A/D Oscillator + 'OBV', # On Balance Volume + }, + 'Volatility Indicators': { + 'ATR', # Average True Range + 'NATR', # Normalized Average True Range + 'TRANGE', # True Range + }, + 'Price Transform': { + 'AVGPRICE', # Average Price + 'MEDPRICE', # Median Price + 'TYPPRICE', # Typical Price + 'WCLPRICE', # Weighted Close Price + }, + 'Cycle Indicators': { + 'HT_DCPERIOD', # Hilbert Transform - Dominant Cycle Period + 'HT_DCPHASE', # Hilbert Transform - Dominant Cycle Phase + 'HT_PHASOR-0', # Hilbert Transform - Phasor Components + 'HT_PHASOR-1', # Hilbert Transform - Phasor Components + 'HT_SINE-0', # Hilbert Transform - SineWave + 'HT_SINE-1', # Hilbert Transform - SineWave + 'HT_TRENDMODE', # Hilbert Transform - Trend vs Cycle Mode + }, + 'Pattern Recognition': { + 'CDL2CROWS', # Two Crows + 'CDL3BLACKCROWS', # Three Black Crows + 'CDL3INSIDE', # Three Inside Up/Down + 'CDL3LINESTRIKE', # Three-Line Strike + 'CDL3OUTSIDE', # Three Outside Up/Down + 'CDL3STARSINSOUTH', # Three Stars In The South + 'CDL3WHITESOLDIERS', # Three Advancing White Soldiers + 'CDLABANDONEDBABY', # Abandoned Baby + 'CDLADVANCEBLOCK', # Advance Block + 'CDLBELTHOLD', # Belt-hold + 'CDLBREAKAWAY', # Breakaway + 'CDLCLOSINGMARUBOZU', # Closing Marubozu + 'CDLCONCEALBABYSWALL', # Concealing Baby Swallow + 'CDLCOUNTERATTACK', # Counterattack + 'CDLDARKCLOUDCOVER', # Dark Cloud Cover + 'CDLDOJI', # Doji + 'CDLDOJISTAR', # Doji Star + 'CDLDRAGONFLYDOJI', # Dragonfly Doji + 'CDLENGULFING', # Engulfing Pattern + 'CDLEVENINGDOJISTAR', # Evening Doji Star + 'CDLEVENINGSTAR', # Evening Star + 'CDLGAPSIDESIDEWHITE', # Up/Down-gap side-by-side white lines + 'CDLGRAVESTONEDOJI', # Gravestone Doji + 'CDLHAMMER', # Hammer + 'CDLHANGINGMAN', # Hanging Man + 'CDLHARAMI', # Harami Pattern + 'CDLHARAMICROSS', # Harami Cross Pattern + 'CDLHIGHWAVE', # High-Wave Candle + 'CDLHIKKAKE', # Hikkake Pattern + 'CDLHIKKAKEMOD', # Modified Hikkake Pattern + 'CDLHOMINGPIGEON', # Homing Pigeon + 'CDLIDENTICAL3CROWS', # Identical Three Crows + 'CDLINNECK', # In-Neck Pattern + 'CDLINVERTEDHAMMER', # Inverted Hammer + 'CDLKICKING', # Kicking + 'CDLKICKINGBYLENGTH', # Kicking - bull/bear determined by the longer marubozu + 'CDLLADDERBOTTOM', # Ladder Bottom + 'CDLLONGLEGGEDDOJI', # Long Legged Doji + 'CDLLONGLINE', # Long Line Candle + 'CDLMARUBOZU', # Marubozu + 'CDLMATCHINGLOW', # Matching Low + 'CDLMATHOLD', # Mat Hold + 'CDLMORNINGDOJISTAR', # Morning Doji Star + 'CDLMORNINGSTAR', # Morning Star + 'CDLONNECK', # On-Neck Pattern + 'CDLPIERCING', # Piercing Pattern + 'CDLRICKSHAWMAN', # Rickshaw Man + 'CDLRISEFALL3METHODS', # Rising/Falling Three Methods + 'CDLSEPARATINGLINES', # Separating Lines + 'CDLSHOOTINGSTAR', # Shooting Star + 'CDLSHORTLINE', # Short Line Candle + 'CDLSPINNINGTOP', # Spinning Top + 'CDLSTALLEDPATTERN', # Stalled Pattern + 'CDLSTICKSANDWICH', # Stick Sandwich + # Takuri (Dragonfly Doji with very long lower shadow) + 'CDLTAKURI', + 'CDLTASUKIGAP', # Tasuki Gap + 'CDLTHRUSTING', # Thrusting Pattern + 'CDLTRISTAR', # Tristar Pattern + 'CDLUNIQUE3RIVER', # Unique 3 River + 'CDLUPSIDEGAP2CROWS', # Upside Gap Two Crows + 'CDLXSIDEGAP3METHODS', # Upside/Downside Gap Three Methods + + }, + 'Statistic Functions': { + 'BETA', # Beta + 'CORREL', # Pearson's Correlation Coefficient (r) + 'LINEARREG', # Linear Regression + 'LINEARREG_ANGLE', # Linear Regression Angle + 'LINEARREG_INTERCEPT', # Linear Regression Intercept + 'LINEARREG_SLOPE', # Linear Regression Slope + 'STDDEV', # Standard Deviation + 'TSF', # Time Series Forecast + 'VAR', # Variance + } + +} +god_genes = set() +########################### SETTINGS ############################## + +# god_genes = {'SMA'} +god_genes |= all_god_genes['Overlap Studies'] +god_genes |= all_god_genes['Momentum Indicators'] +god_genes |= all_god_genes['Volume Indicators'] +god_genes |= all_god_genes['Volatility Indicators'] +god_genes |= all_god_genes['Price Transform'] +god_genes |= all_god_genes['Cycle Indicators'] +god_genes |= all_god_genes['Pattern Recognition'] +god_genes |= all_god_genes['Statistic Functions'] + +timeperiods = [5, 6, 12, 15, 50, 55, 100, 110] +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator( + dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD1(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 0.598, + "644": 0.166, + "3269": 0.115, + "7289": 0 + } + + # Stoploss: + stoploss = -0.128 + # Buy hypers + timeframe = '4h' + # Trailing stoploss + trailing_stop = True + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + buy_crossed_indicator0 = CategoricalParameter( + god_genes_with_timeperiod, default="ADD-20", space='buy') + buy_crossed_indicator1 = CategoricalParameter( + god_genes_with_timeperiod, default="ASIN-6", space='buy') + buy_crossed_indicator2 = CategoricalParameter( + god_genes_with_timeperiod, default="CDLEVENINGSTAR-50", space='buy') + + buy_indicator0 = CategoricalParameter( + god_genes_with_timeperiod, default="SMA-100", space='buy') + buy_indicator1 = CategoricalParameter( + god_genes_with_timeperiod, default="WILLR-50", space='buy') + buy_indicator2 = CategoricalParameter( + god_genes_with_timeperiod, default="CDLHANGINGMAN-20", space='buy') + + buy_operator0 = CategoricalParameter(operators, default="/ DataFrame: + ''' + It's good to calculate all indicators in all time periods here and so optimize the strategy. + But this strategy can take much time to generate anything that may not use in his optimization. + I just calculate the specific indicators in specific time period inside buy and sell strategy populator methods if needed. + Also, this method (populate_indicators) just calculates default value of hyperoptable params + so using this method have not big benefits instade of calculating useable things inside buy and sell trand populators + ''' + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + # TODO: Its not dry code! + buy_indicator = self.buy_indicator0.value + buy_crossed_indicator = self.buy_crossed_indicator0.value + buy_operator = self.buy_operator0.value + buy_real_num = self.buy_real_num0.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + # backup + buy_indicator = self.buy_indicator1.value + buy_crossed_indicator = self.buy_crossed_indicator1.value + buy_operator = self.buy_operator1.value + buy_real_num = self.buy_real_num1.value + + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + buy_indicator = self.buy_indicator2.value + buy_crossed_indicator = self.buy_crossed_indicator2.value + buy_operator = self.buy_operator2.value + buy_real_num = self.buy_real_num2.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['bb_lowerband']) + & (dataframe['bb_width'] >= 0.14) + & (dataframe['volume'] > 0) + ) | + reduce(lambda x, y: x & y, conditions), + 'buy']=1 + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + # TODO: Its not dry code! + sell_indicator = self.sell_indicator0.value + sell_crossed_indicator = self.sell_crossed_indicator0.value + sell_operator = self.sell_operator0.value + sell_real_num = self.sell_real_num0.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator1.value + sell_crossed_indicator = self.sell_crossed_indicator1.value + sell_operator = self.sell_operator1.value + sell_real_num = self.sell_real_num1.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator2.value + sell_crossed_indicator = self.sell_crossed_indicator2.value + sell_operator = self.sell_operator2.value + sell_real_num = self.sell_real_num2.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + #dataframe.loc[(dataframe['close'] < dataframe['bb_lowerband']),'sell']=1 + + if conditions: + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + # ( + # # StrategyHelperLocal.four_red_candles(dataframe) & + # # dataframe['bb_width'] >= 0.025 + # ( + # ((dataframe['open'] - dataframe['close'].shift(4)) / dataframe['open'] > 0.01) | + # ((dataframe['open'] - dataframe['close'].shift(5)) / dataframe['open'] > 0.01) | + # ((dataframe['open'] - dataframe['close'].shift(6)) / dataframe['open'] > 0.01) | + # ((dataframe['open'] - dataframe['close'].shift(7)) / dataframe['open'] > 0.01) + # ) + # ) + ), + 'sell']=1 + return dataframe + + +class StrategyHelperLocal: + """ + simple helper class to predefine a couple of patterns for our + strategy + """ + + @staticmethod + def two_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) + # (dataframe['open'].shift(2) < dataframe['close'].shift(2)) + ) + + @staticmethod + def no_more_three_green_candles(dataframe): + """ + evaluates if we are having not more than 3 green candles in a row + :param self: + :param dataframe: + :return: + """ + return not ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) + ) + + @staticmethod + def seven_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) + ) + + @staticmethod + def eight_green_candles(dataframe): + """ + evaluates if we are having 8 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) & + (dataframe['open'].shift(8) < dataframe['close'].shift(8)) + ) + + @staticmethod + def two_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) + # (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def four_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) & + (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + (dataframe['open'].shift(3 + shift) > dataframe['close'].shift(3 + shift)) & + (dataframe['open'].shift(4 + shift) > dataframe['close'].shift(4 + shift)) + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def two_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) + ) + + @staticmethod + def four_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] > dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) + ) + + @staticmethod + def four_red_one_green_candle(dataframe): + """ + evaluates if we are having a green candle and 4 previous red + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) > dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) > dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) > dataframe['close'].shift(4)) + ) diff --git a/GodStraJD2.py b/GodStraJD2.py new file mode 100644 index 0000000..0e1699f --- /dev/null +++ b/GodStraJD2.py @@ -0,0 +1,848 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from freqtrade import data +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle +# TODO: this gene is removed 'MAVP' cuz or error on periods +all_god_genes = { + 'Overlap Studies': { + 'BBANDS-0', # Bollinger Bands + 'BBANDS-1', # Bollinger Bands + 'BBANDS-2', # Bollinger Bands + 'DEMA', # Double Exponential Moving Average + 'EMA', # Exponential Moving Average + 'HT_TRENDLINE', # Hilbert Transform - Instantaneous Trendline + 'KAMA', # Kaufman Adaptive Moving Average + 'MA', # Moving average + 'MAMA-0', # MESA Adaptive Moving Average + 'MAMA-1', # MESA Adaptive Moving Average + # TODO: Fix this + # 'MAVP', # Moving average with variable period + 'MIDPOINT', # MidPoint over period + 'MIDPRICE', # Midpoint Price over period + 'SAR', # Parabolic SAR + 'SAREXT', # Parabolic SAR - Extended + 'SMA', # Simple Moving Average + 'T3', # Triple Exponential Moving Average (T3) + 'TEMA', # Triple Exponential Moving Average + 'TRIMA', # Triangular Moving Average + 'WMA', # Weighted Moving Average + }, + 'Momentum Indicators': { + 'ADX', # Average Directional Movement Index + 'ADXR', # Average Directional Movement Index Rating + 'APO', # Absolute Price Oscillator + 'AROON-0', # Aroon + 'AROON-1', # Aroon + 'AROONOSC', # Aroon Oscillator + 'BOP', # Balance Of Power + 'CCI', # Commodity Channel Index + 'CMO', # Chande Momentum Oscillator + 'DX', # Directional Movement Index + 'MACD-0', # Moving Average Convergence/Divergence + 'MACD-1', # Moving Average Convergence/Divergence + 'MACD-2', # Moving Average Convergence/Divergence + 'MACDEXT-0', # MACD with controllable MA type + 'MACDEXT-1', # MACD with controllable MA type + 'MACDEXT-2', # MACD with controllable MA type + 'MACDFIX-0', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-1', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-2', # Moving Average Convergence/Divergence Fix 12/26 + 'MFI', # Money Flow Index + 'MINUS_DI', # Minus Directional Indicator + 'MINUS_DM', # Minus Directional Movement + 'MOM', # Momentum + 'PLUS_DI', # Plus Directional Indicator + 'PLUS_DM', # Plus Directional Movement + 'PPO', # Percentage Price Oscillator + 'ROC', # Rate of change : ((price/prevPrice)-1)*100 + # Rate of change Percentage: (price-prevPrice)/prevPrice + 'ROCP', + 'ROCR', # Rate of change ratio: (price/prevPrice) + # Rate of change ratio 100 scale: (price/prevPrice)*100 + 'ROCR100', + 'RSI', # Relative Strength Index + 'STOCH-0', # Stochastic + 'STOCH-1', # Stochastic + 'STOCHF-0', # Stochastic Fast + 'STOCHF-1', # Stochastic Fast + 'STOCHRSI-0', # Stochastic Relative Strength Index + 'STOCHRSI-1', # Stochastic Relative Strength Index + # 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA + 'TRIX', + 'ULTOSC', # Ultimate Oscillator + 'WILLR', # Williams' %R + }, + 'Volume Indicators': { + 'AD', # Chaikin A/D Line + 'ADOSC', # Chaikin A/D Oscillator + 'OBV', # On Balance Volume + }, + 'Volatility Indicators': { + 'ATR', # Average True Range + 'NATR', # Normalized Average True Range + 'TRANGE', # True Range + }, + 'Price Transform': { + 'AVGPRICE', # Average Price + 'MEDPRICE', # Median Price + 'TYPPRICE', # Typical Price + 'WCLPRICE', # Weighted Close Price + }, + 'Cycle Indicators': { + 'HT_DCPERIOD', # Hilbert Transform - Dominant Cycle Period + 'HT_DCPHASE', # Hilbert Transform - Dominant Cycle Phase + 'HT_PHASOR-0', # Hilbert Transform - Phasor Components + 'HT_PHASOR-1', # Hilbert Transform - Phasor Components + 'HT_SINE-0', # Hilbert Transform - SineWave + 'HT_SINE-1', # Hilbert Transform - SineWave + 'HT_TRENDMODE', # Hilbert Transform - Trend vs Cycle Mode + }, + 'Pattern Recognition': { + 'CDL2CROWS', # Two Crows + 'CDL3BLACKCROWS', # Three Black Crows + 'CDL3INSIDE', # Three Inside Up/Down + 'CDL3LINESTRIKE', # Three-Line Strike + 'CDL3OUTSIDE', # Three Outside Up/Down + 'CDL3STARSINSOUTH', # Three Stars In The South + 'CDL3WHITESOLDIERS', # Three Advancing White Soldiers + 'CDLABANDONEDBABY', # Abandoned Baby + 'CDLADVANCEBLOCK', # Advance Block + 'CDLBELTHOLD', # Belt-hold + 'CDLBREAKAWAY', # Breakaway + 'CDLCLOSINGMARUBOZU', # Closing Marubozu + 'CDLCONCEALBABYSWALL', # Concealing Baby Swallow + 'CDLCOUNTERATTACK', # Counterattack + 'CDLDARKCLOUDCOVER', # Dark Cloud Cover + 'CDLDOJI', # Doji + 'CDLDOJISTAR', # Doji Star + 'CDLDRAGONFLYDOJI', # Dragonfly Doji + 'CDLENGULFING', # Engulfing Pattern + 'CDLEVENINGDOJISTAR', # Evening Doji Star + 'CDLEVENINGSTAR', # Evening Star + 'CDLGAPSIDESIDEWHITE', # Up/Down-gap side-by-side white lines + 'CDLGRAVESTONEDOJI', # Gravestone Doji + 'CDLHAMMER', # Hammer + 'CDLHANGINGMAN', # Hanging Man + 'CDLHARAMI', # Harami Pattern + 'CDLHARAMICROSS', # Harami Cross Pattern + 'CDLHIGHWAVE', # High-Wave Candle + 'CDLHIKKAKE', # Hikkake Pattern + 'CDLHIKKAKEMOD', # Modified Hikkake Pattern + 'CDLHOMINGPIGEON', # Homing Pigeon + 'CDLIDENTICAL3CROWS', # Identical Three Crows + 'CDLINNECK', # In-Neck Pattern + 'CDLINVERTEDHAMMER', # Inverted Hammer + 'CDLKICKING', # Kicking + 'CDLKICKINGBYLENGTH', # Kicking - bull/bear determined by the longer marubozu + 'CDLLADDERBOTTOM', # Ladder Bottom + 'CDLLONGLEGGEDDOJI', # Long Legged Doji + 'CDLLONGLINE', # Long Line Candle + 'CDLMARUBOZU', # Marubozu + 'CDLMATCHINGLOW', # Matching Low + 'CDLMATHOLD', # Mat Hold + 'CDLMORNINGDOJISTAR', # Morning Doji Star + 'CDLMORNINGSTAR', # Morning Star + 'CDLONNECK', # On-Neck Pattern + 'CDLPIERCING', # Piercing Pattern + 'CDLRICKSHAWMAN', # Rickshaw Man + 'CDLRISEFALL3METHODS', # Rising/Falling Three Methods + 'CDLSEPARATINGLINES', # Separating Lines + 'CDLSHOOTINGSTAR', # Shooting Star + 'CDLSHORTLINE', # Short Line Candle + 'CDLSPINNINGTOP', # Spinning Top + 'CDLSTALLEDPATTERN', # Stalled Pattern + 'CDLSTICKSANDWICH', # Stick Sandwich + # Takuri (Dragonfly Doji with very long lower shadow) + 'CDLTAKURI', + 'CDLTASUKIGAP', # Tasuki Gap + 'CDLTHRUSTING', # Thrusting Pattern + 'CDLTRISTAR', # Tristar Pattern + 'CDLUNIQUE3RIVER', # Unique 3 River + 'CDLUPSIDEGAP2CROWS', # Upside Gap Two Crows + 'CDLXSIDEGAP3METHODS', # Upside/Downside Gap Three Methods + + }, + 'Statistic Functions': { + 'BETA', # Beta + 'CORREL', # Pearson's Correlation Coefficient (r) + 'LINEARREG', # Linear Regression + 'LINEARREG_ANGLE', # Linear Regression Angle + 'LINEARREG_INTERCEPT', # Linear Regression Intercept + 'LINEARREG_SLOPE', # Linear Regression Slope + 'STDDEV', # Standard Deviation + 'TSF', # Time Series Forecast + 'VAR', # Variance + } + +} +god_genes = set() +########################### SETTINGS ############################## + +# god_genes = {'SMA'} +god_genes |= all_god_genes['Overlap Studies'] +god_genes |= all_god_genes['Momentum Indicators'] +god_genes |= all_god_genes['Volume Indicators'] +god_genes |= all_god_genes['Volatility Indicators'] +god_genes |= all_god_genes['Price Transform'] +god_genes |= all_god_genes['Cycle Indicators'] +god_genes |= all_god_genes['Pattern Recognition'] +god_genes |= all_god_genes['Statistic Functions'] + +timeperiods = [5, 6, 12, 15, 50, 55, 100, 110] +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator( + dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD2(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 0.598, + "644": 0.166, + "3269": 0.115, + "7289": 0 + } + + # Stoploss: + stoploss = -0.128 + # Buy hypers + timeframe = '4h' + + # Trailing stoploss + trailing_stop = True + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + buy_crossed_indicator0 = CategoricalParameter( + god_genes_with_timeperiod, default="ADD-20", space='buy') + buy_crossed_indicator1 = CategoricalParameter( + god_genes_with_timeperiod, default="ASIN-6", space='buy') + buy_crossed_indicator2 = CategoricalParameter( + god_genes_with_timeperiod, default="CDLEVENINGSTAR-50", space='buy') + + buy_indicator0 = CategoricalParameter( + god_genes_with_timeperiod, default="SMA-100", space='buy') + buy_indicator1 = CategoricalParameter( + god_genes_with_timeperiod, default="WILLR-50", space='buy') + buy_indicator2 = CategoricalParameter( + god_genes_with_timeperiod, default="CDLHANGINGMAN-20", space='buy') + + buy_operator0 = CategoricalParameter(operators, default="/ DataFrame: + ''' + It's good to calculate all indicators in all time periods here and so optimize the strategy. + But this strategy can take much time to generate anything that may not use in his optimization. + I just calculate the specific indicators in specific time period inside buy and sell strategy populator methods if needed. + Also, this method (populate_indicators) just calculates default value of hyperoptable params + so using this method have not big benefits instade of calculating useable things inside buy and sell trand populators + ''' + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + # TODO: Its not dry code! + buy_indicator = self.buy_indicator0.value + buy_crossed_indicator = self.buy_crossed_indicator0.value + buy_operator = self.buy_operator0.value + buy_real_num = self.buy_real_num0.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + # backup + buy_indicator = self.buy_indicator1.value + buy_crossed_indicator = self.buy_crossed_indicator1.value + buy_operator = self.buy_operator1.value + buy_real_num = self.buy_real_num1.value + + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + buy_indicator = self.buy_indicator2.value + buy_crossed_indicator = self.buy_crossed_indicator2.value + buy_operator = self.buy_operator2.value + buy_real_num = self.buy_real_num2.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['bb_lowerband']) + & (dataframe['bb_width'] >= 0.13) + & (dataframe['volume'] > 0) + ) | + reduce(lambda x, y: x & y, conditions), + 'buy']=1 + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + # TODO: Its not dry code! + sell_indicator = self.sell_indicator0.value + sell_crossed_indicator = self.sell_crossed_indicator0.value + sell_operator = self.sell_operator0.value + sell_real_num = self.sell_real_num0.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator1.value + sell_crossed_indicator = self.sell_crossed_indicator1.value + sell_operator = self.sell_operator1.value + sell_real_num = self.sell_real_num1.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator2.value + sell_crossed_indicator = self.sell_crossed_indicator2.value + sell_operator = self.sell_operator2.value + sell_real_num = self.sell_real_num2.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + #dataframe.loc[(dataframe['close'] < dataframe['bb_lowerband']),'sell']=1 + + if conditions: + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + ), + 'sell']=1 + return dataframe + + +class StrategyHelperLocal: + """ + simple helper class to predefine a couple of patterns for our + strategy + """ + + @staticmethod + def two_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) + # (dataframe['open'].shift(2) < dataframe['close'].shift(2)) + ) + + @staticmethod + def no_more_three_green_candles(dataframe): + """ + evaluates if we are having not more than 3 green candles in a row + :param self: + :param dataframe: + :return: + """ + return not ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) + ) + + @staticmethod + def seven_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) + ) + + @staticmethod + def eight_green_candles(dataframe): + """ + evaluates if we are having 8 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) & + (dataframe['open'].shift(8) < dataframe['close'].shift(8)) + ) + + @staticmethod + def two_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) + # (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def four_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) & + (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + (dataframe['open'].shift(3 + shift) > dataframe['close'].shift(3 + shift)) & + (dataframe['open'].shift(4 + shift) > dataframe['close'].shift(4 + shift)) + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def two_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) + ) + + @staticmethod + def four_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] > dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) + ) + + @staticmethod + def four_red_one_green_candle(dataframe): + """ + evaluates if we are having a green candle and 4 previous red + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) > dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) > dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) > dataframe['close'].shift(4)) + ) diff --git a/GodStraJD3.py b/GodStraJD3.py new file mode 100644 index 0000000..caefc0e --- /dev/null +++ b/GodStraJD3.py @@ -0,0 +1,863 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from freqtrade import data +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle +# TODO: this gene is removed 'MAVP' cuz or error on periods +all_god_genes = { + 'Overlap Studies': { + 'BBANDS-0', # Bollinger Bands + 'BBANDS-1', # Bollinger Bands + 'BBANDS-2', # Bollinger Bands + 'DEMA', # Double Exponential Moving Average + 'EMA', # Exponential Moving Average + 'HT_TRENDLINE', # Hilbert Transform - Instantaneous Trendline + 'KAMA', # Kaufman Adaptive Moving Average + 'MA', # Moving average + 'MAMA-0', # MESA Adaptive Moving Average + 'MAMA-1', # MESA Adaptive Moving Average + # TODO: Fix this + # 'MAVP', # Moving average with variable period + 'MIDPOINT', # MidPoint over period + 'MIDPRICE', # Midpoint Price over period + 'SAR', # Parabolic SAR + 'SAREXT', # Parabolic SAR - Extended + 'SMA', # Simple Moving Average + 'T3', # Triple Exponential Moving Average (T3) + 'TEMA', # Triple Exponential Moving Average + 'TRIMA', # Triangular Moving Average + 'WMA', # Weighted Moving Average + }, + 'Momentum Indicators': { + 'ADX', # Average Directional Movement Index + 'ADXR', # Average Directional Movement Index Rating + 'APO', # Absolute Price Oscillator + 'AROON-0', # Aroon + 'AROON-1', # Aroon + 'AROONOSC', # Aroon Oscillator + 'BOP', # Balance Of Power + 'CCI', # Commodity Channel Index + 'CMO', # Chande Momentum Oscillator + 'DX', # Directional Movement Index + 'MACD-0', # Moving Average Convergence/Divergence + 'MACD-1', # Moving Average Convergence/Divergence + 'MACD-2', # Moving Average Convergence/Divergence + 'MACDEXT-0', # MACD with controllable MA type + 'MACDEXT-1', # MACD with controllable MA type + 'MACDEXT-2', # MACD with controllable MA type + 'MACDFIX-0', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-1', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-2', # Moving Average Convergence/Divergence Fix 12/26 + 'MFI', # Money Flow Index + 'MINUS_DI', # Minus Directional Indicator + 'MINUS_DM', # Minus Directional Movement + 'MOM', # Momentum + 'PLUS_DI', # Plus Directional Indicator + 'PLUS_DM', # Plus Directional Movement + 'PPO', # Percentage Price Oscillator + 'ROC', # Rate of change : ((price/prevPrice)-1)*100 + # Rate of change Percentage: (price-prevPrice)/prevPrice + 'ROCP', + 'ROCR', # Rate of change ratio: (price/prevPrice) + # Rate of change ratio 100 scale: (price/prevPrice)*100 + 'ROCR100', + 'RSI', # Relative Strength Index + 'STOCH-0', # Stochastic + 'STOCH-1', # Stochastic + 'STOCHF-0', # Stochastic Fast + 'STOCHF-1', # Stochastic Fast + 'STOCHRSI-0', # Stochastic Relative Strength Index + 'STOCHRSI-1', # Stochastic Relative Strength Index + # 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA + 'TRIX', + 'ULTOSC', # Ultimate Oscillator + 'WILLR', # Williams' %R + }, + 'Volume Indicators': { + 'AD', # Chaikin A/D Line + 'ADOSC', # Chaikin A/D Oscillator + 'OBV', # On Balance Volume + }, + 'Volatility Indicators': { + 'ATR', # Average True Range + 'NATR', # Normalized Average True Range + 'TRANGE', # True Range + }, + 'Price Transform': { + 'AVGPRICE', # Average Price + 'MEDPRICE', # Median Price + 'TYPPRICE', # Typical Price + 'WCLPRICE', # Weighted Close Price + }, + 'Cycle Indicators': { + 'HT_DCPERIOD', # Hilbert Transform - Dominant Cycle Period + 'HT_DCPHASE', # Hilbert Transform - Dominant Cycle Phase + 'HT_PHASOR-0', # Hilbert Transform - Phasor Components + 'HT_PHASOR-1', # Hilbert Transform - Phasor Components + 'HT_SINE-0', # Hilbert Transform - SineWave + 'HT_SINE-1', # Hilbert Transform - SineWave + 'HT_TRENDMODE', # Hilbert Transform - Trend vs Cycle Mode + }, + 'Pattern Recognition': { + 'CDL2CROWS', # Two Crows + 'CDL3BLACKCROWS', # Three Black Crows + 'CDL3INSIDE', # Three Inside Up/Down + 'CDL3LINESTRIKE', # Three-Line Strike + 'CDL3OUTSIDE', # Three Outside Up/Down + 'CDL3STARSINSOUTH', # Three Stars In The South + 'CDL3WHITESOLDIERS', # Three Advancing White Soldiers + 'CDLABANDONEDBABY', # Abandoned Baby + 'CDLADVANCEBLOCK', # Advance Block + 'CDLBELTHOLD', # Belt-hold + 'CDLBREAKAWAY', # Breakaway + 'CDLCLOSINGMARUBOZU', # Closing Marubozu + 'CDLCONCEALBABYSWALL', # Concealing Baby Swallow + 'CDLCOUNTERATTACK', # Counterattack + 'CDLDARKCLOUDCOVER', # Dark Cloud Cover + 'CDLDOJI', # Doji + 'CDLDOJISTAR', # Doji Star + 'CDLDRAGONFLYDOJI', # Dragonfly Doji + 'CDLENGULFING', # Engulfing Pattern + 'CDLEVENINGDOJISTAR', # Evening Doji Star + 'CDLEVENINGSTAR', # Evening Star + 'CDLGAPSIDESIDEWHITE', # Up/Down-gap side-by-side white lines + 'CDLGRAVESTONEDOJI', # Gravestone Doji + 'CDLHAMMER', # Hammer + 'CDLHANGINGMAN', # Hanging Man + 'CDLHARAMI', # Harami Pattern + 'CDLHARAMICROSS', # Harami Cross Pattern + 'CDLHIGHWAVE', # High-Wave Candle + 'CDLHIKKAKE', # Hikkake Pattern + 'CDLHIKKAKEMOD', # Modified Hikkake Pattern + 'CDLHOMINGPIGEON', # Homing Pigeon + 'CDLIDENTICAL3CROWS', # Identical Three Crows + 'CDLINNECK', # In-Neck Pattern + 'CDLINVERTEDHAMMER', # Inverted Hammer + 'CDLKICKING', # Kicking + 'CDLKICKINGBYLENGTH', # Kicking - bull/bear determined by the longer marubozu + 'CDLLADDERBOTTOM', # Ladder Bottom + 'CDLLONGLEGGEDDOJI', # Long Legged Doji + 'CDLLONGLINE', # Long Line Candle + 'CDLMARUBOZU', # Marubozu + 'CDLMATCHINGLOW', # Matching Low + 'CDLMATHOLD', # Mat Hold + 'CDLMORNINGDOJISTAR', # Morning Doji Star + 'CDLMORNINGSTAR', # Morning Star + 'CDLONNECK', # On-Neck Pattern + 'CDLPIERCING', # Piercing Pattern + 'CDLRICKSHAWMAN', # Rickshaw Man + 'CDLRISEFALL3METHODS', # Rising/Falling Three Methods + 'CDLSEPARATINGLINES', # Separating Lines + 'CDLSHOOTINGSTAR', # Shooting Star + 'CDLSHORTLINE', # Short Line Candle + 'CDLSPINNINGTOP', # Spinning Top + 'CDLSTALLEDPATTERN', # Stalled Pattern + 'CDLSTICKSANDWICH', # Stick Sandwich + # Takuri (Dragonfly Doji with very long lower shadow) + 'CDLTAKURI', + 'CDLTASUKIGAP', # Tasuki Gap + 'CDLTHRUSTING', # Thrusting Pattern + 'CDLTRISTAR', # Tristar Pattern + 'CDLUNIQUE3RIVER', # Unique 3 River + 'CDLUPSIDEGAP2CROWS', # Upside Gap Two Crows + 'CDLXSIDEGAP3METHODS', # Upside/Downside Gap Three Methods + + }, + 'Statistic Functions': { + 'BETA', # Beta + 'CORREL', # Pearson's Correlation Coefficient (r) + 'LINEARREG', # Linear Regression + 'LINEARREG_ANGLE', # Linear Regression Angle + 'LINEARREG_INTERCEPT', # Linear Regression Intercept + 'LINEARREG_SLOPE', # Linear Regression Slope + 'STDDEV', # Standard Deviation + 'TSF', # Time Series Forecast + 'VAR', # Variance + } + +} +god_genes = set() +########################### SETTINGS ############################## + +# god_genes = {'SMA'} +god_genes |= all_god_genes['Overlap Studies'] +god_genes |= all_god_genes['Momentum Indicators'] +god_genes |= all_god_genes['Volume Indicators'] +god_genes |= all_god_genes['Volatility Indicators'] +god_genes |= all_god_genes['Price Transform'] +god_genes |= all_god_genes['Cycle Indicators'] +god_genes |= all_god_genes['Pattern Recognition'] +god_genes |= all_god_genes['Statistic Functions'] + +timeperiods = [5, 6, 12, 15, 50, 55, 100, 110] +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD3(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 0.598, + "644": 0.166, + "3269": 0.115, + "7289": 0 + } + + # Stoploss: + stoploss = -0.128 + # Buy hypers + timeframe = '4h' + + # Trailing stoploss + trailing_stop = True + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bbwitth': {'color': 'blue'}, + } + } + } + + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + buy_crossed_indicator0 = CategoricalParameter( + god_genes_with_timeperiod, default="ADD-20", space='buy') + buy_crossed_indicator1 = CategoricalParameter( + god_genes_with_timeperiod, default="ASIN-6", space='buy') + buy_crossed_indicator2 = CategoricalParameter( + god_genes_with_timeperiod, default="CDLEVENINGSTAR-50", space='buy') + + buy_indicator0 = CategoricalParameter( + god_genes_with_timeperiod, default="SMA-100", space='buy') + buy_indicator1 = CategoricalParameter( + god_genes_with_timeperiod, default="WILLR-50", space='buy') + buy_indicator2 = CategoricalParameter( + god_genes_with_timeperiod, default="CDLHANGINGMAN-20", space='buy') + + buy_operator0 = CategoricalParameter(operators, default="/ DataFrame: + ''' + It's good to calculate all indicators in all time periods here and so optimize the strategy. + But this strategy can take much time to generate anything that may not use in his optimization. + I just calculate the specific indicators in specific time period inside buy and sell strategy populator methods if needed. + Also, this method (populate_indicators) just calculates default value of hyperoptable params + so using this method have not big benefits instade of calculating useable things inside buy and sell trand populators + ''' + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + # TODO: Its not dry code! + buy_indicator = self.buy_indicator0.value + buy_crossed_indicator = self.buy_crossed_indicator0.value + buy_operator = self.buy_operator0.value + buy_real_num = self.buy_real_num0.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + # backup + buy_indicator = self.buy_indicator1.value + buy_crossed_indicator = self.buy_crossed_indicator1.value + buy_operator = self.buy_operator1.value + buy_real_num = self.buy_real_num1.value + + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + buy_indicator = self.buy_indicator2.value + buy_crossed_indicator = self.buy_crossed_indicator2.value + buy_operator = self.buy_operator2.value + buy_real_num = self.buy_real_num2.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['bb_lowerband']) + & (dataframe['bb_width'] >= 0.12) + & (dataframe['volume'] > 0) + ) | + reduce(lambda x, y: x & y, conditions), + 'buy']=1 + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + # TODO: Its not dry code! + sell_indicator = self.sell_indicator0.value + sell_crossed_indicator = self.sell_crossed_indicator0.value + sell_operator = self.sell_operator0.value + sell_real_num = self.sell_real_num0.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator1.value + sell_crossed_indicator = self.sell_crossed_indicator1.value + sell_operator = self.sell_operator1.value + sell_real_num = self.sell_real_num1.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator2.value + sell_crossed_indicator = self.sell_crossed_indicator2.value + sell_operator = self.sell_operator2.value + sell_real_num = self.sell_real_num2.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + #dataframe.loc[(dataframe['close'] < dataframe['bb_lowerband']),'sell']=1 + + if conditions: + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + ), + 'sell']=1 + return dataframe + + +class StrategyHelperLocal: + """ + simple helper class to predefine a couple of patterns for our + strategy + """ + + @staticmethod + def two_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) + # (dataframe['open'].shift(2) < dataframe['close'].shift(2)) + ) + + @staticmethod + def no_more_three_green_candles(dataframe): + """ + evaluates if we are having not more than 3 green candles in a row + :param self: + :param dataframe: + :return: + """ + return not ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) + ) + + @staticmethod + def seven_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) + ) + + @staticmethod + def eight_green_candles(dataframe): + """ + evaluates if we are having 8 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) & + (dataframe['open'].shift(8) < dataframe['close'].shift(8)) + ) + + @staticmethod + def two_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) + # (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def four_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) & + (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + (dataframe['open'].shift(3 + shift) > dataframe['close'].shift(3 + shift)) & + (dataframe['open'].shift(4 + shift) > dataframe['close'].shift(4 + shift)) + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def two_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) + ) + + @staticmethod + def four_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] > dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) + ) + + @staticmethod + def four_red_one_green_candle(dataframe): + """ + evaluates if we are having a green candle and 4 previous red + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) > dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) > dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) > dataframe['close'].shift(4)) + ) diff --git a/GodStraJD3_1.json b/GodStraJD3_1.json new file mode 100644 index 0000000..4a5a28e --- /dev/null +++ b/GodStraJD3_1.json @@ -0,0 +1,51 @@ +{ + "strategy_name": "GodStraJD3_1", + "params": { + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.15, + "trailing_stop_positive_offset": 0.2, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_crossed_indicator0": "CDLINNECK-6", + "buy_crossed_indicator1": "DEMA-12", + "buy_crossed_indicator2": "CDLHAMMER-12", + "buy_indicator0": "CDLXSIDEGAP3METHODS-55", + "buy_indicator1": "HT_DCPERIOD-55", + "buy_indicator2": "LINEARREG_INTERCEPT-6", + "buy_operator0": "=R", + "buy_operator1": ">R", + "buy_operator2": ">R", + "buy_real_num0": 0.0, + "buy_real_num1": 0.1, + "buy_real_num2": 0.3 + }, + "sell": { + "sell_crossed_indicator0": "AVGPRICE-100", + "sell_crossed_indicator1": "CDLGAPSIDESIDEWHITE-6", + "sell_crossed_indicator2": "PLUS_DI-15", + "sell_indicator0": "BBANDS-2-50", + "sell_indicator1": "PLUS_DM-6", + "sell_indicator2": "MIDPOINT-100", + "sell_operator0": "/=R", + "sell_operator1": "CUT", + "sell_operator2": "CUT", + "sell_real_num0": 0.1, + "sell_real_num1": 0.2, + "sell_real_num2": 1.0 + }, + "protection": {}, + "roi": { + "0": 0.594, + "1344": 0.201, + "3068": 0.081, + "5986": 0 + }, + "stoploss": { + "stoploss": -0.116 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-01-03 05:11:14.667810+00:00" +} diff --git a/GodStraJD3_1.py b/GodStraJD3_1.py new file mode 100644 index 0000000..97cde64 --- /dev/null +++ b/GodStraJD3_1.py @@ -0,0 +1,1033 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import datetime + +from freqtrade import data +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle +# TODO: this gene is removed 'MAVP' cuz or error on periods +all_god_genes = { + 'Overlap Studies': { + 'BBANDS-0', # Bollinger Bands + 'BBANDS-1', # Bollinger Bands + 'BBANDS-2', # Bollinger Bands + 'DEMA', # Double Exponential Moving Average + 'EMA', # Exponential Moving Average + 'HT_TRENDLINE', # Hilbert Transform - Instantaneous Trendline + 'KAMA', # Kaufman Adaptive Moving Average + 'MA', # Moving average + 'MAMA-0', # MESA Adaptive Moving Average + 'MAMA-1', # MESA Adaptive Moving Average + # TODO: Fix this + # 'MAVP', # Moving average with variable period + 'MIDPOINT', # MidPoint over period + 'MIDPRICE', # Midpoint Price over period + 'SAR', # Parabolic SAR + 'SAREXT', # Parabolic SAR - Extended + 'SMA', # Simple Moving Average + 'T3', # Triple Exponential Moving Average (T3) + 'TEMA', # Triple Exponential Moving Average + 'TRIMA', # Triangular Moving Average + 'WMA', # Weighted Moving Average + }, + 'Momentum Indicators': { + 'ADX', # Average Directional Movement Index + 'ADXR', # Average Directional Movement Index Rating + 'APO', # Absolute Price Oscillator + 'AROON-0', # Aroon + 'AROON-1', # Aroon + 'AROONOSC', # Aroon Oscillator + 'BOP', # Balance Of Power + 'CCI', # Commodity Channel Index + 'CMO', # Chande Momentum Oscillator + 'DX', # Directional Movement Index + 'MACD-0', # Moving Average Convergence/Divergence + 'MACD-1', # Moving Average Convergence/Divergence + 'MACD-2', # Moving Average Convergence/Divergence + 'MACDEXT-0', # MACD with controllable MA type + 'MACDEXT-1', # MACD with controllable MA type + 'MACDEXT-2', # MACD with controllable MA type + 'MACDFIX-0', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-1', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-2', # Moving Average Convergence/Divergence Fix 12/26 + 'MFI', # Money Flow Index + 'MINUS_DI', # Minus Directional Indicator + 'MINUS_DM', # Minus Directional Movement + 'MOM', # Momentum + 'PLUS_DI', # Plus Directional Indicator + 'PLUS_DM', # Plus Directional Movement + 'PPO', # Percentage Price Oscillator + 'ROC', # Rate of change : ((price/prevPrice)-1)*100 + # Rate of change Percentage: (price-prevPrice)/prevPrice + 'ROCP', + 'ROCR', # Rate of change ratio: (price/prevPrice) + # Rate of change ratio 100 scale: (price/prevPrice)*100 + 'ROCR100', + 'RSI', # Relative Strength Index + 'STOCH-0', # Stochastic + 'STOCH-1', # Stochastic + 'STOCHF-0', # Stochastic Fast + 'STOCHF-1', # Stochastic Fast + 'STOCHRSI-0', # Stochastic Relative Strength Index + 'STOCHRSI-1', # Stochastic Relative Strength Index + # 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA + 'TRIX', + 'ULTOSC', # Ultimate Oscillator + 'WILLR', # Williams' %R + }, + 'Volume Indicators': { + 'AD', # Chaikin A/D Line + 'ADOSC', # Chaikin A/D Oscillator + 'OBV', # On Balance Volume + }, + 'Volatility Indicators': { + 'ATR', # Average True Range + 'NATR', # Normalized Average True Range + 'TRANGE', # True Range + }, + 'Price Transform': { + 'AVGPRICE', # Average Price + 'MEDPRICE', # Median Price + 'TYPPRICE', # Typical Price + 'WCLPRICE', # Weighted Close Price + }, + 'Cycle Indicators': { + 'HT_DCPERIOD', # Hilbert Transform - Dominant Cycle Period + 'HT_DCPHASE', # Hilbert Transform - Dominant Cycle Phase + 'HT_PHASOR-0', # Hilbert Transform - Phasor Components + 'HT_PHASOR-1', # Hilbert Transform - Phasor Components + 'HT_SINE-0', # Hilbert Transform - SineWave + 'HT_SINE-1', # Hilbert Transform - SineWave + 'HT_TRENDMODE', # Hilbert Transform - Trend vs Cycle Mode + }, + 'Pattern Recognition': { + 'CDL2CROWS', # Two Crows + 'CDL3BLACKCROWS', # Three Black Crows + 'CDL3INSIDE', # Three Inside Up/Down + 'CDL3LINESTRIKE', # Three-Line Strike + 'CDL3OUTSIDE', # Three Outside Up/Down + 'CDL3STARSINSOUTH', # Three Stars In The South + 'CDL3WHITESOLDIERS', # Three Advancing White Soldiers + 'CDLABANDONEDBABY', # Abandoned Baby + 'CDLADVANCEBLOCK', # Advance Block + 'CDLBELTHOLD', # Belt-hold + 'CDLBREAKAWAY', # Breakaway + 'CDLCLOSINGMARUBOZU', # Closing Marubozu + 'CDLCONCEALBABYSWALL', # Concealing Baby Swallow + 'CDLCOUNTERATTACK', # Counterattack + 'CDLDARKCLOUDCOVER', # Dark Cloud Cover + 'CDLDOJI', # Doji + 'CDLDOJISTAR', # Doji Star + 'CDLDRAGONFLYDOJI', # Dragonfly Doji + 'CDLENGULFING', # Engulfing Pattern + 'CDLEVENINGDOJISTAR', # Evening Doji Star + 'CDLEVENINGSTAR', # Evening Star + 'CDLGAPSIDESIDEWHITE', # Up/Down-gap side-by-side white lines + 'CDLGRAVESTONEDOJI', # Gravestone Doji + 'CDLHAMMER', # Hammer + 'CDLHANGINGMAN', # Hanging Man + 'CDLHARAMI', # Harami Pattern + 'CDLHARAMICROSS', # Harami Cross Pattern + 'CDLHIGHWAVE', # High-Wave Candle + 'CDLHIKKAKE', # Hikkake Pattern + 'CDLHIKKAKEMOD', # Modified Hikkake Pattern + 'CDLHOMINGPIGEON', # Homing Pigeon + 'CDLIDENTICAL3CROWS', # Identical Three Crows + 'CDLINNECK', # In-Neck Pattern + 'CDLINVERTEDHAMMER', # Inverted Hammer + 'CDLKICKING', # Kicking + 'CDLKICKINGBYLENGTH', # Kicking - bull/bear determined by the longer marubozu + 'CDLLADDERBOTTOM', # Ladder Bottom + 'CDLLONGLEGGEDDOJI', # Long Legged Doji + 'CDLLONGLINE', # Long Line Candle + 'CDLMARUBOZU', # Marubozu + 'CDLMATCHINGLOW', # Matching Low + 'CDLMATHOLD', # Mat Hold + 'CDLMORNINGDOJISTAR', # Morning Doji Star + 'CDLMORNINGSTAR', # Morning Star + 'CDLONNECK', # On-Neck Pattern + 'CDLPIERCING', # Piercing Pattern + 'CDLRICKSHAWMAN', # Rickshaw Man + 'CDLRISEFALL3METHODS', # Rising/Falling Three Methods + 'CDLSEPARATINGLINES', # Separating Lines + 'CDLSHOOTINGSTAR', # Shooting Star + 'CDLSHORTLINE', # Short Line Candle + 'CDLSPINNINGTOP', # Spinning Top + 'CDLSTALLEDPATTERN', # Stalled Pattern + 'CDLSTICKSANDWICH', # Stick Sandwich + # Takuri (Dragonfly Doji with very long lower shadow) + 'CDLTAKURI', + 'CDLTASUKIGAP', # Tasuki Gap + 'CDLTHRUSTING', # Thrusting Pattern + 'CDLTRISTAR', # Tristar Pattern + 'CDLUNIQUE3RIVER', # Unique 3 River + 'CDLUPSIDEGAP2CROWS', # Upside Gap Two Crows + 'CDLXSIDEGAP3METHODS', # Upside/Downside Gap Three Methods + + }, + 'Statistic Functions': { + 'BETA', # Beta + 'CORREL', # Pearson's Correlation Coefficient (r) + 'LINEARREG', # Linear Regression + 'LINEARREG_ANGLE', # Linear Regression Angle + 'LINEARREG_INTERCEPT', # Linear Regression Intercept + 'LINEARREG_SLOPE', # Linear Regression Slope + 'STDDEV', # Standard Deviation + 'TSF', # Time Series Forecast + 'VAR', # Variance + } + +} +god_genes = set() +########################### SETTINGS ############################## + +# god_genes = {'SMA'} +god_genes |= all_god_genes['Overlap Studies'] +god_genes |= all_god_genes['Momentum Indicators'] +god_genes |= all_god_genes['Volume Indicators'] +god_genes |= all_god_genes['Volatility Indicators'] +god_genes |= all_god_genes['Price Transform'] +god_genes |= all_god_genes['Cycle Indicators'] +god_genes |= all_god_genes['Pattern Recognition'] +god_genes |= all_god_genes['Statistic Functions'] + +timeperiods = [5, 6, 12, 15, 50, 55, 100, 110] +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator( + dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD3_1(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 1, + "600": 0.166, + "1200": 0.14, + "2400": 0.1, + "3600": 0.05, + "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '4h' + + # Trailing stoploss + trailing_stop = True + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + 'bb_min': {'color': 'red'}, + }, + # "ADX": { + # 'adx': {'color': 'white'}, + # 'minus_dm': {'color': 'blue'}, + # 'plus_dm': {'color': 'red'} + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + }, + "percent": { + "percent": {'color': 'green'}, + "percent5": {'color': 'red'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + buy_crossed_indicator0 = CategoricalParameter( + god_genes_with_timeperiod, default="ADD-20", space='buy') + buy_crossed_indicator1 = CategoricalParameter( + god_genes_with_timeperiod, default="ASIN-6", space='buy') + buy_crossed_indicator2 = CategoricalParameter( + god_genes_with_timeperiod, default="CDLEVENINGSTAR-50", space='buy') + + buy_indicator0 = CategoricalParameter( + god_genes_with_timeperiod, default="SMA-100", space='buy') + buy_indicator1 = CategoricalParameter( + god_genes_with_timeperiod, default="WILLR-50", space='buy') + buy_indicator2 = CategoricalParameter( + god_genes_with_timeperiod, default="CDLHANGINGMAN-20", space='buy') + + buy_operator0 = CategoricalParameter(operators, default="/ float: + # + # dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + # current_candle = dataframe.iloc[-1].squeeze() + # + # # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + # if current_candle['bb_width'] > 0.065: + # # print("use more stake", pair, " ", proposed_stake * 2) + # return min(max_stake, proposed_stake * 2) + # + # if current_candle['bb_width'] > 0.045: + # # print("use more stake", pair, " ", proposed_stake * 1.5) + # return min(max_stake, proposed_stake * 1.5) + # + # # if current_candle['bb_width'] < 0.020: + # # print("use less stake", pair, " ", proposed_stake / 2) + # # return min(max_stake, proposed_stake / 2) + # # if self.config['stake_amount'] == 'unlimited': + # # # Use entire available wallet during favorable conditions when in compounding mode. + # # return max_stake + # # else: + # # # Compound profits during favorable conditions instead of using a static stake. + # # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + # + # # Use default stake amount. + # return proposed_stake + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # (last_candle['percent5'] < -0.005) \ + # if (0 < current_profit < 0.005) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 2): + # # & (previous_last_candle['sma10'] > last_candle['sma10']): + # print("too_small_gain", pair, trade, " profit=", current_profit, " rate=", current_rate, " percent5=", + # last_candle['percent5']) + # return 'too_small_gain' + + # if (current_profit < -0.05) \ + # & ((current_time - trade.open_date_utc).days >= 3): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'stop_loss_profit' + + # if (current_profit > 0.02) \ + # & (last_candle['percent'] < 0.01) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'lost_half_profit' + + if (current_profit > 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \ + & (previous_5_candle['sma20'] > last_candle['sma20']) \ + & (last_candle['percent'] < 0) \ + & (last_candle['percent5'] < 0): + # print("over_bb_band_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_bb_band_sma20_desc' + + if (current_profit > 0) \ + & (last_candle['rsi'] > 75): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + # description trade + # Trade(id=0, pair=CAKE/USDT, amount=4.19815281, open_rate=11.91000000, open_since=2021-12-22 17:55:00) + # print(last_candle) + if 0.015 < current_profit < 1: + if ( + (previous_last_candle['sma10'] > last_candle['sma10'] * 1.005) & + (current_time - trade.open_date_utc).seconds >= 3600 * 3 + # ) | ( + # (current_time - trade.open_date_utc).seconds >= 3600 * 6 + ): + # self.lock_pair(pair, until=current_time + timedelta(hours=3)) + + # print("profit_3h_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_3h_sma10_desc' + + if (0 < current_profit < 0.1) \ + & (previous_last_candle['sma20'] > last_candle['sma20']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 5): + # print("profit_5h_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_5h_sma20_desc' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # dataframe['min'] = ta.MIN(dataframe) + # dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + # TODO: Its not dry code! + buy_indicator = self.buy_indicator0.value + buy_crossed_indicator = self.buy_crossed_indicator0.value + buy_operator = self.buy_operator0.value + buy_real_num = self.buy_real_num0.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + # backup + buy_indicator = self.buy_indicator1.value + buy_crossed_indicator = self.buy_crossed_indicator1.value + buy_operator = self.buy_operator1.value + buy_real_num = self.buy_real_num1.value + + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + buy_indicator = self.buy_indicator2.value + buy_crossed_indicator = self.buy_crossed_indicator2.value + buy_operator = self.buy_operator2.value + buy_real_num = self.buy_real_num2.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['bb_lowerband']) + & (dataframe['bb_width'] >= 0.12) + & (dataframe['volume'] * dataframe['close'] / 1000 > 100) + ) | + reduce(lambda x, y: x & y, conditions), + 'buy']=1 + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + # TODO: Its not dry code! + sell_indicator = self.sell_indicator0.value + sell_crossed_indicator = self.sell_crossed_indicator0.value + sell_operator = self.sell_operator0.value + sell_real_num = self.sell_real_num0.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator1.value + sell_crossed_indicator = self.sell_crossed_indicator1.value + sell_operator = self.sell_operator1.value + sell_real_num = self.sell_real_num1.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator2.value + sell_crossed_indicator = self.sell_crossed_indicator2.value + sell_operator = self.sell_operator2.value + sell_real_num = self.sell_real_num2.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + #dataframe.loc[(dataframe['close'] < dataframe['bb_lowerband']),'sell']=1 + + if conditions: + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + ), + 'sell']=1 + return dataframe + + +class StrategyHelperLocal: + """ + simple helper class to predefine a couple of patterns for our + strategy + """ + + @staticmethod + def two_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) + # (dataframe['open'].shift(2) < dataframe['close'].shift(2)) + ) + + @staticmethod + def no_more_three_green_candles(dataframe): + """ + evaluates if we are having not more than 3 green candles in a row + :param self: + :param dataframe: + :return: + """ + return not ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) + ) + + @staticmethod + def seven_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) + ) + + @staticmethod + def eight_green_candles(dataframe): + """ + evaluates if we are having 8 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) & + (dataframe['open'].shift(8) < dataframe['close'].shift(8)) + ) + + @staticmethod + def two_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) + # (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def four_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) & + (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + (dataframe['open'].shift(3 + shift) > dataframe['close'].shift(3 + shift)) & + (dataframe['open'].shift(4 + shift) > dataframe['close'].shift(4 + shift)) + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def two_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) + ) + + @staticmethod + def four_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] > dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) + ) + + @staticmethod + def four_red_one_green_candle(dataframe): + """ + evaluates if we are having a green candle and 4 previous red + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) > dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) > dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) > dataframe['close'].shift(4)) + ) diff --git a/GodStraJD3_2.json b/GodStraJD3_2.json new file mode 100644 index 0000000..e2623b4 --- /dev/null +++ b/GodStraJD3_2.json @@ -0,0 +1,84 @@ +{ + "strategy_name": "GodStraJD3_2", + "params": { + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.15, + "trailing_stop_positive_offset": 0.2, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_indicator0": "CDLSHOOTINGSTAR-110", + "buy_crossed_indicator0": "CDLTASUKIGAP-5", + "buy_operator0": ">", + "buy_real_num0": 1.0, + "buy_indicator1": "MACDFIX-2-12", + "buy_crossed_indicator1": "CDLSPINNINGTOP-6", + "buy_operator1": "<", + "buy_real_num1": 0.2, + "buy_indicator2": "DEMA-110", + "buy_crossed_indicator2": "CDL3WHITESOLDIERS-110", + "buy_operator2": "/R", + "sell_real_num0": 0.9, + "sell_real_num1": 0.6, + "sell_real_num2": 0.3 + }, + "protections": [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 5 + }, + { + "method": "MaxDrawdown", + "lookback_period_candles": 48, + "trade_limit": 2, + "stop_duration_candles": 100, + "max_allowed_drawdown": 0.1 + }, + { + "method": "StoplossGuard", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": 2, + "only_per_pair": false + }, + { + "method": "LowProfitPairs", + "lookback_period_candles": 6, + "trade_limit": 2, + "stop_duration_candles": 60, + "required_profit": 0.02 + }, + { + "method": "LowProfitPairs", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": 2, + "required_profit": 0.01 + } + ], + "roi": { + "0": 0.893, + "1606": 0.31, + "4383": 0.122, + "10010": 0 + }, + "stoploss": { + "stoploss": -1 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-01-02 04:52:51.009356+00:00" +} diff --git a/GodStraJD3_2.jsonOLD b/GodStraJD3_2.jsonOLD new file mode 100644 index 0000000..5c4bb5f --- /dev/null +++ b/GodStraJD3_2.jsonOLD @@ -0,0 +1,84 @@ +{ + "strategy_name": "GodStraJD3_2", + "params": { + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.15, + "trailing_stop_positive_offset": 0.2, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_indicator0": "CDLSHOOTINGSTAR-110", + "buy_crossed_indicator0": "CDLTASUKIGAP-5", + "buy_operator0": ">", + "buy_real_num0": 1.0, + "buy_indicator1": "MACDFIX-2-12", + "buy_crossed_indicator1": "CDLSPINNINGTOP-6", + "buy_operator1": "<", + "buy_real_num1": 0.2, + "buy_indicator2": "DEMA-110", + "buy_crossed_indicator2": "CDL3WHITESOLDIERS-110", + "buy_operator2": "/R", + "sell_real_num0": 0.9, + "sell_real_num1": 0.6, + "sell_real_num2": 0.3 + }, + "protections": [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 5 + }, + { + "method": "MaxDrawdown", + "lookback_period_candles": 48, + "trade_limit": 2, + "stop_duration_candles": 100, + "max_allowed_drawdown": 0.1 + }, + { + "method": "StoplossGuard", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": 2, + "only_per_pair": false + }, + { + "method": "LowProfitPairs", + "lookback_period_candles": 6, + "trade_limit": 2, + "stop_duration_candles": 60, + "required_profit": 0.02 + }, + { + "method": "LowProfitPairs", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": 2, + "required_profit": 0.01 + } + ], + "roi": { + "0": 0.893, + "1606": 0.31, + "4383": 0.122, + "10010": 0 + }, + "stoploss": { + "stoploss": -2 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-01-02 04:52:51.009356+00:00" +} diff --git a/GodStraJD3_2.py b/GodStraJD3_2.py new file mode 100644 index 0000000..2db2341 --- /dev/null +++ b/GodStraJD3_2.py @@ -0,0 +1,1078 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import datetime + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle +# TODO: this gene is removed 'MAVP' cuz or error on periods +all_god_genes = { + 'Overlap Studies': { + 'BBANDS-0', # Bollinger Bands + 'BBANDS-1', # Bollinger Bands + 'BBANDS-2', # Bollinger Bands + 'DEMA', # Double Exponential Moving Average + 'EMA', # Exponential Moving Average + 'HT_TRENDLINE', # Hilbert Transform - Instantaneous Trendline + 'KAMA', # Kaufman Adaptive Moving Average + 'MA', # Moving average + 'MAMA-0', # MESA Adaptive Moving Average + 'MAMA-1', # MESA Adaptive Moving Average + # TODO: Fix this + # 'MAVP', # Moving average with variable period + 'MIDPOINT', # MidPoint over period + 'MIDPRICE', # Midpoint Price over period + 'SAR', # Parabolic SAR + 'SAREXT', # Parabolic SAR - Extended + 'SMA', # Simple Moving Average + 'T3', # Triple Exponential Moving Average (T3) + 'TEMA', # Triple Exponential Moving Average + 'TRIMA', # Triangular Moving Average + 'WMA', # Weighted Moving Average + }, + 'Momentum Indicators': { + 'ADX', # Average Directional Movement Index + 'ADXR', # Average Directional Movement Index Rating + 'APO', # Absolute Price Oscillator + 'AROON-0', # Aroon + 'AROON-1', # Aroon + 'AROONOSC', # Aroon Oscillator + 'BOP', # Balance Of Power + 'CCI', # Commodity Channel Index + 'CMO', # Chande Momentum Oscillator + 'DX', # Directional Movement Index + 'MACD-0', # Moving Average Convergence/Divergence + 'MACD-1', # Moving Average Convergence/Divergence + 'MACD-2', # Moving Average Convergence/Divergence + 'MACDEXT-0', # MACD with controllable MA type + 'MACDEXT-1', # MACD with controllable MA type + 'MACDEXT-2', # MACD with controllable MA type + 'MACDFIX-0', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-1', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-2', # Moving Average Convergence/Divergence Fix 12/26 + 'MFI', # Money Flow Index + 'MINUS_DI', # Minus Directional Indicator + 'MINUS_DM', # Minus Directional Movement + 'MOM', # Momentum + 'PLUS_DI', # Plus Directional Indicator + 'PLUS_DM', # Plus Directional Movement + 'PPO', # Percentage Price Oscillator + 'ROC', # Rate of change : ((price/prevPrice)-1)*100 + # Rate of change Percentage: (price-prevPrice)/prevPrice + 'ROCP', + 'ROCR', # Rate of change ratio: (price/prevPrice) + # Rate of change ratio 100 scale: (price/prevPrice)*100 + 'ROCR100', + 'RSI', # Relative Strength Index + 'STOCH-0', # Stochastic + 'STOCH-1', # Stochastic + 'STOCHF-0', # Stochastic Fast + 'STOCHF-1', # Stochastic Fast + 'STOCHRSI-0', # Stochastic Relative Strength Index + 'STOCHRSI-1', # Stochastic Relative Strength Index + # 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA + 'TRIX', + 'ULTOSC', # Ultimate Oscillator + 'WILLR', # Williams' %R + }, + 'Volume Indicators': { + 'AD', # Chaikin A/D Line + 'ADOSC', # Chaikin A/D Oscillator + 'OBV', # On Balance Volume + }, + 'Volatility Indicators': { + 'ATR', # Average True Range + 'NATR', # Normalized Average True Range + 'TRANGE', # True Range + }, + 'Price Transform': { + 'AVGPRICE', # Average Price + 'MEDPRICE', # Median Price + 'TYPPRICE', # Typical Price + 'WCLPRICE', # Weighted Close Price + }, + 'Cycle Indicators': { + 'HT_DCPERIOD', # Hilbert Transform - Dominant Cycle Period + 'HT_DCPHASE', # Hilbert Transform - Dominant Cycle Phase + 'HT_PHASOR-0', # Hilbert Transform - Phasor Components + 'HT_PHASOR-1', # Hilbert Transform - Phasor Components + 'HT_SINE-0', # Hilbert Transform - SineWave + 'HT_SINE-1', # Hilbert Transform - SineWave + 'HT_TRENDMODE', # Hilbert Transform - Trend vs Cycle Mode + }, + 'Pattern Recognition': { + 'CDL2CROWS', # Two Crows + 'CDL3BLACKCROWS', # Three Black Crows + 'CDL3INSIDE', # Three Inside Up/Down + 'CDL3LINESTRIKE', # Three-Line Strike + 'CDL3OUTSIDE', # Three Outside Up/Down + 'CDL3STARSINSOUTH', # Three Stars In The South + 'CDL3WHITESOLDIERS', # Three Advancing White Soldiers + 'CDLABANDONEDBABY', # Abandoned Baby + 'CDLADVANCEBLOCK', # Advance Block + 'CDLBELTHOLD', # Belt-hold + 'CDLBREAKAWAY', # Breakaway + 'CDLCLOSINGMARUBOZU', # Closing Marubozu + 'CDLCONCEALBABYSWALL', # Concealing Baby Swallow + 'CDLCOUNTERATTACK', # Counterattack + 'CDLDARKCLOUDCOVER', # Dark Cloud Cover + 'CDLDOJI', # Doji + 'CDLDOJISTAR', # Doji Star + 'CDLDRAGONFLYDOJI', # Dragonfly Doji + 'CDLENGULFING', # Engulfing Pattern + 'CDLEVENINGDOJISTAR', # Evening Doji Star + 'CDLEVENINGSTAR', # Evening Star + 'CDLGAPSIDESIDEWHITE', # Up/Down-gap side-by-side white lines + 'CDLGRAVESTONEDOJI', # Gravestone Doji + 'CDLHAMMER', # Hammer + 'CDLHANGINGMAN', # Hanging Man + 'CDLHARAMI', # Harami Pattern + 'CDLHARAMICROSS', # Harami Cross Pattern + 'CDLHIGHWAVE', # High-Wave Candle + 'CDLHIKKAKE', # Hikkake Pattern + 'CDLHIKKAKEMOD', # Modified Hikkake Pattern + 'CDLHOMINGPIGEON', # Homing Pigeon + 'CDLIDENTICAL3CROWS', # Identical Three Crows + 'CDLINNECK', # In-Neck Pattern + 'CDLINVERTEDHAMMER', # Inverted Hammer + 'CDLKICKING', # Kicking + 'CDLKICKINGBYLENGTH', # Kicking - bull/bear determined by the longer marubozu + 'CDLLADDERBOTTOM', # Ladder Bottom + 'CDLLONGLEGGEDDOJI', # Long Legged Doji + 'CDLLONGLINE', # Long Line Candle + 'CDLMARUBOZU', # Marubozu + 'CDLMATCHINGLOW', # Matching Low + 'CDLMATHOLD', # Mat Hold + 'CDLMORNINGDOJISTAR', # Morning Doji Star + 'CDLMORNINGSTAR', # Morning Star + 'CDLONNECK', # On-Neck Pattern + 'CDLPIERCING', # Piercing Pattern + 'CDLRICKSHAWMAN', # Rickshaw Man + 'CDLRISEFALL3METHODS', # Rising/Falling Three Methods + 'CDLSEPARATINGLINES', # Separating Lines + 'CDLSHOOTINGSTAR', # Shooting Star + 'CDLSHORTLINE', # Short Line Candle + 'CDLSPINNINGTOP', # Spinning Top + 'CDLSTALLEDPATTERN', # Stalled Pattern + 'CDLSTICKSANDWICH', # Stick Sandwich + # Takuri (Dragonfly Doji with very long lower shadow) + 'CDLTAKURI', + 'CDLTASUKIGAP', # Tasuki Gap + 'CDLTHRUSTING', # Thrusting Pattern + 'CDLTRISTAR', # Tristar Pattern + 'CDLUNIQUE3RIVER', # Unique 3 River + 'CDLUPSIDEGAP2CROWS', # Upside Gap Two Crows + 'CDLXSIDEGAP3METHODS', # Upside/Downside Gap Three Methods + + }, + 'Statistic Functions': { + 'BETA', # Beta + 'CORREL', # Pearson's Correlation Coefficient (r) + 'LINEARREG', # Linear Regression + 'LINEARREG_ANGLE', # Linear Regression Angle + 'LINEARREG_INTERCEPT', # Linear Regression Intercept + 'LINEARREG_SLOPE', # Linear Regression Slope + 'STDDEV', # Standard Deviation + 'TSF', # Time Series Forecast + 'VAR', # Variance + } + +} +god_genes = set() +########################### SETTINGS ############################## + +# god_genes = {'SMA'} +god_genes |= all_god_genes['Overlap Studies'] +god_genes |= all_god_genes['Momentum Indicators'] +god_genes |= all_god_genes['Volume Indicators'] +god_genes |= all_god_genes['Volatility Indicators'] +god_genes |= all_god_genes['Price Transform'] +god_genes |= all_god_genes['Cycle Indicators'] +god_genes |= all_god_genes['Pattern Recognition'] +god_genes |= all_god_genes['Statistic Functions'] + +timeperiods = [5, 6, 12, 15, 50, 55, 100, 110] +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator( + dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD3_2(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 1, + "600": 0.166, + "1200": 0.14, + "2400": 0.1, + "3600": 0.05, + "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '4h' + + # Trailing stoploss + trailing_stop = True + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + 'bb_min': {'color': 'red'}, + }, + "Ind": { + 'CDLTASUKIGAP-5': {'color': 'white'}, + 'CDLSPINNINGTOP-6': {'color': 'blue'}, + 'CDL3WHITESOLDIERS-110': {'color': 'red'} + }, + "Ind2": { + 'CDLSHOOTINGSTAR-110': {'color': 'white'}, + 'MACDFIX-2-12': {'color': 'blue'}, + 'DEMA-110': {'color': 'red'} + }, + + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + }, + "percent": { + "percent": {'color': 'green'}, + "percent5": {'color': 'red'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + buy_crossed_indicator0 = CategoricalParameter( + god_genes_with_timeperiod, default="ADD-20", space='buy') + buy_crossed_indicator1 = CategoricalParameter( + god_genes_with_timeperiod, default="ASIN-6", space='buy') + buy_crossed_indicator2 = CategoricalParameter( + god_genes_with_timeperiod, default="CDLEVENINGSTAR-50", space='buy') + + buy_indicator0 = CategoricalParameter( + god_genes_with_timeperiod, default="SMA-100", space='buy') + buy_indicator1 = CategoricalParameter( + god_genes_with_timeperiod, default="WILLR-50", space='buy') + buy_indicator2 = CategoricalParameter( + god_genes_with_timeperiod, default="CDLHANGINGMAN-20", space='buy') + + buy_operator0 = CategoricalParameter(operators, default="/ float: + # + # dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + # current_candle = dataframe.iloc[-1].squeeze() + # + # # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + # if current_candle['bb_width'] > 0.065: + # # print("use more stake", pair, " ", proposed_stake * 2) + # return min(max_stake, proposed_stake * 2) + # + # if current_candle['bb_width'] > 0.045: + # # print("use more stake", pair, " ", proposed_stake * 1.5) + # return min(max_stake, proposed_stake * 1.5) + # + # # if current_candle['bb_width'] < 0.020: + # # print("use less stake", pair, " ", proposed_stake / 2) + # # return min(max_stake, proposed_stake / 2) + # # if self.config['stake_amount'] == 'unlimited': + # # # Use entire available wallet during favorable conditions when in compounding mode. + # # return max_stake + # # else: + # # # Compound profits during favorable conditions instead of using a static stake. + # # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + # + # # Use default stake amount. + # return proposed_stake + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # (last_candle['percent5'] < -0.005) \ + # if (0 < current_profit < 0.005) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 2): + # # & (previous_last_candle['sma10'] > last_candle['sma10']): + # print("too_small_gain", pair, trade, " profit=", current_profit, " rate=", current_rate, " percent5=", + # last_candle['percent5']) + # return 'too_small_gain' + + # if (current_profit < -0.05) \ + # & ((current_time - trade.open_date_utc).days >= 3): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'stop_loss_profit' + + # if (current_profit > 0.02) \ + # & (last_candle['percent'] < 0.01) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'lost_half_profit' + + if (current_profit > 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \ + & (previous_5_candle['sma20'] > last_candle['sma20'] * 1.001) \ + & (last_candle['percent'] < 0) \ + & (last_candle['percent5'] < 0): + print("over_bb_band_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_bb_band_sma20_desc' + + if (current_profit > 0) \ + & (last_candle['rsi'] > 75): + print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + # description trade + # Trade(id=0, pair=CAKE/USDT, amount=4.19815281, open_rate=11.91000000, open_since=2021-12-22 17:55:00) + # print(last_candle) + if 0.05 < current_profit < 1: + if ( + (previous_last_candle['sma10'] > last_candle['sma10'] * 1.005) & + (current_time - trade.open_date_utc).seconds >= 3600 * 3 + # ) | ( + # (current_time - trade.open_date_utc).seconds >= 3600 * 6 + ): + # self.lock_pair(pair, until=current_time + timedelta(hours=3)) + + print("profit_3h_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_3h_sma10_desc' + + if (0 < current_profit < 0.1) \ + & (previous_last_candle['sma20'] > last_candle['sma20']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 5): + print("profit_5h_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_5h_sma20_desc' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # dataframe['min'] = ta.MIN(dataframe) + # dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + # TODO: Its not dry code! + buy_indicator = self.buy_indicator0.value + buy_crossed_indicator = self.buy_crossed_indicator0.value + buy_operator = self.buy_operator0.value + buy_real_num = self.buy_real_num0.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + # backup + buy_indicator = self.buy_indicator1.value + buy_crossed_indicator = self.buy_crossed_indicator1.value + buy_operator = self.buy_operator1.value + buy_real_num = self.buy_real_num1.value + + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + buy_indicator = self.buy_indicator2.value + buy_crossed_indicator = self.buy_crossed_indicator2.value + buy_operator = self.buy_operator2.value + buy_real_num = self.buy_real_num2.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + #( + # (dataframe['close'] < dataframe['bb_lowerband']) + # & (dataframe['bb_width'] >= 0.12) + # & (dataframe['volume'] * dataframe['close'] / 1000 > 100) + #) | + reduce(lambda x, y: x & y, conditions), + 'buy']=1 + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + # TODO: Its not dry code! + sell_indicator = self.sell_indicator0.value + sell_crossed_indicator = self.sell_crossed_indicator0.value + sell_operator = self.sell_operator0.value + sell_real_num = self.sell_real_num0.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator1.value + sell_crossed_indicator = self.sell_crossed_indicator1.value + sell_operator = self.sell_operator1.value + sell_real_num = self.sell_real_num1.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator2.value + sell_crossed_indicator = self.sell_crossed_indicator2.value + sell_operator = self.sell_operator2.value + sell_real_num = self.sell_real_num2.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + #dataframe.loc[(dataframe['close'] < dataframe['bb_lowerband']),'sell']=1 + + if conditions: + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + ), + 'sell']=1 + return dataframe + + +class StrategyHelperLocal: + """ + simple helper class to predefine a couple of patterns for our + strategy + """ + + @staticmethod + def two_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) + # (dataframe['open'].shift(2) < dataframe['close'].shift(2)) + ) + + @staticmethod + def no_more_three_green_candles(dataframe): + """ + evaluates if we are having not more than 3 green candles in a row + :param self: + :param dataframe: + :return: + """ + return not ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) + ) + + @staticmethod + def seven_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) + ) + + @staticmethod + def eight_green_candles(dataframe): + """ + evaluates if we are having 8 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) & + (dataframe['open'].shift(8) < dataframe['close'].shift(8)) + ) + + @staticmethod + def two_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) + # (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def four_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) & + (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + (dataframe['open'].shift(3 + shift) > dataframe['close'].shift(3 + shift)) & + (dataframe['open'].shift(4 + shift) > dataframe['close'].shift(4 + shift)) + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def two_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) + ) + + @staticmethod + def four_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] > dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) + ) + + @staticmethod + def four_red_one_green_candle(dataframe): + """ + evaluates if we are having a green candle and 4 previous red + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) > dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) > dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) > dataframe['close'].shift(4)) + ) diff --git a/GodStraJD3_3.py b/GodStraJD3_3.py new file mode 100644 index 0000000..8d361f1 --- /dev/null +++ b/GodStraJD3_3.py @@ -0,0 +1,1111 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy, SellCheckTuple +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle +# TODO: this gene is removed 'MAVP' cuz or error on periods +all_god_genes = { + 'Overlap Studies': { + 'BBANDS-0', # Bollinger Bands + 'BBANDS-1', # Bollinger Bands + 'BBANDS-2', # Bollinger Bands + 'DEMA', # Double Exponential Moving Average + 'EMA', # Exponential Moving Average + 'HT_TRENDLINE', # Hilbert Transform - Instantaneous Trendline + 'KAMA', # Kaufman Adaptive Moving Average + 'MA', # Moving average + 'MAMA-0', # MESA Adaptive Moving Average + 'MAMA-1', # MESA Adaptive Moving Average + # TODO: Fix this + # 'MAVP', # Moving average with variable period + 'MIDPOINT', # MidPoint over period + 'MIDPRICE', # Midpoint Price over period + 'SAR', # Parabolic SAR + 'SAREXT', # Parabolic SAR - Extended + 'SMA', # Simple Moving Average + 'T3', # Triple Exponential Moving Average (T3) + 'TEMA', # Triple Exponential Moving Average + 'TRIMA', # Triangular Moving Average + 'WMA', # Weighted Moving Average + }, + 'Momentum Indicators': { + 'ADX', # Average Directional Movement Index + 'ADXR', # Average Directional Movement Index Rating + 'APO', # Absolute Price Oscillator + 'AROON-0', # Aroon + 'AROON-1', # Aroon + 'AROONOSC', # Aroon Oscillator + 'BOP', # Balance Of Power + 'CCI', # Commodity Channel Index + 'CMO', # Chande Momentum Oscillator + 'DX', # Directional Movement Index + 'MACD-0', # Moving Average Convergence/Divergence + 'MACD-1', # Moving Average Convergence/Divergence + 'MACD-2', # Moving Average Convergence/Divergence + 'MACDEXT-0', # MACD with controllable MA type + 'MACDEXT-1', # MACD with controllable MA type + 'MACDEXT-2', # MACD with controllable MA type + 'MACDFIX-0', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-1', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-2', # Moving Average Convergence/Divergence Fix 12/26 + 'MFI', # Money Flow Index + 'MINUS_DI', # Minus Directional Indicator + 'MINUS_DM', # Minus Directional Movement + 'MOM', # Momentum + 'PLUS_DI', # Plus Directional Indicator + 'PLUS_DM', # Plus Directional Movement + 'PPO', # Percentage Price Oscillator + 'ROC', # Rate of change : ((price/prevPrice)-1)*100 + # Rate of change Percentage: (price-prevPrice)/prevPrice + 'ROCP', + 'ROCR', # Rate of change ratio: (price/prevPrice) + # Rate of change ratio 100 scale: (price/prevPrice)*100 + 'ROCR100', + 'RSI', # Relative Strength Index + 'STOCH-0', # Stochastic + 'STOCH-1', # Stochastic + 'STOCHF-0', # Stochastic Fast + 'STOCHF-1', # Stochastic Fast + 'STOCHRSI-0', # Stochastic Relative Strength Index + 'STOCHRSI-1', # Stochastic Relative Strength Index + # 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA + 'TRIX', + 'ULTOSC', # Ultimate Oscillator + 'WILLR', # Williams' %R + }, + 'Volume Indicators': { + 'AD', # Chaikin A/D Line + 'ADOSC', # Chaikin A/D Oscillator + 'OBV', # On Balance Volume + }, + 'Volatility Indicators': { + 'ATR', # Average True Range + 'NATR', # Normalized Average True Range + 'TRANGE', # True Range + }, + 'Price Transform': { + 'AVGPRICE', # Average Price + 'MEDPRICE', # Median Price + 'TYPPRICE', # Typical Price + 'WCLPRICE', # Weighted Close Price + }, + 'Cycle Indicators': { + 'HT_DCPERIOD', # Hilbert Transform - Dominant Cycle Period + 'HT_DCPHASE', # Hilbert Transform - Dominant Cycle Phase + 'HT_PHASOR-0', # Hilbert Transform - Phasor Components + 'HT_PHASOR-1', # Hilbert Transform - Phasor Components + 'HT_SINE-0', # Hilbert Transform - SineWave + 'HT_SINE-1', # Hilbert Transform - SineWave + 'HT_TRENDMODE', # Hilbert Transform - Trend vs Cycle Mode + }, + 'Pattern Recognition': { + 'CDL2CROWS', # Two Crows + 'CDL3BLACKCROWS', # Three Black Crows + 'CDL3INSIDE', # Three Inside Up/Down + 'CDL3LINESTRIKE', # Three-Line Strike + 'CDL3OUTSIDE', # Three Outside Up/Down + 'CDL3STARSINSOUTH', # Three Stars In The South + 'CDL3WHITESOLDIERS', # Three Advancing White Soldiers + 'CDLABANDONEDBABY', # Abandoned Baby + 'CDLADVANCEBLOCK', # Advance Block + 'CDLBELTHOLD', # Belt-hold + 'CDLBREAKAWAY', # Breakaway + 'CDLCLOSINGMARUBOZU', # Closing Marubozu + 'CDLCONCEALBABYSWALL', # Concealing Baby Swallow + 'CDLCOUNTERATTACK', # Counterattack + 'CDLDARKCLOUDCOVER', # Dark Cloud Cover + 'CDLDOJI', # Doji + 'CDLDOJISTAR', # Doji Star + 'CDLDRAGONFLYDOJI', # Dragonfly Doji + 'CDLENGULFING', # Engulfing Pattern + 'CDLEVENINGDOJISTAR', # Evening Doji Star + 'CDLEVENINGSTAR', # Evening Star + 'CDLGAPSIDESIDEWHITE', # Up/Down-gap side-by-side white lines + 'CDLGRAVESTONEDOJI', # Gravestone Doji + 'CDLHAMMER', # Hammer + 'CDLHANGINGMAN', # Hanging Man + 'CDLHARAMI', # Harami Pattern + 'CDLHARAMICROSS', # Harami Cross Pattern + 'CDLHIGHWAVE', # High-Wave Candle + 'CDLHIKKAKE', # Hikkake Pattern + 'CDLHIKKAKEMOD', # Modified Hikkake Pattern + 'CDLHOMINGPIGEON', # Homing Pigeon + 'CDLIDENTICAL3CROWS', # Identical Three Crows + 'CDLINNECK', # In-Neck Pattern + 'CDLINVERTEDHAMMER', # Inverted Hammer + 'CDLKICKING', # Kicking + 'CDLKICKINGBYLENGTH', # Kicking - bull/bear determined by the longer marubozu + 'CDLLADDERBOTTOM', # Ladder Bottom + 'CDLLONGLEGGEDDOJI', # Long Legged Doji + 'CDLLONGLINE', # Long Line Candle + 'CDLMARUBOZU', # Marubozu + 'CDLMATCHINGLOW', # Matching Low + 'CDLMATHOLD', # Mat Hold + 'CDLMORNINGDOJISTAR', # Morning Doji Star + 'CDLMORNINGSTAR', # Morning Star + 'CDLONNECK', # On-Neck Pattern + 'CDLPIERCING', # Piercing Pattern + 'CDLRICKSHAWMAN', # Rickshaw Man + 'CDLRISEFALL3METHODS', # Rising/Falling Three Methods + 'CDLSEPARATINGLINES', # Separating Lines + 'CDLSHOOTINGSTAR', # Shooting Star + 'CDLSHORTLINE', # Short Line Candle + 'CDLSPINNINGTOP', # Spinning Top + 'CDLSTALLEDPATTERN', # Stalled Pattern + 'CDLSTICKSANDWICH', # Stick Sandwich + # Takuri (Dragonfly Doji with very long lower shadow) + 'CDLTAKURI', + 'CDLTASUKIGAP', # Tasuki Gap + 'CDLTHRUSTING', # Thrusting Pattern + 'CDLTRISTAR', # Tristar Pattern + 'CDLUNIQUE3RIVER', # Unique 3 River + 'CDLUPSIDEGAP2CROWS', # Upside Gap Two Crows + 'CDLXSIDEGAP3METHODS', # Upside/Downside Gap Three Methods + + }, + 'Statistic Functions': { + 'BETA', # Beta + 'CORREL', # Pearson's Correlation Coefficient (r) + 'LINEARREG', # Linear Regression + 'LINEARREG_ANGLE', # Linear Regression Angle + 'LINEARREG_INTERCEPT', # Linear Regression Intercept + 'LINEARREG_SLOPE', # Linear Regression Slope + 'STDDEV', # Standard Deviation + 'TSF', # Time Series Forecast + 'VAR', # Variance + } + +} +god_genes = set() +########################### SETTINGS ############################## + +# god_genes = {'SMA'} +god_genes |= all_god_genes['Overlap Studies'] +god_genes |= all_god_genes['Momentum Indicators'] +god_genes |= all_god_genes['Volume Indicators'] +god_genes |= all_god_genes['Volatility Indicators'] +god_genes |= all_god_genes['Price Transform'] +god_genes |= all_god_genes['Cycle Indicators'] +god_genes |= all_god_genes['Pattern Recognition'] +god_genes |= all_god_genes['Statistic Functions'] + +timeperiods = [5, 6, 12, 15, 50, 55, 100, 110] +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator( + dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD3_3(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 1, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '4h' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + 'bb_min': {'color': 'red'}, + }, + "Ind": { + 'CDLTASUKIGAP-5': {'color': 'white'}, + 'CDLSPINNINGTOP-6': {'color': 'blue'}, + 'CDL3WHITESOLDIERS-110': {'color': 'red'} + }, + "Ind2": { + 'CDLSHOOTINGSTAR-110': {'color': 'white'}, + 'MACDFIX-2-12': {'color': 'blue'}, + 'DEMA-110': {'color': 'red'} + }, + + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + buy_crossed_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="ADD-20", space='buy') + buy_crossed_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="ASIN-6", space='buy') + buy_crossed_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLEVENINGSTAR-50", space='buy') + + buy_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="SMA-100", space='buy') + buy_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="WILLR-50", space='buy') + buy_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLHANGINGMAN-20", space='buy') + + buy_operator0 = CategoricalParameter(operators, default="/ float: + # + # dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + # current_candle = dataframe.iloc[-1].squeeze() + # + # # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + # if current_candle['bb_width'] > 0.065: + # # print("use more stake", pair, " ", proposed_stake * 2) + # return min(max_stake, proposed_stake * 2) + # + # if current_candle['bb_width'] > 0.045: + # # print("use more stake", pair, " ", proposed_stake * 1.5) + # return min(max_stake, proposed_stake * 1.5) + # + # # if current_candle['bb_width'] < 0.020: + # # print("use less stake", pair, " ", proposed_stake / 2) + # # return min(max_stake, proposed_stake / 2) + # # if self.config['stake_amount'] == 'unlimited': + # # # Use entire available wallet during favorable conditions when in compounding mode. + # # return max_stake + # # else: + # # # Compound profits during favorable conditions instead of using a static stake. + # # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + # + # # Use default stake amount. + # return proposed_stake + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + count = 0 + for coin, balance in self.wallets.get_all_balances().items(): + count = count + 1 + # print(coin, " ", balance) + + # (last_candle['percent5'] < -0.005) \ + # if (0 < current_profit < 0.005) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 2): + # # & (previous_last_candle['sma10'] > last_candle['sma10']): + # print("too_small_gain", pair, trade, " profit=", current_profit, " rate=", current_rate, " percent5=", + # last_candle['percent5']) + # return 'too_small_gain' + + # if (current_profit < -0.05) \ + # & ((current_time - trade.open_date_utc).days >= 3): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'stop_loss_profit' + + # if (current_profit > 0.02) \ + # & (last_candle['percent'] < 0.01) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'lost_half_profit' + + # ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \ + if (current_profit > 0) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): #(current_profit / 4))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_bb_band_sma10_desc' + + #if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \ + # & (previous_last_candle['sma20'] > last_candle['sma20']) \ + # & (last_candle['percent'] < 0): + # print("over_bb_band_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_bb_band_sma20_desc' + + if (current_profit > 0) & (last_candle['rsi'] > 88): + print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + #if 0.015 < current_profit < 0.03: + # if (last_candle['percent3'] < -0.005 ): + # # self.lock_pair(pair, until=current_time + timedelta(hours=3)) + # print("profit_3h_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'profit_percent3' + # if (current_profit > 0) \ + # & (last_candle['percent'] < -0.02): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'stop_percent_loss' + + # description trade + # Trade(id=0, pair=CAKE/USDT, amount=4.19815281, open_rate=11.91000000, open_since=2021-12-22 17:55:00) + # print(last_candle) + # if 0.05 < current_profit < 1: + # if ( + # (previous_last_candle['sma10'] > last_candle['sma10'] * 1.005) & + # (current_time - trade.open_date_utc).seconds >= 3600 * 3 + # # ) | ( + # # (current_time - trade.open_date_utc).seconds >= 3600 * 6 + # ): + # # self.lock_pair(pair, until=current_time + timedelta(hours=3)) + # + # print("profit_3h_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'profit_3h_sma10_desc' + # + # if (0 < current_profit < 0.1) \ + # & (previous_last_candle['sma20'] > last_candle['sma20']) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 5): + # print("profit_5h_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'profit_5h_sma20_desc' + + # if (count == self.config['max_open_trades']) & (current_profit < -0.04) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 6): + # self.lock_pair(pair, until=current_time + timedelta(hours=10)) + # print("stop_short_loss", pair, trade, " profit=", current_profit, " rate=", current_rate, + # "count=", count, "max=", self.config['max_open_trades']) + # return 'stop_short_loss' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # dataframe['min'] = ta.MIN(dataframe) + # dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + dataframe['ADD-20'] = gene_calculator(dataframe, 'ADD-20') + dataframe['ASIN-6'] = gene_calculator(dataframe, 'ASIN-6') + dataframe['CDLEVENINGSTAR-50'] = gene_calculator(dataframe, 'CDLEVENINGSTAR-50') + + dataframe['SMA-100'] = gene_calculator(dataframe, 'SMA-100') + dataframe['WILLR-50'] = gene_calculator(dataframe, 'WILLR-50') + dataframe['CDLHANGINGMAN-20'] = gene_calculator(dataframe, 'CDLHANGINGMAN-20') + + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + # TODO: Its not dry code! + buy_indicator = self.buy_indicator0.value + buy_crossed_indicator = self.buy_crossed_indicator0.value + buy_operator = self.buy_operator0.value + buy_real_num = self.buy_real_num0.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + # backup + buy_indicator = self.buy_indicator1.value + buy_crossed_indicator = self.buy_crossed_indicator1.value + buy_operator = self.buy_operator1.value + buy_real_num = self.buy_real_num1.value + + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + buy_indicator = self.buy_indicator2.value + buy_crossed_indicator = self.buy_crossed_indicator2.value + buy_operator = self.buy_operator2.value + buy_real_num = self.buy_real_num2.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['bb_lowerband']) + & (dataframe['bb_width'] >= 0.12) + & (dataframe['volume'] * dataframe['close'] / 1000 > 100) + ) | + (reduce(lambda x, y: x & y, conditions)), + 'buy']=1 + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + # TODO: Its not dry code! + sell_indicator = self.sell_indicator0.value + sell_crossed_indicator = self.sell_crossed_indicator0.value + sell_operator = self.sell_operator0.value + sell_real_num = self.sell_real_num0.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator1.value + sell_crossed_indicator = self.sell_crossed_indicator1.value + sell_operator = self.sell_operator1.value + sell_real_num = self.sell_real_num1.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator2.value + sell_crossed_indicator = self.sell_crossed_indicator2.value + sell_operator = self.sell_operator2.value + sell_real_num = self.sell_real_num2.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + #dataframe.loc[(dataframe['close'] < dataframe['bb_lowerband']),'sell']=1 + + # count = 0 + # for coin, balance in self.wallets.get_all_balances().items(): + # count = count + 1 + # # print(coin, " ", balance) + # if (count == self.config['max_open_trades']) & (current_profit < -0.04) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 6): + # self.lock_pair(pair, until=current_time + timedelta(hours=10)) + # print("stop_short_loss", pair, trade, " profit=", current_profit, " rate=", current_rate, + # "count=", count, "max=", self.config['max_open_trades']) + # return 'stop_short_loss' + + # print("sell pair=", self.wallets.get_all_balances()[metadata['pair']]) + + if conditions: + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) & + (dataframe['volume'] * dataframe['close'] / 1000 >= 100) + # (dataframe['open'] < dataframe['sma10']) + ), + 'sell']=1 + return dataframe + + +class StrategyHelperLocal: + """ + simple helper class to predefine a couple of patterns for our + strategy + """ + + @staticmethod + def two_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) + # (dataframe['open'].shift(2) < dataframe['close'].shift(2)) + ) + + @staticmethod + def no_more_three_green_candles(dataframe): + """ + evaluates if we are having not more than 3 green candles in a row + :param self: + :param dataframe: + :return: + """ + return not ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) + ) + + @staticmethod + def seven_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) + ) + + @staticmethod + def eight_green_candles(dataframe): + """ + evaluates if we are having 8 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) & + (dataframe['open'].shift(8) < dataframe['close'].shift(8)) + ) + + @staticmethod + def two_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) + # (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def four_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) & + (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + (dataframe['open'].shift(3 + shift) > dataframe['close'].shift(3 + shift)) & + (dataframe['open'].shift(4 + shift) > dataframe['close'].shift(4 + shift)) + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def two_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) + ) + + @staticmethod + def four_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] > dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) + ) + + @staticmethod + def four_red_one_green_candle(dataframe): + """ + evaluates if we are having a green candle and 4 previous red + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) > dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) > dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) > dataframe['close'].shift(4)) + ) diff --git a/GodStraJD3_4.json b/GodStraJD3_4.json new file mode 100644 index 0000000..0cf25e5 --- /dev/null +++ b/GodStraJD3_4.json @@ -0,0 +1,41 @@ +{ + "strategy_name": "GodStraJD3_4", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.145, + "trailing_stop_positive_offset": 0.146, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_crossed_indicator0": "T3-10", + "buy_crossed_indicator1": "HT_SINE-1-50", + "buy_crossed_indicator2": "PLUS_DI-20", + "buy_indicator0": "TEMA-50", + "buy_indicator1": "ADXR-50", + "buy_indicator2": "CDLSEPARATINGLINES-50", + "buy_operator0": "/", + "buy_operator2": "/R", + "buy_operator1": "<", + "buy_operator2": "CA", + "buy_real_num0": 0.0, + "buy_real_num1": 0.8, + "buy_real_num2": 0.5 + }, + "sell": { + "sell_crossed_indicator0": "TRANGE-12", + "sell_crossed_indicator1": "CDLEVENINGDOJISTAR-55", + "sell_crossed_indicator2": "HT_PHASOR-0-15", + "sell_indicator0": "HT_PHASOR-0-15", + "sell_indicator1": "AROON-1-110", + "sell_indicator2": "ADX-6", + "sell_operator0": "", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD3_4(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + # 'min': {'color': 'white'}, + # 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + 'bb_min': {'color': 'red'}, + }, + "Ind": { + "PLUS_DM-20": {'color': 'green'}, + "CDLTAKURI-5": {'color': 'blue'} + }, + # "Ind2": { + # 'MINUS_DM-5': {'color': 'green'}, + # 'DX-5': {'color': 'blue'}, + # 'LINEARREG-50': {'color': 'red'} + # }, + # "Profit": { + # 'profit': {'color': 'pink'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + profit_no_change = False + profit_old_sma10 = False + profit_over_rsi = True + profit_quick_gain = True + profit_quick_gain_3 = True + profit_quick_lost = False + profit_short_loss = False + profit_sma10 = True + profit_sma20 = True + profit_very_old_sma10 = False + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + buy_crossed_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="ADD-20", space='buy') + buy_crossed_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="ASIN-6", space='buy') + buy_crossed_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLEVENINGSTAR-50", space='buy') + + buy_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="SMA-100", space='buy') + buy_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="WILLR-50", space='buy') + buy_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLHANGINGMAN-20", space='buy') + + buy_operator0 = CategoricalParameter(operators, default="/ float: + # + # dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + # current_candle = dataframe.iloc[-1].squeeze() + # + # # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + # if current_candle['bb_width'] > 0.065: + # # print("use more stake", pair, " ", proposed_stake * 2) + # return min(max_stake, proposed_stake * 2) + # + # if current_candle['bb_width'] > 0.045: + # # print("use more stake", pair, " ", proposed_stake * 1.5) + # return min(max_stake, proposed_stake * 1.5) + # + # # if current_candle['bb_width'] < 0.020: + # # print("use less stake", pair, " ", proposed_stake / 2) + # # return min(max_stake, proposed_stake / 2) + # # if self.config['stake_amount'] == 'unlimited': + # # # Use entire available wallet during favorable conditions when in compounding mode. + # # return max_stake + # # else: + # # # Compound profits during favorable conditions instead of using a static stake. + # # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + # + # # Use default stake amount. + # return proposed_stake + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # if (0 < current_profit) & ((current_time - trade.open_date_utc).seconds > 3600) \ + # & (last_candle['percent10'] < 0.001): + # return 'small_profit' + # + # if (current_profit > 0.01) \ + # & ((previous_last_candle['sma10'] - last_candle['sma10']) / previous_last_candle['sma10'] > 0.003): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma10_quick' + + if (current_profit >= -0.01) & ((current_time - trade.open_date_utc).days >= 5)\ + & ((current_time - trade.open_date_utc).days < 10)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.01" + if (current_profit >= -0.02) & ((current_time - trade.open_date_utc).days >= 10)\ + & ((current_time - trade.open_date_utc).days < 15) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.02" + if (current_profit >= -0.03) & ((current_time - trade.open_date_utc).days >= 15) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.03" + + if self.profit_quick_lost: + if (current_profit >= 0) & (last_candle['percent3'] < -0.015): + return "quick_lost" + + if self.profit_no_change: + if (current_profit > 0.005) & (last_candle['percent10'] < 0.001) & (last_candle['percent5'] < 0) & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + if (current_profit > 0.01) & (last_candle['rsi'] < 30): + return "small_rsi" + if self.profit_quick_gain_3: + if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if self.profit_quick_gain: + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain" + + # if (current_profit > self.sell_percent.value) & (last_candle['percent3'] < - self.sell_percent3.value) \ + # & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_candels.value): + # return "quick_gain_param" + + if self.profit_sma10: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi: + if (current_profit > 0) & (previous_last_candle['rsi'] > 88): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > 82) & (last_candle['percent'] < -0.02): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if self.profit_short_loss: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # Assign tf to each pair so they can be downloaded and cached for strategy. + informative_pairs = [(pair, "4h") for pair in pairs] + #informative_pairs += [(pair, '1d') for pair in pairs] + #informative_pairs += [(pair, '1w') for pair in pairs] + + # Optionally Add additional "static" pairs + # informative_pairs += [("ETH/USDT", "5m"), ("BTC/TUSD", "15m")] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + dataframe[self.buy_indicator0.value] = gene_calculator(dataframe, self.buy_indicator0.value) + dataframe[self.buy_crossed_indicator0.value] = gene_calculator(dataframe, self.buy_crossed_indicator0.value) + dataframe[self.buy_indicator1.value] = gene_calculator(dataframe, self.buy_indicator1.value) + dataframe[self.buy_crossed_indicator1.value] = gene_calculator(dataframe, self.buy_crossed_indicator1.value) + # dataframe[self.buy_indicator2.value] = gene_calculator(dataframe, self.buy_indicator2.value) + # dataframe[self.buy_crossed_indicator2.value] = gene_calculator(dataframe, self.buy_crossed_indicator2.value) + + # dataframe['MINUS_DM-5'] = ta.MINUS_DM(dataframe, timeperiod=5) + # dataframe['LINEARREG-50'] = ta.LINEARREG(dataframe, timeperiod=50) + # dataframe['MA-20'] = ta.MA(dataframe, timeperiod=20) + # stoch = ta.STOCH(dataframe, timeperiod=10) + # # print(stoch) + # dataframe['STOCH-1-10'] = stoch['slowd'] + # dataframe['CDLDRAGONFLYDOJI-5'] = ta.CDLDRAGONFLYDOJI(dataframe, timeperiod=5) + + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # dataframe['min'] = ta.MIN(dataframe) + # dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + # dataframe["sum_percent"] = dataframe["sum_percent"].shift(1) + dataframe["percent"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + + # # INFORMATIVE PAIRS + + # Get the informative pair + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + + informative["percent"] = (informative["close"] - informative["open"]) / informative["open"] + informative["percent5"] = informative["percent"].rolling(5).sum() + informative["percent3"] = informative["percent"].rolling(3).sum() + # print('informative', metadata['pair'], informative.tail(1)) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='1d') + # informative[buy_crossed_indicator0] = gene_calculator(informative, buy_crossed_indicator0) + # informative[buy_indicator0] = gene_calculator(informative, buy_indicator0) + # informative["cond1"] = informative[buy_indicator0].div(informative[buy_crossed_indicator0]) + # informative["percent"] = (informative["close"] - informative["open"]) / informative["open"] + # informative["percent5"] = informative["percent"].rolling(5).sum() + # informative["percent3"] = informative["percent"].rolling(3).sum() + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, '1d', ffill=True) + + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='1w') + # informative[buy_crossed_indicator0] = gene_calculator(informative, buy_crossed_indicator0) + # informative[buy_indicator0] = gene_calculator(informative, buy_indicator0) + # informative["cond1"] = informative[buy_indicator0].div(informative[buy_crossed_indicator0]) + # informative["percent"] = (informative["close"] - informative["open"]) / informative["open"] + # informative["percent5"] = informative["percent"].rolling(5).sum() + # informative["percent3"] = informative["percent"].rolling(3).sum() + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, '1w', ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + # TODO: Its not dry code! + buy_indicator = self.buy_indicator0.value + buy_crossed_indicator = self.buy_crossed_indicator0.value + buy_operator = self.buy_operator0.value + buy_real_num = self.buy_real_num0.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + # backup + buy_indicator = self.buy_indicator1.value + buy_crossed_indicator = self.buy_crossed_indicator1.value + buy_operator = self.buy_operator1.value + buy_real_num = self.buy_real_num1.value + + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + buy_indicator = self.buy_indicator2.value + buy_crossed_indicator = self.buy_crossed_indicator2.value + buy_operator = self.buy_operator2.value + buy_real_num = self.buy_real_num2.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + (reduce(lambda x, y: x & y, conditions) + # & (dataframe['percent_4h'] > 0) + & (dataframe['percent3_4h'] <= 0) + ), + 'buy']=1 + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_4_1.json b/GodStraJD3_4_1.json new file mode 100644 index 0000000..950f042 --- /dev/null +++ b/GodStraJD3_4_1.json @@ -0,0 +1,54 @@ +{ + "strategy_name": "GodStraJD3_4_1", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.145, + "trailing_stop_positive_offset": 0.146, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bbwidth_num0": 0.9, + "buy_cond1_num0": 5.74, + "buy_crossed_indicator0": "CDLIDENTICAL3CROWS-100", + "buy_crossed_indicator1": "CDLMORNINGDOJISTAR-20", + "buy_crossed_indicator2": "CDLEVENINGSTAR-10", + "buy_indicator0": "HT_PHASOR-0-100", + "buy_indicator1": "DEMA-10", + "buy_indicator2": "CDLEVENINGSTAR-10", + "buy_operator0": "/", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD3_4_1(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + buy_crossed_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="ADD-20", space='buy') + buy_crossed_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="ASIN-6", space='buy') + buy_crossed_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLEVENINGSTAR-50", space='buy') + + buy_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="SMA-100", space='buy') + buy_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="WILLR-50", space='buy') + buy_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLHANGINGMAN-20", space='buy') + + buy_operator0 = CategoricalParameter(operators, default="/ float: + # + # dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + # current_candle = dataframe.iloc[-1].squeeze() + # + # # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + # if current_candle['bb_width'] > 0.065: + # # print("use more stake", pair, " ", proposed_stake * 2) + # return min(max_stake, proposed_stake * 2) + # + # if current_candle['bb_width'] > 0.045: + # # print("use more stake", pair, " ", proposed_stake * 1.5) + # return min(max_stake, proposed_stake * 1.5) + # + # # if current_candle['bb_width'] < 0.020: + # # print("use less stake", pair, " ", proposed_stake / 2) + # # return min(max_stake, proposed_stake / 2) + # # if self.config['stake_amount'] == 'unlimited': + # # # Use entire available wallet during favorable conditions when in compounding mode. + # # return max_stake + # # else: + # # # Compound profits during favorable conditions instead of using a static stake. + # # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + # + # # Use default stake amount. + # return proposed_stake + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + if self.profit_quick_lost.value: + if (current_profit >= 0) & (last_candle['percent3'] < -0.01): + return "quick_lost" + + if self.profit_no_change.value: + if (current_profit > 0.005) & (last_candle['percent10'] < 0.001): + return "no_change" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if self.profit_quick_gain_3.value: + if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if self.profit_quick_gain.value: + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0): #& ((current_time - trade.open_date_utc).seconds <= 3600) + return "quick_gain" + + if self.profit_sma10.value: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10'] * 1.005) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20.value: + if (current_profit > 0.005) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10.value: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10.value: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi.value: + if (current_profit > 0) \ + & (last_candle['rsi'] > 88): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if self.profit_short_loss.value: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['MINUS_DM-5'] = ta.MINUS_DM(dataframe, timeperiod=5) + # dataframe['LINEARREG-50'] = ta.LINEARREG(dataframe, timeperiod=50) + # dataframe['MA-20'] = ta.MA(dataframe, timeperiod=20) + # stoch = ta.STOCH(dataframe, timeperiod=10) + # # print(stoch) + # dataframe['STOCH-1-10'] = stoch['slowd'] + # dataframe['CDLDRAGONFLYDOJI-5'] = ta.CDLDRAGONFLYDOJI(dataframe, timeperiod=5) + + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # dataframe['min'] = ta.MIN(dataframe) + # dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + # dataframe["sum_percent"] = dataframe["sum_percent"].shift(1) + dataframe["percent"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + dataframe[self.buy_indicator0.value] = gene_calculator(dataframe, self.buy_indicator0.value) + dataframe[self.buy_crossed_indicator0.value] = gene_calculator(dataframe, self.buy_crossed_indicator0.value) + dataframe[self.buy_indicator1.value] = gene_calculator(dataframe, self.buy_indicator1.value) + dataframe[self.buy_crossed_indicator1.value] = gene_calculator(dataframe, self.buy_crossed_indicator1.value) + dataframe[self.buy_indicator2.value] = gene_calculator(dataframe, self.buy_indicator2.value) + dataframe[self.buy_crossed_indicator2.value] = gene_calculator(dataframe, self.buy_crossed_indicator2.value) + + dataframe["cond0"] = dataframe[self.buy_indicator0.value].div(dataframe[self.buy_crossed_indicator0.value]) + dataframe["cond1"] = dataframe[self.buy_indicator1.value] + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + condition, dataframe = condition_generator( + dataframe, + self.buy_operator0.value, + self.buy_indicator0.value, + self.buy_crossed_indicator0.value, + self.buy_real_num0.value + ) + conditions.append(condition) + + condition, dataframe = condition_generator( + dataframe, + self.buy_operator1.value, + self.buy_indicator1.value, + self.buy_crossed_indicator1.value, + self.buy_real_num1.value + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + (reduce(lambda x, y: x & y, conditions) + & (dataframe['sma20'].shift(self.buy_shift_bb_lowerband.value) < dataframe['sma20']) + & (dataframe['sma50'].shift(self.buy_shift_bb_lowerband.value) < dataframe['sma50']) + ), + 'buy']=1 + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + # # TODO: Its not dry code! + # sell_indicator = self.sell_indicator0.value + # sell_crossed_indicator = self.sell_crossed_indicator0.value + # sell_operator = self.sell_operator0.value + # sell_real_num = self.sell_real_num0.value + # condition, dataframe = condition_generator( + # dataframe, + # sell_operator, + # sell_indicator, + # sell_crossed_indicator, + # sell_real_num + # ) + # conditions.append(condition) + # + # sell_indicator = self.sell_indicator1.value + # sell_crossed_indicator = self.sell_crossed_indicator1.value + # sell_operator = self.sell_operator1.value + # sell_real_num = self.sell_real_num1.value + # condition, dataframe = condition_generator( + # dataframe, + # sell_operator, + # sell_indicator, + # sell_crossed_indicator, + # sell_real_num + # ) + # conditions.append(condition) + # + # sell_indicator = self.sell_indicator2.value + # sell_crossed_indicator = self.sell_crossed_indicator2.value + # sell_operator = self.sell_operator2.value + # sell_real_num = self.sell_real_num2.value + # condition, dataframe = condition_generator( + # dataframe, + # sell_operator, + # sell_indicator, + # sell_crossed_indicator, + # sell_real_num + # ) + # conditions.append(condition) + + #dataframe.loc[(dataframe['close'] < dataframe['bb_lowerband']),'sell']=1 + + # count = 0 + # for coin, balance in self.wallets.get_all_balances().items(): + # count = count + 1 + # # print(coin, " ", balance) + # if (count == self.config['max_open_trades']) & (current_profit < -0.04) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 6): + # self.lock_pair(pair, until=current_time + timedelta(hours=10)) + # print("stop_short_loss", pair, trade, " profit=", current_profit, " rate=", current_rate, + # "count=", count, "max=", self.config['max_open_trades']) + # return 'stop_short_loss' + + # print("sell pair=", self.wallets.get_all_balances()[metadata['pair']]) + + # if conditions: + # dataframe.loc[ + # ( + # reduce(lambda x, y: x & y, conditions) & + # (dataframe['volume'] * dataframe['close'] / 1000 >= 100) + # # (dataframe['open'] < dataframe['sma10']) + # ), + # 'sell']=1 + return dataframe + + +class StrategyHelperLocal: + """ + simple helper class to predefine a couple of patterns for our + strategy + """ + + @staticmethod + def two_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) + # (dataframe['open'].shift(2) < dataframe['close'].shift(2)) + ) + + @staticmethod + def no_more_three_green_candles(dataframe): + """ + evaluates if we are having not more than 3 green candles in a row + :param self: + :param dataframe: + :return: + """ + return not ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) + ) + + @staticmethod + def seven_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) + ) + + @staticmethod + def eight_green_candles(dataframe): + """ + evaluates if we are having 8 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) & + (dataframe['open'].shift(8) < dataframe['close'].shift(8)) + ) + + @staticmethod + def two_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) + # (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def four_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) & + (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + (dataframe['open'].shift(3 + shift) > dataframe['close'].shift(3 + shift)) & + (dataframe['open'].shift(4 + shift) > dataframe['close'].shift(4 + shift)) + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def two_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) + ) + + @staticmethod + def four_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] > dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) + ) + + @staticmethod + def four_red_one_green_candle(dataframe): + """ + evaluates if we are having a green candle and 4 previous red + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) > dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) > dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) > dataframe['close'].shift(4)) + ) diff --git a/GodStraJD3_5.json b/GodStraJD3_5.json new file mode 100644 index 0000000..8debc36 --- /dev/null +++ b/GodStraJD3_5.json @@ -0,0 +1,26 @@ +{ + "strategy_name": "GodStraJD3_5", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.15, + "trailing_stop_positive_offset": 0.2, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_real_num0": 0.7, + "buy_real_num1": 0.48, + "buy_real_num2": 0.67 + }, + "sell": {}, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2022-01-14 20:24:56.873912+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_5.py b/GodStraJD3_5.py new file mode 100644 index 0000000..665c2d0 --- /dev/null +++ b/GodStraJD3_5.py @@ -0,0 +1,618 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + return condition, dataframe + + +class GodStraJD3_5(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + # 'min': {'color': 'white'}, + # 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + 'bb_min': {'color': 'red'}, + }, + "Ind0": { + buy_crossed_indicator0: {'color': 'green'}, + buy_indicator0: {'color': 'red'} + }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + # "percent": { + # "percent": {'color': 'green'}, + # "percent3": {'color': 'blue'}, + # "percent5": {'color': 'red'} + # } + } + } + + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + # "buy_real_num0": 0.46, + # "buy_real_num1": 0.48, + # "buy_real_num2": 0.67 + # + buy_real_num0 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.46, space='buy') + buy_real_num1 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.48, space='buy') + buy_real_num2 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.67, space='buy') + + # protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + # protection_stop = IntParameter(0, 100, default=48, space='protection') + # protection_stoploss_stop = IntParameter(0, 100, default=48, space='protection') + # lookback = IntParameter(0, 200, default=48, space='protection') + # trade_limit = IntParameter(0, 10, default=2, space='protection') + # + # @property + # def protections(self): + # return [ + # { + # "method": "CooldownPeriod", + # "stop_duration_candles": 10 + # }, + # { + # "method": "MaxDrawdown", + # "lookback_period_candles": self.lookback.value, + # "trade_limit": self.trade_limit.value, + # "stop_duration_candles": self.protection_stop.value, + # "max_allowed_drawdown": self.protection_max_allowed_dd.value, + # "only_per_pair": False + # }, + # { + # "method": "StoplossGuard", + # "lookback_period_candles": 24, + # "trade_limit": 4, + # "stop_duration_candles": self.protection_stoploss_stop.value, + # "only_per_pair": False + # } + # ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + if (current_profit >= 0) & (last_candle['percent3'] < -0.01): + return "quick_lost" + + if (current_profit > 0.005) & (last_candle['percent10'] < 0.001): + return "no_change" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0): #& ((current_time - trade.open_date_utc).seconds <= 3600) + return "quick_gain" + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10'] * 1.005) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if (current_profit > 0.005) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + if (current_profit > 0) \ + & ((current_time - trade.open_date_utc).days >= 3) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'old_sma10' + + if (current_profit > -0.01) \ + & ((current_time - trade.open_date_utc).days >= 6) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'very_old_sma10' + + if (current_profit > 0) \ + & (last_candle['rsi'] > 88): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + + dataframe["q_0.1"] = dataframe.quantile(.1) + dataframe["q_0.25"] = dataframe.quantile(.25) + dataframe["q_0.33"] = dataframe.quantile(.33) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value + ) + conditions.append(condition1) + # # backup + # condition2, dataframe = condition_generator( + # dataframe, + # buy_operator1, + # buy_indicator1, + # buy_crossed_indicator1, + # self.buy_real_num1.value + # ) + # conditions.append(condition2) + # + # condition3, dataframe = condition_generator( + # dataframe, + # buy_operator2, + # buy_indicator2, + # buy_crossed_indicator2, + # self.buy_real_num2.value + # ) + # conditions.append(condition3) + + if conditions: + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (dataframe['volume10'] * dataframe['close'] / 1000 >= 10) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['low'] < dataframe['min20']) + & (dataframe['bb_width'] > 0.035) + # ) | ( + # (dataframe['volume10'] * dataframe['close'] / 1000 >= 10) + # & (dataframe['percent20'] < -0.01) + # & (dataframe['percent3'] > 0) + # & (dataframe['close'] < dataframe['sma10']) + # & (dataframe['sma100'].shift(1) < dataframe['sma100']) + # + # ) | ( + # (dataframe['volume10'].shift(1) * dataframe['close'].shift(1) / 1000 >= 10) + # & (dataframe['percent20'].shift(1) < -0.01) + # & (dataframe['percent3'].shift(1) > 0) + # & (dataframe['close'].shift(1) < dataframe['sma10'].shift(1)) + # & (dataframe['sma100'].shift(1) < dataframe['sma100']) + # # & (dataframe['pente5'].shift(1) > 0) + ), 'buy'] = 1 + + pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + pandas.set_option('display.max_columns', 30) + + # print(reduce(lambda x, y: x & y, conditions)) + # print(reduce(lambda x, y: x & y, condition1), " ", + # reduce(lambda x, y: x & y, condition2), " ", + # reduce(lambda x, y: x & y, condition3), " ", + # # (reduce(lambda x, y: x & y, conditions)) + # dataframe['buy'], + # + # ) + + # if conditions: + # dataframe.loc[ + # ( + # reduce(lambda x, y: x & y, conditions) + # # & (dataframe['volume10'] * dataframe['close'] / 1000 >= 100) + # # & (dataframe['percent5'] < 0.025) + # # (dataframe['percent10'] < 0.04) + # # ) | ( + # # (dataframe['volume10'] * dataframe['close'] / 1000 >= 100) + # # & (dataframe['percent50'] < -0.06) + # # & (dataframe['percent3'] > 0) + # # # & (dataframe['sma10'].shift(12) > dataframe['sma10'].shift(2)) + # # & (dataframe['sma10'].shift(2) > dataframe['sma10'].shift(1)) + # # & (dataframe['sma10'].shift(1) < dataframe['sma10']) + # # # & (dataframe['sma100'].shift(1) < dataframe['sma100']) + # ) + # #(dataframe['percent3'] > 0)) + # ,'buy']=1 + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_5_1.json b/GodStraJD3_5_1.json new file mode 100644 index 0000000..33376f1 --- /dev/null +++ b/GodStraJD3_5_1.json @@ -0,0 +1,41 @@ +{ + "strategy_name": "GodStraJD3_5_1", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1 + }, + "buy": { + "buy_real_num0": 0.46, + "buy_real_num1": 0.48, + "buy_real_num2": 0.67, + "profit_no_change": true, + "profit_old_sma10": true, + "profit_over_rsi": true, + "profit_quick_gain": true, + "profit_quick_gain_3": true, + "profit_short_loss": true, + "profit_sma10": true, + "profit_sma20": true, + "profit_very_old_sma10": true + }, + "sell": {}, + "protection": { + "lookback": 155, + "protection_max_allowed_dd": 0.15, + "protection_stop": 240, + "protection_stoploss_stop": 73, + "trade_limit": 3 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.243, + "trailing_stop_positive_offset": 0.306, + "trailing_only_offset_is_reached": true + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-01-29 13:25:27.974545+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_5_1.jsonDECREASE b/GodStraJD3_5_1.jsonDECREASE new file mode 100644 index 0000000..b686937 --- /dev/null +++ b/GodStraJD3_5_1.jsonDECREASE @@ -0,0 +1,32 @@ +{ + "strategy_name": "GodStraJD3_5_1", + "params": { + "roi": { + "0": 10 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.15, + "trailing_stop_positive_offset": 0.2, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_real_num0": 1.0, + "buy_real_num1": 0.11, + "buy_real_num2": 0.47 + }, + "sell": {}, + "protection": { + "lookback": 156, + "protection_max_allowed_dd": 0.56, + "protection_stop": 62, + "protection_stoploss_stop": 46, + "trade_limit": 6 + }, + "stoploss": { + "stoploss": -0.288 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-01-27 20:28:08.774453+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_5_1.py b/GodStraJD3_5_1.py new file mode 100644 index 0000000..aeacf67 --- /dev/null +++ b/GodStraJD3_5_1.py @@ -0,0 +1,577 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD3_5_1(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + 'bb_min': {'color': 'red'}, + }, + "Ind0": { + buy_crossed_indicator0: {'color': 'green'}, + buy_indicator0: {'color': 'red'} + }, + "Ind1": { + buy_indicator1: {'color': 'yellow'}, + buy_crossed_indicator1: {'color': 'pink'} + }, + "Ind2": { + buy_indicator2: {'color': 'cyan'}, + buy_crossed_indicator2: {'color': 'blue'}, + }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + buy_real_num0 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.1, space='buy') + buy_real_num1 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.0, space='buy') + buy_real_num2 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.5, space='buy') + + protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + protection_stop = IntParameter(0, 100, default=48, space='protection') + protection_stoploss_stop = IntParameter(0, 100, default=48, space='protection') + lookback = IntParameter(0, 200, default=48, space='protection') + trade_limit = IntParameter(0, 10, default=2, space='protection') + + profit_no_change = BooleanParameter(default=True, space="buy") + profit_sma10 = BooleanParameter(default=True, space="buy") + profit_sma20 = BooleanParameter(default=True, space="buy") + profit_quick_gain = BooleanParameter(default=True, space="buy") + profit_quick_gain_3 = BooleanParameter(default=True, space="buy") + profit_old_sma10 = BooleanParameter(default=True, space="buy") + profit_very_old_sma10 = BooleanParameter(default=True, space="buy") + profit_over_rsi = BooleanParameter(default=True, space="buy") + profit_short_loss = BooleanParameter(default=True, space="buy") + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + { + "method": "MaxDrawdown", + "lookback_period_candles": self.lookback.value, + "trade_limit": self.trade_limit.value, + "stop_duration_candles": self.protection_stop.value, + "max_allowed_drawdown": self.protection_max_allowed_dd.value, + "only_per_pair": False + }, + { + "method": "StoplossGuard", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": self.protection_stoploss_stop.value, + "only_per_pair": False + } + ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + if self.profit_no_change.value: + if (current_profit > 0.005) & (last_candle['percent10'] < 0.001): + return "no_change" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0): #& ((current_time - trade.open_date_utc).seconds <= 3600) + return "quick_gain" + + if self.profit_sma10.value: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10'] * 1.005) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20.value: + if (current_profit > 0.005) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + if self.profit_old_sma10.value: + if (current_profit > 0) \ + & ((current_time - trade.open_date_utc).days >= 3) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'old_sma10' + if self.profit_very_old_sma10.value: + if (current_profit > -0.01) \ + & ((current_time - trade.open_date_utc).days >= 6) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'very_old_sma10' + + if self.profit_over_rsi.value: + if (current_profit > 0) \ + & (last_candle['rsi'] > 88): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if self.profit_short_loss.value: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + condition, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value + ) + conditions.append(condition) + + # backup + condition, dataframe = condition_generator( + dataframe, + buy_operator1, + buy_indicator1, + buy_crossed_indicator1, + self.buy_real_num1.value + ) + conditions.append(condition) + + condition, dataframe = condition_generator( + dataframe, + buy_operator2, + buy_indicator2, + buy_crossed_indicator2, + self.buy_real_num2.value + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + ( + # reduce(lambda x, y: x & y, conditions) + # & (dataframe['volume10'] * dataframe['close'] / 1000 >= 100) + # # & (dataframe['percent5'] < 0.025) + # # (dataframe['percent10'] < 0.04) + # ) | ( + (dataframe['volume10'] * dataframe['close'] / 1000 >= 100) + & ((dataframe['percent20'].shift(5) < -0.025) | (dataframe['percent20'].shift(10) < -0.025)) + & ((dataframe['percent5'] > 0.001) | (dataframe['open'] < dataframe['bb_lowerband'])) + & (dataframe['sma10'].shift(1) < dataframe['sma10']) + # & (dataframe['open'].shift(50) > dataframe['open']) + ) + #(dataframe['percent3'] > 0)) + ,'buy']=1 + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_5_2.json b/GodStraJD3_5_2.json new file mode 100644 index 0000000..4197794 --- /dev/null +++ b/GodStraJD3_5_2.json @@ -0,0 +1,30 @@ +{ + "strategy_name": "GodStraJD3_5_2", + "params": { + "roi": { + "0": 1 + }, + "stoploss": { + "stoploss": -1 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.344, + "trailing_stop_positive_offset": 0.354, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_real_num0": 0.46, + "buy_real_num1": 0.48, + "buy_real_num2": 0.67 + }, + "sell": { + "sell_real_num0": 0.11, + "sell_real_num1": 0.78, + "sell_real_num2": 0.36 + }, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2022-01-20 02:01:29.086955+00:00" +} diff --git a/GodStraJD3_5_2.jsonBest b/GodStraJD3_5_2.jsonBest new file mode 100644 index 0000000..3b58dd3 --- /dev/null +++ b/GodStraJD3_5_2.jsonBest @@ -0,0 +1,30 @@ +{ + "strategy_name": "GodStraJD3_5_2", + "params": { + "roi": { + "0": 1 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.344, + "trailing_stop_positive_offset": 0.354, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_real_num0": 0.46, + "buy_real_num1": 0.48, + "buy_real_num2": 0.67 + }, + "sell": { + "sell_real_num0": 0.11, + "sell_real_num1": 0.78, + "sell_real_num2": 0.36 + }, + "protection": {}, + "stoploss": { + "stoploss": -0.02 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-01-20 01:13:41.941514+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_5_2.py b/GodStraJD3_5_2.py new file mode 100644 index 0000000..70dfb8e --- /dev/null +++ b/GodStraJD3_5_2.py @@ -0,0 +1,628 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD3_5_2(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + 'bb_min': {'color': 'red'}, + }, + "Ind0": { + buy_crossed_indicator0: {'color': 'green'}, + buy_indicator0: {'color': 'red'} + }, + "Ind1": { + buy_indicator1: {'color': 'yellow'}, + buy_crossed_indicator1: {'color': 'pink'} + }, + "Ind2": { + buy_indicator2: {'color': 'cyan'}, + buy_crossed_indicator2: {'color': 'blue'}, + }, + "Sell0": { + sell_crossed_indicator0: {'color': 'green'}, + sell_indicator0: {'color': 'red'} + }, + "Sell1": { + sell_indicator1: {'color': 'yellow'}, + sell_crossed_indicator1: {'color': 'pink'} + }, + "Sell2": { + sell_indicator2: {'color': 'cyan'}, + sell_crossed_indicator2: {'color': 'blue'}, + }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + buy_real_num0 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.1, space='buy') + buy_real_num1 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.0, space='buy') + buy_real_num2 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.5, space='buy') + + sell_real_num0 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.09731, space='sell') + sell_real_num1 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.81657, space='sell') + sell_real_num2 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.87267, space='sell') + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 12 + }, + { + "method": "MaxDrawdown", + "lookback_period_candles": 48, + "trade_limit": 2, + "stop_duration_candles": 48, + "max_allowed_drawdown": 0.04, + "only_per_pair": False + }, + ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + if (current_profit > 0.015) & (last_candle['percent'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain" + if (current_profit > 0.01) & (last_candle['rsi'] < 20): + return "small_rsi" + if (current_profit > 0.005) & (last_candle['percent3'] < -0.01): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'quick_lost' + if (current_profit < -0.02) & ((current_time - trade.open_date_utc).seconds >= 3600): + return "stop_loss_1h" + + if (current_profit > 0.005) & (last_candle['percent5'] < -0.015): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'very_quick_lost' + + # if (current_profit > 0.01) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10'] * 1.005) \ + # | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma10' + # + # if (current_profit > 0.005) & (last_candle['percent5'] < 0) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600) \ + # & ((previous_last_candle['sma20'] > last_candle['sma20']) & + # ((last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma20' + # + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if (current_profit > 0) \ + & (previous_last_candle['rsi'] > 88) \ + & ((last_candle['percent'] < - current_profit / 3) | (last_candle['percent3'] < - current_profit / 3)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + # if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + # & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'short_lost' + + #if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe[sell_crossed_indicator0] = gene_calculator(dataframe, sell_crossed_indicator0) + dataframe[sell_crossed_indicator1] = gene_calculator(dataframe, sell_crossed_indicator1) + dataframe[sell_crossed_indicator2] = gene_calculator(dataframe, sell_crossed_indicator2) + + dataframe[sell_indicator0] = gene_calculator(dataframe, sell_indicator0) + dataframe[sell_indicator1] = gene_calculator(dataframe, sell_indicator1) + dataframe[sell_indicator2] = gene_calculator(dataframe, sell_indicator2) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + condition, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value + ) + conditions.append(condition) + + # backup + condition, dataframe = condition_generator( + dataframe, + buy_operator1, + buy_indicator1, + buy_crossed_indicator1, + self.buy_real_num1.value + ) + conditions.append(condition) + + condition, dataframe = condition_generator( + dataframe, + buy_operator2, + buy_indicator2, + buy_crossed_indicator2, + self.buy_real_num2.value + ) + conditions.append(condition) + + # condition_1 = ( + # reduce(lambda x, y: x & y, conditions) & + # (dataframe['volume10'] * dataframe['close'] / 1000 >= 100) & + # (dataframe['open'] < dataframe['sma20']) + # ) + # condition_2 = ( + # (dataframe['bb_width'] > 0.11) & + # (dataframe['volume10'] * dataframe['close'] / 1000 >= 100) & + # (dataframe['rsi'].shift(1) < 20) & + # (dataframe['open'] < dataframe['sma20']) + # ) + # if (condition_1): + # dataframe.loc[condition_1, 'buy'] = 'buy_opt' + # if (condition_2): + # dataframe.loc[condition_2, 'buy'] = 'bb_width' + if conditions: + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) & + (dataframe['volume10'] * dataframe['close'] / 1000 >= 100) & + (dataframe['close'] < dataframe['sma20']) & + (dataframe['close'] < dataframe['sma50']) & + (dataframe['close'] * 0.99 < dataframe['bb_lowerband']) + ) | ( + (dataframe['close'] < dataframe['bb_lowerband']) & + (dataframe['bb_width'] > 0.11) & + (dataframe['volume10'] * dataframe['close'] / 1000 >= 100) & + (dataframe['rsi'] < 30) & + (dataframe['close'] < dataframe['sma20']) & + (dataframe['sma100'].shift(30) < dataframe['sma100']) + ),'buy']=1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = list() + + condition, dataframe = condition_generator( + dataframe, + sell_operator0, + sell_indicator0, + sell_crossed_indicator0, + self.sell_real_num0.value + ) + conditions.append(condition) + + # backup + condition, dataframe = condition_generator( + dataframe, + sell_operator1, + sell_indicator1, + sell_crossed_indicator1, + self.sell_real_num1.value + ) + conditions.append(condition) + + condition, dataframe = condition_generator( + dataframe, + sell_operator2, + sell_indicator2, + sell_crossed_indicator2, + self.sell_real_num2.value + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + ), + 'sell']=1 + return dataframe + diff --git a/GodStraJD3_5_3.json b/GodStraJD3_5_3.json new file mode 100644 index 0000000..dbcf049 --- /dev/null +++ b/GodStraJD3_5_3.json @@ -0,0 +1,29 @@ +{ + "strategy_name": "GodStraJD3_5_3", + "params": { + "roi": { + "0": 1 + }, + "stoploss": { + "stoploss": -0.02 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.344, + "trailing_stop_positive_offset": 0.354, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_sma_max": 0.98, + "buy_sma_min": -1.52 + }, + "sell": { + "sell_real_num0": 0.51, + "sell_real_num1": 0.85, + "sell_real_num2": 0.0 + }, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2022-01-20 17:43:05.699962+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_5_3.py b/GodStraJD3_5_3.py new file mode 100644 index 0000000..0aa4442 --- /dev/null +++ b/GodStraJD3_5_3.py @@ -0,0 +1,609 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD3_5_3(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + 'bb_min': {'color': 'red'}, + }, + "Ind0": { + buy_crossed_indicator0: {'color': 'green'}, + buy_indicator0: {'color': 'red'} + }, + "Ind1": { + buy_indicator1: {'color': 'yellow'}, + buy_crossed_indicator1: {'color': 'pink'} + }, + "Ind2": { + buy_indicator2: {'color': 'cyan'}, + buy_crossed_indicator2: {'color': 'blue'}, + }, + "Sell0": { + sell_crossed_indicator0: {'color': 'green'}, + sell_indicator0: {'color': 'red'} + }, + "Sell1": { + sell_indicator1: {'color': 'yellow'}, + sell_crossed_indicator1: {'color': 'pink'} + }, + "Sell2": { + sell_indicator2: {'color': 'cyan'}, + sell_crossed_indicator2: {'color': 'blue'}, + }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + + buy_real_num0 = 0.46 + buy_real_num1 = 0.48 + buy_real_num2 = 0.67 + #buy_real_num0 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.1, space='buy') + #buy_real_num1 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.0, space='buy') + #buy_real_num2 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.5, space='buy') + + sell_real_num0 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.09731, space='sell') + sell_real_num1 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.81657, space='sell') + sell_real_num2 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.87267, space='sell') + + # buy_bb_width = DecimalParameter(0, 0.2, decimals=3, default=0.12, space='buy') + # buy_bb_min = IntParameter(-30, 50, default=50, space='buy') + + buy_sma_min = DecimalParameter(-2, 2, decimals=2, default=0, space='buy') + buy_sma_max = DecimalParameter(-2, 2, decimals=2, default=0.5, space='buy') + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + { + "method": "MaxDrawdown", + "lookback_period_candles": 48, + "trade_limit": 2, + "stop_duration_candles": 72, + "max_allowed_drawdown": 0.04, + "only_per_pair": False + }, + ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + #if (current_profit > 0.005) & (last_candle['percent10'] < 0.001): + # return "no_change" + if (current_profit > 0.01) & (last_candle['rsi'] < 20): + return "small_rsi" + # if (current_profit > 0.01) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10'] * 1.005) \ + # | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma10' + # + # if (current_profit > 0.005) & (last_candle['percent5'] < 0) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600) \ + # & ((previous_last_candle['sma20'] > last_candle['sma20']) & + # ((last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma20' + # + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if (current_profit > 0) \ + & (last_candle['rsi'] > 88): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + # if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + # & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'short_lost' + + if (current_profit > 0) \ + & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe[sell_crossed_indicator0] = gene_calculator(dataframe, sell_crossed_indicator0) + dataframe[sell_crossed_indicator1] = gene_calculator(dataframe, sell_crossed_indicator1) + dataframe[sell_crossed_indicator2] = gene_calculator(dataframe, sell_crossed_indicator2) + + dataframe[sell_indicator0] = gene_calculator(dataframe, sell_indicator0) + dataframe[sell_indicator1] = gene_calculator(dataframe, sell_indicator1) + dataframe[sell_indicator2] = gene_calculator(dataframe, sell_indicator2) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + condition, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + #self.buy_real_num0.value + self.buy_real_num0 + ) + conditions.append(condition) + + # backup + condition, dataframe = condition_generator( + dataframe, + buy_operator1, + buy_indicator1, + buy_crossed_indicator1, + #self.buy_real_num1.value + self.buy_real_num1 + ) + conditions.append(condition) + + condition, dataframe = condition_generator( + dataframe, + buy_operator2, + buy_indicator2, + buy_crossed_indicator2, + #self.buy_real_num2.value + self.buy_real_num2 + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) & + (dataframe['volume10'] * dataframe['close'] / 1000 >= 100) & + # ((100 * ((dataframe['sma100'] - dataframe['sma100'].shift(10)) / dataframe['sma100'])) >= self.buy_sma_min.value) & + # ((100 * ((dataframe['sma100'] - dataframe['sma100'].shift(10)) / dataframe['sma100'])) <= self.buy_sma_max.value) & + (dataframe['close'] < dataframe['sma20']) + ),'buy']=1 + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = list() + + condition, dataframe = condition_generator( + dataframe, + sell_operator0, + sell_indicator0, + sell_crossed_indicator0, + self.sell_real_num0.value + ) + conditions.append(condition) + + # backup + condition, dataframe = condition_generator( + dataframe, + sell_operator1, + sell_indicator1, + sell_crossed_indicator1, + self.sell_real_num1.value + ) + conditions.append(condition) + + condition, dataframe = condition_generator( + dataframe, + sell_operator2, + sell_indicator2, + sell_crossed_indicator2, + self.sell_real_num2.value + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) # (dataframe['open'] < dataframe['sma10']) + ), + 'sell']=1 + return dataframe + diff --git a/GodStraJD3_6.json b/GodStraJD3_6.json new file mode 100644 index 0000000..2cb855e --- /dev/null +++ b/GodStraJD3_6.json @@ -0,0 +1,48 @@ +{ + "strategy_name": "GodStraJD3_6", + "params": { + "roi": { + "0": 1 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.15, + "trailing_stop_positive_offset": 0.2, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_crossed_indicator0": "CDLTHRUSTING-20", + "buy_crossed_indicator1": "MACDEXT-0-20", + "buy_crossed_indicator2": "CDLXSIDEGAP3METHODS-50", + "buy_indicator0": "MIDPOINT-5", + "buy_indicator1": "RSI-20", + "buy_indicator2": "CDLSHORTLINE-5", + "buy_operator0": "/>R", + "buy_operator1": "", + "sell_operator2": "CB", + "sell_real_num0": 0.4, + "sell_real_num1": 0.7, + "sell_real_num2": 0.8 + }, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2022-01-15 13:33:08.407372+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_6.py b/GodStraJD3_6.py new file mode 100644 index 0000000..b9a1277 --- /dev/null +++ b/GodStraJD3_6.py @@ -0,0 +1,956 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle +# TODO: this gene is removed 'MAVP' cuz or error on periods +all_god_genes = { + 'Overlap Studies': { + 'BBANDS-0', # Bollinger Bands + 'BBANDS-1', # Bollinger Bands + 'BBANDS-2', # Bollinger Bands + 'DEMA', # Double Exponential Moving Average + 'EMA', # Exponential Moving Average + 'HT_TRENDLINE', # Hilbert Transform - Instantaneous Trendline + 'KAMA', # Kaufman Adaptive Moving Average + 'MA', # Moving average + 'MAMA-0', # MESA Adaptive Moving Average + 'MAMA-1', # MESA Adaptive Moving Average + # TODO: Fix this + # 'MAVP', # Moving average with variable period + 'MIDPOINT', # MidPoint over period + 'MIDPRICE', # Midpoint Price over period + 'SAR', # Parabolic SAR + 'SAREXT', # Parabolic SAR - Extended + 'SMA', # Simple Moving Average + 'T3', # Triple Exponential Moving Average (T3) + 'TEMA', # Triple Exponential Moving Average + 'TRIMA', # Triangular Moving Average + 'WMA', # Weighted Moving Average + }, + 'Momentum Indicators': { + 'ADX', # Average Directional Movement Index + 'ADXR', # Average Directional Movement Index Rating + 'APO', # Absolute Price Oscillator + 'AROON-0', # Aroon + 'AROON-1', # Aroon + 'AROONOSC', # Aroon Oscillator + 'BOP', # Balance Of Power + 'CCI', # Commodity Channel Index + 'CMO', # Chande Momentum Oscillator + 'DX', # Directional Movement Index + 'MACD-0', # Moving Average Convergence/Divergence + 'MACD-1', # Moving Average Convergence/Divergence + 'MACD-2', # Moving Average Convergence/Divergence + 'MACDEXT-0', # MACD with controllable MA type + 'MACDEXT-1', # MACD with controllable MA type + 'MACDEXT-2', # MACD with controllable MA type + 'MACDFIX-0', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-1', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-2', # Moving Average Convergence/Divergence Fix 12/26 + 'MFI', # Money Flow Index + 'MINUS_DI', # Minus Directional Indicator + 'MINUS_DM', # Minus Directional Movement + 'MOM', # Momentum + 'PLUS_DI', # Plus Directional Indicator + 'PLUS_DM', # Plus Directional Movement + 'PPO', # Percentage Price Oscillator + 'ROC', # Rate of change : ((price/prevPrice)-1)*100 + # Rate of change Percentage: (price-prevPrice)/prevPrice + 'ROCP', + 'ROCR', # Rate of change ratio: (price/prevPrice) + # Rate of change ratio 100 scale: (price/prevPrice)*100 + 'ROCR100', + 'RSI', # Relative Strength Index + 'STOCH-0', # Stochastic + 'STOCH-1', # Stochastic + 'STOCHF-0', # Stochastic Fast + 'STOCHF-1', # Stochastic Fast + 'STOCHRSI-0', # Stochastic Relative Strength Index + 'STOCHRSI-1', # Stochastic Relative Strength Index + # 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA + 'TRIX', + 'ULTOSC', # Ultimate Oscillator + 'WILLR', # Williams' %R + }, + 'Volume Indicators': { + 'AD', # Chaikin A/D Line + 'ADOSC', # Chaikin A/D Oscillator + 'OBV', # On Balance Volume + }, + 'Volatility Indicators': { + 'ATR', # Average True Range + 'NATR', # Normalized Average True Range + 'TRANGE', # True Range + }, + 'Price Transform': { + 'AVGPRICE', # Average Price + 'MEDPRICE', # Median Price + 'TYPPRICE', # Typical Price + 'WCLPRICE', # Weighted Close Price + }, + 'Cycle Indicators': { + 'HT_DCPERIOD', # Hilbert Transform - Dominant Cycle Period + 'HT_DCPHASE', # Hilbert Transform - Dominant Cycle Phase + 'HT_PHASOR-0', # Hilbert Transform - Phasor Components + 'HT_PHASOR-1', # Hilbert Transform - Phasor Components + 'HT_SINE-0', # Hilbert Transform - SineWave + 'HT_SINE-1', # Hilbert Transform - SineWave + 'HT_TRENDMODE', # Hilbert Transform - Trend vs Cycle Mode + }, + 'Pattern Recognition': { + 'CDL2CROWS', # Two Crows + 'CDL3BLACKCROWS', # Three Black Crows + 'CDL3INSIDE', # Three Inside Up/Down + 'CDL3LINESTRIKE', # Three-Line Strike + 'CDL3OUTSIDE', # Three Outside Up/Down + 'CDL3STARSINSOUTH', # Three Stars In The South + 'CDL3WHITESOLDIERS', # Three Advancing White Soldiers + 'CDLABANDONEDBABY', # Abandoned Baby + 'CDLADVANCEBLOCK', # Advance Block + 'CDLBELTHOLD', # Belt-hold + 'CDLBREAKAWAY', # Breakaway + 'CDLCLOSINGMARUBOZU', # Closing Marubozu + 'CDLCONCEALBABYSWALL', # Concealing Baby Swallow + 'CDLCOUNTERATTACK', # Counterattack + 'CDLDARKCLOUDCOVER', # Dark Cloud Cover + 'CDLDOJI', # Doji + 'CDLDOJISTAR', # Doji Star + 'CDLDRAGONFLYDOJI', # Dragonfly Doji + 'CDLENGULFING', # Engulfing Pattern + 'CDLEVENINGDOJISTAR', # Evening Doji Star + 'CDLEVENINGSTAR', # Evening Star + 'CDLGAPSIDESIDEWHITE', # Up/Down-gap side-by-side white lines + 'CDLGRAVESTONEDOJI', # Gravestone Doji + 'CDLHAMMER', # Hammer + 'CDLHANGINGMAN', # Hanging Man + 'CDLHARAMI', # Harami Pattern + 'CDLHARAMICROSS', # Harami Cross Pattern + 'CDLHIGHWAVE', # High-Wave Candle + 'CDLHIKKAKE', # Hikkake Pattern + 'CDLHIKKAKEMOD', # Modified Hikkake Pattern + 'CDLHOMINGPIGEON', # Homing Pigeon + 'CDLIDENTICAL3CROWS', # Identical Three Crows + 'CDLINNECK', # In-Neck Pattern + 'CDLINVERTEDHAMMER', # Inverted Hammer + 'CDLKICKING', # Kicking + 'CDLKICKINGBYLENGTH', # Kicking - bull/bear determined by the longer marubozu + 'CDLLADDERBOTTOM', # Ladder Bottom + 'CDLLONGLEGGEDDOJI', # Long Legged Doji + 'CDLLONGLINE', # Long Line Candle + 'CDLMARUBOZU', # Marubozu + 'CDLMATCHINGLOW', # Matching Low + 'CDLMATHOLD', # Mat Hold + 'CDLMORNINGDOJISTAR', # Morning Doji Star + 'CDLMORNINGSTAR', # Morning Star + 'CDLONNECK', # On-Neck Pattern + 'CDLPIERCING', # Piercing Pattern + 'CDLRICKSHAWMAN', # Rickshaw Man + 'CDLRISEFALL3METHODS', # Rising/Falling Three Methods + 'CDLSEPARATINGLINES', # Separating Lines + 'CDLSHOOTINGSTAR', # Shooting Star + 'CDLSHORTLINE', # Short Line Candle + 'CDLSPINNINGTOP', # Spinning Top + 'CDLSTALLEDPATTERN', # Stalled Pattern + 'CDLSTICKSANDWICH', # Stick Sandwich + # Takuri (Dragonfly Doji with very long lower shadow) + 'CDLTAKURI', + 'CDLTASUKIGAP', # Tasuki Gap + 'CDLTHRUSTING', # Thrusting Pattern + 'CDLTRISTAR', # Tristar Pattern + 'CDLUNIQUE3RIVER', # Unique 3 River + 'CDLUPSIDEGAP2CROWS', # Upside Gap Two Crows + 'CDLXSIDEGAP3METHODS', # Upside/Downside Gap Three Methods + + }, + 'Statistic Functions': { + 'BETA', # Beta + 'CORREL', # Pearson's Correlation Coefficient (r) + 'LINEARREG', # Linear Regression + 'LINEARREG_ANGLE', # Linear Regression Angle + 'LINEARREG_INTERCEPT', # Linear Regression Intercept + 'LINEARREG_SLOPE', # Linear Regression Slope + 'STDDEV', # Standard Deviation + 'TSF', # Time Series Forecast + 'VAR', # Variance + } + +} +god_genes = set() +########################### SETTINGS ############################## + +# god_genes = {'SMA'} +god_genes |= all_god_genes['Overlap Studies'] +god_genes |= all_god_genes['Momentum Indicators'] +god_genes |= all_god_genes['Volume Indicators'] +god_genes |= all_god_genes['Volatility Indicators'] +god_genes |= all_god_genes['Price Transform'] +god_genes |= all_god_genes['Cycle Indicators'] +god_genes |= all_god_genes['Pattern Recognition'] +god_genes |= all_god_genes['Statistic Functions'] + +#timeperiods = [5, 6, 12, 15, 50, 55, 100, 110] +timeperiods = [5, 10, 20, 50, 100] + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD3_6(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 1, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + 'bb_min': {'color': 'red'}, + }, + "Ind": { + 'MA-20': {'color': 'green'}, + 'STOCH-1-10': {'color': 'blue'}, + 'CDLDRAGONFLYDOJI-5': {'color': 'red'} + }, + "Ind2": { + 'MINUS_DM-5': {'color': 'green'}, + 'DX-5': {'color': 'blue'}, + 'LINEARREG-50': {'color': 'red'} + }, + "Profit": { + 'profit': {'color': 'pink'}, + }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + buy_crossed_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="ADD-20", space='buy') + buy_crossed_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="ASIN-6", space='buy') + buy_crossed_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLEVENINGSTAR-50", space='buy') + + buy_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="SMA-100", space='buy') + buy_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="WILLR-50", space='buy') + buy_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLHANGINGMAN-20", space='buy') + + buy_operator0 = CategoricalParameter(operators, default="/ float: + # + # dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + # current_candle = dataframe.iloc[-1].squeeze() + # + # # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + # if current_candle['bb_width'] > 0.065: + # # print("use more stake", pair, " ", proposed_stake * 2) + # return min(max_stake, proposed_stake * 2) + # + # if current_candle['bb_width'] > 0.045: + # # print("use more stake", pair, " ", proposed_stake * 1.5) + # return min(max_stake, proposed_stake * 1.5) + # + # # if current_candle['bb_width'] < 0.020: + # # print("use less stake", pair, " ", proposed_stake / 2) + # # return min(max_stake, proposed_stake / 2) + # # if self.config['stake_amount'] == 'unlimited': + # # # Use entire available wallet during favorable conditions when in compounding mode. + # # return max_stake + # # else: + # # # Compound profits during favorable conditions instead of using a static stake. + # # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + # + # # Use default stake amount. + # return proposed_stake + + # def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + # current_profit: float, **kwargs): + # dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + # last_candle = dataframe.iloc[-1].squeeze() + # previous_last_candle = dataframe.iloc[-2].squeeze() + # previous_5_candle = dataframe.iloc[-5].squeeze() + # #print("last_candle", last_candle) + # #print("previous_last_candle", previous_last_candle) + # + # #dataframe.iloc[-1]['profit'] = current_profit + # count = 0 + # for coin, balance in self.wallets.get_all_balances().items(): + # count = count + 1 + # # print(coin, " ", balance) + # # print("count=", count) + # + # # (last_candle['percent5'] < -0.005) \ + # # if (0 < current_profit < 0.005) \ + # # & ((current_time - trade.open_date_utc).seconds >= 3600 * 2): + # # # & (previous_last_candle['sma10'] > last_candle['sma10']): + # # print("too_small_gain", pair, trade, " profit=", current_profit, " rate=", current_rate, " percent5=", + # # last_candle['percent5']) + # # return 'too_small_gain' + # + # # if (current_profit < -0.05) \ + # # & ((current_time - trade.open_date_utc).days >= 3): + # # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # # return 'stop_loss_profit' + # + # # if (current_profit > 0.02) \ + # # & (last_candle['percent'] < 0.01) \ + # # & ((current_time - trade.open_date_utc).seconds >= 3600): + # # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # # return 'lost_half_profit' + # + # # ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \ + # if (current_profit > 0) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10'] * 1.005) | (last_candle['percent3'] < -0.01)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_bb_band_sma10_desc' + # + # # if (current_profit > 0) \ + # # & (last_candle['percent'] < -0.02): + # # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # # return 'stop_percent_loss' + # + # #if (current_profit > 0) \ + # # & ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \ + # # & (previous_last_candle['sma20'] > last_candle['sma20']) \ + # # & (last_candle['percent'] < 0): + # # print("over_bb_band_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # # return 'over_bb_band_sma20_desc' + # + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 88): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi' + # + # # description trade + # # Trade(id=0, pair=CAKE/USDT, amount=4.19815281, open_rate=11.91000000, open_since=2021-12-22 17:55:00) + # # print(last_candle) + # #if 0.015 < current_profit < 0.03: + # # if (last_candle['percent3'] < -0.005 ): + # # # self.lock_pair(pair, until=current_time + timedelta(hours=3)) + # # print("profit_3h_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # # return 'profit_percent3' + # + # # + # # if (0 < current_profit < 0.1) \ + # # & (previous_last_candle['sma20'] > last_candle['sma20']) \ + # # & ((current_time - trade.open_date_utc).seconds >= 3600 * 5): + # # print("profit_5h_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # # return 'profit_5h_sma20_desc' + # + # # if (count == self.config['max_open_trades']) & (current_profit < -0.04) \ + # # & ((current_time - trade.open_date_utc).seconds >= 3600 * 6): + # # self.lock_pair(pair, until=current_time + timedelta(hours=10)) + # # print("stop_short_loss", pair, trade, " profit=", current_profit, " rate=", current_rate, + # # "count=", count, "max=", self.config['max_open_trades']) + # # return 'stop_short_loss' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + dataframe['MINUS_DM-5'] = gene_calculator(dataframe, 'MINUS_DM-5') + dataframe['LINEARREG-50'] = gene_calculator(dataframe, 'LINEARREG-50') + dataframe['MA-20'] = gene_calculator(dataframe, 'MA-20') + + dataframe['STOCH-1-10'] = gene_calculator(dataframe, 'STOCH-1-10') + dataframe['CDLDRAGONFLYDOJI-5'] = gene_calculator(dataframe, 'CDLDRAGONFLYDOJI-5') + dataframe['DX-5'] = gene_calculator(dataframe, 'DX-5') + + # dataframe['MINUS_DM-5'] = ta.MINUS_DM(dataframe, timeperiod=5) + # dataframe['LINEARREG-50'] = ta.LINEARREG(dataframe, timeperiod=50) + # dataframe['MA-20'] = ta.MA(dataframe, timeperiod=20) + # stoch = ta.STOCH(dataframe, timeperiod=10) + # # print(stoch) + # dataframe['STOCH-1-10'] = stoch['slowd'] + # dataframe['CDLDRAGONFLYDOJI-5'] = ta.CDLDRAGONFLYDOJI(dataframe, timeperiod=5) + + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # dataframe['min'] = ta.MIN(dataframe) + # dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + # TODO: Its not dry code! + buy_indicator = self.buy_indicator0.value + buy_crossed_indicator = self.buy_crossed_indicator0.value + buy_operator = self.buy_operator0.value + buy_real_num = self.buy_real_num0.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + # backup + buy_indicator = self.buy_indicator1.value + buy_crossed_indicator = self.buy_crossed_indicator1.value + buy_operator = self.buy_operator1.value + buy_real_num = self.buy_real_num1.value + + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + buy_indicator = self.buy_indicator2.value + buy_crossed_indicator = self.buy_crossed_indicator2.value + buy_operator = self.buy_operator2.value + buy_real_num = self.buy_real_num2.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + # ( + # (dataframe['close'] < dataframe['bb_lowerband']) + # & (dataframe['bb_width'] >= 0.045) + # & (dataframe['volume'] * dataframe['close'] / 1000 > 100) + # ) | + (reduce(lambda x, y: x & y, conditions)), + 'buy']=1 + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + # TODO: Its not dry code! + sell_indicator = self.sell_indicator0.value + sell_crossed_indicator = self.sell_crossed_indicator0.value + sell_operator = self.sell_operator0.value + sell_real_num = self.sell_real_num0.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator1.value + sell_crossed_indicator = self.sell_crossed_indicator1.value + sell_operator = self.sell_operator1.value + sell_real_num = self.sell_real_num1.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator2.value + sell_crossed_indicator = self.sell_crossed_indicator2.value + sell_operator = self.sell_operator2.value + sell_real_num = self.sell_real_num2.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + #dataframe.loc[(dataframe['close'] < dataframe['bb_lowerband']),'sell']=1 + + # count = 0 + # for coin, balance in self.wallets.get_all_balances().items(): + # count = count + 1 + # # print(coin, " ", balance) + # if (count == self.config['max_open_trades']) & (current_profit < -0.04) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 6): + # self.lock_pair(pair, until=current_time + timedelta(hours=10)) + # print("stop_short_loss", pair, trade, " profit=", current_profit, " rate=", current_rate, + # "count=", count, "max=", self.config['max_open_trades']) + # return 'stop_short_loss' + + # print("sell pair=", self.wallets.get_all_balances()[metadata['pair']]) + + if conditions: + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) & + (dataframe['volume'] * dataframe['close'] / 1000 >= 100) + # (dataframe['open'] < dataframe['sma10']) + ), + 'sell']=1 + return dataframe diff --git a/GodStraJD3_6_53.json b/GodStraJD3_6_53.json new file mode 100644 index 0000000..abd47b8 --- /dev/null +++ b/GodStraJD3_6_53.json @@ -0,0 +1,48 @@ +{ + "strategy_name": "GodStraJD3_6_53", + "params": { + "roi": { + "0": 1 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.15, + "trailing_stop_positive_offset": 0.2, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_crossed_indicator0": "HT_TRENDLINE-100", + "buy_crossed_indicator1": "MACD-1-100", + "buy_crossed_indicator2": "CCI-20", + "buy_indicator0": "CDLDOJISTAR-10", + "buy_indicator1": "WCLPRICE-10", + "buy_indicator2": "DEMA-50", + "buy_operator0": "/>R", + "buy_operator1": "R", + "sell_operator2": ">", + "sell_real_num0": 0.1, + "sell_real_num1": 0.8, + "sell_real_num2": 1.0 + }, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2022-02-01 22:59:30.762086+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_6_53.jsonOLD b/GodStraJD3_6_53.jsonOLD new file mode 100644 index 0000000..8b6205a --- /dev/null +++ b/GodStraJD3_6_53.jsonOLD @@ -0,0 +1,48 @@ +{ + "strategy_name": "GodStraJD3_6_53", + "params": { + "roi": { + "0": 1 + }, + "stoploss": { + "stoploss": -2.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.15, + "trailing_stop_positive_offset": 0.2, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_crossed_indicator0": "HT_TRENDLINE-100", + "buy_crossed_indicator1": "MACD-1-100", + "buy_crossed_indicator2": "CCI-20", + "buy_indicator0": "CDLDOJISTAR-10", + "buy_indicator1": "WCLPRICE-10", + "buy_indicator2": "DEMA-50", + "buy_operator0": "/>R", + "buy_operator1": "R", + "sell_real_num0": 0.1, + "sell_real_num1": 0.8, + "sell_real_num2": 0.9 + }, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2022-01-15 13:26:31.727849+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_6_53.py b/GodStraJD3_6_53.py new file mode 100644 index 0000000..9334487 --- /dev/null +++ b/GodStraJD3_6_53.py @@ -0,0 +1,973 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy, SellCheckTuple +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle +# TODO: this gene is removed 'MAVP' cuz or error on periods +all_god_genes = { + 'Overlap Studies': { + 'BBANDS-0', # Bollinger Bands + 'BBANDS-1', # Bollinger Bands + 'BBANDS-2', # Bollinger Bands + 'DEMA', # Double Exponential Moving Average + 'EMA', # Exponential Moving Average + 'HT_TRENDLINE', # Hilbert Transform - Instantaneous Trendline + 'KAMA', # Kaufman Adaptive Moving Average + 'MA', # Moving average + 'MAMA-0', # MESA Adaptive Moving Average + 'MAMA-1', # MESA Adaptive Moving Average + # TODO: Fix this + # 'MAVP', # Moving average with variable period + 'MIDPOINT', # MidPoint over period + 'MIDPRICE', # Midpoint Price over period + 'SAR', # Parabolic SAR + 'SAREXT', # Parabolic SAR - Extended + 'SMA', # Simple Moving Average + 'T3', # Triple Exponential Moving Average (T3) + 'TEMA', # Triple Exponential Moving Average + 'TRIMA', # Triangular Moving Average + 'WMA', # Weighted Moving Average + }, + 'Momentum Indicators': { + 'ADX', # Average Directional Movement Index + 'ADXR', # Average Directional Movement Index Rating + 'APO', # Absolute Price Oscillator + 'AROON-0', # Aroon + 'AROON-1', # Aroon + 'AROONOSC', # Aroon Oscillator + 'BOP', # Balance Of Power + 'CCI', # Commodity Channel Index + 'CMO', # Chande Momentum Oscillator + 'DX', # Directional Movement Index + 'MACD-0', # Moving Average Convergence/Divergence + 'MACD-1', # Moving Average Convergence/Divergence + 'MACD-2', # Moving Average Convergence/Divergence + 'MACDEXT-0', # MACD with controllable MA type + 'MACDEXT-1', # MACD with controllable MA type + 'MACDEXT-2', # MACD with controllable MA type + 'MACDFIX-0', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-1', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-2', # Moving Average Convergence/Divergence Fix 12/26 + 'MFI', # Money Flow Index + 'MINUS_DI', # Minus Directional Indicator + 'MINUS_DM', # Minus Directional Movement + 'MOM', # Momentum + 'PLUS_DI', # Plus Directional Indicator + 'PLUS_DM', # Plus Directional Movement + 'PPO', # Percentage Price Oscillator + 'ROC', # Rate of change : ((price/prevPrice)-1)*100 + # Rate of change Percentage: (price-prevPrice)/prevPrice + 'ROCP', + 'ROCR', # Rate of change ratio: (price/prevPrice) + # Rate of change ratio 100 scale: (price/prevPrice)*100 + 'ROCR100', + 'RSI', # Relative Strength Index + 'STOCH-0', # Stochastic + 'STOCH-1', # Stochastic + 'STOCHF-0', # Stochastic Fast + 'STOCHF-1', # Stochastic Fast + 'STOCHRSI-0', # Stochastic Relative Strength Index + 'STOCHRSI-1', # Stochastic Relative Strength Index + # 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA + 'TRIX', + 'ULTOSC', # Ultimate Oscillator + 'WILLR', # Williams' %R + }, + 'Volume Indicators': { + 'AD', # Chaikin A/D Line + 'ADOSC', # Chaikin A/D Oscillator + 'OBV', # On Balance Volume + }, + 'Volatility Indicators': { + 'ATR', # Average True Range + 'NATR', # Normalized Average True Range + 'TRANGE', # True Range + }, + 'Price Transform': { + 'AVGPRICE', # Average Price + 'MEDPRICE', # Median Price + 'TYPPRICE', # Typical Price + 'WCLPRICE', # Weighted Close Price + }, + 'Cycle Indicators': { + 'HT_DCPERIOD', # Hilbert Transform - Dominant Cycle Period + 'HT_DCPHASE', # Hilbert Transform - Dominant Cycle Phase + 'HT_PHASOR-0', # Hilbert Transform - Phasor Components + 'HT_PHASOR-1', # Hilbert Transform - Phasor Components + 'HT_SINE-0', # Hilbert Transform - SineWave + 'HT_SINE-1', # Hilbert Transform - SineWave + 'HT_TRENDMODE', # Hilbert Transform - Trend vs Cycle Mode + }, + 'Pattern Recognition': { + 'CDL2CROWS', # Two Crows + 'CDL3BLACKCROWS', # Three Black Crows + 'CDL3INSIDE', # Three Inside Up/Down + 'CDL3LINESTRIKE', # Three-Line Strike + 'CDL3OUTSIDE', # Three Outside Up/Down + 'CDL3STARSINSOUTH', # Three Stars In The South + 'CDL3WHITESOLDIERS', # Three Advancing White Soldiers + 'CDLABANDONEDBABY', # Abandoned Baby + 'CDLADVANCEBLOCK', # Advance Block + 'CDLBELTHOLD', # Belt-hold + 'CDLBREAKAWAY', # Breakaway + 'CDLCLOSINGMARUBOZU', # Closing Marubozu + 'CDLCONCEALBABYSWALL', # Concealing Baby Swallow + 'CDLCOUNTERATTACK', # Counterattack + 'CDLDARKCLOUDCOVER', # Dark Cloud Cover + 'CDLDOJI', # Doji + 'CDLDOJISTAR', # Doji Star + 'CDLDRAGONFLYDOJI', # Dragonfly Doji + 'CDLENGULFING', # Engulfing Pattern + 'CDLEVENINGDOJISTAR', # Evening Doji Star + 'CDLEVENINGSTAR', # Evening Star + 'CDLGAPSIDESIDEWHITE', # Up/Down-gap side-by-side white lines + 'CDLGRAVESTONEDOJI', # Gravestone Doji + 'CDLHAMMER', # Hammer + 'CDLHANGINGMAN', # Hanging Man + 'CDLHARAMI', # Harami Pattern + 'CDLHARAMICROSS', # Harami Cross Pattern + 'CDLHIGHWAVE', # High-Wave Candle + 'CDLHIKKAKE', # Hikkake Pattern + 'CDLHIKKAKEMOD', # Modified Hikkake Pattern + 'CDLHOMINGPIGEON', # Homing Pigeon + 'CDLIDENTICAL3CROWS', # Identical Three Crows + 'CDLINNECK', # In-Neck Pattern + 'CDLINVERTEDHAMMER', # Inverted Hammer + 'CDLKICKING', # Kicking + 'CDLKICKINGBYLENGTH', # Kicking - bull/bear determined by the longer marubozu + 'CDLLADDERBOTTOM', # Ladder Bottom + 'CDLLONGLEGGEDDOJI', # Long Legged Doji + 'CDLLONGLINE', # Long Line Candle + 'CDLMARUBOZU', # Marubozu + 'CDLMATCHINGLOW', # Matching Low + 'CDLMATHOLD', # Mat Hold + 'CDLMORNINGDOJISTAR', # Morning Doji Star + 'CDLMORNINGSTAR', # Morning Star + 'CDLONNECK', # On-Neck Pattern + 'CDLPIERCING', # Piercing Pattern + 'CDLRICKSHAWMAN', # Rickshaw Man + 'CDLRISEFALL3METHODS', # Rising/Falling Three Methods + 'CDLSEPARATINGLINES', # Separating Lines + 'CDLSHOOTINGSTAR', # Shooting Star + 'CDLSHORTLINE', # Short Line Candle + 'CDLSPINNINGTOP', # Spinning Top + 'CDLSTALLEDPATTERN', # Stalled Pattern + 'CDLSTICKSANDWICH', # Stick Sandwich + # Takuri (Dragonfly Doji with very long lower shadow) + 'CDLTAKURI', + 'CDLTASUKIGAP', # Tasuki Gap + 'CDLTHRUSTING', # Thrusting Pattern + 'CDLTRISTAR', # Tristar Pattern + 'CDLUNIQUE3RIVER', # Unique 3 River + 'CDLUPSIDEGAP2CROWS', # Upside Gap Two Crows + 'CDLXSIDEGAP3METHODS', # Upside/Downside Gap Three Methods + + }, + 'Statistic Functions': { + 'BETA', # Beta + 'CORREL', # Pearson's Correlation Coefficient (r) + 'LINEARREG', # Linear Regression + 'LINEARREG_ANGLE', # Linear Regression Angle + 'LINEARREG_INTERCEPT', # Linear Regression Intercept + 'LINEARREG_SLOPE', # Linear Regression Slope + 'STDDEV', # Standard Deviation + 'TSF', # Time Series Forecast + 'VAR', # Variance + } + +} +god_genes = set() +########################### SETTINGS ############################## + +# god_genes = {'SMA'} +god_genes |= all_god_genes['Overlap Studies'] +god_genes |= all_god_genes['Momentum Indicators'] +god_genes |= all_god_genes['Volume Indicators'] +god_genes |= all_god_genes['Volatility Indicators'] +god_genes |= all_god_genes['Price Transform'] +god_genes |= all_god_genes['Cycle Indicators'] +god_genes |= all_god_genes['Pattern Recognition'] +god_genes |= all_god_genes['Statistic Functions'] + +#timeperiods = [5, 6, 12, 15, 50, 55, 100, 110] +timeperiods = [5, 10, 20, 50, 100] + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD3_6_53(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 1, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + # 'min': {'color': 'white'}, + # 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + # "BB": { + # 'bb_width': {'color': 'white'}, + # 'bb_min': {'color': 'red'}, + # }, + # "Ind": { + # ind_1: {'color': 'green'}, + # ind_2: {'color': 'blue'}, + # ind_3: {'color': 'red'} + # }, + # "Ind2": { + # ind_4: {'color': 'green'}, + # ind_5: {'color': 'blue'}, + # ind_6: {'color': 'red'} + # }, + "Pentes": { + 'pente5': {'color': 'green'}, + 'pente10': {'color': 'pink'}, + 'pente20': {'color': 'green'}, + 'pente50': {'color': 'yellow'} + }, + # "Rsi": { + # 'rsi': {'color': 'pink'}, + # }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'}, + "percent10": {'color': 'green'}, + "percent20": {'color': 'red'}, + "percent50": {'color': 'pink'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + buy_crossed_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="ADD-20", space='buy') + buy_crossed_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="ASIN-6", space='buy') + buy_crossed_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLEVENINGSTAR-50", space='buy') + + buy_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="SMA-100", space='buy') + buy_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="WILLR-50", space='buy') + buy_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLHANGINGMAN-20", space='buy') + + buy_operator0 = CategoricalParameter(operators, default="/= 0) & \ + ( + (last_candle['percent3'] < -0.01) | + ((last_candle['percent'] < -0.01) & (previous_last_candle['rsi'] > 72)) + ): + return "quick_lost" + + if (current_profit > 0.005) & (last_candle['percent10'] < 0.001): + return "no_change" + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0): #& ((current_time - trade.open_date_utc).seconds <= 3600) + return "quick_gain" + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10'] * 1.005) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if (current_profit > 0.005) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + if (current_profit > 0) \ + & ((current_time - trade.open_date_utc).days >= 3) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'old_sma10' + + if (current_profit > -0.01) \ + & ((current_time - trade.open_date_utc).days >= 6) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'very_old_sma10' + + if (current_profit > 0) \ + & (last_candle['rsi'] > 88): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + + dataframe[ind_1] = gene_calculator(dataframe, ind_1) + dataframe[ind_2] = gene_calculator(dataframe, ind_2) + dataframe[ind_3] = gene_calculator(dataframe, ind_3) + + dataframe[ind_4] = gene_calculator(dataframe, ind_4) + dataframe[ind_5] = gene_calculator(dataframe, ind_5) + dataframe[ind_6] = gene_calculator(dataframe, ind_6) + + dataframe[sell_1] = gene_calculator(dataframe, sell_1) + dataframe[sell_2] = gene_calculator(dataframe, sell_2) + dataframe[sell_3] = gene_calculator(dataframe, sell_3) + + dataframe[sell_4] = gene_calculator(dataframe, sell_4) + dataframe[sell_5] = gene_calculator(dataframe, sell_5) + dataframe[sell_6] = gene_calculator(dataframe, sell_6) + + # dataframe['MINUS_DM-5'] = ta.MINUS_DM(dataframe, timeperiod=5) + # dataframe['LINEARREG-50'] = ta.LINEARREG(dataframe, timeperiod=50) + # dataframe['MA-20'] = ta.MA(dataframe, timeperiod=20) + # stoch = ta.STOCH(dataframe, timeperiod=10) + # # print(stoch) + # dataframe['STOCH-1-10'] = stoch['slowd'] + # dataframe['CDLDRAGONFLYDOJI-5'] = ta.CDLDRAGONFLYDOJI(dataframe, timeperiod=5) + + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # dataframe['min'] = ta.MIN(dataframe) + # dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + dataframe['pente5'] = (100 * (dataframe["sma5"] - dataframe["sma5"].shift(1)) / dataframe["sma5"]) + dataframe['pente10'] = (100 * (dataframe["sma10"] - dataframe["sma10"].shift(1)) / dataframe["sma10"]) + dataframe['pente20'] = (100 * (dataframe["sma20"] - dataframe["sma20"].shift(1)) / dataframe["sma20"]) + dataframe['pente50'] = (100 * (dataframe["sma50"] - dataframe["sma50"].shift(1)) / dataframe["sma50"]) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + # TODO: Its not dry code! + buy_indicator = self.buy_indicator0.value + buy_crossed_indicator = self.buy_crossed_indicator0.value + buy_operator = self.buy_operator0.value + buy_real_num = self.buy_real_num0.value + condition1, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition1) + # backup + buy_indicator = self.buy_indicator1.value + buy_crossed_indicator = self.buy_crossed_indicator1.value + buy_operator = self.buy_operator1.value + buy_real_num = self.buy_real_num1.value + + condition2, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition2) + + buy_indicator = self.buy_indicator2.value + buy_crossed_indicator = self.buy_crossed_indicator2.value + buy_operator = self.buy_operator2.value + buy_real_num = self.buy_real_num2.value + condition3, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition3) + + if conditions: + dataframe.loc[ + ( + (reduce(lambda x, y: x & y, conditions)) + & (dataframe['volume10'] * dataframe['close'] / 1000 >= 10) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['low'] < dataframe['min20']) + ) | ( + (dataframe['volume10'] * dataframe['close'] / 1000 >= 10) + & (dataframe['percent20'].shift(10) < -0.03) + & (dataframe['pente10'].shift(5) < 0) + & (dataframe['pente10'] > 0) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['low'] < dataframe['min20']) + # ) | ( + # (dataframe['volume10'] * dataframe['close'] / 1000 >= 10) + # & (dataframe['percent20'].shift(5) < -0.01) + # & (dataframe['percent3'] > 0) + # & (dataframe['open'] < dataframe['sma10']) + # & (dataframe['close'] < dataframe['sma10']) + # & (dataframe['sma100'].shift(1) < dataframe['sma100']) + # # & (dataframe['pente5'] > 0) + # + # ) | ( + # (dataframe['volume10'].shift(1) * dataframe['close'].shift(1) / 1000 >= 10) + # & (dataframe['percent20'].shift(6) < -0.01) + # & (dataframe['percent3'].shift(1) > 0) + # & (dataframe['open'].shift(1) < dataframe['sma10'].shift(1)) + # & (dataframe['close'].shift(1) < dataframe['sma10'].shift(1)) + # & (dataframe['sma100'].shift(1) < dataframe['sma100']) + # # & (dataframe['pente5'].shift(1) > 0) + ), + 'buy']=1 + # print(len(dataframe.keys())) + + # print( + # reduce(lambda x, y: x & y, condition1), " ", + # reduce(lambda x, y: x & y, condition2), " ", + # reduce(lambda x, y: x & y, condition3), " ", + # # (reduce(lambda x, y: x & y, conditions)) + # dataframe['buy'], + # ) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + # TODO: Its not dry code! + sell_indicator = self.sell_indicator0.value + sell_crossed_indicator = self.sell_crossed_indicator0.value + sell_operator = self.sell_operator0.value + sell_real_num = self.sell_real_num0.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator1.value + sell_crossed_indicator = self.sell_crossed_indicator1.value + sell_operator = self.sell_operator1.value + sell_real_num = self.sell_real_num1.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator2.value + sell_crossed_indicator = self.sell_crossed_indicator2.value + sell_operator = self.sell_operator2.value + sell_real_num = self.sell_real_num2.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + #dataframe.loc[(dataframe['close'] < dataframe['bb_lowerband']),'sell']=1 + + # count = 0 + # for coin, balance in self.wallets.get_all_balances().items(): + # count = count + 1 + # # print(coin, " ", balance) + # if (count == self.config['max_open_trades']) & (current_profit < -0.04) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 6): + # self.lock_pair(pair, until=current_time + timedelta(hours=10)) + # print("stop_short_loss", pair, trade, " profit=", current_profit, " rate=", current_rate, + # "count=", count, "max=", self.config['max_open_trades']) + # return 'stop_short_loss' + + # print("sell pair=", self.wallets.get_all_balances()[metadata['pair']]) + + # if conditions: + # dataframe.loc[ + # ( + # reduce(lambda x, y: x & y, conditions) #(dataframe['percent3'] > 0) + # ), + # 'sell']=1 + return dataframe + diff --git a/GodStraJD3_6_53_1.json b/GodStraJD3_6_53_1.json new file mode 100644 index 0000000..f2c9986 --- /dev/null +++ b/GodStraJD3_6_53_1.json @@ -0,0 +1,51 @@ +{ + "strategy_name": "GodStraJD3_6_53_1", + "params": { + "roi": { + "0": 10 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.15, + "trailing_stop_positive_offset": 0.2, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_crossed_indicator0": "HT_TRENDLINE-100", + "buy_crossed_indicator1": "MACD-1-100", + "buy_crossed_indicator2": "CCI-20", + "buy_indicator0": "CDLDOJISTAR-10", + "buy_indicator1": "WCLPRICE-10", + "buy_indicator2": "DEMA-50", + "buy_operator0": "/>R", + "buy_operator1": "R", + "sell_operator2": ">", + "sell_real_num0": 0.1, + "sell_real_num1": 0.8, + "sell_real_num2": 1.0 + }, + "protection": { + "protection_max_allowed_dd": 0.36, + "protection_stop": 25 + }, + "stoploss": { + "stoploss": -0.189 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-01-23 14:20:28.600763+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_6_53_1.py b/GodStraJD3_6_53_1.py new file mode 100644 index 0000000..4a4a5b2 --- /dev/null +++ b/GodStraJD3_6_53_1.py @@ -0,0 +1,920 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy, SellCheckTuple +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle +# TODO: this gene is removed 'MAVP' cuz or error on periods +all_god_genes = { + 'Overlap Studies': { + 'BBANDS-0', # Bollinger Bands + 'BBANDS-1', # Bollinger Bands + 'BBANDS-2', # Bollinger Bands + 'DEMA', # Double Exponential Moving Average + 'EMA', # Exponential Moving Average + 'HT_TRENDLINE', # Hilbert Transform - Instantaneous Trendline + 'KAMA', # Kaufman Adaptive Moving Average + 'MA', # Moving average + 'MAMA-0', # MESA Adaptive Moving Average + 'MAMA-1', # MESA Adaptive Moving Average + # TODO: Fix this + # 'MAVP', # Moving average with variable period + 'MIDPOINT', # MidPoint over period + 'MIDPRICE', # Midpoint Price over period + 'SAR', # Parabolic SAR + 'SAREXT', # Parabolic SAR - Extended + 'SMA', # Simple Moving Average + 'T3', # Triple Exponential Moving Average (T3) + 'TEMA', # Triple Exponential Moving Average + 'TRIMA', # Triangular Moving Average + 'WMA', # Weighted Moving Average + }, + 'Momentum Indicators': { + 'ADX', # Average Directional Movement Index + 'ADXR', # Average Directional Movement Index Rating + 'APO', # Absolute Price Oscillator + 'AROON-0', # Aroon + 'AROON-1', # Aroon + 'AROONOSC', # Aroon Oscillator + 'BOP', # Balance Of Power + 'CCI', # Commodity Channel Index + 'CMO', # Chande Momentum Oscillator + 'DX', # Directional Movement Index + 'MACD-0', # Moving Average Convergence/Divergence + 'MACD-1', # Moving Average Convergence/Divergence + 'MACD-2', # Moving Average Convergence/Divergence + 'MACDEXT-0', # MACD with controllable MA type + 'MACDEXT-1', # MACD with controllable MA type + 'MACDEXT-2', # MACD with controllable MA type + 'MACDFIX-0', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-1', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-2', # Moving Average Convergence/Divergence Fix 12/26 + 'MFI', # Money Flow Index + 'MINUS_DI', # Minus Directional Indicator + 'MINUS_DM', # Minus Directional Movement + 'MOM', # Momentum + 'PLUS_DI', # Plus Directional Indicator + 'PLUS_DM', # Plus Directional Movement + 'PPO', # Percentage Price Oscillator + 'ROC', # Rate of change : ((price/prevPrice)-1)*100 + # Rate of change Percentage: (price-prevPrice)/prevPrice + 'ROCP', + 'ROCR', # Rate of change ratio: (price/prevPrice) + # Rate of change ratio 100 scale: (price/prevPrice)*100 + 'ROCR100', + 'RSI', # Relative Strength Index + 'STOCH-0', # Stochastic + 'STOCH-1', # Stochastic + 'STOCHF-0', # Stochastic Fast + 'STOCHF-1', # Stochastic Fast + 'STOCHRSI-0', # Stochastic Relative Strength Index + 'STOCHRSI-1', # Stochastic Relative Strength Index + # 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA + 'TRIX', + 'ULTOSC', # Ultimate Oscillator + 'WILLR', # Williams' %R + }, + 'Volume Indicators': { + 'AD', # Chaikin A/D Line + 'ADOSC', # Chaikin A/D Oscillator + 'OBV', # On Balance Volume + }, + 'Volatility Indicators': { + 'ATR', # Average True Range + 'NATR', # Normalized Average True Range + 'TRANGE', # True Range + }, + 'Price Transform': { + 'AVGPRICE', # Average Price + 'MEDPRICE', # Median Price + 'TYPPRICE', # Typical Price + 'WCLPRICE', # Weighted Close Price + }, + 'Cycle Indicators': { + 'HT_DCPERIOD', # Hilbert Transform - Dominant Cycle Period + 'HT_DCPHASE', # Hilbert Transform - Dominant Cycle Phase + 'HT_PHASOR-0', # Hilbert Transform - Phasor Components + 'HT_PHASOR-1', # Hilbert Transform - Phasor Components + 'HT_SINE-0', # Hilbert Transform - SineWave + 'HT_SINE-1', # Hilbert Transform - SineWave + 'HT_TRENDMODE', # Hilbert Transform - Trend vs Cycle Mode + }, + 'Pattern Recognition': { + 'CDL2CROWS', # Two Crows + 'CDL3BLACKCROWS', # Three Black Crows + 'CDL3INSIDE', # Three Inside Up/Down + 'CDL3LINESTRIKE', # Three-Line Strike + 'CDL3OUTSIDE', # Three Outside Up/Down + 'CDL3STARSINSOUTH', # Three Stars In The South + 'CDL3WHITESOLDIERS', # Three Advancing White Soldiers + 'CDLABANDONEDBABY', # Abandoned Baby + 'CDLADVANCEBLOCK', # Advance Block + 'CDLBELTHOLD', # Belt-hold + 'CDLBREAKAWAY', # Breakaway + 'CDLCLOSINGMARUBOZU', # Closing Marubozu + 'CDLCONCEALBABYSWALL', # Concealing Baby Swallow + 'CDLCOUNTERATTACK', # Counterattack + 'CDLDARKCLOUDCOVER', # Dark Cloud Cover + 'CDLDOJI', # Doji + 'CDLDOJISTAR', # Doji Star + 'CDLDRAGONFLYDOJI', # Dragonfly Doji + 'CDLENGULFING', # Engulfing Pattern + 'CDLEVENINGDOJISTAR', # Evening Doji Star + 'CDLEVENINGSTAR', # Evening Star + 'CDLGAPSIDESIDEWHITE', # Up/Down-gap side-by-side white lines + 'CDLGRAVESTONEDOJI', # Gravestone Doji + 'CDLHAMMER', # Hammer + 'CDLHANGINGMAN', # Hanging Man + 'CDLHARAMI', # Harami Pattern + 'CDLHARAMICROSS', # Harami Cross Pattern + 'CDLHIGHWAVE', # High-Wave Candle + 'CDLHIKKAKE', # Hikkake Pattern + 'CDLHIKKAKEMOD', # Modified Hikkake Pattern + 'CDLHOMINGPIGEON', # Homing Pigeon + 'CDLIDENTICAL3CROWS', # Identical Three Crows + 'CDLINNECK', # In-Neck Pattern + 'CDLINVERTEDHAMMER', # Inverted Hammer + 'CDLKICKING', # Kicking + 'CDLKICKINGBYLENGTH', # Kicking - bull/bear determined by the longer marubozu + 'CDLLADDERBOTTOM', # Ladder Bottom + 'CDLLONGLEGGEDDOJI', # Long Legged Doji + 'CDLLONGLINE', # Long Line Candle + 'CDLMARUBOZU', # Marubozu + 'CDLMATCHINGLOW', # Matching Low + 'CDLMATHOLD', # Mat Hold + 'CDLMORNINGDOJISTAR', # Morning Doji Star + 'CDLMORNINGSTAR', # Morning Star + 'CDLONNECK', # On-Neck Pattern + 'CDLPIERCING', # Piercing Pattern + 'CDLRICKSHAWMAN', # Rickshaw Man + 'CDLRISEFALL3METHODS', # Rising/Falling Three Methods + 'CDLSEPARATINGLINES', # Separating Lines + 'CDLSHOOTINGSTAR', # Shooting Star + 'CDLSHORTLINE', # Short Line Candle + 'CDLSPINNINGTOP', # Spinning Top + 'CDLSTALLEDPATTERN', # Stalled Pattern + 'CDLSTICKSANDWICH', # Stick Sandwich + # Takuri (Dragonfly Doji with very long lower shadow) + 'CDLTAKURI', + 'CDLTASUKIGAP', # Tasuki Gap + 'CDLTHRUSTING', # Thrusting Pattern + 'CDLTRISTAR', # Tristar Pattern + 'CDLUNIQUE3RIVER', # Unique 3 River + 'CDLUPSIDEGAP2CROWS', # Upside Gap Two Crows + 'CDLXSIDEGAP3METHODS', # Upside/Downside Gap Three Methods + + }, + 'Statistic Functions': { + 'BETA', # Beta + 'CORREL', # Pearson's Correlation Coefficient (r) + 'LINEARREG', # Linear Regression + 'LINEARREG_ANGLE', # Linear Regression Angle + 'LINEARREG_INTERCEPT', # Linear Regression Intercept + 'LINEARREG_SLOPE', # Linear Regression Slope + 'STDDEV', # Standard Deviation + 'TSF', # Time Series Forecast + 'VAR', # Variance + } + +} +god_genes = set() +########################### SETTINGS ############################## + +# god_genes = {'SMA'} +god_genes |= all_god_genes['Overlap Studies'] +god_genes |= all_god_genes['Momentum Indicators'] +god_genes |= all_god_genes['Volume Indicators'] +god_genes |= all_god_genes['Volatility Indicators'] +god_genes |= all_god_genes['Price Transform'] +god_genes |= all_god_genes['Cycle Indicators'] +god_genes |= all_god_genes['Pattern Recognition'] +god_genes |= all_god_genes['Statistic Functions'] + +#timeperiods = [5, 6, 12, 15, 50, 55, 100, 110] +timeperiods = [5, 10, 20, 50, 100] + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD3_6_53_1(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 1, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + 'bb_min': {'color': 'red'}, + }, + "Ind": { + ind_1: {'color': 'green'}, + ind_2: {'color': 'blue'}, + ind_3: {'color': 'red'} + }, + "Ind2": { + ind_4: {'color': 'green'}, + ind_5: {'color': 'blue'}, + ind_6: {'color': 'red'} + }, + "Profit": { + 'profit': {'color': 'pink'}, + }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + buy_crossed_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="ADD-20", space='buy') + buy_crossed_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="ASIN-6", space='buy') + buy_crossed_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLEVENINGSTAR-50", space='buy') + + buy_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="SMA-100", space='buy') + buy_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="WILLR-50", space='buy') + buy_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLHANGINGMAN-20", space='buy') + + buy_operator0 = CategoricalParameter(operators, default="/ DataFrame: + + conditions = list() + + # TODO: Its not dry code! + buy_indicator = self.buy_indicator0.value + buy_crossed_indicator = self.buy_crossed_indicator0.value + buy_operator = self.buy_operator0.value + buy_real_num = self.buy_real_num0.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + # backup + buy_indicator = self.buy_indicator1.value + buy_crossed_indicator = self.buy_crossed_indicator1.value + buy_operator = self.buy_operator1.value + buy_real_num = self.buy_real_num1.value + + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + buy_indicator = self.buy_indicator2.value + buy_crossed_indicator = self.buy_crossed_indicator2.value + buy_operator = self.buy_operator2.value + buy_real_num = self.buy_real_num2.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + ( + (dataframe['close'].shift(2) < dataframe['bb_lowerband'].shift(2)) + & (dataframe['bb_width'].shift(2) >= 0.12) + & (dataframe['volume'].shift(2) * dataframe['close'].shift(2) / 1000 > 100) + ) | ( + (reduce(lambda x, y: x & y, conditions)) + & (dataframe['volume10'] * dataframe['close'] / 1000 >= 100) + & (dataframe['close'] < dataframe['sma20']) + & (dataframe['close'] < dataframe['sma50']) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + # (dataframe['close'] * 0.99 < dataframe['bb_lowerband']) + # (dataframe['rsi'] < 45) + ), + 'buy']=1 + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + # TODO: Its not dry code! + sell_indicator = self.sell_indicator0.value + sell_crossed_indicator = self.sell_crossed_indicator0.value + sell_operator = self.sell_operator0.value + sell_real_num = self.sell_real_num0.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator1.value + sell_crossed_indicator = self.sell_crossed_indicator1.value + sell_operator = self.sell_operator1.value + sell_real_num = self.sell_real_num1.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator2.value + sell_crossed_indicator = self.sell_crossed_indicator2.value + sell_operator = self.sell_operator2.value + sell_real_num = self.sell_real_num2.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + #dataframe.loc[(dataframe['close'] < dataframe['bb_lowerband']),'sell']=1 + + # count = 0 + # for coin, balance in self.wallets.get_all_balances().items(): + # count = count + 1 + # # print(coin, " ", balance) + # if (count == self.config['max_open_trades']) & (current_profit < -0.04) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 6): + # self.lock_pair(pair, until=current_time + timedelta(hours=10)) + # print("stop_short_loss", pair, trade, " profit=", current_profit, " rate=", current_rate, + # "count=", count, "max=", self.config['max_open_trades']) + # return 'stop_short_loss' + + # print("sell pair=", self.wallets.get_all_balances()[metadata['pair']]) + + # if conditions: + # dataframe.loc[ + # ( + # reduce(lambda x, y: x & y, conditions) #(dataframe['percent3'] > 0) + # # | (dataframe['percent3'] < -0.03) + # ), + # 'sell']=1 + + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), 'sell'] = 1 + + return dataframe + diff --git a/GodStraJD3_6_53_2.json b/GodStraJD3_6_53_2.json new file mode 100644 index 0000000..b1cb738 --- /dev/null +++ b/GodStraJD3_6_53_2.json @@ -0,0 +1,29 @@ +{ + "strategy_name": "GodStraJD3_6_53_2", + "params": { + "roi": { + "0": 2 + }, + "stoploss": { + "stoploss": -0.02 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.15, + "trailing_stop_positive_offset": 0.2, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_real_num0": 0.87, + "buy_real_num1": 0.33, + "buy_real_num2": 0.89 + }, + "sell": {}, + "protection": { + "protection_max_allowed_dd": 0.04, + "protection_stop": 288 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-01-23 13:16:55.016896+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_6_53_2.py b/GodStraJD3_6_53_2.py new file mode 100644 index 0000000..1f1ce32 --- /dev/null +++ b/GodStraJD3_6_53_2.py @@ -0,0 +1,520 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD3_6_53_2(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 2, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + 'bb_min': {'color': 'red'}, + }, + "Ind0": { + buy_crossed_indicator0: {'color': 'green'}, + buy_indicator0: {'color': 'red'} + }, + "Ind1": { + buy_indicator1: {'color': 'yellow'}, + buy_crossed_indicator1: {'color': 'pink'} + }, + "Ind2": { + buy_indicator2: {'color': 'cyan'}, + buy_crossed_indicator2: {'color': 'blue'}, + }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'} + } + } + } + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + buy_real_num0 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.1, space='buy') + buy_real_num1 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.0, space='buy') + buy_real_num2 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.5, space='buy') + + protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + protection_stop = IntParameter(0, 100, default=48, space='protection') + + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + { + "method": "MaxDrawdown", + "lookback_period_candles": 48, + "trade_limit": 2, + "stop_duration_candles": self.protection_stop.value, + "max_allowed_drawdown": self.protection_max_allowed_dd.value, + "only_per_pair": False + }, + ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + if (current_profit > 0) \ + & ((previous_5_candle['sma10'] > last_candle['sma10'] * 1.005) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if (current_profit > 0.01) \ + & ((previous_5_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent10'] < -0.01) | (last_candle['percent20'] < -0.01))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + if (current_profit > 0) \ + & ((current_time - trade.open_date_utc).days >= 3) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | ( + last_candle['percent5'] < -0.0105)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'old_sma10' + + if (current_profit > -0.01) \ + & ((current_time - trade.open_date_utc).days >= 6) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | ( + last_candle['percent5'] < 0)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'very_old_sma10' + + if (current_profit > 0) \ + & (last_candle['rsi'] > 88): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + condition, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value + ) + conditions.append(condition) + + # backup + condition, dataframe = condition_generator( + dataframe, + buy_operator1, + buy_indicator1, + buy_crossed_indicator1, + self.buy_real_num1.value + ) + conditions.append(condition) + + condition, dataframe = condition_generator( + dataframe, + buy_operator2, + buy_indicator2, + buy_crossed_indicator2, + self.buy_real_num2.value + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + ( + (reduce(lambda x, y: x & y, conditions)) & + (dataframe['volume10'] * dataframe['close'] / 1000 >= 10) + # (dataframe['rsi'] < 45) + ), + 'buy'] = 1 + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + return dataframe diff --git a/GodStraJD3_7.json b/GodStraJD3_7.json new file mode 100644 index 0000000..68b0b51 --- /dev/null +++ b/GodStraJD3_7.json @@ -0,0 +1,33 @@ +{ + "strategy_name": "GodStraJD3_7", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.15, + "trailing_stop_positive_offset": 0.2, + "trailing_only_offset_is_reached": true + }, + "buy": { + "profit_no_change": false, + "profit_old_sma10": false, + "profit_over_rsi": true, + "profit_quick_gain": false, + "profit_quick_gain_3": false, + "profit_quick_lost": true, + "profit_short_loss": true, + "profit_sma10": false, + "profit_sma20": true, + "profit_very_old_sma10": true + }, + "sell": {}, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2022-02-04 20:21:28.189888+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_7.py b/GodStraJD3_7.py new file mode 100644 index 0000000..418fed3 --- /dev/null +++ b/GodStraJD3_7.py @@ -0,0 +1,636 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + return condition, dataframe + + +class GodStraJD3_7(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + # 'min': {'color': 'white'}, + # 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + 'bb_min': {'color': 'red'}, + }, + "Ind0": { + buy_crossed_indicator0: {'color': 'green'}, + buy_indicator0: {'color': 'red'} + }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + # "percent": { + # "percent": {'color': 'green'}, + # "percent3": {'color': 'blue'}, + # "percent5": {'color': 'red'} + # } + } + } + + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + # "buy_real_num0": 0.46, + # "buy_real_num1": 0.48, + # "buy_real_num2": 0.67 + # + # buy_real_num0 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.46, space='buy') + # buy_real_num1 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.48, space='buy') + # buy_real_num2 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.67, space='buy') + + profit_no_change = BooleanParameter(default=True, space="buy") + profit_quick_lost = BooleanParameter(default=True, space="buy") + profit_sma10 = BooleanParameter(default=True, space="buy") + profit_sma20 = BooleanParameter(default=True, space="buy") + profit_quick_gain = BooleanParameter(default=True, space="buy") + profit_quick_gain_3 = BooleanParameter(default=True, space="buy") + profit_old_sma10 = BooleanParameter(default=True, space="buy") + profit_very_old_sma10 = BooleanParameter(default=True, space="buy") + profit_over_rsi = BooleanParameter(default=True, space="buy") + profit_short_loss = BooleanParameter(default=True, space="buy") + + # protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + # protection_stop = IntParameter(0, 100, default=48, space='protection') + # protection_stoploss_stop = IntParameter(0, 100, default=48, space='protection') + # lookback = IntParameter(0, 200, default=48, space='protection') + # trade_limit = IntParameter(0, 10, default=2, space='protection') + # + # @property + # def protections(self): + # return [ + # { + # "method": "CooldownPeriod", + # "stop_duration_candles": 10 + # }, + # { + # "method": "MaxDrawdown", + # "lookback_period_candles": self.lookback.value, + # "trade_limit": self.trade_limit.value, + # "stop_duration_candles": self.protection_stop.value, + # "max_allowed_drawdown": self.protection_max_allowed_dd.value, + # "only_per_pair": False + # }, + # { + # "method": "StoplossGuard", + # "lookback_period_candles": 24, + # "trade_limit": 4, + # "stop_duration_candles": self.protection_stoploss_stop.value, + # "only_per_pair": False + # } + # ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + if self.profit_quick_lost.value: + if (current_profit >= 0) & (last_candle['percent3'] < -0.01): + return "quick_lost" + + if self.profit_no_change.value: + if (current_profit > 0.005) & (last_candle['percent10'] < 0.001): + return "no_change" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if self.profit_quick_gain_3.value: + if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if self.profit_quick_gain.value: + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0): #& ((current_time - trade.open_date_utc).seconds <= 3600) + return "quick_gain" + + if self.profit_sma10.value: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10'] * 1.005) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20.value: + if (current_profit > 0.005) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10.value: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10.value: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi.value: + if (current_profit > 0) \ + & (last_candle['rsi'] > 88): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if self.profit_short_loss.value: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + 0.7 #self.buy_real_num0.value + ) + conditions.append(condition1) + + # # backup + # condition2, dataframe = condition_generator( + # dataframe, + # buy_operator1, + # buy_indicator1, + # buy_crossed_indicator1, + # self.buy_real_num1.value + # ) + # conditions.append(condition2) + # + # condition3, dataframe = condition_generator( + # dataframe, + # buy_operator2, + # buy_indicator2, + # buy_crossed_indicator2, + # self.buy_real_num2.value + # ) + # conditions.append(condition3) + + if conditions: + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (dataframe['volume10'] * dataframe['close'] / 1000 >= 10) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['low'] < dataframe['min20']) + # ) | ( + # (dataframe['volume10'] * dataframe['close'] / 1000 >= 10) + # & (dataframe['percent20'] < -0.01) + # & (dataframe['percent3'] > 0) + # & (dataframe['close'] < dataframe['sma10']) + # & (dataframe['sma100'].shift(1) < dataframe['sma100']) + # + # ) | ( + # (dataframe['volume10'].shift(1) * dataframe['close'].shift(1) / 1000 >= 10) + # & (dataframe['percent20'].shift(1) < -0.01) + # & (dataframe['percent3'].shift(1) > 0) + # & (dataframe['close'].shift(1) < dataframe['sma10'].shift(1)) + # & (dataframe['sma100'].shift(1) < dataframe['sma100']) + # # & (dataframe['pente5'].shift(1) > 0) + ), 'buy'] = 1 + + pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + pandas.set_option('display.max_columns', 30) + # print(condition1) + + # print(reduce(lambda x, y: x & y, conditions)) + # print(reduce(lambda x, y: x & y, condition1), " ", + # reduce(lambda x, y: x & y, condition2), " ", + # reduce(lambda x, y: x & y, condition3), " ", + # # (reduce(lambda x, y: x & y, conditions)) + # dataframe['buy'], + # + # ) + + # if conditions: + # dataframe.loc[ + # ( + # reduce(lambda x, y: x & y, conditions) + # # & (dataframe['volume10'] * dataframe['close'] / 1000 >= 100) + # # & (dataframe['percent5'] < 0.025) + # # (dataframe['percent10'] < 0.04) + # # ) | ( + # # (dataframe['volume10'] * dataframe['close'] / 1000 >= 100) + # # & (dataframe['percent50'] < -0.06) + # # & (dataframe['percent3'] > 0) + # # # & (dataframe['sma10'].shift(12) > dataframe['sma10'].shift(2)) + # # & (dataframe['sma10'].shift(2) > dataframe['sma10'].shift(1)) + # # & (dataframe['sma10'].shift(1) < dataframe['sma10']) + # # # & (dataframe['sma100'].shift(1) < dataframe['sma100']) + # ) + # #(dataframe['percent3'] > 0)) + # ,'buy']=1 + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_7_1.json b/GodStraJD3_7_1.json new file mode 100644 index 0000000..a6054d0 --- /dev/null +++ b/GodStraJD3_7_1.json @@ -0,0 +1,31 @@ +{ + "strategy_name": "GodStraJD3_7_1", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.15, + "trailing_stop_positive_offset": 0.2, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_real_num0": 0.5, + "buy_signal_bb_width": 0.029 + }, + "sell": {}, + "protection": { + "lookback": 124, + "protection_max_allowed_dd": 0.85, + "protection_stop": 26, + "protection_stoploss_stop": 91, + "trade_limit": 3 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-02-18 23:22:31.726399+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_7_1.py b/GodStraJD3_7_1.py new file mode 100644 index 0000000..5ce7d84 --- /dev/null +++ b/GodStraJD3_7_1.py @@ -0,0 +1,540 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator] > dataframe[crossed_indicator]) + elif operator == "=": + condition = (np.isclose(dataframe[indicator], dataframe[crossed_indicator])) + elif operator == "<": + condition = (dataframe[indicator] < dataframe[crossed_indicator]) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above(dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator], dataframe[crossed_indicator])) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) + elif operator == ">R": + condition = (dataframe[indicator] > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator], real_num)) + elif operator == "R": + condition = (dataframe[indicator].div(dataframe[crossed_indicator]) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].div(dataframe[crossed_indicator]), real_num)) + elif operator == "/ dataframe[indicator_trend_sma]) + elif operator == "DT": + condition = (dataframe[indicator] < dataframe[indicator_trend_sma]) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator], dataframe[indicator_trend_sma])) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator], dataframe[indicator_trend_sma]) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator], dataframe[indicator_trend_sma]) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator], dataframe[indicator_trend_sma]) + ) | + ( + qtpylib.crossed_above(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + ) & + ( + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + ) + return condition, dataframe + + +class GodStraJD3_7_1(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + # 'min': {'color': 'white'}, + # 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + 'bb_min': {'color': 'red'}, + }, + "Ind0": { + buy_crossed_indicator0: {'color': 'green'}, + buy_indicator0: {'color': 'red'} + }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + # "percent": { + # "percent": {'color': 'green'}, + # "percent3": {'color': 'blue'}, + # "percent5": {'color': 'red'} + # } + } + } + + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + # "buy_real_num0": 0.46, + # "buy_real_num1": 0.48, + # "buy_real_num2": 0.67 + # + # buy_real_num0 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.46, space='buy') + # buy_real_num1 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.48, space='buy') + # buy_real_num2 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.67, space='buy') + + profit_no_change = False + profit_old_sma10 = False + profit_over_rsi = True + profit_quick_gain = True + profit_quick_gain_3 = True + profit_quick_lost = True + profit_short_loss = True + profit_sma10 = False + profit_sma20 = True + profit_very_old_sma10 = False + + # profit_no_change = BooleanParameter(default=True, space="buy") + # profit_quick_lost = BooleanParameter(default=True, space="buy") + # profit_sma10 = BooleanParameter(default=True, space="buy") + # profit_sma20 = BooleanParameter(default=True, space="buy") + # profit_quick_gain = BooleanParameter(default=True, space="buy") + # profit_quick_gain_3 = BooleanParameter(default=True, space="buy") + # profit_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_very_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_over_rsi = BooleanParameter(default=True, space="buy") + # profit_short_loss = BooleanParameter(default=True, space="buy") + + buy_signal_bb_width = DecimalParameter(0, 0.15, decimals=3, default=0.05, space='buy') + buy_real_num0 = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + + protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + protection_stop = IntParameter(1, 100, default=48, space='protection') + protection_stoploss_stop = IntParameter(1, 100, default=48, space='protection') + lookback = IntParameter(1, 200, default=48, space='protection') + trade_limit = IntParameter(1, 10, default=2, space='protection') + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + { + "method": "MaxDrawdown", + "lookback_period_candles": self.lookback.value, + "trade_limit": self.trade_limit.value, + "stop_duration_candles": self.protection_stop.value, + "max_allowed_drawdown": self.protection_max_allowed_dd.value, + "only_per_pair": False + }, + { + "method": "StoplossGuard", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": self.protection_stoploss_stop.value, + "only_per_pair": False + } + ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + if self.profit_quick_lost: + if (current_profit >= 0) & (last_candle['percent3'] < -0.01): + return "quick_lost" + + if self.profit_no_change: + if (current_profit > 0.005) & (last_candle['percent10'] < 0.001) & (last_candle['percent5'] < 0) & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if self.profit_quick_gain_3: + if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if self.profit_quick_gain: + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0): #& ((current_time - trade.open_date_utc).seconds <= 3600) + return "quick_gain" + + if self.profit_sma10: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10'] * 1.005) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20: + if (current_profit > 0.005) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi: + if (current_profit > 0) & (previous_last_candle['rsi'] > 88): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if self.profit_short_loss: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value + ) + conditions.append(condition1) + + if conditions: + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (dataframe['volume10'] * dataframe['close'] / 1000 >= 10) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['low'] < dataframe['min50']) + & (dataframe['bb_width'] > self.buy_signal_bb_width.value) + ), 'buy'] = 1 + + pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + pandas.set_option('display.max_columns', 30) + # print(condition1) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_7_2.json b/GodStraJD3_7_2.json new file mode 100644 index 0000000..82321f2 --- /dev/null +++ b/GodStraJD3_7_2.json @@ -0,0 +1,32 @@ +{ + "strategy_name": "GodStraJD3_7_2", + "params": { + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.042, + "trailing_stop_positive_offset": 0.119, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_decalage0": 6, + "buy_real_num0": 0.9, + "buy_signal_bb_width": 0.0 + }, + "sell": {}, + "protection": { + "lookback": 133, + "protection_max_allowed_dd": 0.71, + "protection_stop": 61, + "protection_stoploss_stop": 50, + "trade_limit": 9 + }, + "roi": { + "0": 2 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-07 17:59:59.847942+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_7_2.py b/GodStraJD3_7_2.py new file mode 100644 index 0000000..c7edeae --- /dev/null +++ b/GodStraJD3_7_2.py @@ -0,0 +1,606 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), + real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + + +class GodStraJD3_7_2(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min20': {'color': '#87e470'}, + 'min50': {'color': '#ac3e2a'}, + "min1.1": {'color': 'yellow'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'} + }, + # "Ind0": { + # buy_crossed_indicator0: {'color': 'green'}, + # buy_indicator0: {'color': 'red'} + # }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "Ecart": { + 'ecart_20': {'color': 'red'}, + 'ecart_50': {'color': 'yellow'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'}, + "distance_min": {'color': 'white'} + } + } + } + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + # "buy_real_num0": 0.46, + # "buy_real_num1": 0.48, + # "buy_real_num2": 0.67 + # + # buy_real_num0 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.46, space='buy') + # buy_real_num1 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.48, space='buy') + # buy_real_num2 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.67, space='buy') + + profit_no_change = False + profit_old_sma10 = False + profit_over_rsi = True + profit_quick_gain = True + profit_quick_gain_3 = True + profit_quick_lost = False + profit_short_loss = False + profit_sma10 = False + profit_sma20 = True + profit_very_old_sma10 = False + + # profit_no_change = BooleanParameter(default=True, space="buy") + # profit_quick_lost = BooleanParameter(default=True, space="buy") + # profit_sma10 = BooleanParameter(default=True, space="buy") + # profit_sma20 = BooleanParameter(default=True, space="buy") + # profit_quick_gain = BooleanParameter(default=True, space="buy") + # profit_quick_gain_3 = BooleanParameter(default=True, space="buy") + # profit_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_very_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_over_rsi = BooleanParameter(default=True, space="buy") + # profit_short_loss = BooleanParameter(default=True, space="buy") + + buy_signal_bb_width = DecimalParameter(0, 0.15, decimals=2, default=0.05, space='buy') + buy_real_num0 = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_decalage0 = IntParameter(1, 10, default=5, space='buy') + + protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + protection_stop = IntParameter(1, 100, default=48, space='protection') + protection_stoploss_stop = IntParameter(1, 100, default=48, space='protection') + lookback = IntParameter(1, 200, default=48, space='protection') + trade_limit = IntParameter(1, 10, default=2, space='protection') + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + { + "method": "MaxDrawdown", + "lookback_period_candles": self.lookback.value, + "trade_limit": self.trade_limit.value, + "stop_duration_candles": self.protection_stop.value, + "max_allowed_drawdown": self.protection_max_allowed_dd.value, + "only_per_pair": False + }, + { + "method": "StoplossGuard", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": self.protection_stoploss_stop.value, + "only_per_pair": False + } + ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + if (current_profit >= -0.01) & ((current_time - trade.open_date_utc).days >= 5) \ + & ((current_time - trade.open_date_utc).days < 10) \ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.01" + if (current_profit >= -0.02) & ((current_time - trade.open_date_utc).days >= 10) \ + & ((current_time - trade.open_date_utc).days < 15) \ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.02" + if (current_profit >= -0.03) & ((current_time - trade.open_date_utc).days >= 15) \ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.03" + + if self.profit_quick_lost: + if (current_profit >= 0) & (last_candle['percent3'] < -0.015): + return "quick_lost" + + if self.profit_no_change: + if (current_profit > 0.005) & (last_candle['percent10'] < 0.001) & (last_candle['percent5'] < 0) & ( + (current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + # if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if self.profit_quick_gain_3: + if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ( + (current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if self.profit_quick_gain: + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0) & ( + (current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain" + + if self.profit_sma10: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10'] * 1.005) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20: + if (current_profit > 0.005) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi: + if (current_profit > 0) \ + & (previous_last_candle['rsi'] > 88) & ( \ + (last_candle['percent'] < - current_profit / 3) | + (last_candle['percent3'] < - current_profit / 3)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + if False & (current_profit > 0) & (last_candle[ + 'rsi'] > 88): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if False & (current_profit > 0) & (previous_last_candle['rsi'] > 82) & ( + last_candle['percent'] < -0.02): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if self.profit_short_loss: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0) \ + & (( + current_time - trade.open_date_utc).days >= 1): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['ecart_20'] = dataframe['close'].rolling(20).var() / dataframe['close'] + dataframe['ecart_50'] = dataframe['close'].rolling(50).var() / dataframe['close'] + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=72) + dataframe['min10'] = ta.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + + # dataframe["dist_min_50"] = dataframe['close'] - dataframe['min50'] + # dataframe["dist_min_20"] = dataframe['close'] - dataframe['min20'] + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value, + self.buy_decalage0.value + ) + conditions.append(condition1) + + if conditions: + for decalage in range(3, self.buy_decalage0.value): + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= -0.01) + # & (dataframe['min20'] == dataframe['min50']) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= self.buy_real_num0.value / 2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + #  & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + # & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= -0.02) + # & (dataframe['min20'] == dataframe['min50']) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + + # pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + # pandas.set_option('display.max_columns', 30) + # print(condition1) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe diff --git a/GodStraJD3_7_3.json b/GodStraJD3_7_3.json new file mode 100644 index 0000000..92a69c1 --- /dev/null +++ b/GodStraJD3_7_3.json @@ -0,0 +1,34 @@ +{ + "strategy_name": "GodStraJD3_7_3", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.15, + "trailing_stop_positive_offset": 0.2, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_decalage0": 6, + "buy_real_num0": 0.9, + "buy_decalage2": 5, + "buy_real_num2": 1.5, + "buy_signal_bb_width": 0.06 + }, + "sell": {}, + "protection": { + "lookback": 133, + "protection_max_allowed_dd": 0.71, + "protection_stop": 61, + "protection_stoploss_stop": 50, + "trade_limit": 9 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-02-25 21:24:08.672826+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_7_3.py b/GodStraJD3_7_3.py new file mode 100644 index 0000000..7ebb5f8 --- /dev/null +++ b/GodStraJD3_7_3.py @@ -0,0 +1,641 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + + +class GodStraJD3_7_3(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min20': {'color': '#87e470'}, + 'min50': {'color': '#ac3e2a'}, + "min1.1": {'color': 'yellow'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'} + }, + # "Ind0": { + # buy_crossed_indicator0: {'color': 'green'}, + # buy_indicator0: {'color': 'red'} + # }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "Ecart": { + 'ecart_20': {'color': 'red'}, + 'ecart_50': {'color': 'yellow'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'}, + "distance_min": {'color': 'white'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + # "buy_real_num0": 0.46, + # "buy_real_num1": 0.48, + # "buy_real_num2": 0.67 + # + # buy_real_num0 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.46, space='buy') + # buy_real_num1 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.48, space='buy') + # buy_real_num2 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.67, space='buy') + + profit_no_change = False + profit_old_sma10 = False + profit_over_rsi = True + profit_quick_gain = True + profit_quick_gain_3 = True + profit_quick_lost = False + profit_short_loss = False + profit_sma10 = False + profit_sma20 = True + profit_very_old_sma10 = False + + # profit_no_change = BooleanParameter(default=True, space="buy") + # profit_quick_lost = BooleanParameter(default=True, space="buy") + # profit_sma10 = BooleanParameter(default=True, space="buy") + # profit_sma20 = BooleanParameter(default=True, space="buy") + # profit_quick_gain = BooleanParameter(default=True, space="buy") + # profit_quick_gain_3 = BooleanParameter(default=True, space="buy") + # profit_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_very_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_over_rsi = BooleanParameter(default=True, space="buy") + # profit_short_loss = BooleanParameter(default=True, space="buy") + + buy_signal_bb_width = DecimalParameter(0.06, 0.15, decimals=2, default=0.065, space='buy') + buy_real_num0 = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_decalage0 = IntParameter(1, 10, default=5, space='buy') + + buy_real_num2 = DecimalParameter(1, 3, decimals=2, default=0.67, space='buy') + buy_decalage2 = IntParameter(1, 10, default=5, space='buy') + + protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + protection_stop = IntParameter(1, 100, default=48, space='protection') + protection_stoploss_stop = IntParameter(1, 100, default=48, space='protection') + lookback = IntParameter(1, 200, default=48, space='protection') + trade_limit = IntParameter(1, 10, default=2, space='protection') + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + { + "method": "MaxDrawdown", + "lookback_period_candles": self.lookback.value, + "trade_limit": self.trade_limit.value, + "stop_duration_candles": self.protection_stop.value, + "max_allowed_drawdown": self.protection_max_allowed_dd.value, + "only_per_pair": False + }, + { + "method": "StoplossGuard", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": self.protection_stoploss_stop.value, + "only_per_pair": False + } + ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # if (0 < current_profit) & ((current_time - trade.open_date_utc).seconds > 3600) \ + # & (last_candle['percent10'] < 0.001): + # return 'small_profit' + # + # if (current_profit > 0.01) \ + # & ((previous_last_candle['sma10'] - last_candle['sma10']) / previous_last_candle['sma10'] > 0.003): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma10_quick' + + if (current_profit >= -0.01) & ((current_time - trade.open_date_utc).days >= 5)\ + & ((current_time - trade.open_date_utc).days < 10)\ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.01" + if (current_profit >= -0.02) & ((current_time - trade.open_date_utc).days >= 10)\ + & ((current_time - trade.open_date_utc).days < 15) \ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.02" + if (current_profit >= -0.03) & ((current_time - trade.open_date_utc).days >= 15) \ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.03" + + if self.profit_quick_lost: + if (current_profit >= 0) & (last_candle['percent3'] < -0.015): + return "quick_lost" + + if self.profit_no_change: + if (current_profit > 0.005) & (last_candle['percent10'] < 0.001) & (last_candle['percent5'] < 0) & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if self.profit_quick_gain_3: + if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if self.profit_quick_gain: + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0): #& ((current_time - trade.open_date_utc).seconds <= 3600) + return "quick_gain" + + if self.profit_sma10: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10'] * 1.005) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20: + if (current_profit > 0.005) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi: + if (current_profit > 0) & (last_candle['rsi'] > 88): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > 82) & (last_candle['percent'] < -0.02): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if self.profit_short_loss: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['ecart_20'] = dataframe['close'].rolling(20).var() / dataframe['close'] + dataframe['ecart_50'] = dataframe['close'].rolling(50).var() / dataframe['close'] + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=72) + dataframe['min10'] = ta.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + + # dataframe["dist_min_50"] = dataframe['close'] - dataframe['min50'] + # dataframe["dist_min_20"] = dataframe['close'] - dataframe['min20'] + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value, + self.buy_decalage0.value + ) + conditions.append(condition1) + + conditions2 = list() + + condition2, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num2.value, + self.buy_decalage2.value + ) + conditions2.append(condition2) + + if conditions: + for decalage in range(3, self.buy_decalage0.value): + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= -0.01) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= 0.02) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= self.buy_real_num0.value / 2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= -0.02) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= 0.02) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + # dataframe.loc[ + # ( + # (dataframe['cond1'].shift(decalage) <= 1.2) + # & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + # & (dataframe['close'] < dataframe['sma10']) + # & (dataframe['open'] < dataframe['sma10']) + # & (dataframe['min50'].shift(decalage) == dataframe['min50']) + # #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + # & (dataframe['percent20'].shift(decalage) <= -0.065) + # & (dataframe['distance_min'] <= 0.01) + # ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + # if conditions2: + # for decalage in range(1, self.buy_decalage2.value): #self.buy_decalage.value): + # dataframe.loc[ + # ( + # reduce(lambda x, y: x & y, conditions2) + # & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) < dataframe['sma10']) + # & (dataframe['close'] < dataframe['bb_middleband']) + # # & (dataframe['open'] < dataframe['sma10']) + # & (dataframe['min50'].shift(decalage) == dataframe['min50']) + # & (dataframe['bb_width'].shift(decalage) > self.buy_signal_bb_width.value) + # & (dataframe['percent20'].shift(decalage) <= -0.01) + # # & (dataframe['min20'] == dataframe['min50']) + # ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + + pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + pandas.set_option('display.max_columns', 30) + # print(condition1) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_7_4.json b/GodStraJD3_7_4.json new file mode 100644 index 0000000..f7ad4db --- /dev/null +++ b/GodStraJD3_7_4.json @@ -0,0 +1,45 @@ +{ + "strategy_name": "GodStraJD3_7_4", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.026, + "trailing_stop_positive_offset": 0.077, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_0": true, + "buy_0_distance": 0.04, + "buy_0_percent20": 0.02, + "buy_2": true, + "buy_2_distance": 0.02, + "buy_2_percent20": 0.08, + "buy_3": true, + "buy_3_distance": -0.06, + "buy_3_percent20": 0.1, + "buy_decalage0": 8, + "buy_decalage2": 8, + "buy_decalage3": 6, + "buy_decalage_deb_0": 3, + "buy_decalage_deb_2": 1, + "buy_decalage_deb_3": 3, + "buy_min_horizon": 83 + }, + "sell": {}, + "protection": { + "lookback": 133, + "protection_max_allowed_dd": 0.71, + "protection_stop": 61, + "protection_stoploss_stop": 50, + "trade_limit": 9 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-08 17:38:42.327292+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_7_4.py b/GodStraJD3_7_4.py new file mode 100644 index 0000000..b68a2bc --- /dev/null +++ b/GodStraJD3_7_4.py @@ -0,0 +1,804 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import calendar + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + + +class GodStraJD3_7_4(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min20': {'color': '#87e470'}, + 'min50': {'color': '#ac3e2a'}, + "min1.1": {'color': 'yellow'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'} + }, + # "Ind0": { + # buy_crossed_indicator0: {'color': 'green'}, + # buy_indicator0: {'color': 'red'} + # }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "Ecart": { + 'ecart_20': {'color': 'red'}, + 'ecart_50': {'color': 'yellow'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'}, + "distance_min": {'color': 'white'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + # "buy_real_num0": 0.46, + # "buy_real_num1": 0.48, + # "buy_real_num2": 0.67 + # + # buy_real_num0 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.46, space='buy') + # buy_real_num1 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.48, space='buy') + # buy_real_num2 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.67, space='buy') + + profit_no_change = False + profit_old_sma10 = False + profit_over_rsi = True + profit_quick_gain = True + profit_quick_gain_3 = True + profit_quick_lost = False + profit_short_loss = False + profit_sma10 = False + profit_sma20 = True + profit_very_old_sma10 = False + + trades = list() + # profit_no_change = BooleanParameter(default=True, space="buy") + # profit_quick_lost = BooleanParameter(default=True, space="buy") + # profit_sma10 = BooleanParameter(default=True, space="buy") + # profit_sma20 = BooleanParameter(default=True, space="buy") + # profit_quick_gain = BooleanParameter(default=True, space="buy") + # profit_quick_gain_3 = BooleanParameter(default=True, space="buy") + # profit_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_very_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_over_rsi = BooleanParameter(default=True, space="buy") + # profit_short_loss = BooleanParameter(default=True, space="buy") + + # buy_signal_bb_width = DecimalParameter(0.06, 0.15, decimals=2, default=0.065, space='buy') + + # buy_real_num0 = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + # buy_decalage0 = IntParameter(1, 10, default=5, space='buy') + # # + # buy_real_num1 = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + # buy_decalage1 = IntParameter(1, 10, default=5, space='buy') + # + # buy_real_num2 = DecimalParameter(1, 3, decimals=2, default=0.67, space='buy') + # buy_decalage2 = IntParameter(1, 10, default=5, space='buy') + + buy_min_horizon = IntParameter(50, 200, default=72, space='buy') + + buy_0 = BooleanParameter(default=True, space="buy") + buy_2 = BooleanParameter(default=True, space="buy") + buy_3 = BooleanParameter(default=True, space="buy") + + buy_0_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_2_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_3_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + + buy_0_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_2_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_3_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + + buy_decalage_deb_0 = IntParameter(1, 3, default=5, space='buy') + buy_decalage_deb_2 = IntParameter(1, 3, default=5, space='buy') + buy_decalage_deb_3 = IntParameter(1, 3, default=5, space='buy') + + buy_decalage0 = IntParameter(buy_decalage_deb_0.value + 1, 8, default=5, space='buy') + buy_decalage2 = IntParameter(buy_decalage_deb_2.value + 1, 8, default=5, space='buy') + buy_decalage3 = IntParameter(buy_decalage_deb_3.value + 1, 8, default=5, space='buy') + + protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + protection_stop = IntParameter(1, 100, default=48, space='protection') + protection_stoploss_stop = IntParameter(1, 100, default=48, space='protection') + lookback = IntParameter(1, 200, default=48, space='protection') + trade_limit = IntParameter(1, 10, default=2, space='protection') + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + + # {'symbol': 'FTM/USDT', 'timestamp': 1646494199570, 'datetime': '2022-03-05T15:29:59.570Z', 'high': 1.7489, + # 'low': 1.6084, 'bid': 1.6505, 'bidVolume': 2135.0, 'ask': 1.6508, 'askVolume': 2815.0, 'vwap': 1.66852198, + # 'open': 1.7313, 'close': 1.6505, 'last': 1.6505, 'previousClose': '1.73170000', 'change': -0.0808, + # 'percentage': -4.667, 'average': 1.6909, 'baseVolume': 124656725.0, 'quoteVolume': 207992485.7799, + # 'info': + # {'symbol': 'FTMUSDT', 'priceChange': '-0.08080000', 'priceChangePercent': '-4.667', + # 'weightedAvgPrice': '1.66852198', 'prevClosePrice': '1.73170000', 'lastPrice': '1.65050000', + # 'lastQty': '143.00000000', 'bidPrice': '1.65050000', 'bidQty': '2135.00000000', + # 'askPrice': '1.65080000', 'askQty': '2815.00000000', 'openPrice': '1.73130000', + # 'highPrice': '1.74890000', 'lowPrice': '1.60840000', 'volume': '124656725.00000000', + # 'quoteVolume': '207992485.77990000', 'openTime': '1646407799570', 'closeTime': '1646494199570', + # 'firstId': '137149614', 'lastId': '137450289', 'count': '300676'}} - 0.9817468621938484 + + allow_to_buy = True + max_gain = -100 + sum_gain = 0 + max_time = 0 + + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + if len(self.trades) == 0: + print('search') + self.trades = Trade.get_open_trades() + + if len(self.trades) >= self.config['max_open_trades'] / 2: + for trade in self.trades: + ticker = self.dp.ticker(trade.pair) + last_price = ticker['last'] + gain = (last_price - trade.open_rate) / trade.open_rate + max_gain = max(max_gain, gain) + sum_gain += gain + max_time = max(max_time, datetime.timestamp(trade.open_date)) + print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + now = datetime.now() + diff = (datetime.timestamp(now) - max_time / 3600) + if (max_gain <= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + print("allow_to_buy=false") + allow_to_buy = False + print(pair, allow_to_buy, len(self.trades), + "max gain=", max_gain, + "sum_gain=", sum_gain, + "now=", now, + "max=", max_time, + "diff=", datetime.timestamp(now) - max_time) + + if allow_to_buy: + self.trades = list() + + return allow_to_buy + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + { + "method": "MaxDrawdown", + "lookback_period_candles": self.lookback.value, + "trade_limit": self.trade_limit.value, + "stop_duration_candles": self.protection_stop.value, + "max_allowed_drawdown": self.protection_max_allowed_dd.value, + "only_per_pair": False + }, + { + "method": "StoplossGuard", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": self.protection_stoploss_stop.value, + "only_per_pair": False + } + ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # if (0 < current_profit) & ((current_time - trade.open_date_utc).seconds > 3600) \ + # & (last_candle['percent10'] < 0.001): + # return 'small_profit' + # + # if (current_profit > 0.01) \ + # & ((previous_last_candle['sma10'] - last_candle['sma10']) / previous_last_candle['sma10'] > 0.003): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma10_quick' + + if (current_profit >= -0.01) & ((current_time - trade.open_date_utc).days >= 5)\ + & ((current_time - trade.open_date_utc).days < 10)\ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.01" + if (current_profit >= -0.02) & ((current_time - trade.open_date_utc).days >= 10)\ + & ((current_time - trade.open_date_utc).days < 15) \ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.02" + if (current_profit >= -0.03) & ((current_time - trade.open_date_utc).days >= 15) \ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.03" + + if self.profit_quick_lost: + if (current_profit >= 0) & (last_candle['percent3'] < -0.015): + return "quick_lost" + + if self.profit_no_change: + if (current_profit > 0.005) & (last_candle['percent10'] < 0.001) & (last_candle['percent5'] < 0) & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if self.profit_quick_gain_3: + if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if self.profit_quick_gain: + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0): #& ((current_time - trade.open_date_utc).seconds <= 3600) + return "quick_gain" + + if self.profit_sma10: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10'] * 1.005) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20: + if (current_profit > 0.005) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi: + if (current_profit > 0) & (last_candle['rsi'] > 88): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > 82) & (last_candle['percent'] < -0.02): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if self.profit_short_loss: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['ecart_20'] = dataframe['close'].rolling(20).var() / dataframe['close'] + dataframe['ecart_50'] = dataframe['close'].rolling(50).var() / dataframe['close'] + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = ta.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + + # dataframe["dist_min_50"] = dataframe['close'] - dataframe['min50'] + # dataframe["dist_min_20"] = dataframe['close'] - dataframe['min20'] + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + # pandas.set_option('display.max_columns', 50) + # + # allow_to_buy = True + # max_gain = -100 + # trades = Trade.get_open_trades() + # + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # pairs = self.dp.current_whitelist() + # pairs_len = len(pairs) + # pair_index = pairs.index(metadata['pair']) + # + # # print(pair_index, " ", metadata['pair']) + # + # # ob = self.dp.orderbook(metadata['pair'], 1) + # # dataframe['best_bid'] = ob['bids'][0][0] + # # dataframe['best_ask'] = ob['asks'][0][0] + # # print(ob) + # + # for trade in trades: + # # if (metadata['pair'] == trade.pair): + # ticker = self.dp.ticker(trade.pair) #metadata['pair']) + # last_price = ticker['last'] + # # dataframe['volume24h'] = ticker['quoteVolume'] + # # dataframe['vwap'] = ticker['vwap'] + # # d = dataframe.tail(1) + # # print(dataframe) + # gain = (last_price - trade.open_rate) / trade.open_rate + # + # # print("Found open trade: ", trade, " ", ticker['last'], " ", trade.open_rate, gain) + # max_gain = max(max_gain, gain) + # + # if max_gain > - 0.05: + # allow_to_buy = False + # + # # print(metadata['pair'], max_gain, allow_to_buy, len(trades)) + + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + 0.9, #self.buy_real_num0.value, + 6 #self.buy_decalage0.value + ) + conditions.append(condition1) + + conditions2 = list() + + condition2, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + 1.5, #self.buy_real_num2.value, + 5 #self.buy_decalage2.value + ) + conditions2.append(condition2) + + if conditions: + for decalage in range(self.buy_decalage_deb_0.value, self.buy_decalage0.value): + if self.buy_0.value: + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_0_distance.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_2.value, self.buy_decalage2.value): + if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_real_num0.value / 2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_3.value, self.buy_decalage3.value): + if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + # if conditions2: + # for decalage in range(1, self.buy_decalage2.value): #self.buy_decalage.value): + # dataframe.loc[ + # ( + # reduce(lambda x, y: x & y, conditions2) + # & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) < dataframe['sma10']) + # & (dataframe['close'] < dataframe['bb_middleband']) + # # & (dataframe['open'] < dataframe['sma10']) + # & (dataframe['min50'].shift(decalage) == dataframe['min50']) + # & (dataframe['bb_width'].shift(decalage) > self.buy_signal_bb_width.value) + # & (dataframe['percent20'].shift(decalage) <= -0.01) + # # & (dataframe['min20'] == dataframe['min50']) + # ), ['buy', 'buy_tag']] = (1, 'buy_4_' + str(decalage)) + + # pair = metadata['pair'] + # allow_to_buy = True + # max_gain = -100 + # sum_gain = 0 + # max_time = 0 + # if len(self.trades) == 0: + # print('search') + # self.trades = Trade.get_open_trades() + # + # # if self.dp: + # # if self.dp.runmode.value in ('live', 'dry_run'): + # if len(self.trades) >= self.config['max_open_trades'] / 2: + # for trade in self.trades: + # ticker = self.dp.ticker(trade.pair) + # last_price = ticker['last'] + # gain = (last_price - trade.open_rate) / trade.open_rate + # max_gain = max(max_gain, gain) + # sum_gain += gain + # max_time = max(max_time, datetime.timestamp(trade.open_date)) + # print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + # datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + # now = datetime.now() + # diff = (datetime.timestamp(now) - max_time / 3600) + # if (max_gain >= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + # print("allow_to_buy=false") + # allow_to_buy = False + # print(pair, allow_to_buy, len(self.trades), + # "max gain=", max_gain, + # "sum_gain=", sum_gain, + # "now=", now, + # "max=", max_time, + # "diff=", datetime.timestamp(now) - max_time) + # + # if allow_to_buy: + # self.trades = list() + + # print(condition1) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_7_5.json b/GodStraJD3_7_5.json new file mode 100644 index 0000000..30bf5d4 --- /dev/null +++ b/GodStraJD3_7_5.json @@ -0,0 +1,48 @@ +{ + "strategy_name": "GodStraJD3_7_5", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.026, + "trailing_stop_positive_offset": 0.077, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_0": true, + "buy_0_distance": 0.06, + "buy_0_percent20": 0.02, + "buy_2": true, + "buy_2_distance": 0.09, + "buy_2_percent20": 0.05, + "buy_3": true, + "buy_3_distance": 0.03, + "buy_3_percent20": 0.06, + "buy_decalage0": 7, + "buy_decalage2": 8, + "buy_decalage3": 6, + "buy_decalage_deb_0": 2, + "buy_decalage_deb_2": 1, + "buy_decalage_deb_3": 3, + "buy_min_horizon": 179, + "buy_real_num0": 0.82, + "buy_real_num1": 0.42, + "buy_real_num2": 0.21 + }, + "sell": {}, + "protection": { + "lookback": 133, + "protection_max_allowed_dd": 0.71, + "protection_stop": 61, + "protection_stoploss_stop": 50, + "trade_limit": 9 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-09 00:06:48.822740+00:00" +} diff --git a/GodStraJD3_7_5.py b/GodStraJD3_7_5.py new file mode 100644 index 0000000..7c8014e --- /dev/null +++ b/GodStraJD3_7_5.py @@ -0,0 +1,764 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import calendar +from freqtrade.loggers import setup_logging + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + + +class GodStraJD3_7_5(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min20': {'color': '#87e470'}, + 'min50': {'color': '#ac3e2a'}, + "min1.1": {'color': 'yellow'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'} + }, + # "Ind0": { + # buy_crossed_indicator0: {'color': 'green'}, + # buy_indicator0: {'color': 'red'} + # }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "Ecart": { + 'normal_var_20': {'color': 'red'}, + 'normal_var_50': {'color': 'yellow'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'}, + "distance_min": {'color': 'white'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + profit_no_change = False + profit_old_sma10 = False + profit_over_rsi = True + profit_quick_gain = True + profit_quick_gain_3 = True + profit_quick_lost = False + profit_short_loss = False + profit_sma10 = True + profit_sma20 = True + profit_very_old_sma10 = False + + trades = list() + # profit_no_change = BooleanParameter(default=True, space="buy") + # profit_quick_lost = BooleanParameter(default=True, space="buy") + # profit_sma10 = BooleanParameter(default=True, space="buy") + # profit_sma20 = BooleanParameter(default=True, space="buy") + # profit_quick_gain = BooleanParameter(default=True, space="buy") + # profit_quick_gain_3 = BooleanParameter(default=True, space="buy") + # profit_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_very_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_over_rsi = BooleanParameter(default=True, space="buy") + # profit_short_loss = BooleanParameter(default=True, space="buy") + + # buy_signal_bb_width = DecimalParameter(0.06, 0.15, decimals=2, default=0.065, space='buy') + + buy_real_num0 = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_real_num1 = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_real_num2 = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + + buy_min_horizon = IntParameter(50, 200, default=72, space='buy') + + buy_0 = BooleanParameter(default=True, space="buy") + buy_2 = BooleanParameter(default=True, space="buy") + buy_3 = BooleanParameter(default=True, space="buy") + + buy_0_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_2_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_3_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + + buy_0_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_2_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_3_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + + buy_decalage_deb_0 = IntParameter(1, 3, default=5, space='buy') + buy_decalage_deb_2 = IntParameter(1, 3, default=5, space='buy') + buy_decalage_deb_3 = IntParameter(1, 3, default=5, space='buy') + + buy_decalage0 = IntParameter(buy_decalage_deb_0.value + 1, 8, default=5, space='buy') + buy_decalage2 = IntParameter(buy_decalage_deb_2.value + 1, 8, default=5, space='buy') + buy_decalage3 = IntParameter(buy_decalage_deb_3.value + 1, 8, default=5, space='buy') + + protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + protection_stop = IntParameter(1, 100, default=48, space='protection') + protection_stoploss_stop = IntParameter(1, 100, default=48, space='protection') + lookback = IntParameter(1, 200, default=48, space='protection') + trade_limit = IntParameter(1, 10, default=2, space='protection') + + sell_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + + # {'symbol': 'FTM/USDT', 'timestamp': 1646494199570, 'datetime': '2022-03-05T15:29:59.570Z', 'high': 1.7489, + # 'low': 1.6084, 'bid': 1.6505, 'bidVolume': 2135.0, 'ask': 1.6508, 'askVolume': 2815.0, 'vwap': 1.66852198, + # 'open': 1.7313, 'close': 1.6505, 'last': 1.6505, 'previousClose': '1.73170000', 'change': -0.0808, + # 'percentage': -4.667, 'average': 1.6909, 'baseVolume': 124656725.0, 'quoteVolume': 207992485.7799, + # 'info': + # {'symbol': 'FTMUSDT', 'priceChange': '-0.08080000', 'priceChangePercent': '-4.667', + # 'weightedAvgPrice': '1.66852198', 'prevClosePrice': '1.73170000', 'lastPrice': '1.65050000', + # 'lastQty': '143.00000000', 'bidPrice': '1.65050000', 'bidQty': '2135.00000000', + # 'askPrice': '1.65080000', 'askQty': '2815.00000000', 'openPrice': '1.73130000', + # 'highPrice': '1.74890000', 'lowPrice': '1.60840000', 'volume': '124656725.00000000', + # 'quoteVolume': '207992485.77990000', 'openTime': '1646407799570', 'closeTime': '1646494199570', + # 'firstId': '137149614', 'lastId': '137450289', 'count': '300676'}} - 0.9817468621938484 + + allow_to_buy = True + max_gain = -100 + sum_gain = 0 + max_time = 0 + + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + if len(self.trades) == 0: + print('search') + self.trades = Trade.get_open_trades() + + if len(self.trades) >= self.config['max_open_trades'] / 2: + for trade in self.trades: + ticker = self.dp.ticker(trade.pair) + last_price = ticker['last'] + gain = (last_price - trade.open_rate) / trade.open_rate + max_gain = max(max_gain, gain) + sum_gain += gain + max_time = max(max_time, datetime.timestamp(trade.open_date)) + print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + now = datetime.now() + diff = (datetime.timestamp(now) - max_time / 3600) + if (max_gain <= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + print("allow_to_buy=false") + allow_to_buy = False + print(pair, allow_to_buy, len(self.trades), + "max gain=", max_gain, + "sum_gain=", sum_gain, + "now=", now, + "max=", max_time, + "diff=", datetime.timestamp(now) - max_time) + + if allow_to_buy: + self.trades = list() + + return allow_to_buy + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + { + "method": "MaxDrawdown", + "lookback_period_candles": self.lookback.value, + "trade_limit": self.trade_limit.value, + "stop_duration_candles": self.protection_stop.value, + "max_allowed_drawdown": self.protection_max_allowed_dd.value, + "only_per_pair": False + }, + { + "method": "StoplossGuard", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": self.protection_stoploss_stop.value, + "only_per_pair": False + } + ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # if (0 < current_profit) & ((current_time - trade.open_date_utc).seconds > 3600) \ + # & (last_candle['percent10'] < 0.001): + # return 'small_profit' + # + # if (current_profit > 0.01) \ + # & ((previous_last_candle['sma10'] - last_candle['sma10']) / previous_last_candle['sma10'] > 0.003): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma10_quick' + + if (current_profit >= -0.01) & ((current_time - trade.open_date_utc).days >= 5)\ + & ((current_time - trade.open_date_utc).days < 10)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.01" + if (current_profit >= -0.02) & ((current_time - trade.open_date_utc).days >= 10)\ + & ((current_time - trade.open_date_utc).days < 15) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.02" + if (current_profit >= -0.03) & ((current_time - trade.open_date_utc).days >= 15) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.03" + + if self.profit_quick_lost: + if (current_profit >= 0) & (last_candle['percent3'] < -0.015): + return "quick_lost" + + if self.profit_no_change: + if (current_profit > 0.005) & (last_candle['percent10'] < 0.001) & (last_candle['percent5'] < 0) & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if self.profit_quick_gain_3: + if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if self.profit_quick_gain: + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain" + + if self.profit_sma10: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi: + if (current_profit > 0) & (previous_last_candle['rsi'] > 88): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > 82) & (last_candle['percent'] < -0.02): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if self.profit_short_loss: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['ecart_20'] = dataframe['close'].rolling(20).var() / dataframe['close'] + dataframe['ecart_50'] = dataframe['close'].rolling(50).var() / dataframe['close'] + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = ta.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + + # dataframe["dist_min_50"] = dataframe['close'] - dataframe['min50'] + # dataframe["dist_min_20"] = dataframe['close'] - dataframe['min20'] + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + # pandas.set_option('display.max_columns', 50) + # + # allow_to_buy = True + # max_gain = -100 + # trades = Trade.get_open_trades() + # + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # pairs = self.dp.current_whitelist() + # pairs_len = len(pairs) + # pair_index = pairs.index(metadata['pair']) + # + # # print(pair_index, " ", metadata['pair']) + # + # # ob = self.dp.orderbook(metadata['pair'], 1) + # # dataframe['best_bid'] = ob['bids'][0][0] + # # dataframe['best_ask'] = ob['asks'][0][0] + # # print(ob) + # + # for trade in trades: + # # if (metadata['pair'] == trade.pair): + # ticker = self.dp.ticker(trade.pair) #metadata['pair']) + # last_price = ticker['last'] + # # dataframe['volume24h'] = ticker['quoteVolume'] + # # dataframe['vwap'] = ticker['vwap'] + # # d = dataframe.tail(1) + # # print(dataframe) + # gain = (last_price - trade.open_rate) / trade.open_rate + # + # # print("Found open trade: ", trade, " ", ticker['last'], " ", trade.open_rate, gain) + # max_gain = max(max_gain, gain) + # + # if max_gain > - 0.05: + # allow_to_buy = False + # + # # print(metadata['pair'], max_gain, allow_to_buy, len(trades)) + + for decalage in range(self.buy_decalage_deb_0.value, self.buy_decalage0.value): + if self.buy_0.value: + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value, + self.buy_decalage0.value + ) + conditions.append(condition1) + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_0_distance.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_2.value, self.buy_decalage2.value): + if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_real_num0.value / 2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_3.value, self.buy_decalage3.value): + if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + # pair = metadata['pair'] + # allow_to_buy = True + # max_gain = -100 + # sum_gain = 0 + # max_time = 0 + # if len(self.trades) == 0: + # print('search') + # self.trades = Trade.get_open_trades() + # + # # if self.dp: + # # if self.dp.runmode.value in ('live', 'dry_run'): + # if len(self.trades) >= self.config['max_open_trades'] / 2: + # for trade in self.trades: + # ticker = self.dp.ticker(trade.pair) + # last_price = ticker['last'] + # gain = (last_price - trade.open_rate) / trade.open_rate + # max_gain = max(max_gain, gain) + # sum_gain += gain + # max_time = max(max_time, datetime.timestamp(trade.open_date)) + # print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + # datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + # now = datetime.now() + # diff = (datetime.timestamp(now) - max_time / 3600) + # if (max_gain >= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + # print("allow_to_buy=false") + # allow_to_buy = False + # print(pair, allow_to_buy, len(self.trades), + # "max gain=", max_gain, + # "sum_gain=", sum_gain, + # "now=", now, + # "max=", max_time, + # "diff=", datetime.timestamp(now) - max_time) + # + # if allow_to_buy: + # self.trades = list() + + # print(condition1) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_7_5_1.json b/GodStraJD3_7_5_1.json new file mode 100644 index 0000000..a3fe3ed --- /dev/null +++ b/GodStraJD3_7_5_1.json @@ -0,0 +1,57 @@ +{ + "strategy_name": "GodStraJD3_7_5_1", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.026, + "trailing_stop_positive_offset": 0.077, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_0": false, + "buy_0_percent20": 0.07, + "buy_1_decalage": 8, + "buy_1_decalage_deb": 2, + "buy_1_distance": 0.03, + "buy_1_min": 1.09, + "buy_1_normal_var": 4.1, + "buy_1_real_num": 0.13, + "buy_1_volume": 94, + "buy_2": false, + "buy_2_decalage": 8, + "buy_2_decalage_deb": 1, + "buy_2_distance": 0.06, + "buy_2_min": 1.07, + "buy_2_normal_var": 2.1, + "buy_2_percent20": -0.08, + "buy_2_real_num": 0.96, + "buy_2_volume": 19, + "buy_3": true, + "buy_3_decalage": 8, + "buy_3_decalage_deb": 2, + "buy_3_distance": 0.06, + "buy_3_min": 1.01, + "buy_3_normal_var": 0.8, + "buy_3_percent20": 0.01, + "buy_3_real_num": 0.19, + "buy_3_volume": 32, + "buy_min_horizon": 139 + }, + "sell": {}, + "protection": { + "lookback": 133, + "protection_max_allowed_dd": 0.71, + "protection_stop": 61, + "protection_stoploss_stop": 50, + "trade_limit": 9 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-17 11:40:13.234887+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_7_5_1.py b/GodStraJD3_7_5_1.py new file mode 100644 index 0000000..5fbdf5a --- /dev/null +++ b/GodStraJD3_7_5_1.py @@ -0,0 +1,823 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import calendar +from freqtrade.loggers import setup_logging + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + + +class GodStraJD3_7_5_1(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min20': {'color': '#87e470'}, + 'min50': {'color': '#ac3e2a'}, + "min1.1": {'color': 'yellow'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'} + }, + # "Ind0": { + # buy_crossed_indicator0: {'color': 'green'}, + # buy_indicator0: {'color': 'red'} + # }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "Ecart": { + 'normal_var_20': {'color': 'red'}, + 'normal_var_50': {'color': 'yellow'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'}, + "distance_min": {'color': 'white'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + profit_no_change = False + profit_old_sma10 = False + profit_over_rsi = True + profit_quick_gain = True + profit_quick_gain_3 = True + profit_quick_lost = False + profit_short_loss = False + profit_sma10 = True + profit_sma20 = True + profit_very_old_sma10 = False + + trades = list() + # profit_no_change = BooleanParameter(default=True, space="buy") + # profit_quick_lost = BooleanParameter(default=True, space="buy") + # profit_sma10 = BooleanParameter(default=True, space="buy") + # profit_sma20 = BooleanParameter(default=True, space="buy") + # profit_quick_gain = BooleanParameter(default=True, space="buy") + # profit_quick_gain_3 = BooleanParameter(default=True, space="buy") + # profit_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_very_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_over_rsi = BooleanParameter(default=True, space="buy") + # profit_short_loss = BooleanParameter(default=True, space="buy") + + # buy_signal_bb_width = DecimalParameter(0.06, 0.15, decimals=2, default=0.065, space='buy') + + buy_1_real_num = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_2_real_num = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_3_real_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + + buy_min_horizon = IntParameter(50, 200, default=72, space='buy') + + buy_0 = BooleanParameter(default=True, space="buy") + buy_2 = BooleanParameter(default=True, space="buy") + buy_3 = BooleanParameter(default=True, space="buy") + + buy_0_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_2_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_3_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + + buy_1_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_2_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_3_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + + buy_1_min = DecimalParameter(1, 1.1, decimals=2, default=1.02, space='buy') + buy_2_min = DecimalParameter(1, 1.1, decimals=2, default=1.02, space='buy') + buy_3_min = DecimalParameter(1, 1.1, decimals=2, default=1.02, space='buy') + + buy_1_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + buy_2_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + buy_3_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + + buy_1_decalage_deb = IntParameter(1, 3, default=5, space='buy') + buy_2_decalage_deb = IntParameter(1, 3, default=5, space='buy') + buy_3_decalage_deb = IntParameter(1, 3, default=5, space='buy') + + buy_1_decalage = IntParameter(buy_1_decalage_deb.value + 1, 8, default=5, space='buy') + buy_2_decalage = IntParameter(buy_2_decalage_deb.value + 1, 8, default=5, space='buy') + buy_3_decalage = IntParameter(buy_3_decalage_deb.value + 1, 8, default=5, space='buy') + + buy_1_volume = IntParameter(0, 100, default=10, space='buy') + buy_2_volume = IntParameter(0, 100, default=10, space='buy') + buy_3_volume = IntParameter(0, 100, default=10, space='buy') + + protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + protection_stop = IntParameter(1, 100, default=48, space='protection') + protection_stoploss_stop = IntParameter(1, 100, default=48, space='protection') + lookback = IntParameter(1, 200, default=48, space='protection') + trade_limit = IntParameter(1, 10, default=2, space='protection') + + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + + # for decalage in range(self.buy_3_decalage_deb.value, self.buy_3_decalage.value): + # decalage_candle = dataframe.iloc[- decalage].squeeze() + # # if (decalage_candle['normal_var_20'] >= 0.6): + # amount = 10 * (- decalage_candle['percent20'] * 100) + # print("use more stake", pair, " ", amount) + # return min(max_stake, amount) + + # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + # if current_candle['bb_width'] > 0.05: + # print("use more stake", pair, " ", proposed_stake * 2) + # return min(max_stake, proposed_stake * 2) + # + # if current_candle['bb_width'] > 0.035: + # print("use more stake", pair, " ", proposed_stake * 1.5) + # return min(max_stake, proposed_stake * 1.5) + + # if current_candle['bb_width'] < 0.020: + # print("use less stake", pair, " ", proposed_stake / 2) + # return min(max_stake, proposed_stake / 2) + # if self.config['stake_amount'] == 'unlimited': + # # Use entire available wallet during favorable conditions when in compounding mode. + # return max_stake + # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + + + # Use default stake amount. + return proposed_stake + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + + # {'symbol': 'FTM/USDT', 'timestamp': 1646494199570, 'datetime': '2022-03-05T15:29:59.570Z', 'high': 1.7489, + # 'low': 1.6084, 'bid': 1.6505, 'bidVolume': 2135.0, 'ask': 1.6508, 'askVolume': 2815.0, 'vwap': 1.66852198, + # 'open': 1.7313, 'close': 1.6505, 'last': 1.6505, 'previousClose': '1.73170000', 'change': -0.0808, + # 'percentage': -4.667, 'average': 1.6909, 'baseVolume': 124656725.0, 'quoteVolume': 207992485.7799, + # 'info': + # {'symbol': 'FTMUSDT', 'priceChange': '-0.08080000', 'priceChangePercent': '-4.667', + # 'weightedAvgPrice': '1.66852198', 'prevClosePrice': '1.73170000', 'lastPrice': '1.65050000', + # 'lastQty': '143.00000000', 'bidPrice': '1.65050000', 'bidQty': '2135.00000000', + # 'askPrice': '1.65080000', 'askQty': '2815.00000000', 'openPrice': '1.73130000', + # 'highPrice': '1.74890000', 'lowPrice': '1.60840000', 'volume': '124656725.00000000', + # 'quoteVolume': '207992485.77990000', 'openTime': '1646407799570', 'closeTime': '1646494199570', + # 'firstId': '137149614', 'lastId': '137450289', 'count': '300676'}} - 0.9817468621938484 + + allow_to_buy = True + max_gain = -100 + sum_gain = 0 + max_time = 0 + + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + if len(self.trades) == 0: + print('search') + self.trades = Trade.get_open_trades() + + if len(self.trades) >= self.config['max_open_trades'] / 2: + for trade in self.trades: + ticker = self.dp.ticker(trade.pair) + last_price = ticker['last'] + gain = (last_price - trade.open_rate) / trade.open_rate + max_gain = max(max_gain, gain) + sum_gain += gain + max_time = max(max_time, datetime.timestamp(trade.open_date)) + print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + now = datetime.now() + diff = (datetime.timestamp(now) - max_time / 3600) + if (max_gain <= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + print("allow_to_buy=false") + allow_to_buy = False + print(pair, allow_to_buy, len(self.trades), + "max gain=", max_gain, + "sum_gain=", sum_gain, + "now=", now, + "max=", max_time, + "diff=", datetime.timestamp(now) - max_time) + + if allow_to_buy: + self.trades = list() + + return allow_to_buy + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + { + "method": "MaxDrawdown", + "lookback_period_candles": self.lookback.value, + "trade_limit": self.trade_limit.value, + "stop_duration_candles": self.protection_stop.value, + "max_allowed_drawdown": self.protection_max_allowed_dd.value, + "only_per_pair": False + }, + { + "method": "StoplossGuard", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": self.protection_stoploss_stop.value, + "only_per_pair": False + } + ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # if (0 < current_profit) & ((current_time - trade.open_date_utc).seconds > 3600) \ + # & (last_candle['percent10'] < 0.001): + # return 'small_profit' + # + # if (current_profit > 0.01) \ + # & ((previous_last_candle['sma10'] - last_candle['sma10']) / previous_last_candle['sma10'] > 0.003): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma10_quick' + + if (current_profit >= -0.01) & ((current_time - trade.open_date_utc).days >= 5)\ + & ((current_time - trade.open_date_utc).days < 10)\ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.01" + if (current_profit >= -0.02) & ((current_time - trade.open_date_utc).days >= 10)\ + & ((current_time - trade.open_date_utc).days < 15) \ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.02" + if (current_profit >= -0.03) & ((current_time - trade.open_date_utc).days >= 15) \ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.03" + + if self.profit_quick_lost: + if (current_profit >= 0) & (last_candle['percent3'] < -0.015): + return "quick_lost" + + if self.profit_no_change: + if (current_profit > 0.005) & (last_candle['percent10'] < 0.001) & (last_candle['percent5'] < 0) & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + if (current_profit > 0.005) & (last_candle['normal_var_20'] < 0.008): + return "no_change_20" + + if (current_profit > 0.01) & (last_candle['normal_var_10'] < 0.008): + return "no_change_10" + if (current_profit > 0.01) & (last_candle['normal_var_5'] < 0.005): + return "no_change_5" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if self.profit_quick_gain_3: + if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if self.profit_quick_gain: + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0): #& ((current_time - trade.open_date_utc).seconds <= 3600) + return "quick_gain" + + if self.profit_sma10: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10'] * 1.005) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi: + if (current_profit > 0) & (last_candle['rsi'] > 88): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > 82) & (last_candle['percent'] < -0.02): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if self.profit_short_loss: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['ecart_20'] = dataframe['close'].rolling(20).var() / dataframe['close'] + dataframe['ecart_50'] = dataframe['close'].rolling(50).var() / dataframe['close'] + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = ta.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_5'] = dataframe['normal'].rolling(5).var() + dataframe['normal_var_10'] = dataframe['normal'].rolling(10).var() + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + + # dataframe["dist_min_50"] = dataframe['close'] - dataframe['min50'] + # dataframe["dist_min_20"] = dataframe['close'] - dataframe['min20'] + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + # pandas.set_option('display.max_columns', 50) + # + # allow_to_buy = True + # max_gain = -100 + # trades = Trade.get_open_trades() + # + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # pairs = self.dp.current_whitelist() + # pairs_len = len(pairs) + # pair_index = pairs.index(metadata['pair']) + # + # # print(pair_index, " ", metadata['pair']) + # + # # ob = self.dp.orderbook(metadata['pair'], 1) + # # dataframe['best_bid'] = ob['bids'][0][0] + # # dataframe['best_ask'] = ob['asks'][0][0] + # # print(ob) + # + # for trade in trades: + # # if (metadata['pair'] == trade.pair): + # ticker = self.dp.ticker(trade.pair) #metadata['pair']) + # last_price = ticker['last'] + # # dataframe['volume24h'] = ticker['quoteVolume'] + # # dataframe['vwap'] = ticker['vwap'] + # # d = dataframe.tail(1) + # # print(dataframe) + # gain = (last_price - trade.open_rate) / trade.open_rate + # + # # print("Found open trade: ", trade, " ", ticker['last'], " ", trade.open_rate, gain) + # max_gain = max(max_gain, gain) + # + # if max_gain > - 0.05: + # allow_to_buy = False + # + # # print(metadata['pair'], max_gain, allow_to_buy, len(trades)) + + for decalage in range(self.buy_1_decalage_deb.value, self.buy_1_decalage.value): + if self.buy_0.value: + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_1_real_num.value, + self.buy_1_decalage.value + ) + conditions.append(condition1) + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= self.buy_1_volume.value) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * self.buy_1_min.value) + & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_1_distance.value) + & (dataframe['normal_var_20'] >= self.buy_1_normal_var.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_2_decalage_deb.value, self.buy_2_decalage.value): + if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_1_real_num.value / 2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= self.buy_2_volume.value) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + # & (dataframe['min10'] <= dataframe['min50'] * self.buy_2_min.value) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + & (dataframe['normal_var_20'] >= self.buy_2_normal_var.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + for decalage in range(self.buy_3_decalage_deb.value, self.buy_3_decalage.value): + if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= self.buy_3_volume.value) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + # & (dataframe['min10'] <= dataframe['min50'] * self.buy_3_min.value) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + & (dataframe['normal_var_20'] >= self.buy_3_normal_var.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + # pair = metadata['pair'] + # allow_to_buy = True + # max_gain = -100 + # sum_gain = 0 + # max_time = 0 + # if len(self.trades) == 0: + # print('search') + # self.trades = Trade.get_open_trades() + # + # # if self.dp: + # # if self.dp.runmode.value in ('live', 'dry_run'): + # if len(self.trades) >= self.config['max_open_trades'] / 2: + # for trade in self.trades: + # ticker = self.dp.ticker(trade.pair) + # last_price = ticker['last'] + # gain = (last_price - trade.open_rate) / trade.open_rate + # max_gain = max(max_gain, gain) + # sum_gain += gain + # max_time = max(max_time, datetime.timestamp(trade.open_date)) + # print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + # datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + # now = datetime.now() + # diff = (datetime.timestamp(now) - max_time / 3600) + # if (max_gain >= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + # print("allow_to_buy=false") + # allow_to_buy = False + # print(pair, allow_to_buy, len(self.trades), + # "max gain=", max_gain, + # "sum_gain=", sum_gain, + # "now=", now, + # "max=", max_time, + # "diff=", datetime.timestamp(now) - max_time) + # + # if allow_to_buy: + # self.trades = list() + + # print(condition1) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_7_5_10.json b/GodStraJD3_7_5_10.json new file mode 100644 index 0000000..bd065c1 --- /dev/null +++ b/GodStraJD3_7_5_10.json @@ -0,0 +1,72 @@ +{ + "strategy_name": "GodStraJD3_7_5_10", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.026, + "trailing_stop_positive_offset": 0.077, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_0_distance": 0.01, + "buy_0_percent20": 0.1, + "buy_1_bb_lower_5": 0.52, + "buy_1_pente_sma10": 0.25, + "buy_1_pente_sma20": 0.38, + "buy_1_percent_5m_num": -0.04, + "buy_2_bb_lower_5": 0.56, + "buy_2_distance": 0.1, + "buy_2_pente_sma10": 0.55, + "buy_2_pente_sma20": 0.25, + "buy_2_percent20": 0.04, + "buy_2_percent_5m_num": -0.07, + "buy_3_bb_lower_5": 0.6, + "buy_3_distance": -0.01, + "buy_3_pente_sma10": 0.32, + "buy_3_pente_sma20": 0.5, + "buy_3_percent20": 0.09, + "buy_3_percent_5m_num": -0.04, + "buy_4_day_week_percent": 0.0, + "buy_4_pente_sma10": 0.56, + "buy_4_pente_sma20": 0.13, + "buy_day_week": "percent3_1d", + "buy_decalage0": 7, + "buy_decalage2": 8, + "buy_decalage3": 8, + "buy_decalage_deb_0": 3, + "buy_decalage_deb_2": 0, + "buy_decalage_deb_3": 0, + "buy_min_horizon": 83, + "buy_real_num0": 1.63, + "buy_real_num1": 0.42, + "buy_real_num2": 0.38 + }, + "sell": { + "sell_RSI": 89, + "sell_RSI2": 78, + "sell_RSI2_percent": 0.001, + "sell_candels": 4, + "sell_percent": 0.02, + "sell_percent3": 0.014, + "sell_profit_no_change": 0.02, + "sell_profit_percent10": 0.0013, + "sell_too_old_day": 7, + "sell_too_old_percent": 0.019 + }, + "protection": { + "lookback": 133, + "protection_max_allowed_dd": 0.71, + "protection_stop": 61, + "protection_stoploss_stop": 50, + "trade_limit": 9 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-29 07:11:59.278799+00:00" +} diff --git a/GodStraJD3_7_5_10.py b/GodStraJD3_7_5_10.py new file mode 100644 index 0000000..daec1d7 --- /dev/null +++ b/GodStraJD3_7_5_10.py @@ -0,0 +1,962 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + + +class GodStraJD3_7_5_10(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma5': {'color': 'pink'}, + 'sma10': {'color': 'yellow'}, + 'min20': {'color': '#87e470'}, + 'min50': {'color': '#ac3e2a'}, + "min1.1": {'color': 'yellow'}, + 'sma20': {'color': 'cyan'}, + 'open_1d': {'color': 'white'}, + 'close_1d': {'color': 'white'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + 'bb_lower_5': {'color': 'yellow'} + }, + # "Ind0": { + # buy_crossed_indicator0: {'color': 'green'}, + # buy_indicator0: {'color': 'red'} + # }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "Ecart": { + 'normal_var_20': {'color': 'red'}, + 'normal_var_50': {'color': 'yellow'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'}, + "distance_min": {'color': 'white'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + profit_no_change = True + profit_old_sma10 = False + profit_old_sma5 = False + profit_over_rsi = True + profit_quick_gain = True + profit_quick_gain_3 = True + profit_quick_lost = True + profit_short_loss = False + profit_sma5 = True + profit_sma10 = True + profit_sma20 = True + profit_very_old_sma10 = False + + trades = list() + # profit_no_change = BooleanParameter(default=True, space="buy") + # profit_quick_lost = BooleanParameter(default=True, space="buy") + # profit_sma10 = BooleanParameter(default=True, space="buy") + # profit_sma20 = BooleanParameter(default=True, space="buy") + # profit_quick_gain = BooleanParameter(default=True, space="buy") + # profit_quick_gain_3 = BooleanParameter(default=True, space="buy") + # profit_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_very_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_over_rsi = BooleanParameter(default=True, space="buy") + # profit_short_loss = BooleanParameter(default=True, space="buy") + + # buy_signal_bb_width = DecimalParameter(0.06, 0.15, decimals=2, default=0.065, space='buy') + + buy_real_num0 = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_real_num1 = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_real_num2 = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + buy_1_percent_5m_num = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.01, space='buy') + buy_2_percent_5m_num = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.01, space='buy') + buy_3_percent_5m_num = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.01, space='buy') + buy_4_day_week_percent = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='buy') + + buy_day_week = CategoricalParameter([ + 'percent_1d', 'percent3_1d', 'percent5_1d', 'percent_1w', 'percent3_1w'], + default="percent_1d", space='buy') + #buy_3_cond_1h_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + # buy_1_percent_4h_num = DecimalParameter(-0.1, 0.1, decimals=2, default=0.67, space='buy') + # buy_2_percent_4h_num = DecimalParameter(-0.1, 0.1, decimals=2, default=0.67, space='buy') + # buy_3_percent_4h_num = DecimalParameter(-0.1, 0.1, decimals=2, default=0.67, space='buy') + #buy_3_percent_1d_num = DecimalParameter(-0.1, 0.1, decimals=2, default=0.67, space='buy') + #buy_3_percent_1w_num = DecimalParameter(-0.1, 0.1, decimals=2, default=0.67, space='buy') + + buy_min_horizon = IntParameter(50, 200, default=72, space='buy') + + #buy_0 = BooleanParameter(default=True, space="buy") + #buy_2 = BooleanParameter(default=True, space="buy") + #buy_3 = BooleanParameter(default=True, space="buy") + + buy_0_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_2_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_3_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + + buy_0_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_2_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_3_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + + buy_decalage_deb_0 = IntParameter(0, 3, default=5, space='buy') + buy_decalage_deb_2 = IntParameter(0, 3, default=5, space='buy') + buy_decalage_deb_3 = IntParameter(0, 3, default=5, space='buy') + + buy_decalage0 = IntParameter(buy_decalage_deb_0.value + 1, 8, default=5, space='buy') + buy_decalage2 = IntParameter(buy_decalage_deb_2.value + 1, 8, default=5, space='buy') + buy_decalage3 = IntParameter(buy_decalage_deb_3.value + 1, 8, default=5, space='buy') + + buy_1_pente_sma10 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + buy_2_pente_sma10 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + buy_3_pente_sma10 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + buy_4_pente_sma10 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + + buy_1_pente_sma20 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + buy_2_pente_sma20 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + buy_3_pente_sma20 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + buy_4_pente_sma20 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + + # buy_1_bb_lower_var_5 = DecimalParameter(0, 0.4, decimals=2, default=0.07, space='buy') + # buy_2_bb_lower_var_5 = DecimalParameter(0, 0.4, decimals=2, default=0.07, space='buy') + # buy_3_bb_lower_var_5 = DecimalParameter(0, 0.4, decimals=2, default=0.07, space='buy') + + buy_1_bb_lower_5 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + buy_2_bb_lower_5 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + buy_3_bb_lower_5 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + + protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + protection_stop = IntParameter(1, 100, default=48, space='protection') + protection_stoploss_stop = IntParameter(1, 100, default=48, space='protection') + lookback = IntParameter(1, 200, default=48, space='protection') + trade_limit = IntParameter(1, 10, default=2, space='protection') + + sell_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_candels = IntParameter(0, 48, default=12, space='sell') + + sell_too_old_day = IntParameter(0, 10, default=5, space='sell') + sell_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + sell_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') + sell_profit_percent10 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') + + sell_RSI = IntParameter(70, 98, default=88, space='sell') + sell_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + + # {'symbol': 'FTM/USDT', 'timestamp': 1646494199570, 'datetime': '2022-03-05T15:29:59.570Z', 'high': 1.7489, + # 'low': 1.6084, 'bid': 1.6505, 'bidVolume': 2135.0, 'ask': 1.6508, 'askVolume': 2815.0, 'vwap': 1.66852198, + # 'open': 1.7313, 'close': 1.6505, 'last': 1.6505, 'previousClose': '1.73170000', 'change': -0.0808, + # 'percentage': -4.667, 'average': 1.6909, 'baseVolume': 124656725.0, 'quoteVolume': 207992485.7799, + # 'info': + # {'symbol': 'FTMUSDT', 'priceChange': '-0.08080000', 'priceChangePercent': '-4.667', + # 'weightedAvgPrice': '1.66852198', 'prevClosePrice': '1.73170000', 'lastPrice': '1.65050000', + # 'lastQty': '143.00000000', 'bidPrice': '1.65050000', 'bidQty': '2135.00000000', + # 'askPrice': '1.65080000', 'askQty': '2815.00000000', 'openPrice': '1.73130000', + # 'highPrice': '1.74890000', 'lowPrice': '1.60840000', 'volume': '124656725.00000000', + # 'quoteVolume': '207992485.77990000', 'openTime': '1646407799570', 'closeTime': '1646494199570', + # 'firstId': '137149614', 'lastId': '137450289', 'count': '300676'}} - 0.9817468621938484 + + allow_to_buy = True + max_gain = -100 + sum_gain = 0 + max_time = 0 + + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + if len(self.trades) == 0: + print('search') + self.trades = Trade.get_open_trades() + + if len(self.trades) >= self.config['max_open_trades'] / 2: + for trade in self.trades: + ticker = self.dp.ticker(trade.pair) + last_price = ticker['last'] + gain = (last_price - trade.open_rate) / trade.open_rate + max_gain = max(max_gain, gain) + sum_gain += gain + max_time = max(max_time, datetime.timestamp(trade.open_date)) + print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + now = datetime.now() + diff = (datetime.timestamp(now) - max_time / 3600) + if (max_gain <= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + print("allow_to_buy=false") + allow_to_buy = False + print(pair, allow_to_buy, len(self.trades), + "max gain=", max_gain, + "sum_gain=", sum_gain, + "now=", now, + "max=", max_time, + "diff=", datetime.timestamp(now) - max_time) + + if allow_to_buy: + self.trades = list() + + return allow_to_buy + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + # { + # "method": "MaxDrawdown", + # "lookback_period_candles": self.lookback.value, + # "trade_limit": self.trade_limit.value, + # "stop_duration_candles": self.protection_stop.value, + # "max_allowed_drawdown": self.protection_max_allowed_dd.value, + # "only_per_pair": False + # }, + # { + # "method": "StoplossGuard", + # "lookback_period_candles": 24, + # "trade_limit": 4, + # "stop_duration_candles": self.protection_stoploss_stop.value, + # "only_per_pair": False + # }, + # { + # "method": "EmergencyStop", + # "min_percent": -0.05, + # "candels": 1 + # } + ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # if (0 < current_profit) & ((current_time - trade.open_date_utc).seconds > 3600) \ + # & (last_candle['percent10'] < 0.001): + # return 'small_profit' + # + # if (current_profit > 0.01) \ + # & ((previous_last_candle['sma10'] - last_candle['sma10']) / previous_last_candle['sma10'] > 0.003): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma10_quick' + + if (current_profit >= - self.sell_too_old_percent.value) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value)\ + & ((current_time - trade.open_date_utc).days < self.sell_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.01" + if (current_profit >= - self.sell_too_old_percent.value * 2) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value * 2)\ + & ((current_time - trade.open_date_utc).days < self.sell_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.02" + if (current_profit >= - self.sell_too_old_percent.value * 3) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.03" + + if self.profit_quick_lost and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "quick_lost" + + if self.profit_no_change and (current_profit > self.sell_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + # if self.profit_quick_gain_3: + # if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + # return "quick_gain_3" + # if self.profit_quick_gain: + # if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + # return "quick_gain" + # if (current_profit > 0 & last_candle['bb_width'] < current_profit): + # return "more_bb_width" + + + if (current_profit > self.sell_percent.value) & (last_candle['percent3'] < - self.sell_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_candels.value): + return "quick_gain_param" + + if self.profit_sma5: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma10: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI2.value) & \ + (last_candle['percent'] < - self.sell_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if self.profit_short_loss: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # Assign tf to each pair so they can be downloaded and cached for strategy. + informative_pairs = [(pair, "5m") for pair in pairs] + informative_pairs += [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '1w') for pair in pairs] + + # Optionally Add additional "static" pairs + # informative_pairs = [("BTC/USDT", "1w"), ("BTC/USDT", "1d"), ("BTC/USDT", "5m")] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['ecart_20'] = dataframe['close'].rolling(20).var() / dataframe['close'] + dataframe['ecart_50'] = dataframe['close'].rolling(50).var() / dataframe['close'] + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = ta.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + # dataframe['TR'] = ta.TRANGE(dataframe) + # dataframe['ATR'] = ta.SMA(dataframe['TR'], 21) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + + # dataframe["dist_min_50"] = dataframe['close'] - dataframe['min50'] + # dataframe["dist_min_20"] = dataframe['close'] - dataframe['min20'] + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + + # # INFORMATIVE PAIRS + + # # Get the informative pair + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1w") + informative["percent"] = (informative["close"] - informative["open"]) / informative["open"] + informative["percent5"] = informative["percent"].rolling(5).sum() + informative["percent3"] = informative["percent"].rolling(3).sum() + # logger.info("Strategy informative using %s %s", "BTC/TUSD", "5m") + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1w", ffill=True) + + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="5m") + informative["percent"] = (informative["close"] - informative["open"]) / informative["open"] + informative["percent5"] = informative["percent"].rolling(5).sum() + informative["percent3"] = informative["percent"].rolling(3).sum() + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "5m", ffill=True) + + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["percent"] = (informative["close"] - informative["open"]) / informative["open"] + informative["percent5"] = informative["percent"].rolling(5).sum() + informative["percent3"] = informative["percent"].rolling(3).sum() + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + # logger.info("Strategy informative using %s %s", "BTC/TUSD", "1h") + + # # Get the informative pair + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + # informative[buy_crossed_indicator0] = gene_calculator(informative, buy_crossed_indicator0) + # informative[buy_indicator0] = gene_calculator(informative, buy_indicator0) + # informative["cond1"] = informative[buy_indicator0].div(informative[buy_crossed_indicator0]) + # informative["percent"] = (informative["close"] - informative["open"]) / informative["open"] + # informative["percent5"] = informative["percent"].rolling(5).sum() + # informative["percent3"] = informative["percent"].rolling(3).sum() + # pandas.set_option('display.max_rows', informative.shape[0] + 1) + # pandas.set_option('display.max_columns', 50) + # # print('informative', metadata['pair'], informative.tail(1)) + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='1d') + # informative[buy_crossed_indicator0] = gene_calculator(informative, buy_crossed_indicator0) + # informative[buy_indicator0] = gene_calculator(informative, buy_indicator0) + # informative["cond1"] = informative[buy_indicator0].div(informative[buy_crossed_indicator0]) + # informative["percent"] = (informative["close"] - informative["open"]) / informative["open"] + # informative['max_open'] = ta.MAX(informative["open"], timeperiod=2) + # informative['max_close'] = ta.MIN(informative["close"], timeperiod=2) + # informative["percent5"] = informative["percent"].rolling(5).sum() + # informative["percent3"] = informative["percent"].rolling(3).sum() + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, '1d', ffill=True) + + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='1w') + # informative[buy_crossed_indicator0] = gene_calculator(informative, buy_crossed_indicator0) + # informative[buy_indicator0] = gene_calculator(informative, buy_indicator0) + # informative["cond1"] = informative[buy_indicator0].div(informative[buy_crossed_indicator0]) + # informative["percent"] = (informative["close"] - informative["open"]) / informative["open"] + # informative["percent5"] = informative["percent"].rolling(5).sum() + # informative["percent3"] = informative["percent"].rolling(3).sum() + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, '1w', ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + # pandas.set_option('display.max_columns', 50) + # + # allow_to_buy = True + # max_gain = -100 + # trades = Trade.get_open_trades() + # + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # pairs = self.dp.current_whitelist() + # pairs_len = len(pairs) + # pair_index = pairs.index(metadata['pair']) + # + # # print(pair_index, " ", metadata['pair']) + # + # # ob = self.dp.orderbook(metadata['pair'], 1) + # # dataframe['best_bid'] = ob['bids'][0][0] + # # dataframe['best_ask'] = ob['asks'][0][0] + # # print(ob) + # + # for trade in trades: + # # if (metadata['pair'] == trade.pair): + # ticker = self.dp.ticker(trade.pair) #metadata['pair']) + # last_price = ticker['last'] + # # dataframe['volume24h'] = ticker['quoteVolume'] + # # dataframe['vwap'] = ticker['vwap'] + # # d = dataframe.tail(1) + # # print(dataframe) + # gain = (last_price - trade.open_rate) / trade.open_rate + # + # # print("Found open trade: ", trade, " ", ticker['last'], " ", trade.open_rate, gain) + # max_gain = max(max_gain, gain) + # + # if max_gain > - 0.05: + # allow_to_buy = False + # + # # print(metadata['pair'], max_gain, allow_to_buy, len(trades)) + + # self.protections.global_stop(current_time); + + for decalage in range(self.buy_decalage_deb_0.value, self.buy_decalage0.value): + #if self.buy_0.value: + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value, + self.buy_decalage0.value + ) + conditions.append(condition1) + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_0_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift(decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_1_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_1_bb_lower_5.value) + & (((dataframe['sma10'].shift(1) - dataframe['sma10']) / dataframe['sma10']) < self.buy_1_pente_sma10.value / 100) + & (((dataframe['sma20'].shift(1) - dataframe['sma20']) / dataframe['sma20']) < self.buy_1_pente_sma20.value / 100) + & (dataframe['percent_5m'].shift(1) >= self.buy_1_percent_5m_num.value) + & (dataframe[self.buy_day_week.value] < self.buy_4_day_week_percent.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_2.value, self.buy_decalage2.value): + #if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_real_num0.value / 2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_2_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_2_bb_lower_5.value) + & (((dataframe['sma10'].shift(1) - dataframe['sma10']) / dataframe[ + 'sma10']) < self.buy_2_pente_sma10.value / 100) + & (((dataframe['sma20'].shift(1) - dataframe['sma20']) / dataframe[ + 'sma20']) < self.buy_2_pente_sma20.value / 100) + & (dataframe['percent_5m'].shift(1) >= self.buy_2_percent_5m_num.value) + & (dataframe[self.buy_day_week.value] < self.buy_4_day_week_percent.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_3.value, self.buy_decalage3.value): + #if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_3_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_3_bb_lower_5.value) + & (((dataframe['sma10'].shift(1) - dataframe['sma10']) / dataframe[ + 'sma10']) < self.buy_3_pente_sma10.value / 100) + & (((dataframe['sma20'].shift(1) - dataframe['sma20']) / dataframe[ + 'sma20']) < self.buy_3_pente_sma20.value / 100) + & (dataframe['percent_5m'].shift(1) >= self.buy_3_percent_5m_num.value) + & (dataframe[self.buy_day_week.value] < self.buy_4_day_week_percent.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + # "buy_2_distance": 0.09, + # "buy_2_percent20": 0.05, + decalage = 1 + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) # self.buy_real_num0.value / 2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 19) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['percent20'].shift(decalage) <= 0.05) #self.buy_2_percent20.value) + & (dataframe['distance_min'] <= 0.09) #self.buy_2_distance.value) + & (dataframe[self.buy_day_week.value] >= self.buy_4_day_week_percent.value) + ), ['buy', 'buy_tag']] = (1, 'buy_4_' + str(decalage)) + + # dataframe.loc[ + # ( + # (dataframe['close'] < dataframe['close_1d']) + # & (dataframe['close'] > dataframe['bb_upperband']) + # ), ['buy', 'buy_tag']] = (1, 'buy_4') + + # pair = metadata['pair'] + # allow_to_buy = True + # max_gain = -100 + # sum_gain = 0 + # max_time = 0 + # if len(self.trades) == 0: + # print('search') + # self.trades = Trade.get_open_trades() + # + # # if self.dp: + # # if self.dp.runmode.value in ('live', 'dry_run'): + # if len(self.trades) >= self.config['max_open_trades'] / 2: + # for trade in self.trades: + # ticker = self.dp.ticker(trade.pair) + # last_price = ticker['last'] + # gain = (last_price - trade.open_rate) / trade.open_rate + # max_gain = max(max_gain, gain) + # sum_gain += gain + # max_time = max(max_time, datetime.timestamp(trade.open_date)) + # print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + # datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + # now = datetime.now() + # diff = (datetime.timestamp(now) - max_time / 3600) + # if (max_gain >= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + # print("allow_to_buy=false") + # allow_to_buy = False + # print(pair, allow_to_buy, len(self.trades), + # "max gain=", max_gain, + # "sum_gain=", sum_gain, + # "now=", now, + # "max=", max_time, + # "diff=", datetime.timestamp(now) - max_time) + # + # if allow_to_buy: + # self.trades = list() + + # print(condition1) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_7_5_2.json b/GodStraJD3_7_5_2.json new file mode 100644 index 0000000..87d992a --- /dev/null +++ b/GodStraJD3_7_5_2.json @@ -0,0 +1,51 @@ +{ + "strategy_name": "GodStraJD3_7_5_2", + "params": { + "roi": { + "0": 2 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.02, + "trailing_stop_positive_offset": 0.03, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_1": false, + "buy_1_bb_diff_lower": 0.0007, + "buy_1_decalage": 6, + "buy_1_decalage_deb": 1, + "buy_1_distance": 0.03, + "buy_1_percent20": 0.06, + "buy_1_real_num": 0.1, + "buy_2": false, + "buy_2_bb_diff_lower": 0.0004, + "buy_2_decalage": 7, + "buy_2_decalage_deb": 3, + "buy_2_distance": 0.06, + "buy_2_percent20": 0.01, + "buy_2_real_num": 1.7, + "buy_3": true, + "buy_3_bb_diff_lower": 0.0, + "buy_3_decalage": 7, + "buy_3_decalage_deb": 2, + "buy_3_distance": 0.09, + "buy_3_percent20": 0.01, + "buy_3_real_num": 0.99, + "buy_min_horizon": 191 + }, + "sell": {}, + "protection": { + "lookback": 133, + "protection_max_allowed_dd": 0.71, + "protection_stop": 61, + "protection_stoploss_stop": 50, + "trade_limit": 9 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-12 21:14:24.255458+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_7_5_2.py b/GodStraJD3_7_5_2.py new file mode 100644 index 0000000..9d7e6b8 --- /dev/null +++ b/GodStraJD3_7_5_2.py @@ -0,0 +1,781 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import calendar +from freqtrade.loggers import setup_logging + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + + +class GodStraJD3_7_5_2(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min20': {'color': '#87e470'}, + 'min50': {'color': '#ac3e2a'}, + "min1.1": {'color': 'yellow'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'} + }, + # "Ind0": { + # buy_crossed_indicator0: {'color': 'green'}, + # buy_indicator0: {'color': 'red'} + # }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "Ecart": { + 'normal_var_20': {'color': 'red'}, + 'normal_var_50': {'color': 'yellow'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'}, + "bb_diff_lower": {'color': 'white'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + profit_no_change = False + profit_old_sma10 = False + profit_over_rsi = True + profit_quick_gain = True + profit_quick_gain_3 = True + profit_quick_lost = False + profit_short_loss = False + profit_sma10 = True + profit_sma20 = True + profit_very_old_sma10 = False + + trades = list() + # profit_no_change = BooleanParameter(default=True, space="buy") + # profit_quick_lost = BooleanParameter(default=True, space="buy") + # profit_sma10 = BooleanParameter(default=True, space="buy") + # profit_sma20 = BooleanParameter(default=True, space="buy") + # profit_quick_gain = BooleanParameter(default=True, space="buy") + # profit_quick_gain_3 = BooleanParameter(default=True, space="buy") + # profit_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_very_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_over_rsi = BooleanParameter(default=True, space="buy") + # profit_short_loss = BooleanParameter(default=True, space="buy") + + # buy_signal_bb_width = DecimalParameter(0.06, 0.15, decimals=2, default=0.065, space='buy') + + buy_1_real_num = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_2_real_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + buy_3_real_num = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + + buy_min_horizon = IntParameter(50, 200, default=72, space='buy') + + buy_1 = BooleanParameter(default=True, space="buy") + buy_2 = BooleanParameter(default=True, space="buy") + buy_3 = BooleanParameter(default=True, space="buy") + + buy_1_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_2_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_3_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + + buy_1_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_2_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_3_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + + buy_1_bb_diff_lower = DecimalParameter(0, 0.001, decimals=4, default=0.0009, space='buy') + buy_2_bb_diff_lower = DecimalParameter(0, 0.001, decimals=4, default=0.0009, space='buy') + buy_3_bb_diff_lower = DecimalParameter(0, 0.001, decimals=4, default=0.0009, space='buy') + + buy_1_decalage_deb = IntParameter(1, 3, default=5, space='buy') + buy_2_decalage_deb = IntParameter(1, 3, default=5, space='buy') + buy_3_decalage_deb = IntParameter(1, 3, default=5, space='buy') + + buy_1_decalage = IntParameter(buy_1_decalage_deb.value + 1, 8, default=5, space='buy') + buy_2_decalage = IntParameter(buy_2_decalage_deb.value + 1, 8, default=5, space='buy') + buy_3_decalage = IntParameter(buy_3_decalage_deb.value + 1, 8, default=5, space='buy') + + protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + protection_stop = IntParameter(1, 100, default=48, space='protection') + protection_stoploss_stop = IntParameter(1, 100, default=48, space='protection') + lookback = IntParameter(1, 200, default=48, space='protection') + trade_limit = IntParameter(1, 10, default=2, space='protection') + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + + # {'symbol': 'FTM/USDT', 'timestamp': 1646494199570, 'datetime': '2022-03-05T15:29:59.570Z', 'high': 1.7489, + # 'low': 1.6084, 'bid': 1.6505, 'bidVolume': 2135.0, 'ask': 1.6508, 'askVolume': 2815.0, 'vwap': 1.66852198, + # 'open': 1.7313, 'close': 1.6505, 'last': 1.6505, 'previousClose': '1.73170000', 'change': -0.0808, + # 'percentage': -4.667, 'average': 1.6909, 'baseVolume': 124656725.0, 'quoteVolume': 207992485.7799, + # 'info': + # {'symbol': 'FTMUSDT', 'priceChange': '-0.08080000', 'priceChangePercent': '-4.667', + # 'weightedAvgPrice': '1.66852198', 'prevClosePrice': '1.73170000', 'lastPrice': '1.65050000', + # 'lastQty': '143.00000000', 'bidPrice': '1.65050000', 'bidQty': '2135.00000000', + # 'askPrice': '1.65080000', 'askQty': '2815.00000000', 'openPrice': '1.73130000', + # 'highPrice': '1.74890000', 'lowPrice': '1.60840000', 'volume': '124656725.00000000', + # 'quoteVolume': '207992485.77990000', 'openTime': '1646407799570', 'closeTime': '1646494199570', + # 'firstId': '137149614', 'lastId': '137450289', 'count': '300676'}} - 0.9817468621938484 + + allow_to_buy = True + max_gain = -100 + sum_gain = 0 + max_time = 0 + + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + if len(self.trades) == 0: + print('search') + self.trades = Trade.get_open_trades() + + if len(self.trades) >= self.config['max_open_trades'] / 2: + for trade in self.trades: + ticker = self.dp.ticker(trade.pair) + last_price = ticker['last'] + gain = (last_price - trade.open_rate) / trade.open_rate + max_gain = max(max_gain, gain) + sum_gain += gain + max_time = max(max_time, datetime.timestamp(trade.open_date)) + print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + now = datetime.now() + diff = (datetime.timestamp(now) - max_time / 3600) + if (max_gain <= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + print("allow_to_buy=false") + allow_to_buy = False + print(pair, allow_to_buy, len(self.trades), + "max gain=", max_gain, + "sum_gain=", sum_gain, + "now=", now, + "max=", max_time, + "diff=", datetime.timestamp(now) - max_time) + + if allow_to_buy: + self.trades = list() + + return allow_to_buy + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + { + "method": "MaxDrawdown", + "lookback_period_candles": self.lookback.value, + "trade_limit": self.trade_limit.value, + "stop_duration_candles": self.protection_stop.value, + "max_allowed_drawdown": self.protection_max_allowed_dd.value, + "only_per_pair": False + }, + { + "method": "StoplossGuard", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": self.protection_stoploss_stop.value, + "only_per_pair": False + } + ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # if (0 < current_profit) & ((current_time - trade.open_date_utc).seconds > 3600) \ + # & (last_candle['percent10'] < 0.001): + # return 'small_profit' + # + # if (current_profit > 0.01) \ + # & ((previous_last_candle['sma10'] - last_candle['sma10']) / previous_last_candle['sma10'] > 0.003): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma10_quick' + + days = (current_time - trade.open_date_utc).days + # if (current_profit >= -0.01 * days) & (days >= 5) \ + # & (previous_last_candle['sma20'] > last_candle['sma20']): + # return "too_old_" + days + if (current_profit >= -0.01) & (days >= 5) & (days < 10)\ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.01" + if (current_profit >= -0.02) & (days >= 10) & (days < 15) \ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.02" + if (current_profit >= -0.03) & (days >= 15) \ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.03" + + if self.profit_quick_lost: + if (current_profit >= 0) & (last_candle['percent3'] < -0.015): + return "quick_lost" + + if self.profit_no_change: + if (current_profit > 0.005) & (last_candle['percent10'] < 0.001) & (last_candle['percent5'] < 0) & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if self.profit_quick_gain_3: + if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if self.profit_quick_gain: + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0): #& ((current_time - trade.open_date_utc).seconds <= 3600) + return "quick_gain" + + if self.profit_sma10: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10'] * 1.005) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10: + # if (current_profit > 0) \ + # & (days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10: + # if (current_profit > -0.01) \ + # & (days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi: + + # if (current_profit > 0) \ + # & (previous_last_candle['rsi'] > 88) \ + # & ( + # (last_candle['percent'] < - current_profit / 3) | (last_candle['percent3'] < - current_profit / 3)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi' + + if (current_profit > 0) & (last_candle['rsi'] > 88): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > 82) & (last_candle['percent'] < -0.02): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if self.profit_short_loss: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['ecart_20'] = dataframe['close'].rolling(20).var() / dataframe['close'] + dataframe['ecart_50'] = dataframe['close'].rolling(50).var() / dataframe['close'] + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = ta.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['volume_max'] = dataframe['volume10'] * dataframe['close'] / 1000 + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe["bb_diff_lower"] = (dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1)) / dataframe["bb_lowerband"] + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) - 100 + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + + # dataframe["dist_min_50"] = dataframe['close'] - dataframe['min50'] + # dataframe["dist_min_20"] = dataframe['close'] - dataframe['min20'] + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + # pandas.set_option('display.max_columns', 50) + # + # allow_to_buy = True + # max_gain = -100 + # trades = Trade.get_open_trades() + # + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # pairs = self.dp.current_whitelist() + # pairs_len = len(pairs) + # pair_index = pairs.index(metadata['pair']) + # + # # print(pair_index, " ", metadata['pair']) + # + # # ob = self.dp.orderbook(metadata['pair'], 1) + # # dataframe['best_bid'] = ob['bids'][0][0] + # # dataframe['best_ask'] = ob['asks'][0][0] + # # print(ob) + # + # for trade in trades: + # # if (metadata['pair'] == trade.pair): + # ticker = self.dp.ticker(trade.pair) #metadata['pair']) + # last_price = ticker['last'] + # # dataframe['volume24h'] = ticker['quoteVolume'] + # # dataframe['vwap'] = ticker['vwap'] + # # d = dataframe.tail(1) + # # print(dataframe) + # gain = (last_price - trade.open_rate) / trade.open_rate + # + # # print("Found open trade: ", trade, " ", ticker['last'], " ", trade.open_rate, gain) + # max_gain = max(max_gain, gain) + # + # if max_gain > - 0.05: + # allow_to_buy = False + # + # # print(metadata['pair'], max_gain, allow_to_buy, len(trades)) + + for decalage in range(self.buy_1_decalage_deb.value, self.buy_1_decalage.value): + if self.buy_1.value: + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_1_real_num.value, + self.buy_1_decalage.value + ) + conditions.append(condition1) + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_1_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['bb_diff_lower'] >= - self.buy_1_bb_diff_lower.value) + # & (dataframe['distance_min'] <= self.buy_1_distance.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_2_decalage_deb.value, self.buy_2_decalage.value): + if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= self.buy_2_real_num.value) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + for decalage in range(self.buy_3_decalage_deb.value, self.buy_3_decalage.value): + if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= self.buy_3_real_num.value) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + # & (dataframe['bb_width'] >= 0.07) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + & (dataframe['bb_diff_lower'] >= - self.buy_3_bb_diff_lower.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + # pair = metadata['pair'] + # allow_to_buy = True + # max_gain = -100 + # sum_gain = 0 + # max_time = 0 + # if len(self.trades) == 0: + # print('search') + # self.trades = Trade.get_open_trades() + # + # # if self.dp: + # # if self.dp.runmode.value in ('live', 'dry_run'): + # if len(self.trades) >= self.config['max_open_trades'] / 2: + # for trade in self.trades: + # ticker = self.dp.ticker(trade.pair) + # last_price = ticker['last'] + # gain = (last_price - trade.open_rate) / trade.open_rate + # max_gain = max(max_gain, gain) + # sum_gain += gain + # max_time = max(max_time, datetime.timestamp(trade.open_date)) + # print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + # datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + # now = datetime.now() + # diff = (datetime.timestamp(now) - max_time / 3600) + # if (max_gain >= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + # print("allow_to_buy=false") + # allow_to_buy = False + # print(pair, allow_to_buy, len(self.trades), + # "max gain=", max_gain, + # "sum_gain=", sum_gain, + # "now=", now, + # "max=", max_time, + # "diff=", datetime.timestamp(now) - max_time) + # + # if allow_to_buy: + # self.trades = list() + + # print(condition1) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_7_5_3.json b/GodStraJD3_7_5_3.json new file mode 100644 index 0000000..37c68ce --- /dev/null +++ b/GodStraJD3_7_5_3.json @@ -0,0 +1,51 @@ +{ + "strategy_name": "GodStraJD3_7_5_3", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.026, + "trailing_stop_positive_offset": 0.077, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_0": false, + "buy_0_percent20": -0.07, + "buy_1_decalage": 6, + "buy_1_decalage_deb": 3, + "buy_1_distance": 0.02, + "buy_1_normal_var": 5.0, + "buy_1_real_num": 0.61, + "buy_2": true, + "buy_2_decalage": 6, + "buy_2_decalage_deb": 1, + "buy_2_distance": 0.1, + "buy_2_normal_var": 0.0, + "buy_2_percent20": 0.02, + "buy_2_real_num": 0.35, + "buy_3": true, + "buy_3_decalage": 6, + "buy_3_decalage_deb": 2, + "buy_3_distance": 0.01, + "buy_3_normal_var": 1.8, + "buy_3_percent20": -0.03, + "buy_3_real_num": 1.18, + "buy_min_horizon": 165 + }, + "sell": {}, + "protection": { + "lookback": 133, + "protection_max_allowed_dd": 0.71, + "protection_stop": 61, + "protection_stoploss_stop": 50, + "trade_limit": 9 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-13 17:21:19.281512+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_7_5_3.json1 b/GodStraJD3_7_5_3.json1 new file mode 100644 index 0000000..a2b3e0b --- /dev/null +++ b/GodStraJD3_7_5_3.json1 @@ -0,0 +1,51 @@ +{ + "strategy_name": "GodStraJD3_7_5_3", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.026, + "trailing_stop_positive_offset": 0.077, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_0": true, + "buy_0_percent20": 0.09, + "buy_1_decalage": 6, + "buy_1_decalage_deb": 1, + "buy_1_distance": 0.06, + "buy_1_normal_var": 4.7, + "buy_1_real_num": 0.96, + "buy_2": true, + "buy_2_decalage": 6, + "buy_2_decalage_deb": 2, + "buy_2_distance": 0.03, + "buy_2_normal_var": 1.6, + "buy_2_percent20": 0.09, + "buy_2_real_num": 0.08, + "buy_3": false, + "buy_3_decalage": 6, + "buy_3_decalage_deb": 2, + "buy_3_distance": -0.08, + "buy_3_normal_var": 1.9, + "buy_3_percent20": -0.06, + "buy_3_real_num": 0.81, + "buy_min_horizon": 186 + }, + "sell": {}, + "protection": { + "lookback": 133, + "protection_max_allowed_dd": 0.71, + "protection_stop": 61, + "protection_stoploss_stop": 50, + "trade_limit": 9 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-12 23:10:38.145463+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_7_5_3.py b/GodStraJD3_7_5_3.py new file mode 100644 index 0000000..9b32d5f --- /dev/null +++ b/GodStraJD3_7_5_3.py @@ -0,0 +1,808 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import calendar +from freqtrade.loggers import setup_logging + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + + +class GodStraJD3_7_5_3(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min20': {'color': '#87e470'}, + 'min50': {'color': '#ac3e2a'}, + "min1.1": {'color': 'yellow'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'} + }, + # "Ind0": { + # buy_crossed_indicator0: {'color': 'green'}, + # buy_indicator0: {'color': 'red'} + # }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "Ecart": { + 'normal_var_20': {'color': 'red'}, + 'normal_var_50': {'color': 'yellow'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'}, + "distance_min": {'color': 'white'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + profit_no_change = False + profit_old_sma10 = False + profit_over_rsi = True + profit_quick_gain = True + profit_quick_gain_3 = True + profit_quick_lost = False + profit_short_loss = False + profit_sma10 = True + profit_sma20 = True + profit_very_old_sma10 = False + + trades = list() + # profit_no_change = BooleanParameter(default=True, space="buy") + # profit_quick_lost = BooleanParameter(default=True, space="buy") + # profit_sma10 = BooleanParameter(default=True, space="buy") + # profit_sma20 = BooleanParameter(default=True, space="buy") + # profit_quick_gain = BooleanParameter(default=True, space="buy") + # profit_quick_gain_3 = BooleanParameter(default=True, space="buy") + # profit_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_very_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_over_rsi = BooleanParameter(default=True, space="buy") + # profit_short_loss = BooleanParameter(default=True, space="buy") + + # buy_signal_bb_width = DecimalParameter(0.06, 0.15, decimals=2, default=0.065, space='buy') + + buy_1_real_num = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_2_real_num = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_3_real_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + + buy_min_horizon = IntParameter(50, 200, default=72, space='buy') + + buy_0 = BooleanParameter(default=True, space="buy") + buy_2 = BooleanParameter(default=True, space="buy") + buy_3 = BooleanParameter(default=True, space="buy") + + buy_0_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_2_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_3_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + + buy_1_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_2_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_3_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + + buy_1_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + buy_2_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + buy_3_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + + buy_1_decalage_deb = IntParameter(1, 3, default=5, space='buy') + buy_2_decalage_deb = IntParameter(1, 3, default=5, space='buy') + buy_3_decalage_deb = IntParameter(1, 3, default=5, space='buy') + + buy_1_decalage = IntParameter(buy_1_decalage_deb.value + 1, 8, default=5, space='buy') + buy_2_decalage = IntParameter(buy_2_decalage_deb.value + 1, 8, default=5, space='buy') + buy_3_decalage = IntParameter(buy_3_decalage_deb.value + 1, 8, default=5, space='buy') + + protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + protection_stop = IntParameter(1, 100, default=48, space='protection') + protection_stoploss_stop = IntParameter(1, 100, default=48, space='protection') + lookback = IntParameter(1, 200, default=48, space='protection') + trade_limit = IntParameter(1, 10, default=2, space='protection') + + # def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + # proposed_stake: float, min_stake: float, max_stake: float, + # **kwargs) -> float: + # + # dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + # current_candle = dataframe.iloc[-1].squeeze() + # + # # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + # # if current_candle['bb_width'] > 0.05: + # # print("use more stake", pair, " ", proposed_stake * 2) + # # return min(max_stake, proposed_stake * 2) + # # + # # if current_candle['bb_width'] > 0.035: + # # print("use more stake", pair, " ", proposed_stake * 1.5) + # # return min(max_stake, proposed_stake * 1.5) + # + # # if current_candle['bb_width'] < 0.020: + # # print("use less stake", pair, " ", proposed_stake / 2) + # # return min(max_stake, proposed_stake / 2) + # # if self.config['stake_amount'] == 'unlimited': + # # # Use entire available wallet during favorable conditions when in compounding mode. + # # return max_stake + # # else: + # # # Compound profits during favorable conditions instead of using a static stake. + # # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + # + # + # # Use default stake amount. + # return proposed_stake + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + + # {'symbol': 'FTM/USDT', 'timestamp': 1646494199570, 'datetime': '2022-03-05T15:29:59.570Z', 'high': 1.7489, + # 'low': 1.6084, 'bid': 1.6505, 'bidVolume': 2135.0, 'ask': 1.6508, 'askVolume': 2815.0, 'vwap': 1.66852198, + # 'open': 1.7313, 'close': 1.6505, 'last': 1.6505, 'previousClose': '1.73170000', 'change': -0.0808, + # 'percentage': -4.667, 'average': 1.6909, 'baseVolume': 124656725.0, 'quoteVolume': 207992485.7799, + # 'info': + # {'symbol': 'FTMUSDT', 'priceChange': '-0.08080000', 'priceChangePercent': '-4.667', + # 'weightedAvgPrice': '1.66852198', 'prevClosePrice': '1.73170000', 'lastPrice': '1.65050000', + # 'lastQty': '143.00000000', 'bidPrice': '1.65050000', 'bidQty': '2135.00000000', + # 'askPrice': '1.65080000', 'askQty': '2815.00000000', 'openPrice': '1.73130000', + # 'highPrice': '1.74890000', 'lowPrice': '1.60840000', 'volume': '124656725.00000000', + # 'quoteVolume': '207992485.77990000', 'openTime': '1646407799570', 'closeTime': '1646494199570', + # 'firstId': '137149614', 'lastId': '137450289', 'count': '300676'}} - 0.9817468621938484 + + allow_to_buy = True + max_gain = -100 + sum_gain = 0 + max_time = 0 + + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + if len(self.trades) == 0: + print('search') + self.trades = Trade.get_open_trades() + + if len(self.trades) >= self.config['max_open_trades'] / 2: + for trade in self.trades: + ticker = self.dp.ticker(trade.pair) + last_price = ticker['last'] + gain = (last_price - trade.open_rate) / trade.open_rate + max_gain = max(max_gain, gain) + sum_gain += gain + max_time = max(max_time, datetime.timestamp(trade.open_date)) + print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + now = datetime.now() + diff = (datetime.timestamp(now) - max_time / 3600) + if (max_gain <= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + print("allow_to_buy=false") + allow_to_buy = False + print(pair, allow_to_buy, len(self.trades), + "max gain=", max_gain, + "sum_gain=", sum_gain, + "now=", now, + "max=", max_time, + "diff=", datetime.timestamp(now) - max_time) + + if allow_to_buy: + self.trades = list() + + return allow_to_buy + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + { + "method": "MaxDrawdown", + "lookback_period_candles": self.lookback.value, + "trade_limit": self.trade_limit.value, + "stop_duration_candles": self.protection_stop.value, + "max_allowed_drawdown": self.protection_max_allowed_dd.value, + "only_per_pair": False + }, + { + "method": "StoplossGuard", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": self.protection_stoploss_stop.value, + "only_per_pair": False + } + ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # if (0 < current_profit) & ((current_time - trade.open_date_utc).seconds > 3600) \ + # & (last_candle['percent10'] < 0.001): + # return 'small_profit' + # + # if (current_profit > 0.01) \ + # & ((previous_last_candle['sma10'] - last_candle['sma10']) / previous_last_candle['sma10'] > 0.003): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma10_quick' + + if (current_profit >= -0.01) & ((current_time - trade.open_date_utc).days >= 5)\ + & ((current_time - trade.open_date_utc).days < 10)\ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.01" + if (current_profit >= -0.02) & ((current_time - trade.open_date_utc).days >= 10)\ + & ((current_time - trade.open_date_utc).days < 15) \ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.02" + if (current_profit >= -0.03) & ((current_time - trade.open_date_utc).days >= 15) \ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.03" + + if self.profit_quick_lost: + if (current_profit >= 0) & (last_candle['percent3'] < -0.015): + return "quick_lost" + + if self.profit_no_change: + if (current_profit > 0.005) & (last_candle['percent10'] < 0.001) & (last_candle['percent5'] < 0) & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + if (current_profit > 0.005) & (last_candle['normal_var_20'] < 0.008) & (last_candle['percent3'] < 0): + return "no_change_20" + + if (current_profit > 0.01) & (last_candle['normal_var_10'] < 0.008) & (last_candle['percent3'] < 0): + return "no_change_10" + if (current_profit > 0.01) & (last_candle['normal_var_5'] < 0.005) & (last_candle['percent3'] < 0): + return "no_change_5" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if self.profit_quick_gain_3: + if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if self.profit_quick_gain: + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain" + + if self.profit_sma10: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi: + if (current_profit > 0) & (last_candle['rsi'] > 88): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > 82) & (last_candle['percent'] < -0.02): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if self.profit_short_loss: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['ecart_20'] = dataframe['close'].rolling(20).var() / dataframe['close'] + dataframe['ecart_50'] = dataframe['close'].rolling(50).var() / dataframe['close'] + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = ta.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_5'] = dataframe['normal'].rolling(5).var() + dataframe['normal_var_10'] = dataframe['normal'].rolling(10).var() + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + + # dataframe["dist_min_50"] = dataframe['close'] - dataframe['min50'] + # dataframe["dist_min_20"] = dataframe['close'] - dataframe['min20'] + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + # pandas.set_option('display.max_columns', 50) + # + # allow_to_buy = True + # max_gain = -100 + # trades = Trade.get_open_trades() + # + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # pairs = self.dp.current_whitelist() + # pairs_len = len(pairs) + # pair_index = pairs.index(metadata['pair']) + # + # # print(pair_index, " ", metadata['pair']) + # + # # ob = self.dp.orderbook(metadata['pair'], 1) + # # dataframe['best_bid'] = ob['bids'][0][0] + # # dataframe['best_ask'] = ob['asks'][0][0] + # # print(ob) + # + # for trade in trades: + # # if (metadata['pair'] == trade.pair): + # ticker = self.dp.ticker(trade.pair) #metadata['pair']) + # last_price = ticker['last'] + # # dataframe['volume24h'] = ticker['quoteVolume'] + # # dataframe['vwap'] = ticker['vwap'] + # # d = dataframe.tail(1) + # # print(dataframe) + # gain = (last_price - trade.open_rate) / trade.open_rate + # + # # print("Found open trade: ", trade, " ", ticker['last'], " ", trade.open_rate, gain) + # max_gain = max(max_gain, gain) + # + # if max_gain > - 0.05: + # allow_to_buy = False + # + # # print(metadata['pair'], max_gain, allow_to_buy, len(trades)) + + for decalage in range(self.buy_1_decalage_deb.value, self.buy_1_decalage.value): + if self.buy_0.value: + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_1_real_num.value, + self.buy_1_decalage.value + ) + conditions.append(condition1) + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_1_distance.value) + & (dataframe['normal_var_20'] >= self.buy_1_normal_var.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_2_decalage_deb.value, self.buy_2_decalage.value): + if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_1_real_num.value / 2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + & (dataframe['normal_var_20'] >= self.buy_2_normal_var.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + for decalage in range(self.buy_3_decalage_deb.value, self.buy_3_decalage.value): + if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + & (dataframe['normal_var_20'] >= self.buy_3_normal_var.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + # pair = metadata['pair'] + # allow_to_buy = True + # max_gain = -100 + # sum_gain = 0 + # max_time = 0 + # if len(self.trades) == 0: + # print('search') + # self.trades = Trade.get_open_trades() + # + # # if self.dp: + # # if self.dp.runmode.value in ('live', 'dry_run'): + # if len(self.trades) >= self.config['max_open_trades'] / 2: + # for trade in self.trades: + # ticker = self.dp.ticker(trade.pair) + # last_price = ticker['last'] + # gain = (last_price - trade.open_rate) / trade.open_rate + # max_gain = max(max_gain, gain) + # sum_gain += gain + # max_time = max(max_time, datetime.timestamp(trade.open_date)) + # print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + # datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + # now = datetime.now() + # diff = (datetime.timestamp(now) - max_time / 3600) + # if (max_gain >= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + # print("allow_to_buy=false") + # allow_to_buy = False + # print(pair, allow_to_buy, len(self.trades), + # "max gain=", max_gain, + # "sum_gain=", sum_gain, + # "now=", now, + # "max=", max_time, + # "diff=", datetime.timestamp(now) - max_time) + # + # if allow_to_buy: + # self.trades = list() + + # print(condition1) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_7_5_4.json b/GodStraJD3_7_5_4.json new file mode 100644 index 0000000..0a723b2 --- /dev/null +++ b/GodStraJD3_7_5_4.json @@ -0,0 +1,57 @@ +{ + "strategy_name": "GodStraJD3_7_5_4", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.026, + "trailing_stop_positive_offset": 0.077, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_0": false, + "buy_0_percent20": 0.02, + "buy_1_decalage": 8, + "buy_1_decalage_deb": 3, + "buy_1_distance": 0.07, + "buy_1_min": 1.029, + "buy_1_normal_var": 3.5, + "buy_1_real_num": 0.47, + "buy_1_volume": 71, + "buy_2": true, + "buy_2_decalage": 8, + "buy_2_decalage_deb": 2, + "buy_2_distance": 0.06, + "buy_2_min": 1.029, + "buy_2_normal_var": 0.3, + "buy_2_percent20": 0.06, + "buy_2_real_num": 0.38, + "buy_2_volume": 13, + "buy_3": true, + "buy_3_decalage": 7, + "buy_3_decalage_deb": 2, + "buy_3_distance": 0.03, + "buy_3_min": 1.008, + "buy_3_normal_var": 3.2, + "buy_3_percent20": 0.09, + "buy_3_real_num": 0.73, + "buy_3_volume": 12, + "buy_min_horizon": 141 + }, + "sell": {}, + "protection": { + "lookback": 133, + "protection_max_allowed_dd": 0.71, + "protection_stop": 61, + "protection_stoploss_stop": 50, + "trade_limit": 9 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-17 23:12:39.259525+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_7_5_4.py b/GodStraJD3_7_5_4.py new file mode 100644 index 0000000..6366dec --- /dev/null +++ b/GodStraJD3_7_5_4.py @@ -0,0 +1,827 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import calendar +from freqtrade.loggers import setup_logging +from technical.indicators import cmf + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + + +class GodStraJD3_7_5_4(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min20': {'color': '#87e470'}, + 'min50': {'color': '#ac3e2a'}, + "min1.1": {'color': 'yellow'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'} + }, + # "Ind0": { + # buy_crossed_indicator0: {'color': 'green'}, + # buy_indicator0: {'color': 'red'} + # }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "Ecart": { + 'normal_var_20': {'color': 'red'}, + 'normal_var_50': {'color': 'yellow'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'}, + "distance_min": {'color': 'white'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + profit_no_change = False + profit_old_sma10 = False + profit_over_rsi = True + profit_quick_gain = True + profit_quick_gain_3 = True + profit_quick_lost = False + profit_short_loss = False + profit_sma10 = True + profit_sma20 = True + profit_very_old_sma10 = False + + trades = list() + # profit_no_change = BooleanParameter(default=True, space="buy") + # profit_quick_lost = BooleanParameter(default=True, space="buy") + # profit_sma10 = BooleanParameter(default=True, space="buy") + # profit_sma20 = BooleanParameter(default=True, space="buy") + # profit_quick_gain = BooleanParameter(default=True, space="buy") + # profit_quick_gain_3 = BooleanParameter(default=True, space="buy") + # profit_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_very_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_over_rsi = BooleanParameter(default=True, space="buy") + # profit_short_loss = BooleanParameter(default=True, space="buy") + + # buy_signal_bb_width = DecimalParameter(0.06, 0.15, decimals=2, default=0.065, space='buy') + + buy_1_real_num = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_2_real_num = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_3_real_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + + buy_min_horizon = IntParameter(50, 200, default=72, space='buy') + + buy_0 = BooleanParameter(default=True, space="buy") + buy_2 = BooleanParameter(default=True, space="buy") + buy_3 = BooleanParameter(default=True, space="buy") + + buy_0_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_2_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_3_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + + buy_1_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_2_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_3_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + + buy_1_min = DecimalParameter(1, 1.03, decimals=3, default=1.02, space='buy') + buy_2_min = DecimalParameter(1, 1.03, decimals=3, default=1.02, space='buy') + buy_3_min = DecimalParameter(1, 1.03, decimals=3, default=1.02, space='buy') + + buy_1_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + buy_2_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + buy_3_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + + buy_1_decalage_deb = IntParameter(1, 3, default=5, space='buy') + buy_2_decalage_deb = IntParameter(1, 3, default=5, space='buy') + buy_3_decalage_deb = IntParameter(1, 3, default=5, space='buy') + + buy_1_decalage = IntParameter(buy_1_decalage_deb.value + 1, 8, default=5, space='buy') + buy_2_decalage = IntParameter(buy_2_decalage_deb.value + 1, 8, default=5, space='buy') + buy_3_decalage = IntParameter(buy_3_decalage_deb.value + 1, 8, default=5, space='buy') + + buy_1_volume = IntParameter(0, 100, default=10, space='buy') + buy_2_volume = IntParameter(0, 100, default=10, space='buy') + buy_3_volume = IntParameter(0, 100, default=10, space='buy') + + protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + protection_stop = IntParameter(1, 100, default=48, space='protection') + protection_stoploss_stop = IntParameter(1, 100, default=48, space='protection') + lookback = IntParameter(1, 200, default=48, space='protection') + trade_limit = IntParameter(1, 10, default=2, space='protection') + + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + + # for decalage in range(self.buy_3_decalage_deb.value, self.buy_3_decalage.value): + # decalage_candle = dataframe.iloc[- decalage].squeeze() + # # if (decalage_candle['normal_var_20'] >= 0.6): + # amount = 10 * (- decalage_candle['percent20'] * 100) + # print("use more stake", pair, " ", amount) + # return min(max_stake, amount) + + # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + # if current_candle['bb_width'] > 0.05: + # print("use more stake", pair, " ", proposed_stake * 2) + # return min(max_stake, proposed_stake * 2) + # + # if current_candle['bb_width'] > 0.035: + # print("use more stake", pair, " ", proposed_stake * 1.5) + # return min(max_stake, proposed_stake * 1.5) + + # if current_candle['bb_width'] < 0.020: + # print("use less stake", pair, " ", proposed_stake / 2) + # return min(max_stake, proposed_stake / 2) + # if self.config['stake_amount'] == 'unlimited': + # # Use entire available wallet during favorable conditions when in compounding mode. + # return max_stake + # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + + + # Use default stake amount. + return proposed_stake + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + + # {'symbol': 'FTM/USDT', 'timestamp': 1646494199570, 'datetime': '2022-03-05T15:29:59.570Z', 'high': 1.7489, + # 'low': 1.6084, 'bid': 1.6505, 'bidVolume': 2135.0, 'ask': 1.6508, 'askVolume': 2815.0, 'vwap': 1.66852198, + # 'open': 1.7313, 'close': 1.6505, 'last': 1.6505, 'previousClose': '1.73170000', 'change': -0.0808, + # 'percentage': -4.667, 'average': 1.6909, 'baseVolume': 124656725.0, 'quoteVolume': 207992485.7799, + # 'info': + # {'symbol': 'FTMUSDT', 'priceChange': '-0.08080000', 'priceChangePercent': '-4.667', + # 'weightedAvgPrice': '1.66852198', 'prevClosePrice': '1.73170000', 'lastPrice': '1.65050000', + # 'lastQty': '143.00000000', 'bidPrice': '1.65050000', 'bidQty': '2135.00000000', + # 'askPrice': '1.65080000', 'askQty': '2815.00000000', 'openPrice': '1.73130000', + # 'highPrice': '1.74890000', 'lowPrice': '1.60840000', 'volume': '124656725.00000000', + # 'quoteVolume': '207992485.77990000', 'openTime': '1646407799570', 'closeTime': '1646494199570', + # 'firstId': '137149614', 'lastId': '137450289', 'count': '300676'}} - 0.9817468621938484 + + allow_to_buy = True + max_gain = -100 + sum_gain = 0 + max_time = 0 + + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + if len(self.trades) == 0: + print('search') + self.trades = Trade.get_open_trades() + + if len(self.trades) >= self.config['max_open_trades'] / 2: + for trade in self.trades: + ticker = self.dp.ticker(trade.pair) + last_price = ticker['last'] + gain = (last_price - trade.open_rate) / trade.open_rate + max_gain = max(max_gain, gain) + sum_gain += gain + max_time = max(max_time, datetime.timestamp(trade.open_date)) + print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + now = datetime.now() + diff = (datetime.timestamp(now) - max_time / 3600) + if (max_gain <= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + print("allow_to_buy=false") + allow_to_buy = False + print(pair, allow_to_buy, len(self.trades), + "max gain=", max_gain, + "sum_gain=", sum_gain, + "now=", now, + "max=", max_time, + "diff=", datetime.timestamp(now) - max_time) + + if allow_to_buy: + self.trades = list() + + return allow_to_buy + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + { + "method": "MaxDrawdown", + "lookback_period_candles": self.lookback.value, + "trade_limit": self.trade_limit.value, + "stop_duration_candles": self.protection_stop.value, + "max_allowed_drawdown": self.protection_max_allowed_dd.value, + "only_per_pair": False + }, + { + "method": "StoplossGuard", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": self.protection_stoploss_stop.value, + "only_per_pair": False + } + ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # if (0 < current_profit) & ((current_time - trade.open_date_utc).seconds > 3600) \ + # & (last_candle['percent10'] < 0.001): + # return 'small_profit' + # + # if (current_profit > 0.01) \ + # & ((previous_last_candle['sma10'] - last_candle['sma10']) / previous_last_candle['sma10'] > 0.003): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma10_quick' + + if (current_profit >= -0.01) & ((current_time - trade.open_date_utc).days >= 5)\ + & ((current_time - trade.open_date_utc).days < 10)\ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.01" + if (current_profit >= -0.02) & ((current_time - trade.open_date_utc).days >= 10)\ + & ((current_time - trade.open_date_utc).days < 15) \ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.02" + if (current_profit >= -0.03) & ((current_time - trade.open_date_utc).days >= 15) \ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.03" + + if self.profit_quick_lost: + if (current_profit >= 0) & (last_candle['percent3'] < -0.015): + return "quick_lost" + + if self.profit_no_change: + if (current_profit > 0.005) & (last_candle['percent10'] < 0.001) & (last_candle['percent5'] < 0) & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + if (current_profit > 0.005) & (last_candle['normal_var_20'] < 0.008): + return "no_change_20" + + if (current_profit > 0.01) & (last_candle['normal_var_10'] < 0.008): + return "no_change_10" + if (current_profit > 0.01) & (last_candle['normal_var_5'] < 0.005): + return "no_change_5" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if self.profit_quick_gain_3: + if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if self.profit_quick_gain: + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain" + + if self.profit_sma10: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi: + if (current_profit > 0) & (last_candle['rsi'] > 88): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > 82) & (last_candle['percent'] < -0.02): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if self.profit_short_loss: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['ecart_20'] = dataframe['close'].rolling(20).var() / dataframe['close'] + dataframe['ecart_50'] = dataframe['close'].rolling(50).var() / dataframe['close'] + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = ta.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_5'] = dataframe['normal'].rolling(5).var() + dataframe['normal_var_10'] = dataframe['normal'].rolling(10).var() + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + dataframe['cmf20'] = cmf(dataframe, 20) + dataframe['cmf50'] = cmf(dataframe, 50) + dataframe['cmf100'] = cmf(dataframe, 100) + + # dataframe["dist_min_50"] = dataframe['close'] - dataframe['min50'] + # dataframe["dist_min_20"] = dataframe['close'] - dataframe['min20'] + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + # pandas.set_option('display.max_columns', 50) + # + # allow_to_buy = True + # max_gain = -100 + # trades = Trade.get_open_trades() + # + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # pairs = self.dp.current_whitelist() + # pairs_len = len(pairs) + # pair_index = pairs.index(metadata['pair']) + # + # # print(pair_index, " ", metadata['pair']) + # + # # ob = self.dp.orderbook(metadata['pair'], 1) + # # dataframe['best_bid'] = ob['bids'][0][0] + # # dataframe['best_ask'] = ob['asks'][0][0] + # # print(ob) + # + # for trade in trades: + # # if (metadata['pair'] == trade.pair): + # ticker = self.dp.ticker(trade.pair) #metadata['pair']) + # last_price = ticker['last'] + # # dataframe['volume24h'] = ticker['quoteVolume'] + # # dataframe['vwap'] = ticker['vwap'] + # # d = dataframe.tail(1) + # # print(dataframe) + # gain = (last_price - trade.open_rate) / trade.open_rate + # + # # print("Found open trade: ", trade, " ", ticker['last'], " ", trade.open_rate, gain) + # max_gain = max(max_gain, gain) + # + # if max_gain > - 0.05: + # allow_to_buy = False + # + # # print(metadata['pair'], max_gain, allow_to_buy, len(trades)) + + for decalage in range(self.buy_1_decalage_deb.value, self.buy_1_decalage.value): + if self.buy_0.value: + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_1_real_num.value, + self.buy_1_decalage.value + ) + conditions.append(condition1) + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= self.buy_1_volume.value) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['open'] <= dataframe['min200'] * self.buy_1_min.value) + & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_1_distance.value) + & (dataframe['normal_var_20'] >= self.buy_1_normal_var.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_2_decalage_deb.value, self.buy_2_decalage.value): + if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_1_real_num.value / 2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= self.buy_2_volume.value) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['open'] <= dataframe['min200'] * self.buy_2_min.value) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + & (dataframe['normal_var_20'] >= self.buy_2_normal_var.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + for decalage in range(self.buy_3_decalage_deb.value, self.buy_3_decalage.value): + if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= self.buy_3_volume.value) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['open'] <= dataframe['min200'] * self.buy_3_min.value) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + & (dataframe['normal_var_20'] >= self.buy_3_normal_var.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + # pair = metadata['pair'] + # allow_to_buy = True + # max_gain = -100 + # sum_gain = 0 + # max_time = 0 + # if len(self.trades) == 0: + # print('search') + # self.trades = Trade.get_open_trades() + # + # # if self.dp: + # # if self.dp.runmode.value in ('live', 'dry_run'): + # if len(self.trades) >= self.config['max_open_trades'] / 2: + # for trade in self.trades: + # ticker = self.dp.ticker(trade.pair) + # last_price = ticker['last'] + # gain = (last_price - trade.open_rate) / trade.open_rate + # max_gain = max(max_gain, gain) + # sum_gain += gain + # max_time = max(max_time, datetime.timestamp(trade.open_date)) + # print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + # datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + # now = datetime.now() + # diff = (datetime.timestamp(now) - max_time / 3600) + # if (max_gain >= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + # print("allow_to_buy=false") + # allow_to_buy = False + # print(pair, allow_to_buy, len(self.trades), + # "max gain=", max_gain, + # "sum_gain=", sum_gain, + # "now=", now, + # "max=", max_time, + # "diff=", datetime.timestamp(now) - max_time) + # + # if allow_to_buy: + # self.trades = list() + + # print(condition1) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_7_5_5.json b/GodStraJD3_7_5_5.json new file mode 100644 index 0000000..e8d2263 --- /dev/null +++ b/GodStraJD3_7_5_5.json @@ -0,0 +1,63 @@ +{ + "strategy_name": "GodStraJD3_7_5_5", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.026, + "trailing_stop_positive_offset": 0.077, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_0": true, + "buy_0_percent20": 0.03, + "buy_1_cmf100_inf": 0.3, + "buy_1_cmf100_sup": 0.6, + "buy_1_decalage": 7, + "buy_1_decalage_deb": 2, + "buy_1_distance": -0.01, + "buy_1_min": 1.002, + "buy_1_normal_var": 2.2, + "buy_1_real_num": 0.53, + "buy_1_volume": 65, + "buy_2": true, + "buy_2_cmf100_inf": 1.0, + "buy_2_cmf100_sup": 0.0, + "buy_2_decalage": 6, + "buy_2_decalage_deb": 2, + "buy_2_distance": 0.08, + "buy_2_min": 1.025, + "buy_2_normal_var": 1.3, + "buy_2_percent20": -0.09, + "buy_2_real_num": 0.32, + "buy_2_volume": 38, + "buy_3": true, + "buy_3_cmf100_inf": -0.4, + "buy_3_cmf100_sup": -0.1, + "buy_3_decalage": 7, + "buy_3_decalage_deb": 1, + "buy_3_distance": 0.1, + "buy_3_min": 1.016, + "buy_3_normal_var": 1.6, + "buy_3_percent20": 0.03, + "buy_3_real_num": 1.94, + "buy_3_volume": 97, + "buy_min_horizon": 98 + }, + "sell": {}, + "protection": { + "lookback": 133, + "protection_max_allowed_dd": 0.71, + "protection_stop": 61, + "protection_stoploss_stop": 50, + "trade_limit": 9 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-19 16:02:08.220681+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_7_5_5.py b/GodStraJD3_7_5_5.py new file mode 100644 index 0000000..d547b6e --- /dev/null +++ b/GodStraJD3_7_5_5.py @@ -0,0 +1,875 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import calendar +from freqtrade.loggers import setup_logging +from technical.indicators import cmf +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + + +class GodStraJD3_7_5_5(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min20': {'color': '#87e470'}, + 'min50': {'color': '#ac3e2a'}, + "min1.1": {'color': 'yellow'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'} + }, + # "Ind0": { + # buy_crossed_indicator0: {'color': 'green'}, + # buy_indicator0: {'color': 'red'} + # }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "Ecart": { + 'normal_var_20': {'color': 'red'}, + 'normal_var_50': {'color': 'yellow'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'}, + "distance_min": {'color': 'white'} + } + } + } + + # #################### END OF RESULT PLACE #################### + + profit_no_change = False + profit_old_sma10 = False + profit_over_rsi = True + profit_quick_gain = True + profit_quick_gain_3 = True + profit_quick_lost = False + profit_short_loss = False + profit_sma10 = True + profit_sma20 = True + profit_very_old_sma10 = False + + trades = list() + # profit_no_change = BooleanParameter(default=True, space="buy") + # profit_quick_lost = BooleanParameter(default=True, space="buy") + # profit_sma10 = BooleanParameter(default=True, space="buy") + # profit_sma20 = BooleanParameter(default=True, space="buy") + # profit_quick_gain = BooleanParameter(default=True, space="buy") + # profit_quick_gain_3 = BooleanParameter(default=True, space="buy") + # profit_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_very_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_over_rsi = BooleanParameter(default=True, space="buy") + # profit_short_loss = BooleanParameter(default=True, space="buy") + + # buy_signal_bb_width = DecimalParameter(0.06, 0.15, decimals=2, default=0.065, space='buy') + + buy_1_real_num = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_2_real_num = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_3_real_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + + buy_min_horizon = IntParameter(50, 200, default=72, space='buy') + + buy_0 = BooleanParameter(default=True, space="buy") + buy_2 = BooleanParameter(default=True, space="buy") + buy_3 = BooleanParameter(default=True, space="buy") + + buy_0_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_2_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_3_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + + buy_1_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_2_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_3_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + + buy_1_cmf100_sup = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + buy_2_cmf100_sup = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + buy_3_cmf100_sup = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + + buy_1_cmf100_inf = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + buy_2_cmf100_inf = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + buy_3_cmf100_inf = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + + buy_1_min = DecimalParameter(1, 1.03, decimals=3, default=1.02, space='buy') + buy_2_min = DecimalParameter(1, 1.03, decimals=3, default=1.02, space='buy') + buy_3_min = DecimalParameter(1, 1.03, decimals=3, default=1.02, space='buy') + + buy_1_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + buy_2_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + buy_3_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + + buy_1_decalage_deb = IntParameter(1, 3, default=5, space='buy') + buy_2_decalage_deb = IntParameter(1, 3, default=5, space='buy') + buy_3_decalage_deb = IntParameter(1, 3, default=5, space='buy') + + buy_1_decalage = IntParameter(buy_1_decalage_deb.value + 1, 8, default=5, space='buy') + buy_2_decalage = IntParameter(buy_2_decalage_deb.value + 1, 8, default=5, space='buy') + buy_3_decalage = IntParameter(buy_3_decalage_deb.value + 1, 8, default=5, space='buy') + + buy_1_volume = IntParameter(0, 100, default=10, space='buy') + buy_2_volume = IntParameter(0, 100, default=10, space='buy') + buy_3_volume = IntParameter(0, 100, default=10, space='buy') + + protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + protection_stop = IntParameter(1, 100, default=48, space='protection') + protection_stoploss_stop = IntParameter(1, 100, default=48, space='protection') + lookback = IntParameter(1, 200, default=48, space='protection') + trade_limit = IntParameter(1, 10, default=2, space='protection') + + inf_tf = '1h' + + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + + # for decalage in range(self.buy_3_decalage_deb.value, self.buy_3_decalage.value): + # decalage_candle = dataframe.iloc[- decalage].squeeze() + # # if (decalage_candle['normal_var_20'] >= 0.6): + # amount = 10 * (- decalage_candle['percent20'] * 100) + # print("use more stake", pair, " ", amount) + # return min(max_stake, amount) + + # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + # if current_candle['bb_width'] > 0.05: + # print("use more stake", pair, " ", proposed_stake * 2) + # return min(max_stake, proposed_stake * 2) + # + # if current_candle['bb_width'] > 0.035: + # print("use more stake", pair, " ", proposed_stake * 1.5) + # return min(max_stake, proposed_stake * 1.5) + + # if current_candle['bb_width'] < 0.020: + # print("use less stake", pair, " ", proposed_stake / 2) + # return min(max_stake, proposed_stake / 2) + # if self.config['stake_amount'] == 'unlimited': + # # Use entire available wallet during favorable conditions when in compounding mode. + # return max_stake + # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + + + # Use default stake amount. + return proposed_stake + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + + # {'symbol': 'FTM/USDT', 'timestamp': 1646494199570, 'datetime': '2022-03-05T15:29:59.570Z', 'high': 1.7489, + # 'low': 1.6084, 'bid': 1.6505, 'bidVolume': 2135.0, 'ask': 1.6508, 'askVolume': 2815.0, 'vwap': 1.66852198, + # 'open': 1.7313, 'close': 1.6505, 'last': 1.6505, 'previousClose': '1.73170000', 'change': -0.0808, + # 'percentage': -4.667, 'average': 1.6909, 'baseVolume': 124656725.0, 'quoteVolume': 207992485.7799, + # 'info': + # {'symbol': 'FTMUSDT', 'priceChange': '-0.08080000', 'priceChangePercent': '-4.667', + # 'weightedAvgPrice': '1.66852198', 'prevClosePrice': '1.73170000', 'lastPrice': '1.65050000', + # 'lastQty': '143.00000000', 'bidPrice': '1.65050000', 'bidQty': '2135.00000000', + # 'askPrice': '1.65080000', 'askQty': '2815.00000000', 'openPrice': '1.73130000', + # 'highPrice': '1.74890000', 'lowPrice': '1.60840000', 'volume': '124656725.00000000', + # 'quoteVolume': '207992485.77990000', 'openTime': '1646407799570', 'closeTime': '1646494199570', + # 'firstId': '137149614', 'lastId': '137450289', 'count': '300676'}} - 0.9817468621938484 + + allow_to_buy = True + max_gain = -100 + sum_gain = 0 + max_time = 0 + + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + if len(self.trades) == 0: + print('search') + self.trades = Trade.get_open_trades() + + if len(self.trades) >= self.config['max_open_trades'] / 2: + for trade in self.trades: + ticker = self.dp.ticker(trade.pair) + last_price = ticker['last'] + gain = (last_price - trade.open_rate) / trade.open_rate + max_gain = max(max_gain, gain) + sum_gain += gain + max_time = max(max_time, datetime.timestamp(trade.open_date)) + print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + now = datetime.now() + diff = (datetime.timestamp(now) - max_time / 3600) + if (max_gain <= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + print("allow_to_buy=false") + allow_to_buy = False + print(pair, allow_to_buy, len(self.trades), + "max gain=", max_gain, + "sum_gain=", sum_gain, + "now=", now, + "max=", max_time, + "diff=", datetime.timestamp(now) - max_time) + + if allow_to_buy: + self.trades = list() + + return allow_to_buy + + # @property + # def protections(self): + # return [ + # { + # "method": "CooldownPeriod", + # "stop_duration_candles": 10 + # }, + # { + # "method": "MaxDrawdown", + # "lookback_period_candles": self.lookback.value, + # "trade_limit": self.trade_limit.value, + # "stop_duration_candles": self.protection_stop.value, + # "max_allowed_drawdown": self.protection_max_allowed_dd.value, + # "only_per_pair": False + # }, + # { + # "method": "StoplossGuard", + # "lookback_period_candles": 24, + # "trade_limit": 4, + # "stop_duration_candles": self.protection_stoploss_stop.value, + # "only_per_pair": False + # } + # ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # if (0 < current_profit) & ((current_time - trade.open_date_utc).seconds > 3600) \ + # & (last_candle['percent10'] < 0.001): + # return 'small_profit' + # + # if (current_profit > 0.01) \ + # & ((previous_last_candle['sma10'] - last_candle['sma10']) / previous_last_candle['sma10'] > 0.003): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma10_quick' + + if (current_profit >= -0.01) & ((current_time - trade.open_date_utc).days >= 5)\ + & ((current_time - trade.open_date_utc).days < 10)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.01" + if (current_profit >= -0.02) & ((current_time - trade.open_date_utc).days >= 10)\ + & ((current_time - trade.open_date_utc).days < 15) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.02" + if (current_profit >= -0.03) & ((current_time - trade.open_date_utc).days >= 15) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.03" + + if self.profit_quick_lost: + if (current_profit >= 0) & (last_candle['percent3'] < -0.015): + return "quick_lost" + + if self.profit_no_change: + if (current_profit > 0.005) & (last_candle['percent10'] < 0.001) & (last_candle['percent5'] < 0) & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + if (current_profit > 0.005) & (last_candle['normal_var_20'] < 0.008): + return "no_change_20" + + if (current_profit > 0.01) & (last_candle['normal_var_10'] < 0.008): + return "no_change_10" + if (current_profit > 0.01) & (last_candle['normal_var_5'] < 0.005): + return "no_change_5" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if self.profit_quick_gain_3: + if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if self.profit_quick_gain: + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain" + + if self.profit_sma10: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi: + if (current_profit > 0) & (previous_last_candle['rsi'] > 88): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > 82) & (last_candle['percent'] < -0.02): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if self.profit_short_loss: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # Assign tf to each pair so they can be downloaded and cached for strategy. + informative_pairs = [(pair, self.inf_tf) for pair in pairs] + + # Optionally Add additional "static" pairs + # informative_pairs += [("ETH/USDT", "5m"), ("BTC/TUSD", "15m")] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['ecart_20'] = dataframe['close'].rolling(20).var() / dataframe['close'] + dataframe['ecart_50'] = dataframe['close'].rolling(50).var() / dataframe['close'] + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = ta.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_5'] = dataframe['normal'].rolling(5).var() + dataframe['normal_var_10'] = dataframe['normal'].rolling(10).var() + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + dataframe['cmf20'] = cmf(dataframe, 20) + dataframe['cmf50'] = cmf(dataframe, 50) + dataframe['cmf100'] = cmf(dataframe, 100) + + # dataframe["dist_min_50"] = dataframe['close'] - dataframe['min50'] + # dataframe["dist_min_20"] = dataframe['close'] - dataframe['min20'] + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + + # # INFORMATIVE PAIRS + + # Get the informative pair + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_tf) + # Get the 14 day rsi + informative['rsi'] = ta.RSI(informative, timeperiod=14) + + # informative['cmf'] = cmf(informative, timeperiod=14) + + informative[buy_crossed_indicator0] = gene_calculator(informative, buy_crossed_indicator0) + informative[buy_indicator0] = gene_calculator(informative, buy_indicator0) + informative["cond1"] = informative[buy_indicator0].div(informative[buy_crossed_indicator0]) + + pandas.set_option('display.max_rows', informative.shape[0] + 1) + pandas.set_option('display.max_columns', 50) + + print('informative', metadata['pair'], informative.tail(1)) + + # Use the helper function merge_informative_pair to safely merge the pair + # Automatically renames the columns and merges a shorter timeframe dataframe and a longer timeframe informative pair + # use ffill to have the 1d value available in every row throughout the day. + # Without this, comparisons between columns of the original and the informative pair would only work once per day. + # Full documentation of this method, see below + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, self.inf_tf, ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + # pandas.set_option('display.max_columns', 50) + # + # allow_to_buy = True + # max_gain = -100 + # trades = Trade.get_open_trades() + # + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # pairs = self.dp.current_whitelist() + # pairs_len = len(pairs) + # pair_index = pairs.index(metadata['pair']) + # + # # print(pair_index, " ", metadata['pair']) + # + # # ob = self.dp.orderbook(metadata['pair'], 1) + # # dataframe['best_bid'] = ob['bids'][0][0] + # # dataframe['best_ask'] = ob['asks'][0][0] + # # print(ob) + # + # for trade in trades: + # # if (metadata['pair'] == trade.pair): + # ticker = self.dp.ticker(trade.pair) #metadata['pair']) + # last_price = ticker['last'] + # # dataframe['volume24h'] = ticker['quoteVolume'] + # # dataframe['vwap'] = ticker['vwap'] + # # d = dataframe.tail(1) + # # print(dataframe) + # gain = (last_price - trade.open_rate) / trade.open_rate + # + # # print("Found open trade: ", trade, " ", ticker['last'], " ", trade.open_rate, gain) + # max_gain = max(max_gain, gain) + # + # if max_gain > - 0.05: + # allow_to_buy = False + # + # # print(metadata['pair'], max_gain, allow_to_buy, len(trades)) + + # for decalage in range(self.buy_1_decalage_deb.value, self.buy_1_decalage.value): + # if self.buy_0.value: + # conditions = list() + # condition1, dataframe = condition_generator( + # dataframe, + # buy_operator0, + # buy_indicator0, + # buy_crossed_indicator0, + # self.buy_1_real_num.value, + # self.buy_1_decalage.value + # ) + # conditions.append(condition1) + # dataframe.loc[ + # ( + # reduce(lambda x, y: x & y, conditions) + # & (dataframe['cmf100'] < self.buy_1_cmf100_sup.value) + # & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= self.buy_1_volume.value) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + # & (dataframe['close'] < dataframe['bb_middleband']) + # & (dataframe['open'] < dataframe['sma10']) + # & (dataframe['open'] < dataframe['sma100']) + # & (dataframe['min50'].shift(decalage) == dataframe['min50']) + # & (dataframe['open'] <= dataframe['min200'] * self.buy_1_min.value) + # & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # # & (dataframe['min20'] == dataframe['min50']) + # & (dataframe['distance_min'] <= self.buy_1_distance.value) + # & (dataframe['normal_var_20'] >= self.buy_1_normal_var.value) + # ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_2_decalage_deb.value, self.buy_2_decalage.value): + if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_2_real_num.value) + & (dataframe['cmf100'] >= self.buy_2_cmf100_sup.value) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= self.buy_2_volume.value) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + # & (dataframe['normal_var_20'] >= self.buy_2_normal_var.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + for decalage in range(self.buy_3_decalage_deb.value, self.buy_3_decalage.value): + if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) #self.buy_3_real_num.value) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= self.buy_3_volume.value) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['cmf100'] < self.buy_3_cmf100_sup.value) + # & (dataframe['cmf100'] > self.buy_3_cmf100_inf.value) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + # & (dataframe['open'] <= dataframe['min200'] * self.buy_3_min.value) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + # & (dataframe['normal_var_20'] >= self.buy_3_normal_var.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + # pair = metadata['pair'] + # allow_to_buy = True + # max_gain = -100 + # sum_gain = 0 + # max_time = 0 + # if len(self.trades) == 0: + # print('search') + # self.trades = Trade.get_open_trades() + # + # # if self.dp: + # # if self.dp.runmode.value in ('live', 'dry_run'): + # if len(self.trades) >= self.config['max_open_trades'] / 2: + # for trade in self.trades: + # ticker = self.dp.ticker(trade.pair) + # last_price = ticker['last'] + # gain = (last_price - trade.open_rate) / trade.open_rate + # max_gain = max(max_gain, gain) + # sum_gain += gain + # max_time = max(max_time, datetime.timestamp(trade.open_date)) + # print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + # datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + # now = datetime.now() + # diff = (datetime.timestamp(now) - max_time / 3600) + # if (max_gain >= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + # print("allow_to_buy=false") + # allow_to_buy = False + # print(pair, allow_to_buy, len(self.trades), + # "max gain=", max_gain, + # "sum_gain=", sum_gain, + # "now=", now, + # "max=", max_time, + # "diff=", datetime.timestamp(now) - max_time) + # + # if allow_to_buy: + # self.trades = list() + + # print(condition1) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_7_5_6.json b/GodStraJD3_7_5_6.json new file mode 100644 index 0000000..dc19373 --- /dev/null +++ b/GodStraJD3_7_5_6.json @@ -0,0 +1,48 @@ +{ + "strategy_name": "GodStraJD3_7_5_6", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.026, + "trailing_stop_positive_offset": 0.077, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_0": true, + "buy_0_percent20": 0.08, + "buy_1_volume": 0, + "buy_3": true, + "buy_3_cmf100_inf": 0.4, + "buy_3_cmf100_sup": -0.6, + "buy_3_cond_1h_num": 0.56, + "buy_3_cond_num": 0.76, + "buy_3_decalage": 7, + "buy_3_decalage_deb": 3, + "buy_3_distance": 0.08, + "buy_3_min": 1.016, + "buy_3_normal_var": 3.3, + "buy_3_percent20": 0.1, + "buy_3_percent_1d_num": 0.01, + "buy_3_percent_1w_num": -0.04, + "buy_3_real_num": 0.23, + "buy_3_volume": 96, + "buy_min_horizon": 198 + }, + "sell": {}, + "protection": { + "lookback": 133, + "protection_max_allowed_dd": 0.71, + "protection_stop": 61, + "protection_stoploss_stop": 50, + "trade_limit": 9 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-22 20:30:50.351361+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_7_5_6.py b/GodStraJD3_7_5_6.py new file mode 100644 index 0000000..d667cdc --- /dev/null +++ b/GodStraJD3_7_5_6.py @@ -0,0 +1,898 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import calendar +from freqtrade.loggers import setup_logging +from technical.indicators import cmf +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + + +class GodStraJD3_7_5_6(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min20': {'color': '#87e470'}, + 'min50': {'color': '#ac3e2a'}, + "min1.1": {'color': 'yellow'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'} + }, + # "Ind0": { + # buy_crossed_indicator0: {'color': 'green'}, + # buy_indicator0: {'color': 'red'} + # }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "Ecart": { + 'normal_var_20': {'color': 'red'}, + 'normal_var_50': {'color': 'yellow'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'}, + "distance_min": {'color': 'white'} + } + } + } + + # #################### END OF RESULT PLACE #################### + + profit_no_change = False + profit_old_sma10 = False + profit_over_rsi = True + profit_quick_gain = True + profit_quick_gain_3 = True + profit_quick_lost = False + profit_short_loss = False + profit_sma10 = True + profit_sma20 = True + profit_very_old_sma10 = False + + trades = list() + # profit_no_change = BooleanParameter(default=True, space="buy") + # profit_quick_lost = BooleanParameter(default=True, space="buy") + # profit_sma10 = BooleanParameter(default=True, space="buy") + # profit_sma20 = BooleanParameter(default=True, space="buy") + # profit_quick_gain = BooleanParameter(default=True, space="buy") + # profit_quick_gain_3 = BooleanParameter(default=True, space="buy") + # profit_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_very_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_over_rsi = BooleanParameter(default=True, space="buy") + # profit_short_loss = BooleanParameter(default=True, space="buy") + + # buy_signal_bb_width = DecimalParameter(0.06, 0.15, decimals=2, default=0.065, space='buy') + + # buy_1_cond_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + # buy_2_cond_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + buy_3_cond_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + + # buy_1_cond_1h_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + # buy_2_cond_1h_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + buy_3_cond_1h_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + buy_3_percent_1d_num = DecimalParameter(-0.1, 0.1, decimals=2, default=0.67, space='buy') + buy_3_percent_1w_num = DecimalParameter(-0.1, 0.1, decimals=2, default=0.67, space='buy') + + # buy_1_real_num = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + # buy_2_real_num = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_3_real_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + + buy_min_horizon = IntParameter(50, 200, default=72, space='buy') + + buy_0 = BooleanParameter(default=True, space="buy") + # buy_2 = BooleanParameter(default=True, space="buy") + buy_3 = BooleanParameter(default=True, space="buy") + + buy_0_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + # buy_2_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_3_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + + # buy_1_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + # buy_2_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_3_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + + # buy_1_cmf100_sup = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + # buy_2_cmf100_sup = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + buy_3_cmf100_sup = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + + # buy_1_cmf100_inf = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + # buy_2_cmf100_inf = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + buy_3_cmf100_inf = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + + # buy_1_min = DecimalParameter(1, 1.03, decimals=3, default=1.02, space='buy') + # buy_2_min = DecimalParameter(1, 1.03, decimals=3, default=1.02, space='buy') + buy_3_min = DecimalParameter(1, 1.03, decimals=3, default=1.02, space='buy') + + # buy_1_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + # buy_2_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + buy_3_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + + # buy_1_decalage_deb = IntParameter(1, 3, default=5, space='buy') + # buy_2_decalage_deb = IntParameter(1, 3, default=5, space='buy') + buy_3_decalage_deb = IntParameter(1, 3, default=5, space='buy') + + # buy_1_decalage = IntParameter(buy_1_decalage_deb.value + 1, 8, default=5, space='buy') + # buy_2_decalage = IntParameter(# buy_2_decalage_deb.value + 1, 8, default=5, space='buy') + buy_3_decalage = IntParameter(buy_3_decalage_deb.value + 1, 8, default=5, space='buy') + + buy_1_volume = IntParameter(0, 100, default=10, space='buy') + # buy_2_volume = IntParameter(0, 100, default=10, space='buy') + buy_3_volume = IntParameter(0, 100, default=10, space='buy') + + protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + protection_stop = IntParameter(1, 100, default=48, space='protection') + protection_stoploss_stop = IntParameter(1, 100, default=48, space='protection') + lookback = IntParameter(1, 200, default=48, space='protection') + trade_limit = IntParameter(1, 10, default=2, space='protection') + + inf_tf = '4h' + + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + + # for decalage in range(self.buy_3_decalage_deb.value, self.buy_3_decalage.value): + # decalage_candle = dataframe.iloc[- decalage].squeeze() + # # if (decalage_candle['normal_var_20'] >= 0.6): + # amount = 10 * (- decalage_candle['percent20'] * 100) + # print("use more stake", pair, " ", amount) + # return min(max_stake, amount) + + # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + # if current_candle['bb_width'] > 0.05: + # print("use more stake", pair, " ", proposed_stake * 2) + # return min(max_stake, proposed_stake * 2) + # + # if current_candle['bb_width'] > 0.035: + # print("use more stake", pair, " ", proposed_stake * 1.5) + # return min(max_stake, proposed_stake * 1.5) + + # if current_candle['bb_width'] < 0.020: + # print("use less stake", pair, " ", proposed_stake / 2) + # return min(max_stake, proposed_stake / 2) + # if self.config['stake_amount'] == 'unlimited': + # # Use entire available wallet during favorable conditions when in compounding mode. + # return max_stake + # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + + + # Use default stake amount. + return proposed_stake + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + + # {'symbol': 'FTM/USDT', 'timestamp': 1646494199570, 'datetime': '2022-03-05T15:29:59.570Z', 'high': 1.7489, + # 'low': 1.6084, 'bid': 1.6505, 'bidVolume': 2135.0, 'ask': 1.6508, 'askVolume': 2815.0, 'vwap': 1.66852198, + # 'open': 1.7313, 'close': 1.6505, 'last': 1.6505, 'previousClose': '1.73170000', 'change': -0.0808, + # 'percentage': -4.667, 'average': 1.6909, 'baseVolume': 124656725.0, 'quoteVolume': 207992485.7799, + # 'info': + # {'symbol': 'FTMUSDT', 'priceChange': '-0.08080000', 'priceChangePercent': '-4.667', + # 'weightedAvgPrice': '1.66852198', 'prevClosePrice': '1.73170000', 'lastPrice': '1.65050000', + # 'lastQty': '143.00000000', 'bidPrice': '1.65050000', 'bidQty': '2135.00000000', + # 'askPrice': '1.65080000', 'askQty': '2815.00000000', 'openPrice': '1.73130000', + # 'highPrice': '1.74890000', 'lowPrice': '1.60840000', 'volume': '124656725.00000000', + # 'quoteVolume': '207992485.77990000', 'openTime': '1646407799570', 'closeTime': '1646494199570', + # 'firstId': '137149614', 'lastId': '137450289', 'count': '300676'}} - 0.9817468621938484 + + allow_to_buy = True + max_gain = -100 + sum_gain = 0 + max_time = 0 + + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + if len(self.trades) == 0: + print('search') + self.trades = Trade.get_open_trades() + + if len(self.trades) >= self.config['max_open_trades'] / 2: + for trade in self.trades: + ticker = self.dp.ticker(trade.pair) + last_price = ticker['last'] + gain = (last_price - trade.open_rate) / trade.open_rate + max_gain = max(max_gain, gain) + sum_gain += gain + max_time = max(max_time, datetime.timestamp(trade.open_date)) + print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + now = datetime.now() + diff = (datetime.timestamp(now) - max_time / 3600) + if (max_gain <= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + print("allow_to_buy=false") + allow_to_buy = False + print(pair, allow_to_buy, len(self.trades), + "max gain=", max_gain, + "sum_gain=", sum_gain, + "now=", now, + "max=", max_time, + "diff=", datetime.timestamp(now) - max_time) + + if allow_to_buy: + self.trades = list() + + return allow_to_buy + + # @property + # def protections(self): + # return [ + # { + # "method": "CooldownPeriod", + # "stop_duration_candles": 10 + # }, + # { + # "method": "MaxDrawdown", + # "lookback_period_candles": self.lookback.value, + # "trade_limit": self.trade_limit.value, + # "stop_duration_candles": self.protection_stop.value, + # "max_allowed_drawdown": self.protection_max_allowed_dd.value, + # "only_per_pair": False + # }, + # { + # "method": "StoplossGuard", + # "lookback_period_candles": 24, + # "trade_limit": 4, + # "stop_duration_candles": self.protection_stoploss_stop.value, + # "only_per_pair": False + # } + # ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # if (0 < current_profit) & ((current_time - trade.open_date_utc).seconds > 3600) \ + # & (last_candle['percent10'] < 0.001): + # return 'small_profit' + # + # if (current_profit > 0.01) \ + # & ((previous_last_candle['sma10'] - last_candle['sma10']) / previous_last_candle['sma10'] > 0.003): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma10_quick' + + if (current_profit >= -0.01) & ((current_time - trade.open_date_utc).days >= 5)\ + & ((current_time - trade.open_date_utc).days < 10)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.01" + if (current_profit >= -0.02) & ((current_time - trade.open_date_utc).days >= 10)\ + & ((current_time - trade.open_date_utc).days < 15) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.02" + if (current_profit >= -0.03) & ((current_time - trade.open_date_utc).days >= 15) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.03" + + if self.profit_quick_lost: + if (current_profit >= 0) & (last_candle['percent3'] < -0.015): + return "quick_lost" + + if self.profit_no_change: + if (current_profit > 0.005) & (last_candle['percent10'] < 0.001) & (last_candle['percent5'] < 0) & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + if (current_profit > 0.005) & (last_candle['normal_var_20'] < 0.008): + return "no_change_20" + + if (current_profit > 0.01) & (last_candle['normal_var_10'] < 0.008): + return "no_change_10" + if (current_profit > 0.01) & (last_candle['normal_var_5'] < 0.005): + return "no_change_5" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if self.profit_quick_gain_3: + if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if self.profit_quick_gain: + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0): #& ((current_time - trade.open_date_utc).seconds <= 3600) + return "quick_gain" + + if self.profit_sma10: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi: + if (current_profit > 0) & (previous_last_candle['rsi'] > 88): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > 82) & (last_candle['percent'] < -0.02): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if self.profit_short_loss: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # Assign tf to each pair so they can be downloaded and cached for strategy. + informative_pairs = [(pair, self.inf_tf) for pair in pairs] + informative_pairs += [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '1w') for pair in pairs] + + # Optionally Add additional "static" pairs + # informative_pairs += [("ETH/USDT", "5m"), ("BTC/TUSD", "15m")] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['ecart_20'] = dataframe['close'].rolling(20).var() / dataframe['close'] + dataframe['ecart_50'] = dataframe['close'].rolling(50).var() / dataframe['close'] + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = ta.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = dataframe['bb_lowerband'].rolling(5).var() + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_5'] = dataframe['normal'].rolling(5).var() + dataframe['normal_var_10'] = dataframe['normal'].rolling(10).var() + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + dataframe['cmf20'] = cmf(dataframe, 20) + dataframe['cmf50'] = cmf(dataframe, 50) + dataframe['cmf100'] = cmf(dataframe, 100) + + # dataframe["dist_min_50"] = dataframe['close'] - dataframe['min50'] + # dataframe["dist_min_20"] = dataframe['close'] - dataframe['min20'] + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + + # # INFORMATIVE PAIRS + + # Get the informative pair + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_tf) + informative[buy_crossed_indicator0] = gene_calculator(informative, buy_crossed_indicator0) + informative[buy_indicator0] = gene_calculator(informative, buy_indicator0) + informative["cond1"] = informative[buy_indicator0].div(informative[buy_crossed_indicator0]) + pandas.set_option('display.max_rows', informative.shape[0] + 1) + pandas.set_option('display.max_columns', 50) + # print('informative', metadata['pair'], informative.tail(1)) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, self.inf_tf, ffill=True) + + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='1d') + informative[buy_crossed_indicator0] = gene_calculator(informative, buy_crossed_indicator0) + informative[buy_indicator0] = gene_calculator(informative, buy_indicator0) + informative["cond1"] = informative[buy_indicator0].div(informative[buy_crossed_indicator0]) + informative["percent"] = (informative["close"] - informative["open"]) / informative["open"] + informative["percent5"] = informative["percent"].rolling(5).sum() + informative["percent3"] = informative["percent"].rolling(3).sum() + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, '1d', ffill=True) + + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='1w') + informative[buy_crossed_indicator0] = gene_calculator(informative, buy_crossed_indicator0) + informative[buy_indicator0] = gene_calculator(informative, buy_indicator0) + informative["cond1"] = informative[buy_indicator0].div(informative[buy_crossed_indicator0]) + informative["percent"] = (informative["close"] - informative["open"]) / informative["open"] + informative["percent5"] = informative["percent"].rolling(5).sum() + informative["percent3"] = informative["percent"].rolling(3).sum() + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, '1w', ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + # pandas.set_option('display.max_columns', 50) + # + # allow_to_buy = True + # max_gain = -100 + # trades = Trade.get_open_trades() + # + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # pairs = self.dp.current_whitelist() + # pairs_len = len(pairs) + # pair_index = pairs.index(metadata['pair']) + # + # # print(pair_index, " ", metadata['pair']) + # + # # ob = self.dp.orderbook(metadata['pair'], 1) + # # dataframe['best_bid'] = ob['bids'][0][0] + # # dataframe['best_ask'] = ob['asks'][0][0] + # # print(ob) + # + # for trade in trades: + # # if (metadata['pair'] == trade.pair): + # ticker = self.dp.ticker(trade.pair) #metadata['pair']) + # last_price = ticker['last'] + # # dataframe['volume24h'] = ticker['quoteVolume'] + # # dataframe['vwap'] = ticker['vwap'] + # # d = dataframe.tail(1) + # # print(dataframe) + # gain = (last_price - trade.open_rate) / trade.open_rate + # + # # print("Found open trade: ", trade, " ", ticker['last'], " ", trade.open_rate, gain) + # max_gain = max(max_gain, gain) + # + # if max_gain > - 0.05: + # allow_to_buy = False + # + # # print(metadata['pair'], max_gain, allow_to_buy, len(trades)) + + # for decalage in range(self.buy_1_decalage_deb.value, self.buy_1_decalage.value): + # if self.buy_0.value: + # conditions = list() + # condition1, dataframe = condition_generator( + # dataframe, + # buy_operator0, + # buy_indicator0, + # buy_crossed_indicator0, + # self.buy_1_real_num.value, + # self.buy_1_decalage.value + # ) + # conditions.append(condition1) + # dataframe.loc[ + # ( + # reduce(lambda x, y: x & y, conditions) + # & (dataframe['cmf100'] < self.buy_1_cmf100_sup.value) + # & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= self.buy_1_volume.value) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + # & (dataframe['close'] < dataframe['bb_middleband']) + # & (dataframe['open'] < dataframe['sma10']) + # & (dataframe['open'] < dataframe['sma100']) + # & (dataframe['min50'].shift(decalage) == dataframe['min50']) + # & (dataframe['open'] <= dataframe['min200'] * self.buy_1_min.value) + # & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # # & (dataframe['min20'] == dataframe['min50']) + # & (dataframe['distance_min'] <= self.buy_1_distance.value) + # & (dataframe['normal_var_20'] >= self.buy_1_normal_var.value) + # ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + # for decalage in range(self.buy_2_decalage_deb.value, self.buy_2_decalage.value): + # if self.buy_2.value: + # dataframe.loc[ + # ( + # (dataframe['cond1'].shift(decalage) <= self.buy_2_cond_num.value) + # & (dataframe['cond1'].shift(decalage) >= self.buy_3_cond_num.value) + # & (dataframe['cond1_1h'] <= self.buy_2_cond_1h_num.value) + # & (dataframe['cmf100'] >= self.buy_2_cmf100_sup.value) + # & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= self.buy_2_volume.value) + # # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + # & (dataframe['close'] < dataframe['sma10']) + # & (dataframe['open'] < dataframe['sma100']) + # & (dataframe['open'] < dataframe['sma10']) + # & (dataframe['min50'].shift(decalage) == dataframe['min50']) + # #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + # & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + # # & (dataframe['min20'] == dataframe['min50']) + # & (dataframe['distance_min'] <= self.buy_2_distance.value) + # # & (dataframe['normal_var_20'] >= self.buy_2_normal_var.value) + # ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + for decalage in range(self.buy_3_decalage_deb.value, self.buy_3_decalage.value): + if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= self.buy_3_cond_num.value) + #& (dataframe['percent_1d'] <= self.buy_3_percent_1d_num.value) + #& (dataframe['percent_1w'] <= self.buy_3_percent_1w_num.value) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= self.buy_3_volume.value) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + # & (dataframe['cmf100'] < self.buy_3_cmf100_sup.value) + # & (dataframe['cmf100'] > self.buy_3_cmf100_inf.value) + # & (dataframe['cmf100'] > self.buy_3_cmf100_inf.value) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + # & (dataframe['open'] <= dataframe['min200'] * self.buy_3_min.value) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + # & (dataframe['normal_var_20'] >= self.buy_3_normal_var.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + # pair = metadata['pair'] + # allow_to_buy = True + # max_gain = -100 + # sum_gain = 0 + # max_time = 0 + # if len(self.trades) == 0: + # print('search') + # self.trades = Trade.get_open_trades() + # + # # if self.dp: + # # if self.dp.runmode.value in ('live', 'dry_run'): + # if len(self.trades) >= self.config['max_open_trades'] / 2: + # for trade in self.trades: + # ticker = self.dp.ticker(trade.pair) + # last_price = ticker['last'] + # gain = (last_price - trade.open_rate) / trade.open_rate + # max_gain = max(max_gain, gain) + # sum_gain += gain + # max_time = max(max_time, datetime.timestamp(trade.open_date)) + # print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + # datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + # now = datetime.now() + # diff = (datetime.timestamp(now) - max_time / 3600) + # if (max_gain >= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + # print("allow_to_buy=false") + # allow_to_buy = False + # print(pair, allow_to_buy, len(self.trades), + # "max gain=", max_gain, + # "sum_gain=", sum_gain, + # "now=", now, + # "max=", max_time, + # "diff=", datetime.timestamp(now) - max_time) + # + # if allow_to_buy: + # self.trades = list() + + # print(condition1) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_7_5_7.json b/GodStraJD3_7_5_7.json new file mode 100644 index 0000000..644913a --- /dev/null +++ b/GodStraJD3_7_5_7.json @@ -0,0 +1,63 @@ +{ + "strategy_name": "GodStraJD3_7_5_7", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.026, + "trailing_stop_positive_offset": 0.077, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_0": true, + "buy_0_percent20": 0.05, + "buy_1_cmf100_inf": 0.5, + "buy_1_cmf100_sup": -0.6, + "buy_1_decalage": 6, + "buy_1_decalage_deb": 2, + "buy_1_distance": 0.1, + "buy_1_min": 1.01, + "buy_1_normal_var": 1.0, + "buy_1_real_num": 0.01, + "buy_1_volume": 60, + "buy_2": true, + "buy_2_cmf100_inf": -0.9, + "buy_2_cmf100_sup": -1.0, + "buy_2_decalage": 7, + "buy_2_decalage_deb": 1, + "buy_2_distance": 0.09, + "buy_2_min": 1.025, + "buy_2_normal_var": 4.0, + "buy_2_percent20": 0.06, + "buy_2_real_num": 0.61, + "buy_2_volume": 33, + "buy_3": false, + "buy_3_cmf100_inf": 0.0, + "buy_3_cmf100_sup": 0.7, + "buy_3_decalage": 7, + "buy_3_decalage_deb": 3, + "buy_3_distance": -0.06, + "buy_3_min": 1.006, + "buy_3_normal_var": 4.5, + "buy_3_percent20": 0.1, + "buy_3_real_num": 1.41, + "buy_3_volume": 15, + "buy_min_horizon": 197 + }, + "sell": {}, + "protection": { + "lookback": 133, + "protection_max_allowed_dd": 0.71, + "protection_stop": 61, + "protection_stoploss_stop": 50, + "trade_limit": 9 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-20 19:38:37.829599+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_7_5_7.py b/GodStraJD3_7_5_7.py new file mode 100644 index 0000000..9ed5d0c --- /dev/null +++ b/GodStraJD3_7_5_7.py @@ -0,0 +1,878 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import calendar +from freqtrade.loggers import setup_logging +from technical.indicators import cmf +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + + +class GodStraJD3_7_5_7(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min20': {'color': '#87e470'}, + 'min50': {'color': '#ac3e2a'}, + "min1.1": {'color': 'yellow'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'} + }, + # "Ind0": { + # buy_crossed_indicator0: {'color': 'green'}, + # buy_indicator0: {'color': 'red'} + # }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "Ecart": { + 'normal_var_20': {'color': 'red'}, + 'normal_var_50': {'color': 'yellow'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'}, + "distance_min": {'color': 'white'} + } + } + } + + # #################### END OF RESULT PLACE #################### + + profit_no_change = False + profit_old_sma10 = False + profit_over_rsi = True + profit_quick_gain = True + profit_quick_gain_3 = True + profit_quick_lost = False + profit_short_loss = False + profit_sma10 = True + profit_sma20 = True + profit_very_old_sma10 = False + + trades = list() + # profit_no_change = BooleanParameter(default=True, space="buy") + # profit_quick_lost = BooleanParameter(default=True, space="buy") + # profit_sma10 = BooleanParameter(default=True, space="buy") + # profit_sma20 = BooleanParameter(default=True, space="buy") + # profit_quick_gain = BooleanParameter(default=True, space="buy") + # profit_quick_gain_3 = BooleanParameter(default=True, space="buy") + # profit_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_very_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_over_rsi = BooleanParameter(default=True, space="buy") + # profit_short_loss = BooleanParameter(default=True, space="buy") + + # buy_signal_bb_width = DecimalParameter(0.06, 0.15, decimals=2, default=0.065, space='buy') + + buy_1_real_num = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_2_real_num = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_3_real_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + + buy_min_horizon = IntParameter(50, 200, default=72, space='buy') + + buy_0 = BooleanParameter(default=True, space="buy") + buy_2 = BooleanParameter(default=True, space="buy") + buy_3 = BooleanParameter(default=True, space="buy") + + buy_0_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_2_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_3_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + + buy_1_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_2_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_3_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + + buy_1_cmf100_sup = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + buy_2_cmf100_sup = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + buy_3_cmf100_sup = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + + buy_1_cmf100_inf = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + buy_2_cmf100_inf = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + buy_3_cmf100_inf = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + + buy_1_min = DecimalParameter(1, 1.03, decimals=3, default=1.02, space='buy') + buy_2_min = DecimalParameter(1, 1.03, decimals=3, default=1.02, space='buy') + buy_3_min = DecimalParameter(1, 1.03, decimals=3, default=1.02, space='buy') + + buy_1_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + buy_2_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + buy_3_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + + buy_1_decalage_deb = IntParameter(1, 3, default=5, space='buy') + buy_2_decalage_deb = IntParameter(1, 3, default=5, space='buy') + buy_3_decalage_deb = IntParameter(1, 3, default=5, space='buy') + + buy_1_decalage = IntParameter(buy_1_decalage_deb.value + 1, 8, default=5, space='buy') + buy_2_decalage = IntParameter(buy_2_decalage_deb.value + 1, 8, default=5, space='buy') + buy_3_decalage = IntParameter(buy_3_decalage_deb.value + 1, 8, default=5, space='buy') + + buy_1_volume = IntParameter(0, 100, default=10, space='buy') + buy_2_volume = IntParameter(0, 100, default=10, space='buy') + buy_3_volume = IntParameter(0, 100, default=10, space='buy') + + protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + protection_stop = IntParameter(1, 100, default=48, space='protection') + protection_stoploss_stop = IntParameter(1, 100, default=48, space='protection') + lookback = IntParameter(1, 200, default=48, space='protection') + trade_limit = IntParameter(1, 10, default=2, space='protection') + + inf_tf = '1h' + + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + + # for decalage in range(self.buy_3_decalage_deb.value, self.buy_3_decalage.value): + # decalage_candle = dataframe.iloc[- decalage].squeeze() + # # if (decalage_candle['normal_var_20'] >= 0.6): + # amount = 10 * (- decalage_candle['percent20'] * 100) + # print("use more stake", pair, " ", amount) + # return min(max_stake, amount) + + # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + # if current_candle['bb_width'] > 0.05: + # print("use more stake", pair, " ", proposed_stake * 2) + # return min(max_stake, proposed_stake * 2) + # + # if current_candle['bb_width'] > 0.035: + # print("use more stake", pair, " ", proposed_stake * 1.5) + # return min(max_stake, proposed_stake * 1.5) + + # if current_candle['bb_width'] < 0.020: + # print("use less stake", pair, " ", proposed_stake / 2) + # return min(max_stake, proposed_stake / 2) + # if self.config['stake_amount'] == 'unlimited': + # # Use entire available wallet during favorable conditions when in compounding mode. + # return max_stake + # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + + + # Use default stake amount. + return proposed_stake + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + + # {'symbol': 'FTM/USDT', 'timestamp': 1646494199570, 'datetime': '2022-03-05T15:29:59.570Z', 'high': 1.7489, + # 'low': 1.6084, 'bid': 1.6505, 'bidVolume': 2135.0, 'ask': 1.6508, 'askVolume': 2815.0, 'vwap': 1.66852198, + # 'open': 1.7313, 'close': 1.6505, 'last': 1.6505, 'previousClose': '1.73170000', 'change': -0.0808, + # 'percentage': -4.667, 'average': 1.6909, 'baseVolume': 124656725.0, 'quoteVolume': 207992485.7799, + # 'info': + # {'symbol': 'FTMUSDT', 'priceChange': '-0.08080000', 'priceChangePercent': '-4.667', + # 'weightedAvgPrice': '1.66852198', 'prevClosePrice': '1.73170000', 'lastPrice': '1.65050000', + # 'lastQty': '143.00000000', 'bidPrice': '1.65050000', 'bidQty': '2135.00000000', + # 'askPrice': '1.65080000', 'askQty': '2815.00000000', 'openPrice': '1.73130000', + # 'highPrice': '1.74890000', 'lowPrice': '1.60840000', 'volume': '124656725.00000000', + # 'quoteVolume': '207992485.77990000', 'openTime': '1646407799570', 'closeTime': '1646494199570', + # 'firstId': '137149614', 'lastId': '137450289', 'count': '300676'}} - 0.9817468621938484 + + allow_to_buy = True + max_gain = -100 + sum_gain = 0 + max_time = 0 + + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + if len(self.trades) == 0: + print('search') + self.trades = Trade.get_open_trades() + + if len(self.trades) >= self.config['max_open_trades'] / 2: + for trade in self.trades: + ticker = self.dp.ticker(trade.pair) + last_price = ticker['last'] + gain = (last_price - trade.open_rate) / trade.open_rate + max_gain = max(max_gain, gain) + sum_gain += gain + max_time = max(max_time, datetime.timestamp(trade.open_date)) + print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + now = datetime.now() + diff = (datetime.timestamp(now) - max_time / 3600) + if (max_gain <= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + print("allow_to_buy=false") + allow_to_buy = False + print(pair, allow_to_buy, len(self.trades), + "max gain=", max_gain, + "sum_gain=", sum_gain, + "now=", now, + "max=", max_time, + "diff=", datetime.timestamp(now) - max_time) + + if allow_to_buy: + self.trades = list() + + return allow_to_buy + + # @property + # def protections(self): + # return [ + # { + # "method": "CooldownPeriod", + # "stop_duration_candles": 10 + # }, + # { + # "method": "MaxDrawdown", + # "lookback_period_candles": self.lookback.value, + # "trade_limit": self.trade_limit.value, + # "stop_duration_candles": self.protection_stop.value, + # "max_allowed_drawdown": self.protection_max_allowed_dd.value, + # "only_per_pair": False + # }, + # { + # "method": "StoplossGuard", + # "lookback_period_candles": 24, + # "trade_limit": 4, + # "stop_duration_candles": self.protection_stoploss_stop.value, + # "only_per_pair": False + # } + # ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # if (0 < current_profit) & ((current_time - trade.open_date_utc).seconds > 3600) \ + # & (last_candle['percent10'] < 0.001): + # return 'small_profit' + # + # if (current_profit > 0.01) \ + # & ((previous_last_candle['sma10'] - last_candle['sma10']) / previous_last_candle['sma10'] > 0.003): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma10_quick' + + if (current_profit >= -0.01) & ((current_time - trade.open_date_utc).days >= 5)\ + & ((current_time - trade.open_date_utc).days < 10)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.01" + if (current_profit >= -0.02) & ((current_time - trade.open_date_utc).days >= 10)\ + & ((current_time - trade.open_date_utc).days < 15) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.02" + if (current_profit >= -0.03) & ((current_time - trade.open_date_utc).days >= 15) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.03" + + if self.profit_quick_lost: + if (current_profit >= 0) & (last_candle['percent3'] < -0.015): + return "quick_lost" + + if self.profit_no_change: + if (current_profit > 0.005) & (last_candle['percent10'] < 0.001) & (last_candle['percent5'] < 0) & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + if (current_profit > 0.005) & (last_candle['normal_var_20'] < 0.008): + return "no_change_20" + + if (current_profit > 0.01) & (last_candle['normal_var_10'] < 0.008): + return "no_change_10" + if (current_profit > 0.01) & (last_candle['normal_var_5'] < 0.005): + return "no_change_5" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if self.profit_quick_gain_3: + if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if self.profit_quick_gain: + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0): #& ((current_time - trade.open_date_utc).seconds <= 3600) + return "quick_gain" + + if self.profit_sma10: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent3'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi: + if (current_profit > 0) & (previous_last_candle['rsi'] > 88): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > 82) & (last_candle['percent'] < -0.02): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if self.profit_short_loss: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # Assign tf to each pair so they can be downloaded and cached for strategy. + informative_pairs = [(pair, self.inf_tf) for pair in pairs] + + # Optionally Add additional "static" pairs + # informative_pairs += [("ETH/USDT", "5m"), ("BTC/TUSD", "15m")] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['ecart_20'] = dataframe['close'].rolling(20).var() / dataframe['close'] + dataframe['ecart_50'] = dataframe['close'].rolling(50).var() / dataframe['close'] + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = ta.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_5'] = dataframe['normal'].rolling(5).var() + dataframe['normal_var_10'] = dataframe['normal'].rolling(10).var() + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + dataframe['cmf20'] = cmf(dataframe, 20) + dataframe['cmf50'] = cmf(dataframe, 50) + dataframe['cmf100'] = cmf(dataframe, 100) + + # dataframe["dist_min_50"] = dataframe['close'] - dataframe['min50'] + # dataframe["dist_min_20"] = dataframe['close'] - dataframe['min20'] + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + + # # INFORMATIVE PAIRS + + # Get the informative pair + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_tf) + + # Get the 14 day rsi + informative['rsi'] = ta.RSI(informative, timeperiod=14) + + # informative['cmf'] = cmf(informative, timeperiod=14) + + informative[buy_crossed_indicator0] = gene_calculator(informative, buy_crossed_indicator0) + informative[buy_indicator0] = gene_calculator(informative, buy_indicator0) + informative["cond1"] = informative[buy_indicator0].div(informative[buy_crossed_indicator0]) + + pandas.set_option('display.max_rows', informative.shape[0] + 1) + pandas.set_option('display.max_columns', 50) + + # print('informative', metadata['pair'], informative.tail(1)) + + # Use the helper function merge_informative_pair to safely merge the pair + # Automatically renames the columns and merges a shorter timeframe dataframe and a longer timeframe informative pair + # use ffill to have the 1d value available in every row throughout the day. + # Without this, comparisons between columns of the original and the informative pair would only work once per day. + # Full documentation of this method, see below + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, self.inf_tf, ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + # pandas.set_option('display.max_columns', 50) + # + # allow_to_buy = True + # max_gain = -100 + # trades = Trade.get_open_trades() + # + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # pairs = self.dp.current_whitelist() + # pairs_len = len(pairs) + # pair_index = pairs.index(metadata['pair']) + # + # # print(pair_index, " ", metadata['pair']) + # + # # ob = self.dp.orderbook(metadata['pair'], 1) + # # dataframe['best_bid'] = ob['bids'][0][0] + # # dataframe['best_ask'] = ob['asks'][0][0] + # # print(ob) + # + # for trade in trades: + # # if (metadata['pair'] == trade.pair): + # ticker = self.dp.ticker(trade.pair) #metadata['pair']) + # last_price = ticker['last'] + # # dataframe['volume24h'] = ticker['quoteVolume'] + # # dataframe['vwap'] = ticker['vwap'] + # # d = dataframe.tail(1) + # # print(dataframe) + # gain = (last_price - trade.open_rate) / trade.open_rate + # + # # print("Found open trade: ", trade, " ", ticker['last'], " ", trade.open_rate, gain) + # max_gain = max(max_gain, gain) + # + # if max_gain > - 0.05: + # allow_to_buy = False + # + # # print(metadata['pair'], max_gain, allow_to_buy, len(trades)) + + # for decalage in range(self.buy_1_decalage_deb.value, self.buy_1_decalage.value): + # if self.buy_0.value: + # conditions = list() + # condition1, dataframe = condition_generator( + # dataframe, + # buy_operator0, + # buy_indicator0, + # buy_crossed_indicator0, + # self.buy_1_real_num.value, + # self.buy_1_decalage.value + # ) + # conditions.append(condition1) + # dataframe.loc[ + # ( + # reduce(lambda x, y: x & y, conditions) + # & (dataframe['cmf100'] < self.buy_1_cmf100_sup.value) + # & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= self.buy_1_volume.value) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + # & (dataframe['close'] < dataframe['bb_middleband']) + # & (dataframe['open'] < dataframe['sma10']) + # & (dataframe['open'] < dataframe['sma100']) + # & (dataframe['min50'].shift(decalage) == dataframe['min50']) + # & (dataframe['open'] <= dataframe['min200'] * self.buy_1_min.value) + # & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # # & (dataframe['min20'] == dataframe['min50']) + # & (dataframe['distance_min'] <= self.buy_1_distance.value) + # & (dataframe['normal_var_20'] >= self.buy_1_normal_var.value) + # ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_2_decalage_deb.value, self.buy_2_decalage.value): + if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_2_real_num.value) + & (dataframe['cond1_1h'] <= self.buy_2_real_num.value) + & (dataframe['cmf100'] >= self.buy_2_cmf100_sup.value) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= self.buy_2_volume.value) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + # & (dataframe['normal_var_20'] >= self.buy_2_normal_var.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + for decalage in range(self.buy_3_decalage_deb.value, self.buy_3_decalage.value): + if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) #self.buy_3_real_num.value) + & (dataframe['cond1_1h'] <= self.buy_3_real_num.value) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= self.buy_3_volume.value) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['cmf100'] < self.buy_3_cmf100_sup.value) + # & (dataframe['cmf100'] > self.buy_3_cmf100_inf.value) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + # & (dataframe['open'] <= dataframe['min200'] * self.buy_3_min.value) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + # & (dataframe['normal_var_20'] >= self.buy_3_normal_var.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + # pair = metadata['pair'] + # allow_to_buy = True + # max_gain = -100 + # sum_gain = 0 + # max_time = 0 + # if len(self.trades) == 0: + # print('search') + # self.trades = Trade.get_open_trades() + # + # # if self.dp: + # # if self.dp.runmode.value in ('live', 'dry_run'): + # if len(self.trades) >= self.config['max_open_trades'] / 2: + # for trade in self.trades: + # ticker = self.dp.ticker(trade.pair) + # last_price = ticker['last'] + # gain = (last_price - trade.open_rate) / trade.open_rate + # max_gain = max(max_gain, gain) + # sum_gain += gain + # max_time = max(max_time, datetime.timestamp(trade.open_date)) + # print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + # datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + # now = datetime.now() + # diff = (datetime.timestamp(now) - max_time / 3600) + # if (max_gain >= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + # print("allow_to_buy=false") + # allow_to_buy = False + # print(pair, allow_to_buy, len(self.trades), + # "max gain=", max_gain, + # "sum_gain=", sum_gain, + # "now=", now, + # "max=", max_time, + # "diff=", datetime.timestamp(now) - max_time) + # + # if allow_to_buy: + # self.trades = list() + + # print(condition1) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_7_5_8.json b/GodStraJD3_7_5_8.json new file mode 100644 index 0000000..c9ddc86 --- /dev/null +++ b/GodStraJD3_7_5_8.json @@ -0,0 +1,46 @@ +{ + "strategy_name": "GodStraJD3_7_5_8", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.026, + "trailing_stop_positive_offset": 0.077, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_0": true, + "buy_0_percent20": -0.07, + "buy_1_volume": 43, + "buy_3": true, + "buy_3_cmf100_inf": -0.9, + "buy_3_cmf100_sup": 1.0, + "buy_3_cond_1h_num": 0.37, + "buy_3_cond_num": 1.78, + "buy_3_decalage": 8, + "buy_3_decalage_deb": 1, + "buy_3_distance": 0.1, + "buy_3_min": 1.022, + "buy_3_normal_var": 2.1, + "buy_3_percent20": 0.09, + "buy_3_real_num": 0.37, + "buy_3_volume": 60, + "buy_min_horizon": 58 + }, + "sell": {}, + "protection": { + "lookback": 133, + "protection_max_allowed_dd": 0.71, + "protection_stop": 61, + "protection_stoploss_stop": 50, + "trade_limit": 9 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-20 17:59:02.967586+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_7_5_8.py b/GodStraJD3_7_5_8.py new file mode 100644 index 0000000..3302a48 --- /dev/null +++ b/GodStraJD3_7_5_8.py @@ -0,0 +1,889 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import calendar +from freqtrade.loggers import setup_logging +from technical.indicators import cmf +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + + +class GodStraJD3_7_5_8(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min20': {'color': '#87e470'}, + 'min50': {'color': '#ac3e2a'}, + "min1.1": {'color': 'yellow'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'} + }, + # "Ind0": { + # buy_crossed_indicator0: {'color': 'green'}, + # buy_indicator0: {'color': 'red'} + # }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "Ecart": { + 'normal_var_20': {'color': 'red'}, + 'normal_var_50': {'color': 'yellow'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'}, + "distance_min": {'color': 'white'} + } + } + } + + # #################### END OF RESULT PLACE #################### + + profit_no_change = False + profit_old_sma10 = False + profit_over_rsi = True + profit_quick_gain = True + profit_quick_gain_3 = True + profit_quick_lost = False + profit_short_loss = False + profit_sma10 = True + profit_sma20 = True + profit_very_old_sma10 = False + + trades = list() + # profit_no_change = BooleanParameter(default=True, space="buy") + # profit_quick_lost = BooleanParameter(default=True, space="buy") + # profit_sma10 = BooleanParameter(default=True, space="buy") + # profit_sma20 = BooleanParameter(default=True, space="buy") + # profit_quick_gain = BooleanParameter(default=True, space="buy") + # profit_quick_gain_3 = BooleanParameter(default=True, space="buy") + # profit_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_very_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_over_rsi = BooleanParameter(default=True, space="buy") + # profit_short_loss = BooleanParameter(default=True, space="buy") + + # buy_signal_bb_width = DecimalParameter(0.06, 0.15, decimals=2, default=0.065, space='buy') + + # buy_1_cond_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + # buy_2_cond_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + buy_3_cond_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + + # buy_1_cond_1h_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + # buy_2_cond_1h_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + buy_3_cond_1h_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + + # buy_1_real_num = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + # buy_2_real_num = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_3_real_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + + buy_min_horizon = IntParameter(50, 200, default=72, space='buy') + + buy_0 = BooleanParameter(default=True, space="buy") + # buy_2 = BooleanParameter(default=True, space="buy") + buy_3 = BooleanParameter(default=True, space="buy") + + buy_0_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + # buy_2_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_3_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + + # buy_1_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + # buy_2_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_3_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + + # buy_1_cmf100_sup = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + # buy_2_cmf100_sup = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + buy_3_cmf100_sup = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + + # buy_1_cmf100_inf = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + # buy_2_cmf100_inf = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + buy_3_cmf100_inf = DecimalParameter(-1, 1, decimals=1, default=0.0, space='buy') + + # buy_1_min = DecimalParameter(1, 1.03, decimals=3, default=1.02, space='buy') + # buy_2_min = DecimalParameter(1, 1.03, decimals=3, default=1.02, space='buy') + buy_3_min = DecimalParameter(1, 1.03, decimals=3, default=1.02, space='buy') + + # buy_1_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + # buy_2_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + buy_3_normal_var = DecimalParameter(0, 5, decimals=1, default=0, space='buy') + + # buy_1_decalage_deb = IntParameter(1, 3, default=5, space='buy') + # buy_2_decalage_deb = IntParameter(1, 3, default=5, space='buy') + buy_3_decalage_deb = IntParameter(1, 3, default=5, space='buy') + + # buy_1_decalage = IntParameter(buy_1_decalage_deb.value + 1, 8, default=5, space='buy') + # buy_2_decalage = IntParameter(# buy_2_decalage_deb.value + 1, 8, default=5, space='buy') + buy_3_decalage = IntParameter(buy_3_decalage_deb.value + 1, 8, default=5, space='buy') + + buy_1_volume = IntParameter(0, 100, default=10, space='buy') + # buy_2_volume = IntParameter(0, 100, default=10, space='buy') + buy_3_volume = IntParameter(0, 100, default=10, space='buy') + + protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + protection_stop = IntParameter(1, 100, default=48, space='protection') + protection_stoploss_stop = IntParameter(1, 100, default=48, space='protection') + lookback = IntParameter(1, 200, default=48, space='protection') + trade_limit = IntParameter(1, 10, default=2, space='protection') + + inf_tf = '1h' + + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + + # for decalage in range(self.buy_3_decalage_deb.value, self.buy_3_decalage.value): + # decalage_candle = dataframe.iloc[- decalage].squeeze() + # # if (decalage_candle['normal_var_20'] >= 0.6): + # amount = 10 * (- decalage_candle['percent20'] * 100) + # print("use more stake", pair, " ", amount) + # return min(max_stake, amount) + + # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + # if current_candle['bb_width'] > 0.05: + # print("use more stake", pair, " ", proposed_stake * 2) + # return min(max_stake, proposed_stake * 2) + # + # if current_candle['bb_width'] > 0.035: + # print("use more stake", pair, " ", proposed_stake * 1.5) + # return min(max_stake, proposed_stake * 1.5) + + # if current_candle['bb_width'] < 0.020: + # print("use less stake", pair, " ", proposed_stake / 2) + # return min(max_stake, proposed_stake / 2) + # if self.config['stake_amount'] == 'unlimited': + # # Use entire available wallet during favorable conditions when in compounding mode. + # return max_stake + # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + + + # Use default stake amount. + return proposed_stake + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + + # {'symbol': 'FTM/USDT', 'timestamp': 1646494199570, 'datetime': '2022-03-05T15:29:59.570Z', 'high': 1.7489, + # 'low': 1.6084, 'bid': 1.6505, 'bidVolume': 2135.0, 'ask': 1.6508, 'askVolume': 2815.0, 'vwap': 1.66852198, + # 'open': 1.7313, 'close': 1.6505, 'last': 1.6505, 'previousClose': '1.73170000', 'change': -0.0808, + # 'percentage': -4.667, 'average': 1.6909, 'baseVolume': 124656725.0, 'quoteVolume': 207992485.7799, + # 'info': + # {'symbol': 'FTMUSDT', 'priceChange': '-0.08080000', 'priceChangePercent': '-4.667', + # 'weightedAvgPrice': '1.66852198', 'prevClosePrice': '1.73170000', 'lastPrice': '1.65050000', + # 'lastQty': '143.00000000', 'bidPrice': '1.65050000', 'bidQty': '2135.00000000', + # 'askPrice': '1.65080000', 'askQty': '2815.00000000', 'openPrice': '1.73130000', + # 'highPrice': '1.74890000', 'lowPrice': '1.60840000', 'volume': '124656725.00000000', + # 'quoteVolume': '207992485.77990000', 'openTime': '1646407799570', 'closeTime': '1646494199570', + # 'firstId': '137149614', 'lastId': '137450289', 'count': '300676'}} - 0.9817468621938484 + + allow_to_buy = True + max_gain = -100 + sum_gain = 0 + max_time = 0 + + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + if len(self.trades) == 0: + print('search') + self.trades = Trade.get_open_trades() + + if len(self.trades) >= self.config['max_open_trades'] / 2: + for trade in self.trades: + ticker = self.dp.ticker(trade.pair) + last_price = ticker['last'] + gain = (last_price - trade.open_rate) / trade.open_rate + max_gain = max(max_gain, gain) + sum_gain += gain + max_time = max(max_time, datetime.timestamp(trade.open_date)) + print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + now = datetime.now() + diff = (datetime.timestamp(now) - max_time / 3600) + if (max_gain <= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + print("allow_to_buy=false") + allow_to_buy = False + print(pair, allow_to_buy, len(self.trades), + "max gain=", max_gain, + "sum_gain=", sum_gain, + "now=", now, + "max=", max_time, + "diff=", datetime.timestamp(now) - max_time) + + if allow_to_buy: + self.trades = list() + + return allow_to_buy + + # @property + # def protections(self): + # return [ + # { + # "method": "CooldownPeriod", + # "stop_duration_candles": 10 + # }, + # { + # "method": "MaxDrawdown", + # "lookback_period_candles": self.lookback.value, + # "trade_limit": self.trade_limit.value, + # "stop_duration_candles": self.protection_stop.value, + # "max_allowed_drawdown": self.protection_max_allowed_dd.value, + # "only_per_pair": False + # }, + # { + # "method": "StoplossGuard", + # "lookback_period_candles": 24, + # "trade_limit": 4, + # "stop_duration_candles": self.protection_stoploss_stop.value, + # "only_per_pair": False + # } + # ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # if (0 < current_profit) & ((current_time - trade.open_date_utc).seconds > 3600) \ + # & (last_candle['percent10'] < 0.001): + # return 'small_profit' + # + # if (current_profit > 0.01) \ + # & ((previous_last_candle['sma10'] - last_candle['sma10']) / previous_last_candle['sma10'] > 0.003): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma10_quick' + + if (current_profit >= -0.01) & ((current_time - trade.open_date_utc).days >= 5)\ + & ((current_time - trade.open_date_utc).days < 10)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.01" + if (current_profit >= -0.02) & ((current_time - trade.open_date_utc).days >= 10)\ + & ((current_time - trade.open_date_utc).days < 15) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.02" + if (current_profit >= -0.03) & ((current_time - trade.open_date_utc).days >= 15) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.03" + + if self.profit_quick_lost: + if (current_profit >= 0) & (last_candle['percent3'] < -0.015): + return "quick_lost" + + if self.profit_no_change: + if (current_profit > 0.005) & (last_candle['percent10'] < 0.001) & (last_candle['percent5'] < 0) & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + if (current_profit > 0.005) & (last_candle['normal_var_20'] < 0.008): + return "no_change_20" + + if (current_profit > 0.01) & (last_candle['normal_var_10'] < 0.008): + return "no_change_10" + if (current_profit > 0.01) & (last_candle['normal_var_5'] < 0.005): + return "no_change_5" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if self.profit_quick_gain_3: + if (current_profit >= 0.03) & (last_candle['percent3'] < 0): + return "quick_gain_3" + if self.profit_quick_gain: + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0): + return "quick_gain" + + if self.profit_sma10: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi: + if (current_profit > 0) & (previous_last_candle['rsi'] > 88): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > 82) & (last_candle['percent'] < -0.02): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if self.profit_short_loss: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # Assign tf to each pair so they can be downloaded and cached for strategy. + informative_pairs = [(pair, self.inf_tf) for pair in pairs] + + # Optionally Add additional "static" pairs + # informative_pairs += [("ETH/USDT", "5m"), ("BTC/TUSD", "15m")] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['ecart_20'] = dataframe['close'].rolling(20).var() / dataframe['close'] + dataframe['ecart_50'] = dataframe['close'].rolling(50).var() / dataframe['close'] + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = ta.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_5'] = dataframe['normal'].rolling(5).var() + dataframe['normal_var_10'] = dataframe['normal'].rolling(10).var() + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + dataframe['cmf20'] = cmf(dataframe, 20) + dataframe['cmf50'] = cmf(dataframe, 50) + dataframe['cmf100'] = cmf(dataframe, 100) + + # dataframe["dist_min_50"] = dataframe['close'] - dataframe['min50'] + # dataframe["dist_min_20"] = dataframe['close'] - dataframe['min20'] + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + + # # INFORMATIVE PAIRS + + # Get the informative pair + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_tf) + # Get the 14 day rsi + informative['rsi'] = ta.RSI(informative, timeperiod=14) + # informative["percent"] = (informative["close"] - informative["open"]) / informative["open"] + # informative["percent5"] = informative["percent"].rolling(5).sum() + # informative["percent3"] = informative["percent"].rolling(3).sum() + + + informative[buy_crossed_indicator0] = gene_calculator(informative, buy_crossed_indicator0) + informative[buy_indicator0] = gene_calculator(informative, buy_indicator0) + informative["cond1"] = informative[buy_indicator0].div(informative[buy_crossed_indicator0]) + + pandas.set_option('display.max_rows', informative.shape[0] + 1) + pandas.set_option('display.max_columns', 50) + + # print('informative', metadata['pair'], informative.tail(1)) + + # Use the helper function merge_informative_pair to safely merge the pair + # Automatically renames the columns and merges a shorter timeframe dataframe and a longer timeframe informative pair + # use ffill to have the 1d value available in every row throughout the day. + # Without this, comparisons between columns of the original and the informative pair would only work once per day. + # Full documentation of this method, see below + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, self.inf_tf, ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + # pandas.set_option('display.max_columns', 50) + # + # allow_to_buy = True + # max_gain = -100 + # trades = Trade.get_open_trades() + # + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # pairs = self.dp.current_whitelist() + # pairs_len = len(pairs) + # pair_index = pairs.index(metadata['pair']) + # + # # print(pair_index, " ", metadata['pair']) + # + # # ob = self.dp.orderbook(metadata['pair'], 1) + # # dataframe['best_bid'] = ob['bids'][0][0] + # # dataframe['best_ask'] = ob['asks'][0][0] + # # print(ob) + # + # for trade in trades: + # # if (metadata['pair'] == trade.pair): + # ticker = self.dp.ticker(trade.pair) #metadata['pair']) + # last_price = ticker['last'] + # # dataframe['volume24h'] = ticker['quoteVolume'] + # # dataframe['vwap'] = ticker['vwap'] + # # d = dataframe.tail(1) + # # print(dataframe) + # gain = (last_price - trade.open_rate) / trade.open_rate + # + # # print("Found open trade: ", trade, " ", ticker['last'], " ", trade.open_rate, gain) + # max_gain = max(max_gain, gain) + # + # if max_gain > - 0.05: + # allow_to_buy = False + # + # # print(metadata['pair'], max_gain, allow_to_buy, len(trades)) + + # for decalage in range(self.buy_1_decalage_deb.value, self.buy_1_decalage.value): + # if self.buy_0.value: + # conditions = list() + # condition1, dataframe = condition_generator( + # dataframe, + # buy_operator0, + # buy_indicator0, + # buy_crossed_indicator0, + # self.buy_1_real_num.value, + # self.buy_1_decalage.value + # ) + # conditions.append(condition1) + # dataframe.loc[ + # ( + # reduce(lambda x, y: x & y, conditions) + # & (dataframe['cmf100'] < self.buy_1_cmf100_sup.value) + # & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= self.buy_1_volume.value) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + # & (dataframe['close'] < dataframe['bb_middleband']) + # & (dataframe['open'] < dataframe['sma10']) + # & (dataframe['open'] < dataframe['sma100']) + # & (dataframe['min50'].shift(decalage) == dataframe['min50']) + # & (dataframe['open'] <= dataframe['min200'] * self.buy_1_min.value) + # & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # # & (dataframe['min20'] == dataframe['min50']) + # & (dataframe['distance_min'] <= self.buy_1_distance.value) + # & (dataframe['normal_var_20'] >= self.buy_1_normal_var.value) + # ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + # for decalage in range(self.buy_2_decalage_deb.value, self.buy_2_decalage.value): + # if self.buy_2.value: + # dataframe.loc[ + # ( + # (dataframe['cond1'].shift(decalage) <= self.buy_2_cond_num.value) + # & (dataframe['cond1'].shift(decalage) >= self.buy_3_cond_num.value) + # & (dataframe['cond1_1h'] <= self.buy_2_cond_1h_num.value) + # & (dataframe['cmf100'] >= self.buy_2_cmf100_sup.value) + # & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= self.buy_2_volume.value) + # # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + # & (dataframe['close'] < dataframe['sma10']) + # & (dataframe['open'] < dataframe['sma100']) + # & (dataframe['open'] < dataframe['sma10']) + # & (dataframe['min50'].shift(decalage) == dataframe['min50']) + # #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + # & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + # # & (dataframe['min20'] == dataframe['min50']) + # & (dataframe['distance_min'] <= self.buy_2_distance.value) + # # & (dataframe['normal_var_20'] >= self.buy_2_normal_var.value) + # ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + for decalage in range(self.buy_3_decalage_deb.value, self.buy_3_decalage.value): + if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= self.buy_3_cond_num.value) + & (dataframe['cond1_1h'] <= self.buy_3_cond_1h_num.value) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= self.buy_3_volume.value) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['cmf100'] < self.buy_3_cmf100_sup.value) + & (dataframe['cmf100'] > self.buy_3_cmf100_inf.value) + # & (dataframe['cmf100'] > self.buy_3_cmf100_inf.value) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + # & (dataframe['open'] <= dataframe['min200'] * self.buy_3_min.value) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + # & (dataframe['normal_var_20'] >= self.buy_3_normal_var.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + # pair = metadata['pair'] + # allow_to_buy = True + # max_gain = -100 + # sum_gain = 0 + # max_time = 0 + # if len(self.trades) == 0: + # print('search') + # self.trades = Trade.get_open_trades() + # + # # if self.dp: + # # if self.dp.runmode.value in ('live', 'dry_run'): + # if len(self.trades) >= self.config['max_open_trades'] / 2: + # for trade in self.trades: + # ticker = self.dp.ticker(trade.pair) + # last_price = ticker['last'] + # gain = (last_price - trade.open_rate) / trade.open_rate + # max_gain = max(max_gain, gain) + # sum_gain += gain + # max_time = max(max_time, datetime.timestamp(trade.open_date)) + # print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + # datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + # now = datetime.now() + # diff = (datetime.timestamp(now) - max_time / 3600) + # if (max_gain >= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + # print("allow_to_buy=false") + # allow_to_buy = False + # print(pair, allow_to_buy, len(self.trades), + # "max gain=", max_gain, + # "sum_gain=", sum_gain, + # "now=", now, + # "max=", max_time, + # "diff=", datetime.timestamp(now) - max_time) + # + # if allow_to_buy: + # self.trades = list() + + # print(condition1) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_7_5_9.json b/GodStraJD3_7_5_9.json new file mode 100644 index 0000000..1582465 --- /dev/null +++ b/GodStraJD3_7_5_9.json @@ -0,0 +1,52 @@ +{ + "strategy_name": "GodStraJD3_7_5_9", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.026, + "trailing_stop_positive_offset": 0.077, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_0_distance": -0.08, + "buy_0_percent20": 0.01, + "buy_1_bb_lower_5": 0.06, + "buy_2_bb_lower_5": 0.59, + "buy_2_distance": 0.1, + "buy_2_percent20": 0.05, + "buy_3_bb_lower_5": 0.47, + "buy_3_distance": 0.05, + "buy_3_percent20": -0.07, + "buy_decalage0": 7, + "buy_decalage2": 6, + "buy_decalage3": 8, + "buy_decalage_deb_0": 1, + "buy_decalage_deb_2": 1, + "buy_decalage_deb_3": 2, + "buy_min_horizon": 119, + "buy_real_num0": 0.14, + "buy_real_num1": 0.36, + "buy_real_num2": 1.71 + }, + "sell": { + "sell_candels": 40, + "sell_percent": 0.0, + "sell_percent3": 0.014 + }, + "protection": { + "lookback": 133, + "protection_max_allowed_dd": 0.71, + "protection_stop": 61, + "protection_stoploss_stop": 50, + "trade_limit": 9 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-25 00:29:29.617986+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_7_5_9.py b/GodStraJD3_7_5_9.py new file mode 100644 index 0000000..3acd4bc --- /dev/null +++ b/GodStraJD3_7_5_9.py @@ -0,0 +1,861 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + + +class GodStraJD3_7_5_9(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min20': {'color': '#87e470'}, + 'min50': {'color': '#ac3e2a'}, + "min1.1": {'color': 'yellow'}, + 'sma20': {'color': 'cyan'}, + 'open_1d': {'color': 'white'}, + 'close_1d': {'color': 'white'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + 'bb_lower_5': {'color': 'yellow'} + }, + # "Ind0": { + # buy_crossed_indicator0: {'color': 'green'}, + # buy_indicator0: {'color': 'red'} + # }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "Ecart": { + 'normal_var_20': {'color': 'red'}, + 'normal_var_50': {'color': 'yellow'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'}, + "distance_min": {'color': 'white'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + profit_no_change = False + profit_old_sma10 = False + profit_over_rsi = True + profit_quick_gain = True + profit_quick_gain_3 = True + profit_quick_lost = True + profit_short_loss = False + profit_sma10 = True + profit_sma20 = True + profit_very_old_sma10 = False + + trades = list() + # profit_no_change = BooleanParameter(default=True, space="buy") + # profit_quick_lost = BooleanParameter(default=True, space="buy") + # profit_sma10 = BooleanParameter(default=True, space="buy") + # profit_sma20 = BooleanParameter(default=True, space="buy") + # profit_quick_gain = BooleanParameter(default=True, space="buy") + # profit_quick_gain_3 = BooleanParameter(default=True, space="buy") + # profit_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_very_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_over_rsi = BooleanParameter(default=True, space="buy") + # profit_short_loss = BooleanParameter(default=True, space="buy") + + # buy_signal_bb_width = DecimalParameter(0.06, 0.15, decimals=2, default=0.065, space='buy') + + buy_real_num0 = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_real_num1 = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_real_num2 = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + #buy_3_cond_1h_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + # buy_1_percent_4h_num = DecimalParameter(-0.1, 0.1, decimals=2, default=0.67, space='buy') + # buy_2_percent_4h_num = DecimalParameter(-0.1, 0.1, decimals=2, default=0.67, space='buy') + # buy_3_percent_4h_num = DecimalParameter(-0.1, 0.1, decimals=2, default=0.67, space='buy') + #buy_3_percent_1d_num = DecimalParameter(-0.1, 0.1, decimals=2, default=0.67, space='buy') + #buy_3_percent_1w_num = DecimalParameter(-0.1, 0.1, decimals=2, default=0.67, space='buy') + + buy_min_horizon = IntParameter(50, 200, default=72, space='buy') + + #buy_0 = BooleanParameter(default=True, space="buy") + #buy_2 = BooleanParameter(default=True, space="buy") + #buy_3 = BooleanParameter(default=True, space="buy") + + buy_0_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_2_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_3_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + + buy_0_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_2_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_3_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + + buy_decalage_deb_0 = IntParameter(0, 3, default=5, space='buy') + buy_decalage_deb_2 = IntParameter(0, 3, default=5, space='buy') + buy_decalage_deb_3 = IntParameter(0, 3, default=5, space='buy') + + buy_decalage0 = IntParameter(buy_decalage_deb_0.value + 1, 8, default=5, space='buy') + buy_decalage2 = IntParameter(buy_decalage_deb_2.value + 1, 8, default=5, space='buy') + buy_decalage3 = IntParameter(buy_decalage_deb_3.value + 1, 8, default=5, space='buy') + + # buy_1_bb_lower_var_5 = DecimalParameter(0, 0.4, decimals=2, default=0.07, space='buy') + # buy_2_bb_lower_var_5 = DecimalParameter(0, 0.4, decimals=2, default=0.07, space='buy') + # buy_3_bb_lower_var_5 = DecimalParameter(0, 0.4, decimals=2, default=0.07, space='buy') + + buy_1_bb_lower_5 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + buy_2_bb_lower_5 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + buy_3_bb_lower_5 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + + protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + protection_stop = IntParameter(1, 100, default=48, space='protection') + protection_stoploss_stop = IntParameter(1, 100, default=48, space='protection') + lookback = IntParameter(1, 200, default=48, space='protection') + trade_limit = IntParameter(1, 10, default=2, space='protection') + + sell_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_candels = IntParameter(0, 48, default=12, space='sell') + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + + # {'symbol': 'FTM/USDT', 'timestamp': 1646494199570, 'datetime': '2022-03-05T15:29:59.570Z', 'high': 1.7489, + # 'low': 1.6084, 'bid': 1.6505, 'bidVolume': 2135.0, 'ask': 1.6508, 'askVolume': 2815.0, 'vwap': 1.66852198, + # 'open': 1.7313, 'close': 1.6505, 'last': 1.6505, 'previousClose': '1.73170000', 'change': -0.0808, + # 'percentage': -4.667, 'average': 1.6909, 'baseVolume': 124656725.0, 'quoteVolume': 207992485.7799, + # 'info': + # {'symbol': 'FTMUSDT', 'priceChange': '-0.08080000', 'priceChangePercent': '-4.667', + # 'weightedAvgPrice': '1.66852198', 'prevClosePrice': '1.73170000', 'lastPrice': '1.65050000', + # 'lastQty': '143.00000000', 'bidPrice': '1.65050000', 'bidQty': '2135.00000000', + # 'askPrice': '1.65080000', 'askQty': '2815.00000000', 'openPrice': '1.73130000', + # 'highPrice': '1.74890000', 'lowPrice': '1.60840000', 'volume': '124656725.00000000', + # 'quoteVolume': '207992485.77990000', 'openTime': '1646407799570', 'closeTime': '1646494199570', + # 'firstId': '137149614', 'lastId': '137450289', 'count': '300676'}} - 0.9817468621938484 + + allow_to_buy = True + max_gain = -100 + sum_gain = 0 + max_time = 0 + + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + if len(self.trades) == 0: + print('search') + self.trades = Trade.get_open_trades() + + if len(self.trades) >= self.config['max_open_trades'] / 2: + for trade in self.trades: + ticker = self.dp.ticker(trade.pair) + last_price = ticker['last'] + gain = (last_price - trade.open_rate) / trade.open_rate + max_gain = max(max_gain, gain) + sum_gain += gain + max_time = max(max_time, datetime.timestamp(trade.open_date)) + print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + now = datetime.now() + diff = (datetime.timestamp(now) - max_time / 3600) + if (max_gain <= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + print("allow_to_buy=false") + allow_to_buy = False + print(pair, allow_to_buy, len(self.trades), + "max gain=", max_gain, + "sum_gain=", sum_gain, + "now=", now, + "max=", max_time, + "diff=", datetime.timestamp(now) - max_time) + + if allow_to_buy: + self.trades = list() + + return allow_to_buy + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + { + "method": "MaxDrawdown", + "lookback_period_candles": self.lookback.value, + "trade_limit": self.trade_limit.value, + "stop_duration_candles": self.protection_stop.value, + "max_allowed_drawdown": self.protection_max_allowed_dd.value, + "only_per_pair": False + }, + { + "method": "StoplossGuard", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": self.protection_stoploss_stop.value, + "only_per_pair": False + } + ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # if (0 < current_profit) & ((current_time - trade.open_date_utc).seconds > 3600) \ + # & (last_candle['percent10'] < 0.001): + # return 'small_profit' + # + # if (current_profit > 0.01) \ + # & ((previous_last_candle['sma10'] - last_candle['sma10']) / previous_last_candle['sma10'] > 0.003): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma10_quick' + + if (current_profit >= -0.01) & ((current_time - trade.open_date_utc).days >= 5)\ + & ((current_time - trade.open_date_utc).days < 10)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.01" + if (current_profit >= -0.02) & ((current_time - trade.open_date_utc).days >= 10)\ + & ((current_time - trade.open_date_utc).days < 15) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.02" + if (current_profit >= -0.03) & ((current_time - trade.open_date_utc).days >= 15) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.03" + + if self.profit_quick_lost: + if (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "quick_lost" + + if self.profit_no_change: + if (current_profit > 0.005) & (last_candle['percent10'] < 0.001) & (last_candle['percent5'] < 0) & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + # if self.profit_quick_gain_3: + # if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + # return "quick_gain_3" + # if self.profit_quick_gain: + # if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + # return "quick_gain" + + if (current_profit > self.sell_percent.value) & (last_candle['percent3'] < - self.sell_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_candels.value): + return "quick_gain_param" + + if self.profit_sma10: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi: + if (current_profit > 0) & (previous_last_candle['rsi'] > 88): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > 82) & (last_candle['percent'] < -0.02): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if self.profit_short_loss: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + # def informative_pairs(self): + # # get access to all pairs available in whitelist. + # pairs = self.dp.current_whitelist() + # # Assign tf to each pair so they can be downloaded and cached for strategy. + # # informative_pairs = [(pair, "4h") for pair in pairs] + # informative_pairs = [(pair, '1d') for pair in pairs] + # #informative_pairs += [(pair, '1w') for pair in pairs] + # + # # Optionally Add additional "static" pairs + # # informative_pairs += [("ETH/USDT", "5m"), ("BTC/TUSD", "15m")] + # + # return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['ecart_20'] = dataframe['close'].rolling(20).var() / dataframe['close'] + dataframe['ecart_50'] = dataframe['close'].rolling(50).var() / dataframe['close'] + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = ta.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + # dataframe['TR'] = ta.TRANGE(dataframe) + # dataframe['ATR'] = ta.SMA(dataframe['TR'], 21) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + + # dataframe["dist_min_50"] = dataframe['close'] - dataframe['min50'] + # dataframe["dist_min_20"] = dataframe['close'] - dataframe['min20'] + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + + # # INFORMATIVE PAIRS + + # # Get the informative pair + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + # informative[buy_crossed_indicator0] = gene_calculator(informative, buy_crossed_indicator0) + # informative[buy_indicator0] = gene_calculator(informative, buy_indicator0) + # informative["cond1"] = informative[buy_indicator0].div(informative[buy_crossed_indicator0]) + # informative["percent"] = (informative["close"] - informative["open"]) / informative["open"] + # informative["percent5"] = informative["percent"].rolling(5).sum() + # informative["percent3"] = informative["percent"].rolling(3).sum() + # pandas.set_option('display.max_rows', informative.shape[0] + 1) + # pandas.set_option('display.max_columns', 50) + # # print('informative', metadata['pair'], informative.tail(1)) + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='1d') + # informative[buy_crossed_indicator0] = gene_calculator(informative, buy_crossed_indicator0) + # informative[buy_indicator0] = gene_calculator(informative, buy_indicator0) + # informative["cond1"] = informative[buy_indicator0].div(informative[buy_crossed_indicator0]) + # informative["percent"] = (informative["close"] - informative["open"]) / informative["open"] + # informative['max_open'] = ta.MAX(informative["open"], timeperiod=2) + # informative['max_close'] = ta.MIN(informative["close"], timeperiod=2) + # informative["percent5"] = informative["percent"].rolling(5).sum() + # informative["percent3"] = informative["percent"].rolling(3).sum() + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, '1d', ffill=True) + + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='1w') + # informative[buy_crossed_indicator0] = gene_calculator(informative, buy_crossed_indicator0) + # informative[buy_indicator0] = gene_calculator(informative, buy_indicator0) + # informative["cond1"] = informative[buy_indicator0].div(informative[buy_crossed_indicator0]) + # informative["percent"] = (informative["close"] - informative["open"]) / informative["open"] + # informative["percent5"] = informative["percent"].rolling(5).sum() + # informative["percent3"] = informative["percent"].rolling(3).sum() + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, '1w', ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + # pandas.set_option('display.max_columns', 50) + # + # allow_to_buy = True + # max_gain = -100 + # trades = Trade.get_open_trades() + # + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # pairs = self.dp.current_whitelist() + # pairs_len = len(pairs) + # pair_index = pairs.index(metadata['pair']) + # + # # print(pair_index, " ", metadata['pair']) + # + # # ob = self.dp.orderbook(metadata['pair'], 1) + # # dataframe['best_bid'] = ob['bids'][0][0] + # # dataframe['best_ask'] = ob['asks'][0][0] + # # print(ob) + # + # for trade in trades: + # # if (metadata['pair'] == trade.pair): + # ticker = self.dp.ticker(trade.pair) #metadata['pair']) + # last_price = ticker['last'] + # # dataframe['volume24h'] = ticker['quoteVolume'] + # # dataframe['vwap'] = ticker['vwap'] + # # d = dataframe.tail(1) + # # print(dataframe) + # gain = (last_price - trade.open_rate) / trade.open_rate + # + # # print("Found open trade: ", trade, " ", ticker['last'], " ", trade.open_rate, gain) + # max_gain = max(max_gain, gain) + # + # if max_gain > - 0.05: + # allow_to_buy = False + # + # # print(metadata['pair'], max_gain, allow_to_buy, len(trades)) + + for decalage in range(self.buy_decalage_deb_0.value, self.buy_decalage0.value): + #if self.buy_0.value: + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value, + self.buy_decalage0.value + ) + conditions.append(condition1) + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_0_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift(decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_1_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_1_bb_lower_5.value) + #& (dataframe['percent_1d'] >= self.buy_1_percent_1d_num.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_1_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_2.value, self.buy_decalage2.value): + #if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_real_num0.value / 2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_2_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_2_bb_lower_5.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_2_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_3.value, self.buy_decalage3.value): + #if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_3_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_3_bb_lower_5.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_3_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + # dataframe.loc[ + # ( + # (dataframe['close'] < dataframe['close_1d']) + # & (dataframe['close'] > dataframe['bb_upperband']) + # ), ['buy', 'buy_tag']] = (1, 'buy_4') + + # pair = metadata['pair'] + # allow_to_buy = True + # max_gain = -100 + # sum_gain = 0 + # max_time = 0 + # if len(self.trades) == 0: + # print('search') + # self.trades = Trade.get_open_trades() + # + # # if self.dp: + # # if self.dp.runmode.value in ('live', 'dry_run'): + # if len(self.trades) >= self.config['max_open_trades'] / 2: + # for trade in self.trades: + # ticker = self.dp.ticker(trade.pair) + # last_price = ticker['last'] + # gain = (last_price - trade.open_rate) / trade.open_rate + # max_gain = max(max_gain, gain) + # sum_gain += gain + # max_time = max(max_time, datetime.timestamp(trade.open_date)) + # print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + # datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + # now = datetime.now() + # diff = (datetime.timestamp(now) - max_time / 3600) + # if (max_gain >= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + # print("allow_to_buy=false") + # allow_to_buy = False + # print(pair, allow_to_buy, len(self.trades), + # "max gain=", max_gain, + # "sum_gain=", sum_gain, + # "now=", now, + # "max=", max_time, + # "diff=", datetime.timestamp(now) - max_time) + # + # if allow_to_buy: + # self.trades = list() + + # print(condition1) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_7_5_9_1.json b/GodStraJD3_7_5_9_1.json new file mode 100644 index 0000000..0dcf549 --- /dev/null +++ b/GodStraJD3_7_5_9_1.json @@ -0,0 +1,67 @@ +{ + "strategy_name": "GodStraJD3_7_5_9_1", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.026, + "trailing_stop_positive_offset": 0.077, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_0_distance": -0.03, + "buy_0_percent20": -0.03, + "buy_1_bb_lower_5": 0.19, + "buy_1_pente_sma10": 0.48, + "buy_1_pente_sma20": 0.33, + "buy_2_bb_lower_5": 0.59, + "buy_2_distance": 0.08, + "buy_2_pente_sma10": 0.46, + "buy_2_pente_sma20": 0.08, + "buy_2_percent20": 0.06, + "buy_3_bb_lower_5": 0.05, + "buy_3_distance": 0.01, + "buy_3_pente_sma10": 0.12, + "buy_3_pente_sma20": 0.27, + "buy_3_percent20": -0.03, + "buy_4_pente_sma10": 0.35, + "buy_4_pente_sma20": 0.22, + "buy_decalage0": 7, + "buy_decalage2": 6, + "buy_decalage3": 7, + "buy_decalage_deb_0": 1, + "buy_decalage_deb_2": 1, + "buy_decalage_deb_3": 1, + "buy_min_horizon": 192, + "buy_real_num0": 0.61, + "buy_real_num1": 0.34, + "buy_real_num2": 0.35 + }, + "sell": { + "sell_RSI": 95, + "sell_RSI2": 81, + "sell_RSI2_percent": 0.008, + "sell_candels": 17, + "sell_percent": 0.016, + "sell_percent3": 0.001, + "sell_profit_no_change": 0.016, + "sell_profit_percent10": 0.0002, + "sell_too_old_day": 7, + "sell_too_old_percent": 0.005 + }, + "protection": { + "lookback": 133, + "protection_max_allowed_dd": 0.71, + "protection_stop": 61, + "protection_stoploss_stop": 50, + "trade_limit": 9 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-29 20:57:57.922162+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_7_5_9_1.py b/GodStraJD3_7_5_9_1.py new file mode 100644 index 0000000..247dbfe --- /dev/null +++ b/GodStraJD3_7_5_9_1.py @@ -0,0 +1,863 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + + +class GodStraJD3_7_5_9_1(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min20': {'color': '#87e470'}, + 'min50': {'color': '#ac3e2a'}, + "min1.1": {'color': 'yellow'}, + 'sma20': {'color': 'cyan'}, + 'open_1d': {'color': 'white'}, + 'close_1d': {'color': 'white'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + 'bb_lower_5': {'color': 'yellow'} + }, + # "Ind0": { + # buy_crossed_indicator0: {'color': 'green'}, + # buy_indicator0: {'color': 'red'} + # }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "Ecart": { + 'normal_var_20': {'color': 'red'}, + 'normal_var_50': {'color': 'yellow'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'}, + "distance_min": {'color': 'white'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + profit_no_change = True + profit_old_sma10 = False + profit_over_rsi = True + profit_quick_gain = True + profit_quick_gain_3 = True + profit_quick_lost = True + profit_short_loss = False + profit_sma5 = True + profit_sma10 = True + profit_sma20 = True + profit_very_old_sma10 = False + + trades = list() + # profit_no_change = BooleanParameter(default=True, space="buy") + # profit_quick_lost = BooleanParameter(default=True, space="buy") + # profit_sma10 = BooleanParameter(default=True, space="buy") + # profit_sma20 = BooleanParameter(default=True, space="buy") + # profit_quick_gain = BooleanParameter(default=True, space="buy") + # profit_quick_gain_3 = BooleanParameter(default=True, space="buy") + # profit_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_very_old_sma10 = BooleanParameter(default=True, space="buy") + # profit_over_rsi = BooleanParameter(default=True, space="buy") + # profit_short_loss = BooleanParameter(default=True, space="buy") + + # buy_signal_bb_width = DecimalParameter(0.06, 0.15, decimals=2, default=0.065, space='buy') + + buy_real_num0 = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_real_num1 = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_real_num2 = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + #buy_3_cond_1h_num = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + # buy_1_percent_4h_num = DecimalParameter(-0.1, 0.1, decimals=2, default=0.67, space='buy') + # buy_2_percent_4h_num = DecimalParameter(-0.1, 0.1, decimals=2, default=0.67, space='buy') + # buy_3_percent_4h_num = DecimalParameter(-0.1, 0.1, decimals=2, default=0.67, space='buy') + #buy_3_percent_1d_num = DecimalParameter(-0.1, 0.1, decimals=2, default=0.67, space='buy') + #buy_3_percent_1w_num = DecimalParameter(-0.1, 0.1, decimals=2, default=0.67, space='buy') + + buy_min_horizon = IntParameter(50, 200, default=72, space='buy') + + #buy_0 = BooleanParameter(default=True, space="buy") + #buy_2 = BooleanParameter(default=True, space="buy") + #buy_3 = BooleanParameter(default=True, space="buy") + + buy_0_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_2_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_3_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + + buy_0_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_2_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_3_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + + buy_decalage_deb_0 = IntParameter(0, 3, default=5, space='buy') + buy_decalage_deb_2 = IntParameter(0, 3, default=5, space='buy') + buy_decalage_deb_3 = IntParameter(0, 3, default=5, space='buy') + + buy_decalage0 = IntParameter(buy_decalage_deb_0.value + 1, 8, default=5, space='buy') + buy_decalage2 = IntParameter(buy_decalage_deb_2.value + 1, 8, default=5, space='buy') + buy_decalage3 = IntParameter(buy_decalage_deb_3.value + 1, 8, default=5, space='buy') + + buy_1_pente_sma10 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + buy_2_pente_sma10 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + buy_3_pente_sma10 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + buy_4_pente_sma10 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + + buy_1_pente_sma20 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + buy_2_pente_sma20 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + buy_3_pente_sma20 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + buy_4_pente_sma20 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + + # buy_1_bb_lower_var_5 = DecimalParameter(0, 0.4, decimals=2, default=0.07, space='buy') + # buy_2_bb_lower_var_5 = DecimalParameter(0, 0.4, decimals=2, default=0.07, space='buy') + # buy_3_bb_lower_var_5 = DecimalParameter(0, 0.4, decimals=2, default=0.07, space='buy') + + buy_1_bb_lower_5 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + buy_2_bb_lower_5 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + buy_3_bb_lower_5 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + + protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + protection_stop = IntParameter(1, 100, default=48, space='protection') + protection_stoploss_stop = IntParameter(1, 100, default=48, space='protection') + lookback = IntParameter(1, 200, default=48, space='protection') + trade_limit = IntParameter(1, 10, default=2, space='protection') + + sell_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_candels = IntParameter(0, 48, default=12, space='sell') + + sell_too_old_day = IntParameter(0, 10, default=5, space='sell') + sell_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + sell_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') + sell_profit_percent10 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') + + sell_RSI = IntParameter(70, 98, default=88, space='sell') + sell_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + + # {'symbol': 'FTM/USDT', 'timestamp': 1646494199570, 'datetime': '2022-03-05T15:29:59.570Z', 'high': 1.7489, + # 'low': 1.6084, 'bid': 1.6505, 'bidVolume': 2135.0, 'ask': 1.6508, 'askVolume': 2815.0, 'vwap': 1.66852198, + # 'open': 1.7313, 'close': 1.6505, 'last': 1.6505, 'previousClose': '1.73170000', 'change': -0.0808, + # 'percentage': -4.667, 'average': 1.6909, 'baseVolume': 124656725.0, 'quoteVolume': 207992485.7799, + # 'info': + # {'symbol': 'FTMUSDT', 'priceChange': '-0.08080000', 'priceChangePercent': '-4.667', + # 'weightedAvgPrice': '1.66852198', 'prevClosePrice': '1.73170000', 'lastPrice': '1.65050000', + # 'lastQty': '143.00000000', 'bidPrice': '1.65050000', 'bidQty': '2135.00000000', + # 'askPrice': '1.65080000', 'askQty': '2815.00000000', 'openPrice': '1.73130000', + # 'highPrice': '1.74890000', 'lowPrice': '1.60840000', 'volume': '124656725.00000000', + # 'quoteVolume': '207992485.77990000', 'openTime': '1646407799570', 'closeTime': '1646494199570', + # 'firstId': '137149614', 'lastId': '137450289', 'count': '300676'}} - 0.9817468621938484 + + allow_to_buy = True + max_gain = -100 + sum_gain = 0 + max_time = 0 + + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + if len(self.trades) == 0: + print('search') + self.trades = Trade.get_open_trades() + + if len(self.trades) >= self.config['max_open_trades'] / 2: + for trade in self.trades: + ticker = self.dp.ticker(trade.pair) + last_price = ticker['last'] + gain = (last_price - trade.open_rate) / trade.open_rate + max_gain = max(max_gain, gain) + sum_gain += gain + max_time = max(max_time, datetime.timestamp(trade.open_date)) + print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + now = datetime.now() + diff = (datetime.timestamp(now) - max_time / 3600) + if (max_gain <= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + print("allow_to_buy=false") + allow_to_buy = False + print(pair, allow_to_buy, len(self.trades), + "max gain=", max_gain, + "sum_gain=", sum_gain, + "now=", now, + "max=", max_time, + "diff=", datetime.timestamp(now) - max_time) + + if allow_to_buy: + self.trades = list() + + return allow_to_buy + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + # { + # "method": "MaxDrawdown", + # "lookback_period_candles": self.lookback.value, + # "trade_limit": self.trade_limit.value, + # "stop_duration_candles": self.protection_stop.value, + # "max_allowed_drawdown": self.protection_max_allowed_dd.value, + # "only_per_pair": False + # }, + # { + # "method": "StoplossGuard", + # "lookback_period_candles": 24, + # "trade_limit": 4, + # "stop_duration_candles": self.protection_stoploss_stop.value, + # "only_per_pair": False + # }, + # { + # "method": "EmergencyStop", + # "min_percent": -0.05, + # "candels": 1 + # } + ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # if (current_profit > 0.015) & (last_candle['percent'] < -0.005): + # return 'percent_quick' + + # if (0 < current_profit) & ((current_time - trade.open_date_utc).seconds > 3600) \ + # & (last_candle['percent10'] < 0.001): + # return 'small_profit' + # + # if (current_profit > 0.01) \ + # & ((previous_last_candle['sma10'] - last_candle['sma10']) / previous_last_candle['sma10'] > 0.003): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma10_quick' + + if (current_profit >= - self.sell_too_old_percent.value) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value)\ + & ((current_time - trade.open_date_utc).days < self.sell_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.01" + if (current_profit >= - self.sell_too_old_percent.value * 2) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value * 2)\ + & ((current_time - trade.open_date_utc).days < self.sell_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.02" + if (current_profit >= - self.sell_too_old_percent.value * 3) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.03" + + if self.profit_quick_lost and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "quick_lost" + + if self.profit_no_change and (current_profit > self.sell_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + # if self.profit_quick_gain_3: + # if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + # return "quick_gain_3" + # if self.profit_quick_gain: + # if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + # return "quick_gain" + + if (current_profit > self.sell_percent.value) & (last_candle['percent3'] < - self.sell_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_candels.value): + return "quick_gain_param" + + if self.profit_sma5: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma10: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI2.value) & \ + (last_candle['percent'] < - self.sell_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if self.profit_short_loss: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + # def informative_pairs(self): + # # get access to all pairs available in whitelist. + # pairs = self.dp.current_whitelist() + # # Assign tf to each pair so they can be downloaded and cached for strategy. + # # informative_pairs = [(pair, "4h") for pair in pairs] + # informative_pairs = [(pair, '1d') for pair in pairs] + # #informative_pairs += [(pair, '1w') for pair in pairs] + # + # # Optionally Add additional "static" pairs + # # informative_pairs += [("ETH/USDT", "5m"), ("BTC/TUSD", "15m")] + # + # return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['ecart_20'] = dataframe['close'].rolling(20).var() / dataframe['close'] + dataframe['ecart_50'] = dataframe['close'].rolling(50).var() / dataframe['close'] + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = ta.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + # dataframe['TR'] = ta.TRANGE(dataframe) + # dataframe['ATR'] = ta.SMA(dataframe['TR'], 21) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + + # dataframe["dist_min_50"] = dataframe['close'] - dataframe['min50'] + # dataframe["dist_min_20"] = dataframe['close'] - dataframe['min20'] + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + + # # INFORMATIVE PAIRS + + # # Get the informative pair + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + # informative[buy_crossed_indicator0] = gene_calculator(informative, buy_crossed_indicator0) + # informative[buy_indicator0] = gene_calculator(informative, buy_indicator0) + # informative["cond1"] = informative[buy_indicator0].div(informative[buy_crossed_indicator0]) + # informative["percent"] = (informative["close"] - informative["open"]) / informative["open"] + # informative["percent5"] = informative["percent"].rolling(5).sum() + # informative["percent3"] = informative["percent"].rolling(3).sum() + # pandas.set_option('display.max_rows', informative.shape[0] + 1) + # pandas.set_option('display.max_columns', 50) + # # print('informative', metadata['pair'], informative.tail(1)) + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='1d') + # informative[buy_crossed_indicator0] = gene_calculator(informative, buy_crossed_indicator0) + # informative[buy_indicator0] = gene_calculator(informative, buy_indicator0) + # informative["cond1"] = informative[buy_indicator0].div(informative[buy_crossed_indicator0]) + # informative["percent"] = (informative["close"] - informative["open"]) / informative["open"] + # informative['max_open'] = ta.MAX(informative["open"], timeperiod=2) + # informative['max_close'] = ta.MIN(informative["close"], timeperiod=2) + # informative["percent5"] = informative["percent"].rolling(5).sum() + # informative["percent3"] = informative["percent"].rolling(3).sum() + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, '1d', ffill=True) + + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe='1w') + # informative[buy_crossed_indicator0] = gene_calculator(informative, buy_crossed_indicator0) + # informative[buy_indicator0] = gene_calculator(informative, buy_indicator0) + # informative["cond1"] = informative[buy_indicator0].div(informative[buy_crossed_indicator0]) + # informative["percent"] = (informative["close"] - informative["open"]) / informative["open"] + # informative["percent5"] = informative["percent"].rolling(5).sum() + # informative["percent3"] = informative["percent"].rolling(3).sum() + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, '1w', ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + # pandas.set_option('display.max_columns', 50) + # + # allow_to_buy = True + # max_gain = -100 + # trades = Trade.get_open_trades() + # + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # pairs = self.dp.current_whitelist() + # pairs_len = len(pairs) + # pair_index = pairs.index(metadata['pair']) + # + # # print(pair_index, " ", metadata['pair']) + # + # # ob = self.dp.orderbook(metadata['pair'], 1) + # # dataframe['best_bid'] = ob['bids'][0][0] + # # dataframe['best_ask'] = ob['asks'][0][0] + # # print(ob) + # + # for trade in trades: + # # if (metadata['pair'] == trade.pair): + # ticker = self.dp.ticker(trade.pair) #metadata['pair']) + # last_price = ticker['last'] + # # dataframe['volume24h'] = ticker['quoteVolume'] + # # dataframe['vwap'] = ticker['vwap'] + # # d = dataframe.tail(1) + # # print(dataframe) + # gain = (last_price - trade.open_rate) / trade.open_rate + # + # # print("Found open trade: ", trade, " ", ticker['last'], " ", trade.open_rate, gain) + # max_gain = max(max_gain, gain) + # + # if max_gain > - 0.05: + # allow_to_buy = False + # + # # print(metadata['pair'], max_gain, allow_to_buy, len(trades)) + + for decalage in range(self.buy_decalage_deb_0.value, self.buy_decalage0.value): + #if self.buy_0.value: + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value, + self.buy_decalage0.value + ) + conditions.append(condition1) + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_0_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift(decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_1_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_1_bb_lower_5.value) + #& (dataframe['percent_1d'] >= self.buy_1_percent_1d_num.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_1_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_2.value, self.buy_decalage2.value): + #if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_real_num0.value / 2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_2_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_2_bb_lower_5.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_2_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_3.value, self.buy_decalage3.value): + #if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_3_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_3_bb_lower_5.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_3_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_7_5_9_2.json b/GodStraJD3_7_5_9_2.json new file mode 100644 index 0000000..9e154c3 --- /dev/null +++ b/GodStraJD3_7_5_9_2.json @@ -0,0 +1,97 @@ +{ + "strategy_name": "GodStraJD3_7_5_9_2", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.026, + "trailing_stop_positive_offset": 0.077, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_0_distance": -0.05, + "buy_0_percent20": 0.04, + "buy_1_bb_lower_5": 0.36, + "buy_1_pente_sma10": 0.22, + "buy_1_pente_sma20": 0.6, + "buy_2_bb_lower_5": 0.23, + "buy_2_distance": 0.09, + "buy_2_pente_sma10": 0.36, + "buy_2_pente_sma20": 0.46, + "buy_2_percent20": 0.02, + "buy_3_bb_lower_5": 0.48, + "buy_3_distance": 0.01, + "buy_3_pente_sma10": 0.53, + "buy_3_pente_sma20": 0.11, + "buy_3_percent20": -0.09, + "buy_4_pente_sma10": 0.48, + "buy_4_pente_sma20": 0.38, + "buy_bb_lowerband": 1.02, + "buy_bb_width": 0.08, + "buy_cat": "", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + + +class GodStraJD3_7_5_9_2(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min20': {'color': '#87e470'}, + 'min50': {'color': '#ac3e2a'}, + "min1.1": {'color': 'yellow'}, + 'sma20': {'color': 'cyan'}, + 'open_1d': {'color': 'white'}, + 'close_1d': {'color': 'white'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + 'bb_lower_5': {'color': 'yellow'} + }, + # "Ind0": { + # buy_crossed_indicator0: {'color': 'green'}, + # buy_indicator0: {'color': 'red'} + # }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "Ecart": { + 'normal_var_20': {'color': 'red'}, + 'normal_var_50': {'color': 'yellow'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'}, + "distance_min": {'color': 'white'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + trades = list() + profit_no_change = BooleanParameter(default=True, space="sell") + profit_quick_lost = BooleanParameter(default=True, space="sell") + profit_sma5 = BooleanParameter(default=True, space="sell") + profit_sma10 = BooleanParameter(default=True, space="sell") + profit_sma20 = BooleanParameter(default=True, space="sell") + profit_quick_gain = BooleanParameter(default=True, space="sell") + profit_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_old_sma10 = BooleanParameter(default=True, space="sell") + profit_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_over_rsi = BooleanParameter(default=True, space="sell") + profit_short_loss = BooleanParameter(default=True, space="sell") + + profit_h_no_change = BooleanParameter(default=True, space="sell") + profit_h_quick_lost = BooleanParameter(default=True, space="sell") + profit_h_sma5 = BooleanParameter(default=True, space="sell") + profit_h_sma10 = BooleanParameter(default=True, space="sell") + profit_h_sma20 = BooleanParameter(default=True, space="sell") + profit_h_quick_gain = BooleanParameter(default=True, space="sell") + profit_h_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_h_old_sma10 = BooleanParameter(default=True, space="sell") + profit_h_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_h_over_rsi = BooleanParameter(default=True, space="sell") + profit_h_short_loss = BooleanParameter(default=True, space="sell") + + # buy_signal_bb_width = DecimalParameter(0.06, 0.15, decimals=2, default=0.065, space='buy') + buy_real = DecimalParameter(0.001, 0.999, decimals=4, default=0.11908, space='buy') + buy_cat = CategoricalParameter([">R", "=R", " expected_profit) & (last_candle['pct_change_1_1d'] < 0): + # return "exp_profit_down" + + if (current_profit >= - self.sell_too_old_percent.value) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value)\ + & ((current_time - trade.open_date_utc).days < self.sell_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.01" + if (current_profit >= - self.sell_too_old_percent.value * 2) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value * 2)\ + & ((current_time - trade.open_date_utc).days < self.sell_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.02" + if (current_profit >= - self.sell_too_old_percent.value * 3) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.03" + + if (last_candle['pct_change_1_1d'] > 0): + if self.profit_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "quick_lost" + + if self.profit_no_change.value and (current_profit > self.sell_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + if (current_profit > self.sell_percent.value) & (last_candle['percent3'] < - self.sell_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_candels.value): + return "quick_gain_param" + + if self.profit_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma5' + + if self.profit_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + if self.profit_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI2.value) & \ + (last_candle['percent'] < - self.sell_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if self.profit_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + else: + if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_percent.value) & (last_candle['percent3'] < - self.sell_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (( + current_time - trade.open_date_utc).days >= 1): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe['profit'] = 0 + # RSI + dataframe['trend_ichimoku_base'] = ta2.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta2.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min10'] = ta.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['max50'] = ta.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + dataframe['ecart_20'] = dataframe['close'].rolling(20).var() / dataframe['close'] + dataframe['ecart_50'] = dataframe['close'].rolling(50).var() / dataframe['close'] + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = ta.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = ta.RSI(informative) + informative["max3"] = ta.MAX(informative['close'], timeperiod=3) + informative["min3"] = ta.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = ta.SMA(informative, timeperiod=3) + informative['sma5'] = ta.SMA(informative, timeperiod=5) + informative['sma10'] = ta.SMA(informative, timeperiod=10) + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + for decalage in range(self.buy_decalage_deb_0.value, self.buy_decalage0.value): + #if self.buy_0.value: + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value, + self.buy_decalage0.value + ) + conditions.append(condition1) + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_0_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift(decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_1_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_1_bb_lower_5.value) + #& (dataframe['percent_1d'] >= self.buy_1_percent_1d_num.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_1_percent_4h_num.value) + & (dataframe['pct_change_1_1d'] < self.buy_pct_1.value) + & (dataframe['pct_change_3_1d'] < self.buy_pct_3.value) + & (dataframe['pct_change_5_1d'] < self.buy_pct_5.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_2.value, self.buy_decalage2.value): + #if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_real_num0.value / 2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_2_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_2_bb_lower_5.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_2_percent_4h_num.value) + & (dataframe['pct_change_1_1d'] < self.buy_pct_1.value) + & (dataframe['pct_change_3_1d'] < self.buy_pct_3.value) + & (dataframe['pct_change_5_1d'] < self.buy_pct_5.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_3.value, self.buy_decalage3.value): + #if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_3_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_3_bb_lower_5.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_3_percent_4h_num.value) + & (dataframe['pct_change_1_1d'] < self.buy_pct_1.value) + & (dataframe['pct_change_3_1d'] < self.buy_pct_3.value) + & (dataframe['pct_change_5_1d'] < self.buy_pct_5.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_real.value + OPR = self.buy_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == " self.buy_pct_1.value) + & (dataframe['pct_change_3_1d'] > self.buy_pct_3.value) + & (dataframe['pct_change_5_1d'] > self.buy_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + , + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_7_6.json b/GodStraJD3_7_6.json new file mode 100644 index 0000000..7d1219d --- /dev/null +++ b/GodStraJD3_7_6.json @@ -0,0 +1,63 @@ +{ + "strategy_name": "GodStraJD3_7_6", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.026, + "trailing_stop_positive_offset": 0.077, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_1_distance": 0.06, + "buy_1_percent20": -0.04, + "buy_3_distance": 0.01, + "buy_3_percent20": 0.08, + "buy_decalage1": 8, + "buy_decalage3": 7, + "buy_decalage_deb_1": 2, + "buy_decalage_deb_3": 3, + "buy_min_horizon": 111, + "buy_real_num1": 0.77, + "buy_real_num3": 0.52 + }, + "sell": { + "profit_no_change": false, + "profit_old_sma10": true, + "profit_over_rsi": false, + "profit_over_rsi2_percent": 0.002, + "profit_over_rsi_max_rsi": 90, + "profit_over_rsi_max_rsi2": 94, + "profit_quick_gain": false, + "profit_quick_gain_3": false, + "profit_quick_lost": true, + "profit_quick_lost_max": 0.0, + "profit_quick_lost_max_profit": 0.013, + "profit_short_loss": true, + "profit_sma10": true, + "profit_sma10_current_profit": 0.027, + "profit_sma10_facteur": 1.0, + "profit_sma20": true, + "profit_sma20_current_profit": 0.021, + "profit_sma20_facteur": 1.006, + "profit_too_old": true, + "profit_too_old_days": 9, + "profit_too_old_max": 0.02, + "profit_very_old_sma10": false + }, + "protection": { + "lookback": 133, + "protection_max_allowed_dd": 0.71, + "protection_stop": 61, + "protection_stoploss_stop": 50, + "trade_limit": 9 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-12 10:23:55.007196+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_7_6.py b/GodStraJD3_7_6.py new file mode 100644 index 0000000..aa9c5c9 --- /dev/null +++ b/GodStraJD3_7_6.py @@ -0,0 +1,787 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import calendar +from freqtrade.loggers import setup_logging + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + + +class GodStraJD3_7_6(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min20': {'color': '#87e470'}, + 'min50': {'color': '#ac3e2a'}, + "min1.1": {'color': 'yellow'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'} + }, + # "Ind0": { + # buy_crossed_indicator0: {'color': 'green'}, + # buy_indicator0: {'color': 'red'} + # }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "Ecart": { + 'ecart_20': {'color': 'red'}, + 'ecart_50': {'color': 'yellow'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'}, + "distance_min": {'color': 'white'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + # profit_no_change = False + # profit_old_sma10 = False + # profit_over_rsi = True + # profit_quick_gain = True + # profit_quick_gain_3 = True + # profit_quick_lost = False + # profit_short_loss = False + # profit_sma10 = False + # profit_sma20 = True + # profit_very_old_sma10 = False + + trades = list() + profit_too_old = BooleanParameter(default=True, space="sell") + profit_no_change = BooleanParameter(default=True, space="sell") + profit_quick_lost = BooleanParameter(default=True, space="sell") + profit_sma10 = BooleanParameter(default=True, space="sell") + profit_sma20 = BooleanParameter(default=True, space="sell") + profit_quick_gain = BooleanParameter(default=True, space="sell") + profit_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_old_sma10 = BooleanParameter(default=True, space="sell") + profit_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_over_rsi = BooleanParameter(default=True, space="sell") + profit_short_loss = BooleanParameter(default=True, space="sell") + + profit_too_old_max = DecimalParameter(0, 0.05, decimals=2, default=0.01, space='sell') + profit_too_old_days = IntParameter(1, 10, default=5, space='sell') + profit_quick_lost_max = DecimalParameter(0, 0.03, decimals=3, default=0.015, space='sell') + profit_quick_lost_max_profit = DecimalParameter(0, 0.03, decimals=3, default=0.015, space='sell') + profit_sma10_current_profit = DecimalParameter(0, 0.03, decimals=3, default=0.015, space='sell') + profit_sma10_facteur = DecimalParameter(1, 1.01, decimals=3, default=1.005, space='sell') + + profit_sma20_current_profit = DecimalParameter(0, 0.03, decimals=3, default=0.015, space='sell') + profit_sma20_facteur = DecimalParameter(1, 1.01, decimals=3, default=1.005, space='sell') + + profit_over_rsi_max_rsi = IntParameter(70, 100, default=88, space='sell') + profit_over_rsi_max_rsi2 = IntParameter(70, 100, default=82, space='sell') + + profit_over_rsi2_percent = DecimalParameter(0, 0.05, decimals=3, default=0.02, space='sell') + + # buy_signal_bb_width = DecimalParameter(0.06, 0.15, decimals=2, default=0.065, space='buy') + + # buy_real_num0 = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_real_num1 = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_real_num3 = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + + buy_min_horizon = IntParameter(50, 200, default=72, space='buy') + + # buy_0 = BooleanParameter(default=True, space="buy") + # buy_2 = BooleanParameter(default=True, space="buy") + # buy_3 = BooleanParameter(default=True, space="buy") + + # buy_0_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_1_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_3_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + + # buy_0_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_1_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_3_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + + # buy_decalage_deb_0 = IntParameter(1, 3, default=5, space='buy') + buy_decalage_deb_1 = IntParameter(1, 3, default=5, space='buy') + buy_decalage_deb_3 = IntParameter(1, 3, default=5, space='buy') + + # buy_decalage0 = IntParameter(buy_decalage_deb_0.value + 1, 8, default=5, space='buy') + buy_decalage1 = IntParameter(buy_decalage_deb_1.value + 1, 8, default=5, space='buy') + buy_decalage3 = IntParameter(buy_decalage_deb_3.value + 1, 8, default=5, space='buy') + + protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + protection_stop = IntParameter(1, 100, default=48, space='protection') + protection_stoploss_stop = IntParameter(1, 100, default=48, space='protection') + lookback = IntParameter(1, 200, default=48, space='protection') + trade_limit = IntParameter(1, 10, default=2, space='protection') + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + + # {'symbol': 'FTM/USDT', 'timestamp': 1646494199570, 'datetime': '2022-03-05T15:29:59.570Z', 'high': 1.7489, + # 'low': 1.6084, 'bid': 1.6505, 'bidVolume': 2135.0, 'ask': 1.6508, 'askVolume': 2815.0, 'vwap': 1.66852198, + # 'open': 1.7313, 'close': 1.6505, 'last': 1.6505, 'previousClose': '1.73170000', 'change': -0.0808, + # 'percentage': -4.667, 'average': 1.6909, 'baseVolume': 124656725.0, 'quoteVolume': 207992485.7799, + # 'info': + # {'symbol': 'FTMUSDT', 'priceChange': '-0.08080000', 'priceChangePercent': '-4.667', + # 'weightedAvgPrice': '1.66852198', 'prevClosePrice': '1.73170000', 'lastPrice': '1.65050000', + # 'lastQty': '143.00000000', 'bidPrice': '1.65050000', 'bidQty': '2135.00000000', + # 'askPrice': '1.65080000', 'askQty': '2815.00000000', 'openPrice': '1.73130000', + # 'highPrice': '1.74890000', 'lowPrice': '1.60840000', 'volume': '124656725.00000000', + # 'quoteVolume': '207992485.77990000', 'openTime': '1646407799570', 'closeTime': '1646494199570', + # 'firstId': '137149614', 'lastId': '137450289', 'count': '300676'}} - 0.9817468621938484 + + allow_to_buy = True + max_gain = -100 + sum_gain = 0 + max_time = 0 + + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + if len(self.trades) == 0: + print('search') + self.trades = Trade.get_open_trades() + + if len(self.trades) >= self.config['max_open_trades'] / 2: + for trade in self.trades: + ticker = self.dp.ticker(trade.pair) + last_price = ticker['last'] + gain = (last_price - trade.open_rate) / trade.open_rate + max_gain = max(max_gain, gain) + sum_gain += gain + max_time = max(max_time, datetime.timestamp(trade.open_date)) + print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + now = datetime.now() + diff = (datetime.timestamp(now) - max_time / 3600) + if (max_gain <= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + print("allow_to_buy=false") + allow_to_buy = False + print(pair, allow_to_buy, len(self.trades), + "max gain=", max_gain, + "sum_gain=", sum_gain, + "now=", now, + "max=", max_time, + "diff=", datetime.timestamp(now) - max_time) + + if allow_to_buy: + self.trades = list() + + return allow_to_buy + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + { + "method": "MaxDrawdown", + "lookback_period_candles": self.lookback.value, + "trade_limit": self.trade_limit.value, + "stop_duration_candles": self.protection_stop.value, + "max_allowed_drawdown": self.protection_max_allowed_dd.value, + "only_per_pair": False + }, + { + "method": "StoplossGuard", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": self.protection_stoploss_stop.value, + "only_per_pair": False + } + ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # if (0 < current_profit) & ((current_time - trade.open_date_utc).seconds > 3600) \ + # & (last_candle['percent10'] < 0.001): + # return 'small_profit' + # + # if (current_profit > 0.01) \ + # & ((previous_last_candle['sma10'] - last_candle['sma10']) / previous_last_candle['sma10'] > 0.003): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma10_quick' + + if self.profit_too_old.value: + if (current_profit >= - self.profit_too_old_max.value) \ + & ((current_time - trade.open_date_utc).days >= self.profit_too_old_days.value)\ + & ((current_time - trade.open_date_utc).days < (self.profit_too_old_days.value * 2))\ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.01" + if (current_profit >= - (2 * self.profit_too_old_max.value)) \ + & ((current_time - trade.open_date_utc).days >= (self.profit_too_old_days.value * 2))\ + & ((current_time - trade.open_date_utc).days < (self.profit_too_old_days.value * 3)) \ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.02" + if (current_profit >= - (3 * self.profit_too_old_max.value)) \ + & ((current_time - trade.open_date_utc).days >= (self.profit_too_old_days.value * 3)) \ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.03" + + if self.profit_quick_lost.value: + if (current_profit >= self.profit_quick_lost_max_profit.value) \ + & (last_candle['percent3'] < - self.profit_quick_lost_max.value): + return "quick_lost" + + if self.profit_no_change.value: + if (current_profit > 0.005) \ + & (last_candle['percent10'] < 0.000) \ + & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if self.profit_quick_gain_3.value: + if (current_profit >= 0.03) \ + & (last_candle['percent3'] < 0) \ + & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if self.profit_quick_gain.value: + if (0.01 < current_profit < 0.03) \ + & (last_candle['percent'] < 0) \ + & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain" + + if self.profit_sma10.value: + if (current_profit > self.profit_sma10_current_profit.value) \ + & ((previous_5_candle['sma10'] > (last_candle['sma10'] * self.profit_sma10_facteur.value)) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20.value: + if (current_profit > self.profit_sma20_current_profit.value) & (last_candle['percent5'] < 0) \ + & (previous_5_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > (last_candle['sma20'] * self.profit_sma20_facteur.value)) & + ((last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi.value: + if (current_profit > 0) & (last_candle['rsi'] > self.profit_over_rsi_max_rsi.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.profit_over_rsi_max_rsi2.value) \ + & (last_candle['percent'] < - self.profit_over_rsi2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if self.profit_short_loss.value: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['ecart_20'] = dataframe['close'].rolling(20).var() / dataframe['close'] + dataframe['ecart_50'] = dataframe['close'].rolling(50).var() / dataframe['close'] + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = ta.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + + # dataframe["dist_min_50"] = dataframe['close'] - dataframe['min50'] + # dataframe["dist_min_20"] = dataframe['close'] - dataframe['min20'] + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + # pandas.set_option('display.max_columns', 50) + # + # allow_to_buy = True + # max_gain = -100 + # trades = Trade.get_open_trades() + # + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # pairs = self.dp.current_whitelist() + # pairs_len = len(pairs) + # pair_index = pairs.index(metadata['pair']) + # + # # print(pair_index, " ", metadata['pair']) + # + # # ob = self.dp.orderbook(metadata['pair'], 1) + # # dataframe['best_bid'] = ob['bids'][0][0] + # # dataframe['best_ask'] = ob['asks'][0][0] + # # print(ob) + # + # for trade in trades: + # # if (metadata['pair'] == trade.pair): + # ticker = self.dp.ticker(trade.pair) #metadata['pair']) + # last_price = ticker['last'] + # # dataframe['volume24h'] = ticker['quoteVolume'] + # # dataframe['vwap'] = ticker['vwap'] + # # d = dataframe.tail(1) + # # print(dataframe) + # gain = (last_price - trade.open_rate) / trade.open_rate + # + # # print("Found open trade: ", trade, " ", ticker['last'], " ", trade.open_rate, gain) + # max_gain = max(max_gain, gain) + # + # if max_gain > - 0.05: + # allow_to_buy = False + # + # # print(metadata['pair'], max_gain, allow_to_buy, len(trades)) + + # for decalage in range(self.buy_decalage_deb_0.value, self.buy_decalage0.value): + # if self.buy_0.value: + # conditions = list() + # condition1, dataframe = condition_generator( + # dataframe, + # buy_operator0, + # buy_indicator0, + # buy_crossed_indicator0, + # self.buy_real_num0.value, + # self.buy_decalage0.value + # ) + # conditions.append(condition1) + # dataframe.loc[ + # ( + # reduce(lambda x, y: x & y, conditions) + # & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + # & (dataframe['close'] < dataframe['bb_middleband']) + # & (dataframe['open'] < dataframe['sma10']) + # & (dataframe['open'] < dataframe['sma100']) + # & (dataframe['min50'].shift(decalage) == dataframe['min50']) + # & (dataframe['min10'] <= dataframe['min50'] * 1.02) + # & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # # & (dataframe['min20'] == dataframe['min50']) + # & (dataframe['distance_min'] <= self.buy_0_distance.value) + # ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_1.value, self.buy_decalage1.value): + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= self.buy_real_num1.value) + & (dataframe['bb_width'].shift(decalage) >= 0.07) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['close'] < dataframe['sma10']) + & (dataframe['sma50'].shift(decalage) < dataframe['sma50']) + #& (dataframe['open'] < dataframe['min1.1']) + # & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_1_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_1_distance.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_3.value, self.buy_decalage3.value): + # if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= self.buy_real_num3.value) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['open'] < dataframe['sma10']) + #& (dataframe['open'] < dataframe['min1.1']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + # pair = metadata['pair'] + # allow_to_buy = True + # max_gain = -100 + # sum_gain = 0 + # max_time = 0 + # if len(self.trades) == 0: + # print('search') + # self.trades = Trade.get_open_trades() + # + # # if self.dp: + # # if self.dp.runmode.value in ('live', 'dry_run'): + # if len(self.trades) >= self.config['max_open_trades'] / 2: + # for trade in self.trades: + # ticker = self.dp.ticker(trade.pair) + # last_price = ticker['last'] + # gain = (last_price - trade.open_rate) / trade.open_rate + # max_gain = max(max_gain, gain) + # sum_gain += gain + # max_time = max(max_time, datetime.timestamp(trade.open_date)) + # print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + # datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + # now = datetime.now() + # diff = (datetime.timestamp(now) - max_time / 3600) + # if (max_gain >= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + # print("allow_to_buy=false") + # allow_to_buy = False + # print(pair, allow_to_buy, len(self.trades), + # "max gain=", max_gain, + # "sum_gain=", sum_gain, + # "now=", now, + # "max=", max_time, + # "diff=", datetime.timestamp(now) - max_time) + # + # if allow_to_buy: + # self.trades = list() + + # print(condition1) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_7_6_2.json b/GodStraJD3_7_6_2.json new file mode 100644 index 0000000..db4341c --- /dev/null +++ b/GodStraJD3_7_6_2.json @@ -0,0 +1,63 @@ +{ + "strategy_name": "GodStraJD3_7_6_2", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.026, + "trailing_stop_positive_offset": 0.077, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_1_distance": -0.02, + "buy_1_percent20": 0.03, + "buy_3_distance": 0.05, + "buy_3_percent20": 0.01, + "buy_decalage1": 8, + "buy_decalage3": 8, + "buy_decalage_deb_1": 2, + "buy_decalage_deb_3": 1, + "buy_min_horizon": 195, + "buy_real_num1": 0.55, + "buy_real_num3": 0.55 + }, + "sell": { + "profit_no_change": false, + "profit_old_sma10": true, + "profit_over_rsi": false, + "profit_over_rsi2_percent": 0.002, + "profit_over_rsi_max_rsi": 90, + "profit_over_rsi_max_rsi2": 94, + "profit_quick_gain": false, + "profit_quick_gain_3": false, + "profit_quick_lost": true, + "profit_quick_lost_max": 0.0, + "profit_quick_lost_max_profit": 0.013, + "profit_short_loss": true, + "profit_sma10": true, + "profit_sma10_current_profit": 0.027, + "profit_sma10_facteur": 1.0, + "profit_sma20": true, + "profit_sma20_current_profit": 0.021, + "profit_sma20_facteur": 1.006, + "profit_too_old": true, + "profit_too_old_days": 9, + "profit_too_old_max": 0.02, + "profit_very_old_sma10": false + }, + "protection": { + "lookback": 133, + "protection_max_allowed_dd": 0.71, + "protection_stop": 61, + "protection_stoploss_stop": 50, + "trade_limit": 9 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-12 00:18:04.975397+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_7_6_2.py b/GodStraJD3_7_6_2.py new file mode 100644 index 0000000..064d0f8 --- /dev/null +++ b/GodStraJD3_7_6_2.py @@ -0,0 +1,788 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import calendar +from freqtrade.loggers import setup_logging + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + + +class GodStraJD3_7_6_2(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min20': {'color': '#87e470'}, + 'min50': {'color': '#ac3e2a'}, + "min1.1": {'color': 'yellow'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'} + }, + # "Ind0": { + # buy_crossed_indicator0: {'color': 'green'}, + # buy_indicator0: {'color': 'red'} + # }, + "Cond": { + 'cond1': {'color': 'yellow'}, + }, + # "Ind1": { + # buy_indicator1: {'color': 'yellow'}, + # buy_crossed_indicator1: {'color': 'pink'} + # }, + # "Ind2": { + # buy_indicator2: {'color': 'cyan'}, + # buy_crossed_indicator2: {'color': 'blue'}, + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "Ecart": { + 'ecart_20': {'color': 'red'}, + 'ecart_50': {'color': 'yellow'}, + }, + # "rolling": { + # 'bb_rolling': {'color': '#87e470'}, + # "bb_rolling_min": {'color': '#ac3e2a'} + # }, + "percent": { + "percent": {'color': 'green'}, + "percent3": {'color': 'blue'}, + "percent5": {'color': 'red'}, + "distance_min": {'color': 'white'} + } + } + } + + + # #################### END OF RESULT PLACE #################### + + # profit_no_change = False + # profit_old_sma10 = False + # profit_over_rsi = True + # profit_quick_gain = True + # profit_quick_gain_3 = True + # profit_quick_lost = False + # profit_short_loss = False + # profit_sma10 = False + # profit_sma20 = True + # profit_very_old_sma10 = False + + trades = list() + profit_too_old = BooleanParameter(default=True, space="sell") + profit_no_change = BooleanParameter(default=True, space="sell") + profit_quick_lost = BooleanParameter(default=True, space="sell") + profit_sma10 = BooleanParameter(default=True, space="sell") + profit_sma20 = BooleanParameter(default=True, space="sell") + profit_quick_gain = BooleanParameter(default=True, space="sell") + profit_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_old_sma10 = BooleanParameter(default=True, space="sell") + profit_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_over_rsi = BooleanParameter(default=True, space="sell") + profit_short_loss = BooleanParameter(default=True, space="sell") + + profit_too_old_max = DecimalParameter(0, 0.05, decimals=2, default=0.01, space='sell') + profit_too_old_days = IntParameter(1, 10, default=5, space='sell') + profit_quick_lost_max = DecimalParameter(0, 0.03, decimals=3, default=0.015, space='sell') + profit_quick_lost_max_profit = DecimalParameter(0, 0.03, decimals=3, default=0.015, space='sell') + profit_sma10_current_profit = DecimalParameter(0, 0.03, decimals=3, default=0.015, space='sell') + profit_sma10_facteur = DecimalParameter(1, 1.01, decimals=3, default=1.005, space='sell') + + profit_sma20_current_profit = DecimalParameter(0, 0.03, decimals=3, default=0.015, space='sell') + profit_sma20_facteur = DecimalParameter(1, 1.01, decimals=3, default=1.005, space='sell') + + profit_over_rsi_max_rsi = IntParameter(70, 100, default=88, space='sell') + profit_over_rsi_max_rsi2 = IntParameter(70, 100, default=82, space='sell') + + profit_over_rsi2_percent = DecimalParameter(0, 0.05, decimals=3, default=0.02, space='sell') + + # buy_signal_bb_width = DecimalParameter(0.06, 0.15, decimals=2, default=0.065, space='buy') + + # buy_real_num0 = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_real_num1 = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + buy_real_num3 = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + + buy_min_horizon = IntParameter(50, 200, default=72, space='buy') + + # buy_0 = BooleanParameter(default=True, space="buy") + # buy_2 = BooleanParameter(default=True, space="buy") + # buy_3 = BooleanParameter(default=True, space="buy") + + # buy_0_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_1_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + buy_3_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + + # buy_0_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_1_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + buy_3_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + + # buy_decalage_deb_0 = IntParameter(1, 3, default=5, space='buy') + buy_decalage_deb_1 = IntParameter(1, 3, default=5, space='buy') + buy_decalage_deb_3 = IntParameter(1, 3, default=5, space='buy') + + # buy_decalage0 = IntParameter(buy_decalage_deb_0.value + 1, 8, default=5, space='buy') + buy_decalage1 = IntParameter(buy_decalage_deb_1.value + 1, 8, default=5, space='buy') + buy_decalage3 = IntParameter(buy_decalage_deb_3.value + 1, 8, default=5, space='buy') + + protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection') + protection_stop = IntParameter(1, 100, default=48, space='protection') + protection_stoploss_stop = IntParameter(1, 100, default=48, space='protection') + lookback = IntParameter(1, 200, default=48, space='protection') + trade_limit = IntParameter(1, 10, default=2, space='protection') + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + + # {'symbol': 'FTM/USDT', 'timestamp': 1646494199570, 'datetime': '2022-03-05T15:29:59.570Z', 'high': 1.7489, + # 'low': 1.6084, 'bid': 1.6505, 'bidVolume': 2135.0, 'ask': 1.6508, 'askVolume': 2815.0, 'vwap': 1.66852198, + # 'open': 1.7313, 'close': 1.6505, 'last': 1.6505, 'previousClose': '1.73170000', 'change': -0.0808, + # 'percentage': -4.667, 'average': 1.6909, 'baseVolume': 124656725.0, 'quoteVolume': 207992485.7799, + # 'info': + # {'symbol': 'FTMUSDT', 'priceChange': '-0.08080000', 'priceChangePercent': '-4.667', + # 'weightedAvgPrice': '1.66852198', 'prevClosePrice': '1.73170000', 'lastPrice': '1.65050000', + # 'lastQty': '143.00000000', 'bidPrice': '1.65050000', 'bidQty': '2135.00000000', + # 'askPrice': '1.65080000', 'askQty': '2815.00000000', 'openPrice': '1.73130000', + # 'highPrice': '1.74890000', 'lowPrice': '1.60840000', 'volume': '124656725.00000000', + # 'quoteVolume': '207992485.77990000', 'openTime': '1646407799570', 'closeTime': '1646494199570', + # 'firstId': '137149614', 'lastId': '137450289', 'count': '300676'}} - 0.9817468621938484 + + allow_to_buy = True + max_gain = -100 + sum_gain = 0 + max_time = 0 + + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + if len(self.trades) == 0: + print('search') + self.trades = Trade.get_open_trades() + + if len(self.trades) >= self.config['max_open_trades'] / 2: + for trade in self.trades: + ticker = self.dp.ticker(trade.pair) + last_price = ticker['last'] + gain = (last_price - trade.open_rate) / trade.open_rate + max_gain = max(max_gain, gain) + sum_gain += gain + max_time = max(max_time, datetime.timestamp(trade.open_date)) + print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + now = datetime.now() + diff = (datetime.timestamp(now) - max_time / 3600) + if (max_gain <= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + print("allow_to_buy=false") + allow_to_buy = False + print(pair, allow_to_buy, len(self.trades), + "max gain=", max_gain, + "sum_gain=", sum_gain, + "now=", now, + "max=", max_time, + "diff=", datetime.timestamp(now) - max_time) + + if allow_to_buy: + self.trades = list() + + return allow_to_buy + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + { + "method": "MaxDrawdown", + "lookback_period_candles": self.lookback.value, + "trade_limit": self.trade_limit.value, + "stop_duration_candles": self.protection_stop.value, + "max_allowed_drawdown": self.protection_max_allowed_dd.value, + "only_per_pair": False + }, + { + "method": "StoplossGuard", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": self.protection_stoploss_stop.value, + "only_per_pair": False + } + ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # if (0 < current_profit) & ((current_time - trade.open_date_utc).seconds > 3600) \ + # & (last_candle['percent10'] < 0.001): + # return 'small_profit' + # + # if (current_profit > 0.01) \ + # & ((previous_last_candle['sma10'] - last_candle['sma10']) / previous_last_candle['sma10'] > 0.003): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'sma10_quick' + + if self.profit_too_old.value: + if (current_profit >= - self.profit_too_old_max.value) \ + & ((current_time - trade.open_date_utc).days >= self.profit_too_old_days.value)\ + & ((current_time - trade.open_date_utc).days < (self.profit_too_old_days.value * 2))\ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.01" + if (current_profit >= - (2 * self.profit_too_old_max.value)) \ + & ((current_time - trade.open_date_utc).days >= (self.profit_too_old_days.value * 2))\ + & ((current_time - trade.open_date_utc).days < (self.profit_too_old_days.value * 3)) \ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.02" + if (current_profit >= - (3 * self.profit_too_old_max.value)) \ + & ((current_time - trade.open_date_utc).days >= (self.profit_too_old_days.value * 3)) \ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return "too_old_0.03" + + if self.profit_quick_lost.value: + if (current_profit >= self.profit_quick_lost_max_profit.value) \ + & (last_candle['percent3'] < - self.profit_quick_lost_max.value): + return "quick_lost" + + if self.profit_no_change.value: + if (current_profit > 0.005) \ + & (last_candle['percent10'] < 0.000) \ + & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if self.profit_quick_gain_3.value: + if (current_profit >= 0.03) \ + & (last_candle['percent3'] < 0) \ + & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if self.profit_quick_gain.value: + if (0.01 < current_profit < 0.03) \ + & (last_candle['percent'] < 0) \ + & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain" + + if self.profit_sma10.value: + if (current_profit > self.profit_sma10_current_profit.value) \ + & ((previous_5_candle['sma10'] > (last_candle['sma10'] * self.profit_sma10_facteur.value)) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20.value: + if (current_profit > self.profit_sma20_current_profit.value) & (last_candle['percent5'] < 0) \ + & (previous_5_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > (last_candle['sma20'] * self.profit_sma20_facteur.value)) & + ((last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi.value: + if (current_profit > 0) & (last_candle['rsi'] > self.profit_over_rsi_max_rsi.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.profit_over_rsi_max_rsi2.value) \ + & (last_candle['percent'] < - self.profit_over_rsi2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if self.profit_short_loss.value: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['ecart_20'] = dataframe['close'].rolling(20).var() / dataframe['close'] + dataframe['ecart_50'] = dataframe['close'].rolling(50).var() / dataframe['close'] + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = ta.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + + # Bollinger Bands - Weighted (EMA based instead of SMA) + # weighted_bollinger = qtpylib.weighted_bollinger_bands( + # qtpylib.typical_price(dataframe), window=20, stds=2 + # ) + # dataframe["wbb_upperband"] = weighted_bollinger["upper"] + # dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + # dataframe["wbb_middleband"] = weighted_bollinger["mid"] + # dataframe["wbb_percent"] = ( + # (dataframe["close"] - dataframe["wbb_lowerband"]) / + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + # ) + # dataframe["wbb_width"] = ( + # (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + # ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + + # dataframe["dist_min_50"] = dataframe['close'] - dataframe['min50'] + # dataframe["dist_min_20"] = dataframe['close'] - dataframe['min20'] + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + # pandas.set_option('display.max_columns', 50) + # + # allow_to_buy = True + # max_gain = -100 + # trades = Trade.get_open_trades() + # + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # pairs = self.dp.current_whitelist() + # pairs_len = len(pairs) + # pair_index = pairs.index(metadata['pair']) + # + # # print(pair_index, " ", metadata['pair']) + # + # # ob = self.dp.orderbook(metadata['pair'], 1) + # # dataframe['best_bid'] = ob['bids'][0][0] + # # dataframe['best_ask'] = ob['asks'][0][0] + # # print(ob) + # + # for trade in trades: + # # if (metadata['pair'] == trade.pair): + # ticker = self.dp.ticker(trade.pair) #metadata['pair']) + # last_price = ticker['last'] + # # dataframe['volume24h'] = ticker['quoteVolume'] + # # dataframe['vwap'] = ticker['vwap'] + # # d = dataframe.tail(1) + # # print(dataframe) + # gain = (last_price - trade.open_rate) / trade.open_rate + # + # # print("Found open trade: ", trade, " ", ticker['last'], " ", trade.open_rate, gain) + # max_gain = max(max_gain, gain) + # + # if max_gain > - 0.05: + # allow_to_buy = False + # + # # print(metadata['pair'], max_gain, allow_to_buy, len(trades)) + + # for decalage in range(self.buy_decalage_deb_0.value, self.buy_decalage0.value): + # if self.buy_0.value: + # conditions = list() + # condition1, dataframe = condition_generator( + # dataframe, + # buy_operator0, + # buy_indicator0, + # buy_crossed_indicator0, + # self.buy_real_num0.value, + # self.buy_decalage0.value + # ) + # conditions.append(condition1) + # dataframe.loc[ + # ( + # reduce(lambda x, y: x & y, conditions) + # & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + # & (dataframe['close'] < dataframe['bb_middleband']) + # & (dataframe['open'] < dataframe['sma10']) + # & (dataframe['open'] < dataframe['sma100']) + # & (dataframe['min50'].shift(decalage) == dataframe['min50']) + # & (dataframe['min10'] <= dataframe['min50'] * 1.02) + # & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # # & (dataframe['min20'] == dataframe['min50']) + # & (dataframe['distance_min'] <= self.buy_0_distance.value) + # ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_1.value, self.buy_decalage1.value): + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= self.buy_real_num1.value) + & (dataframe['bb_width'].shift(decalage) >= 0.07) + # & (dataframe['close'].shift(decalage) < dataframe['bb_lowerband'].shift(decalage)) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['sma50'].shift(decalage) < dataframe['sma50']) + #& (dataframe['open'] < dataframe['min1.1']) + # & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_1_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_1_distance.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_3.value, self.buy_decalage3.value): + # if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= self.buy_real_num3.value) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma100']) + & (dataframe['open'] < dataframe['sma10']) + #& (dataframe['open'] < dataframe['min1.1']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + # pair = metadata['pair'] + # allow_to_buy = True + # max_gain = -100 + # sum_gain = 0 + # max_time = 0 + # if len(self.trades) == 0: + # print('search') + # self.trades = Trade.get_open_trades() + # + # # if self.dp: + # # if self.dp.runmode.value in ('live', 'dry_run'): + # if len(self.trades) >= self.config['max_open_trades'] / 2: + # for trade in self.trades: + # ticker = self.dp.ticker(trade.pair) + # last_price = ticker['last'] + # gain = (last_price - trade.open_rate) / trade.open_rate + # max_gain = max(max_gain, gain) + # sum_gain += gain + # max_time = max(max_time, datetime.timestamp(trade.open_date)) + # print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + # datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + # now = datetime.now() + # diff = (datetime.timestamp(now) - max_time / 3600) + # if (max_gain >= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + # print("allow_to_buy=false") + # allow_to_buy = False + # print(pair, allow_to_buy, len(self.trades), + # "max gain=", max_gain, + # "sum_gain=", sum_gain, + # "now=", now, + # "max=", max_time, + # "diff=", datetime.timestamp(now) - max_time) + # + # if allow_to_buy: + # self.trades = list() + + # print(condition1) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + diff --git a/GodStraJD3_8.json b/GodStraJD3_8.json new file mode 100644 index 0000000..b18c0f7 --- /dev/null +++ b/GodStraJD3_8.json @@ -0,0 +1,54 @@ +{ + "strategy_name": "GodStraJD3_8", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.058, + "trailing_stop_positive_offset": 0.079, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bbwidth_num0": 0.05, + "buy_bbwidth_num1": 0.99, + "buy_cond1_num0": 1.29, + "buy_cond1_num1": 1.67, + "profit_no_change": true, + "profit_old_sma10": true, + "profit_over_rsi": true, + "profit_quick_gain": false, + "profit_quick_gain_3": false, + "profit_quick_lost": true, + "profit_short_loss": true, + "profit_sma10": true, + "profit_sma20": true, + "profit_very_old_sma10": false + }, + "sell": { + "sell_crossed_indicator0": "CDLCONCEALBABYSWALL-100", + "sell_indicator0": "BBANDS-2-5", + "sell_operator0": "D", + "sell_percent": 0.01, + "sell_percent3": 0.002, + "sell_percent5": 0.025, + "sell_real_num0": 0.01 + }, + "protection": { + "lookback": 46, + "lookback_stoploss": 144, + "protection_cooldown": 19, + "protection_max_allowed_dd": 0.36, + "protection_stop": 79, + "protection_stoploss_stop": 13, + "trade_limit": 7, + "trade_limit_stoploss": 5 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-02-14 03:19:36.837525+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_8.py b/GodStraJD3_8.py new file mode 100644 index 0000000..51f28a8 --- /dev/null +++ b/GodStraJD3_8.py @@ -0,0 +1,1043 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +# TODO: this gene is removed 'MAVP' cuz or error on periods +all_god_genes = { + 'Overlap Studies': { + 'BBANDS-0', # Bollinger Bands + 'BBANDS-1', # Bollinger Bands + 'BBANDS-2', # Bollinger Bands + 'DEMA', # Double Exponential Moving Average + 'EMA', # Exponential Moving Average + 'HT_TRENDLINE', # Hilbert Transform - Instantaneous Trendline + 'KAMA', # Kaufman Adaptive Moving Average + 'MA', # Moving average + 'MAMA-0', # MESA Adaptive Moving Average + 'MAMA-1', # MESA Adaptive Moving Average + # TODO: Fix this + # 'MAVP', # Moving average with variable period + 'MIDPOINT', # MidPoint over period + 'MIDPRICE', # Midpoint Price over period + 'SAR', # Parabolic SAR + 'SAREXT', # Parabolic SAR - Extended + 'SMA', # Simple Moving Average + 'T3', # Triple Exponential Moving Average (T3) + 'TEMA', # Triple Exponential Moving Average + 'TRIMA', # Triangular Moving Average + 'WMA', # Weighted Moving Average + }, + 'Momentum Indicators': { + 'ADX', # Average Directional Movement Index + 'ADXR', # Average Directional Movement Index Rating + 'APO', # Absolute Price Oscillator + 'AROON-0', # Aroon + 'AROON-1', # Aroon + 'AROONOSC', # Aroon Oscillator + 'BOP', # Balance Of Power + 'CCI', # Commodity Channel Index + 'CMO', # Chande Momentum Oscillator + 'DX', # Directional Movement Index + 'MACD-0', # Moving Average Convergence/Divergence + 'MACD-1', # Moving Average Convergence/Divergence + 'MACD-2', # Moving Average Convergence/Divergence + 'MACDEXT-0', # MACD with controllable MA type + 'MACDEXT-1', # MACD with controllable MA type + 'MACDEXT-2', # MACD with controllable MA type + 'MACDFIX-0', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-1', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-2', # Moving Average Convergence/Divergence Fix 12/26 + 'MFI', # Money Flow Index + 'MINUS_DI', # Minus Directional Indicator + 'MINUS_DM', # Minus Directional Movement + 'MOM', # Momentum + 'PLUS_DI', # Plus Directional Indicator + 'PLUS_DM', # Plus Directional Movement + 'PPO', # Percentage Price Oscillator + 'ROC', # Rate of change : ((price/prevPrice)-1)*100 + # Rate of change Percentage: (price-prevPrice)/prevPrice + 'ROCP', + 'ROCR', # Rate of change ratio: (price/prevPrice) + # Rate of change ratio 100 scale: (price/prevPrice)*100 + 'ROCR100', + 'RSI', # Relative Strength Index + 'STOCH-0', # Stochastic + 'STOCH-1', # Stochastic + 'STOCHF-0', # Stochastic Fast + 'STOCHF-1', # Stochastic Fast + 'STOCHRSI-0', # Stochastic Relative Strength Index + 'STOCHRSI-1', # Stochastic Relative Strength Index + # 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA + 'TRIX', + 'ULTOSC', # Ultimate Oscillator + 'WILLR', # Williams' %R + }, + 'Volume Indicators': { + 'AD', # Chaikin A/D Line + 'ADOSC', # Chaikin A/D Oscillator + 'OBV', # On Balance Volume + }, + 'Volatility Indicators': { + 'ATR', # Average True Range + 'NATR', # Normalized Average True Range + 'TRANGE', # True Range + }, + 'Price Transform': { + 'AVGPRICE', # Average Price + 'MEDPRICE', # Median Price + 'TYPPRICE', # Typical Price + 'WCLPRICE', # Weighted Close Price + }, + 'Cycle Indicators': { + 'HT_DCPERIOD', # Hilbert Transform - Dominant Cycle Period + 'HT_DCPHASE', # Hilbert Transform - Dominant Cycle Phase + 'HT_PHASOR-0', # Hilbert Transform - Phasor Components + 'HT_PHASOR-1', # Hilbert Transform - Phasor Components + 'HT_SINE-0', # Hilbert Transform - SineWave + 'HT_SINE-1', # Hilbert Transform - SineWave + 'HT_TRENDMODE', # Hilbert Transform - Trend vs Cycle Mode + }, + 'Pattern Recognition': { + 'CDL2CROWS', # Two Crows + 'CDL3BLACKCROWS', # Three Black Crows + 'CDL3INSIDE', # Three Inside Up/Down + 'CDL3LINESTRIKE', # Three-Line Strike + 'CDL3OUTSIDE', # Three Outside Up/Down + 'CDL3STARSINSOUTH', # Three Stars In The South + 'CDL3WHITESOLDIERS', # Three Advancing White Soldiers + 'CDLABANDONEDBABY', # Abandoned Baby + 'CDLADVANCEBLOCK', # Advance Block + 'CDLBELTHOLD', # Belt-hold + 'CDLBREAKAWAY', # Breakaway + 'CDLCLOSINGMARUBOZU', # Closing Marubozu + 'CDLCONCEALBABYSWALL', # Concealing Baby Swallow + 'CDLCOUNTERATTACK', # Counterattack + 'CDLDARKCLOUDCOVER', # Dark Cloud Cover + 'CDLDOJI', # Doji + 'CDLDOJISTAR', # Doji Star + 'CDLDRAGONFLYDOJI', # Dragonfly Doji + 'CDLENGULFING', # Engulfing Pattern + 'CDLEVENINGDOJISTAR', # Evening Doji Star + 'CDLEVENINGSTAR', # Evening Star + 'CDLGAPSIDESIDEWHITE', # Up/Down-gap side-by-side white lines + 'CDLGRAVESTONEDOJI', # Gravestone Doji + 'CDLHAMMER', # Hammer + 'CDLHANGINGMAN', # Hanging Man + 'CDLHARAMI', # Harami Pattern + 'CDLHARAMICROSS', # Harami Cross Pattern + 'CDLHIGHWAVE', # High-Wave Candle + 'CDLHIKKAKE', # Hikkake Pattern + 'CDLHIKKAKEMOD', # Modified Hikkake Pattern + 'CDLHOMINGPIGEON', # Homing Pigeon + 'CDLIDENTICAL3CROWS', # Identical Three Crows + 'CDLINNECK', # In-Neck Pattern + 'CDLINVERTEDHAMMER', # Inverted Hammer + 'CDLKICKING', # Kicking + 'CDLKICKINGBYLENGTH', # Kicking - bull/bear determined by the longer marubozu + 'CDLLADDERBOTTOM', # Ladder Bottom + 'CDLLONGLEGGEDDOJI', # Long Legged Doji + 'CDLLONGLINE', # Long Line Candle + 'CDLMARUBOZU', # Marubozu + 'CDLMATCHINGLOW', # Matching Low + 'CDLMATHOLD', # Mat Hold + 'CDLMORNINGDOJISTAR', # Morning Doji Star + 'CDLMORNINGSTAR', # Morning Star + 'CDLONNECK', # On-Neck Pattern + 'CDLPIERCING', # Piercing Pattern + 'CDLRICKSHAWMAN', # Rickshaw Man + 'CDLRISEFALL3METHODS', # Rising/Falling Three Methods + 'CDLSEPARATINGLINES', # Separating Lines + 'CDLSHOOTINGSTAR', # Shooting Star + 'CDLSHORTLINE', # Short Line Candle + 'CDLSPINNINGTOP', # Spinning Top + 'CDLSTALLEDPATTERN', # Stalled Pattern + 'CDLSTICKSANDWICH', # Stick Sandwich + # Takuri (Dragonfly Doji with very long lower shadow) + 'CDLTAKURI', + 'CDLTASUKIGAP', # Tasuki Gap + 'CDLTHRUSTING', # Thrusting Pattern + 'CDLTRISTAR', # Tristar Pattern + 'CDLUNIQUE3RIVER', # Unique 3 River + 'CDLUPSIDEGAP2CROWS', # Upside Gap Two Crows + 'CDLXSIDEGAP3METHODS', # Upside/Downside Gap Three Methods + + }, + 'Statistic Functions': { + 'BETA', # Beta + 'CORREL', # Pearson's Correlation Coefficient (r) + 'LINEARREG', # Linear Regression + 'LINEARREG_ANGLE', # Linear Regression Angle + 'LINEARREG_INTERCEPT', # Linear Regression Intercept + 'LINEARREG_SLOPE', # Linear Regression Slope + 'STDDEV', # Standard Deviation + 'TSF', # Time Series Forecast + 'VAR', # Variance + } + +} +god_genes = set() +########################### SETTINGS ############################## + +# god_genes = {'SMA'} +god_genes |= all_god_genes['Overlap Studies'] +god_genes |= all_god_genes['Momentum Indicators'] +god_genes |= all_god_genes['Volume Indicators'] +god_genes |= all_god_genes['Volatility Indicators'] +god_genes |= all_god_genes['Price Transform'] +god_genes |= all_god_genes['Cycle Indicators'] +god_genes |= all_god_genes['Pattern Recognition'] +god_genes |= all_god_genes['Statistic Functions'] + +#timeperiods = [5, 6, 12, 15, 50, 55, 100, 110] +timeperiods = [5, 10, 20, 50, 100] + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD3_8(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + sell_percent = DecimalParameter(0, 0.02, decimals=3, default=0, space='sell') + sell_percent3 = DecimalParameter(0, 0.03, decimals=3, default=0, space='sell') + sell_percent5 = DecimalParameter(0, 0.04, decimals=3, default=0, space='sell') + + # Sell Hyperoptable Parameters/Spaces. + sell_crossed_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="CDLSHOOTINGSTAR-150", space='sell') + # sell_crossed_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="MAMA-1-100", space='sell') + # sell_crossed_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLMATHOLD-6", space='sell') + + sell_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="CDLUPSIDEGAP2CROWS-5", space='sell') + # sell_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="CDLHARAMICROSS-150", space='sell') + # sell_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDL2CROWS-5", space='sell') + + sell_operator0 = CategoricalParameter(operators, default=" float: + fee = 1.0007 + profit = ((current*fee) - (price*fee)) + + return float(f"{profit:.8f}") + + def calc_percentage_lower(self, price: float, current: float) -> float: + fee = 1.0007 + price = price*fee + current = current*fee + lowerpercent = ((price-current)/(price*fee))*100 + return float(f"{lowerpercent:.8f}") + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": self.protection_cooldown.value + }, + # { + # "method": "MaxDrawdown", + # "lookback_period_candles": self.lookback.value, + # "trade_limit": self.trade_limit.value, + # "stop_duration_candles": self.protection_stop.value, + # "max_allowed_drawdown": self.protection_max_allowed_dd.value, + # "only_per_pair": False + # }, + # { + # "method": "StoplossGuard", + # "lookback_period_candles": self.lookback_stoploss.value, + # "trade_limit": self.trade_limit_stoploss.value, + # "stop_duration_candles": self.protection_stoploss_stop.value, + # "only_per_pair": False + # }, + ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + if (current_profit > 0) & ( + (last_candle['percent5'] <= -0.015) + | (last_candle['percent10'] <= -0.015) + | (last_candle['percent20'] <= -0.015) + ) \ + & ((current_time - trade.open_date_utc).seconds >= 2400): + return "quick_lost" + + if self.profit_quick_lost.value: + if (current_profit >= 0) & (last_candle['percent3'] < -0.01) \ + & ((current_time - trade.open_date_utc).seconds >= 2400): + return "quick_lost_1H" + + if self.profit_no_change.value: + if (current_profit > 0.015) & (last_candle['percent20'] < 0.0) & ((current_time - trade.open_date_utc).seconds >= 2400): + return "no_change" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if self.profit_quick_gain_3.value: + if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if self.profit_quick_gain.value: + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0): #& ((current_time - trade.open_date_utc).seconds <= 3600) + return "quick_gain" + + if self.profit_sma10.value: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10'] * 1.005) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20.value: + if (current_profit > 0.005) & (last_candle['percent5'] < 0) & (last_candle['sma20_pente'] < -0.015)\ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20'] * 1.005) & + ((last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10.value: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10.value: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi.value: + if (current_profit > 0) \ + & (last_candle['rsi'] > 88): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if self.profit_short_loss.value: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max20'] = ta.MAX(dataframe['close'], timeperiod=20) + dataframe['max50'] = ta.MAX(dataframe['close'], timeperiod=50) + + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe["bb_lower_pente"] = 100 * (dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1)) / dataframe["bb_lowerband"] + dataframe["sma10_pente"] = 100 * (dataframe["sma10"] - dataframe["sma10"].shift(1)) / dataframe["sma10"] + dataframe["sma20_pente"] = 100 * (dataframe["sma20"] - dataframe["sma20"].shift(1)) / dataframe["sma20"] + + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["HT_PHASOR-0-100"] = gene_calculator(dataframe, "HT_PHASOR-0-100") + dataframe["CDLIDENTICAL3CROWS-100"] = gene_calculator(dataframe, "CDLIDENTICAL3CROWS-100") + dataframe["DEMA-10"] = gene_calculator(dataframe, "DEMA-10") + dataframe["CDLMORNINGDOJISTAR-20"] = gene_calculator(dataframe, "CDLMORNINGDOJISTAR-20") + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + # dataframe["q_0.1"] = dataframe.rolling(50).quantile(.1)['close'] + # dataframe["q_0.25"] = dataframe.rolling(50).quantile(.25)['close'] + # dataframe["q_0.33"] = dataframe.rolling(50).quantile(.33)['close'] + + dataframe["MACD-2-10"] = gene_calculator(dataframe, "MACD-2-10") + dataframe["CDLIDENTICAL3CROWS-10"] = gene_calculator(dataframe, "CDLIDENTICAL3CROWS-10") + dataframe["PPO-10"] = gene_calculator(dataframe, "PPO-10") + dataframe["MINUS_DM-10"] = gene_calculator(dataframe, "MINUS_DM-10") + dataframe["MOM-10"] = gene_calculator(dataframe, "MOM-10") + dataframe["MACDEXT-0-100"] = gene_calculator(dataframe, "MACDEXT-0-100") + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + + percent_lower = False + current_price = dataframe['close'].iloc[-1] + + dataframe['should_sell'] = False + dataframe['should_buy'] = False + + # Get the previous trade + trade = Trade.get_trades_proxy(is_open=False, pair=metadata['pair']) + if trade: + trade = trade[-1] + lsp = trade.close_rate + if lsp: + percent_lower = self.calc_percentage_lower(price=lsp, current=current_price) + # Found a bug? When force selling it doesnt close it + else: + lsp = trade.open_rate + if lsp: + percent_lower = self.calc_percentage_lower(price=lsp, current=current_price) + else: + lsp = 0.00 + + # Get the current Trade + trade = Trade.get_trades_proxy(is_open=True, pair=metadata['pair']) + if trade: + trade = trade[-1] + lbp = trade.open_rate + open_trade = True + profit = self.calc_profit(price=lbp, current=current_price) + profit_percent = (profit/lbp)*100 + else: + lbp = 0.00 + open_trade = False + profit = False + profit_percent = False + # print("------------") + # print("Last Sold For:", lsp) + + # if open_trade: + # print("Bought for: ", lbp) + # print("Current Price: ", current_price) + # if profit: + # print("Current Profit: ", profit, " ", float(f"{profit_percent:.8f}"), "%") + # if percent_lower and not open_trade: + # print("Percent Lower: ", float(f"{percent_lower:.8f}"), "%") + + # Should we Sell? + if profit_percent: + if profit_percent > 1: + dataframe['should_sell'] = True + + # Should we buy? + if not open_trade: + if (lsp == 0.00) & (lbp == 0.00): + dataframe['should_buy'] = True + # Is the percentage of what we sold for and the current price 2% lower + if percent_lower > 2: + dataframe['should_buy'] = True + + dataframe['last_sell_price'] = lsp + dataframe['last_buy_price'] = lbp + dataframe['current_price'] = current_price + dataframe['profit_percent'] = profit_percent + # print("Current Dataframe:") + # print(dataframe.tail(1)) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + conditions2 = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + 0.67 #self.buy_real_num0.value + ) + conditions.append(condition1) + + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + # & (dataframe['volume10'] * dataframe['close'] / 1000 >= 10) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['low'] < dataframe['min20']) + & (dataframe['bb_width'] > 0.05) + ), ['buy', 'buy_tag']] = (1, 'buy_signal_condition') + + #################################################################### + + condition2, dataframe = condition_generator(dataframe, "/=R", "CMO-10", "HT_DCPERIOD-20", 0.37) + conditions2.append(condition2) + + dataframe.loc[ + (reduce(lambda x, y: x & y, conditions2) + # & (dataframe['volume10'] * dataframe['close'] / 1000 >= 10) + & (dataframe['close'].shift(9) < dataframe['bb_lowerband'].shift(9)) + ), ['buy', 'buy_tag']] = (1, 'buy_bb_width_1') + + #################################################################### + conditions = list() + + condition, dataframe = condition_generator(dataframe, "/= 10) + & (dataframe['sma10_pente'] > 0.02) + & (dataframe['sma10'].shift(1) < dataframe['sma10']) + & (dataframe['sma20'].shift(1) < dataframe['sma20']) + & (dataframe['sma50'].shift(1) < dataframe['sma50']) + & (dataframe['close'] < dataframe['bb_upperband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['percent50'] < 0.025) + ), + ['buy', 'buy_tag']] = (1, 'buy_sma') + # print(len(dataframe.keys())) + + + #################################################################### + # conditions3 = list() + # + # condition3, dataframe = condition_generator(dataframe, "D", "MACD-2-10", "CDLIDENTICAL3CROWS-10", 0.88) + # conditions3.append(condition3) + # + # condition3, dataframe = condition_generator(dataframe, "/", "MOM-10", "MACDEXT-0-100", 0.87) + # #conditions3.append(condition3) + # + # if conditions3: + # dataframe.loc[ + # (reduce(lambda x, y: x & y, conditions3) + # # & (dataframe['volume10'] * dataframe['close'] / 1000 >= 10) + # & (dataframe['close'].shift(7) < dataframe['bb_lowerband'].shift(7)) + # ), + # ['buy', 'buy_tag']] = (1, 'buy_minus_dm') + # print(len(dataframe.keys())) + + # ################################################################### + # + # dataframe.loc[ + # (dataframe['close'] > dataframe['bb_upperband']) + # & (dataframe['percent'] > 0.01) + # & (dataframe['percent20'] < 0.035) + # & (dataframe['cond1'] > 13) + # & (dataframe['bb_width'] > 0.02) + # & (dataframe['cond1'].shift(4) < 8) + # & (dataframe['bb_width'].shift(4) < 0.012) + # , ['buy', 'buy_tag']] = (1, 'buy_cond1') + # + # #################################################################### + # + # dataframe.loc[ + # (dataframe['bb_lower_pente'].shift(4) < -0.50) + # & (dataframe['sma10_pente'] > -0.2) + # & (dataframe['sma10_pente'].shift(4) < -0.4) + # & (dataframe['percent5'] < 0.01) + # , ['buy', 'buy_tag']] = (1, 'buy_lower_pente') + # + # #################################################################### + # + # dataframe.loc[ + # (dataframe['percent50'].shift(4) < -0.05) + # & (dataframe['sma10_pente'].shift(1) < 0) + # & (dataframe['sma10_pente'] >= 0) + # & (dataframe['open'] < dataframe['sma10']) + # , ['buy', 'buy_tag']] = (1, 'buy_sma50') + # + # pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + # pandas.set_option('display.max_columns', 30) + # print(condition1) + # print(dataframe["q_0.1"]) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # conditions = list() + # + # condition1, dataframe = condition_generator( + # dataframe, + # self.sell_operator0.value, + # self.sell_indicator0.value, + # self.sell_crossed_indicator0.value, + # self.sell_real_num0.value + # ) + # conditions.append(condition1) + # + # # print(self.sell_percent.value, ' ', + # # self.sell_percent.value + self.sell_percent3.value, ' ', + # # self.sell_percent.value + self.sell_percent3.value + self.sell_percent5.value) + # dataframe.loc[ + # ( + # (reduce(lambda x, y: x & y, conditions)) & + # (dataframe['percent'] < - self.sell_percent.value) & + # (dataframe['percent3'] < - (self.sell_percent3.value + self.sell_percent.value)) & + # (dataframe['percent5'] < - (self.sell_percent5.value + self.sell_percent3.value + self.sell_percent.value)) & + # (dataframe['close'] < dataframe['open']) + # # (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + # ), 'sell'] = 1 + return dataframe + diff --git a/GodStraJD3_9.json b/GodStraJD3_9.json new file mode 100644 index 0000000..bed9dfb --- /dev/null +++ b/GodStraJD3_9.json @@ -0,0 +1,48 @@ +{ + "strategy_name": "GodStraJD3_9", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.058, + "trailing_stop_positive_offset": 0.079, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_signal_bb_width": 1.21, + "buy_signal_condition_high": 1.02, + "buy_signal_minus": 1.22, + "buy_signal_sma10": 1.08, + "buy_signal_sma10_pente": 0.041, + "buy_signal_sma_max": 1.09, + "buy_signal_sma_min": 1.0, + "buy_signal_sma_percent50": 0.061 + }, + "sell": { + "sell_crossed_indicator0": "CDL3INSIDE-50", + "sell_indicator0": "CDLGAPSIDESIDEWHITE-50", + "sell_operator0": "CUT", + "sell_percent": 0.006, + "sell_percent3": 0.0, + "sell_percent5": 0.004, + "sell_real_num0": 0.13 + }, + "protection": { + "lookback": 46, + "lookback_stoploss": 144, + "protection_cooldown": 19, + "protection_max_allowed_dd": 0.36, + "protection_stop": 79, + "protection_stoploss_stop": 13, + "trade_limit": 7, + "trade_limit_stoploss": 5 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-02-17 19:22:05.539646+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_9.jsonBest b/GodStraJD3_9.jsonBest new file mode 100644 index 0000000..db05efb --- /dev/null +++ b/GodStraJD3_9.jsonBest @@ -0,0 +1,48 @@ +{ + "strategy_name": "GodStraJD3_9", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.058, + "trailing_stop_positive_offset": 0.079, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_signal_bb_width": 1.09, + "buy_signal_condition_high": 1.1, + "buy_signal_minus": 1.09, + "buy_signal_sma10": 1.02, + "buy_signal_sma10_pente": 0.001, + "buy_signal_sma_max": 1.03, + "buy_signal_sma_min": 1.08, + "buy_signal_sma_percent50": 0.036 + }, + "sell": { + "sell_crossed_indicator0": "CDLSHORTLINE-50", + "sell_indicator0": "CDLSEPARATINGLINES-20", + "sell_operator0": "=R", + "sell_percent": 0.01, + "sell_percent3": 0.021, + "sell_percent5": 0.006, + "sell_real_num0": 0.84 + }, + "protection": { + "lookback": 46, + "lookback_stoploss": 144, + "protection_cooldown": 19, + "protection_max_allowed_dd": 0.36, + "protection_stop": 79, + "protection_stoploss_stop": 13, + "trade_limit": 7, + "trade_limit_stoploss": 5 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-02-17 00:06:37.000718+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_9.jsonOld b/GodStraJD3_9.jsonOld new file mode 100644 index 0000000..4b4f77a --- /dev/null +++ b/GodStraJD3_9.jsonOld @@ -0,0 +1,47 @@ +{ + "strategy_name": "GodStraJD3_9", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.058, + "trailing_stop_positive_offset": 0.079, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_signal_bb_width": 1.1, + "buy_signal_condition_high": 1.07, + "buy_signal_minus": 1.07, + "buy_signal_sma": 1.08, + "buy_signal_sma10": 1.08, + "buy_signal_sma10_pente": 0.041, + "buy_signal_sma_percent50": 0.092 + }, + "sell": { + "sell_crossed_indicator0": "CDLSHORTLINE-50", + "sell_indicator0": "CDLSEPARATINGLINES-20", + "sell_operator0": "=R", + "sell_percent": 0.01, + "sell_percent3": 0.021, + "sell_percent5": 0.006, + "sell_real_num0": 0.84 + }, + "protection": { + "lookback": 46, + "lookback_stoploss": 144, + "protection_cooldown": 19, + "protection_max_allowed_dd": 0.36, + "protection_stop": 79, + "protection_stoploss_stop": 13, + "trade_limit": 7, + "trade_limit_stoploss": 5 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-02-16 18:51:17.244506+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_9.jsonOld2 b/GodStraJD3_9.jsonOld2 new file mode 100644 index 0000000..423c009 --- /dev/null +++ b/GodStraJD3_9.jsonOld2 @@ -0,0 +1,48 @@ +{ + "strategy_name": "GodStraJD3_9", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.058, + "trailing_stop_positive_offset": 0.079, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_signal_bb_width": 1.81, + "buy_signal_condition_high": 1.28, + "buy_signal_minus": 1.95, + "buy_signal_sma10": 1.02, + "buy_signal_sma10_pente": 0.055, + "buy_signal_sma_max": 1.02, + "buy_signal_sma_min": 1.03, + "buy_signal_sma_percent50": 0.089 + }, + "sell": { + "sell_crossed_indicator0": "CDLSHORTLINE-50", + "sell_indicator0": "CDLSEPARATINGLINES-20", + "sell_operator0": "=R", + "sell_percent": 0.01, + "sell_percent3": 0.021, + "sell_percent5": 0.006, + "sell_real_num0": 0.84 + }, + "protection": { + "lookback": 46, + "lookback_stoploss": 144, + "protection_cooldown": 19, + "protection_max_allowed_dd": 0.36, + "protection_stop": 79, + "protection_stoploss_stop": 13, + "trade_limit": 7, + "trade_limit_stoploss": 5 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-02-17 15:42:20.763706+00:00" +} \ No newline at end of file diff --git a/GodStraJD3_9.py b/GodStraJD3_9.py new file mode 100644 index 0000000..18ce8ae --- /dev/null +++ b/GodStraJD3_9.py @@ -0,0 +1,1197 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from datetime import timedelta, datetime + +from freqtrade.strategy.strategy_helper import merge_informative_pair + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle + +# TODO: this gene is removed 'MAVP' cuz or error on periods +import user_data.strategies.custom_indicators as csa + +all_god_genes = { + 'Overlap Studies': { + 'BBANDS-0', # Bollinger Bands + 'BBANDS-1', # Bollinger Bands + 'BBANDS-2', # Bollinger Bands + 'DEMA', # Double Exponential Moving Average + 'EMA', # Exponential Moving Average + 'HT_TRENDLINE', # Hilbert Transform - Instantaneous Trendline + 'KAMA', # Kaufman Adaptive Moving Average + 'MA', # Moving average + 'MAMA-0', # MESA Adaptive Moving Average + 'MAMA-1', # MESA Adaptive Moving Average + # TODO: Fix this + # 'MAVP', # Moving average with variable period + 'MIDPOINT', # MidPoint over period + 'MIDPRICE', # Midpoint Price over period + 'SAR', # Parabolic SAR + 'SAREXT', # Parabolic SAR - Extended + 'SMA', # Simple Moving Average + 'T3', # Triple Exponential Moving Average (T3) + 'TEMA', # Triple Exponential Moving Average + 'TRIMA', # Triangular Moving Average + 'WMA', # Weighted Moving Average + }, + 'Momentum Indicators': { + 'ADX', # Average Directional Movement Index + 'ADXR', # Average Directional Movement Index Rating + 'APO', # Absolute Price Oscillator + 'AROON-0', # Aroon + 'AROON-1', # Aroon + 'AROONOSC', # Aroon Oscillator + 'BOP', # Balance Of Power + 'CCI', # Commodity Channel Index + 'CMO', # Chande Momentum Oscillator + 'DX', # Directional Movement Index + 'MACD-0', # Moving Average Convergence/Divergence + 'MACD-1', # Moving Average Convergence/Divergence + 'MACD-2', # Moving Average Convergence/Divergence + 'MACDEXT-0', # MACD with controllable MA type + 'MACDEXT-1', # MACD with controllable MA type + 'MACDEXT-2', # MACD with controllable MA type + 'MACDFIX-0', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-1', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-2', # Moving Average Convergence/Divergence Fix 12/26 + 'MFI', # Money Flow Index + 'MINUS_DI', # Minus Directional Indicator + 'MINUS_DM', # Minus Directional Movement + 'MOM', # Momentum + 'PLUS_DI', # Plus Directional Indicator + 'PLUS_DM', # Plus Directional Movement + 'PPO', # Percentage Price Oscillator + 'ROC', # Rate of change : ((price/prevPrice)-1)*100 + # Rate of change Percentage: (price-prevPrice)/prevPrice + 'ROCP', + 'ROCR', # Rate of change ratio: (price/prevPrice) + # Rate of change ratio 100 scale: (price/prevPrice)*100 + 'ROCR100', + 'RSI', # Relative Strength Index + 'STOCH-0', # Stochastic + 'STOCH-1', # Stochastic + 'STOCHF-0', # Stochastic Fast + 'STOCHF-1', # Stochastic Fast + 'STOCHRSI-0', # Stochastic Relative Strength Index + 'STOCHRSI-1', # Stochastic Relative Strength Index + # 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA + 'TRIX', + 'ULTOSC', # Ultimate Oscillator + 'WILLR', # Williams' %R + }, + 'Volume Indicators': { + 'AD', # Chaikin A/D Line + 'ADOSC', # Chaikin A/D Oscillator + 'OBV', # On Balance Volume + }, + 'Volatility Indicators': { + 'ATR', # Average True Range + 'NATR', # Normalized Average True Range + 'TRANGE', # True Range + }, + 'Price Transform': { + 'AVGPRICE', # Average Price + 'MEDPRICE', # Median Price + 'TYPPRICE', # Typical Price + 'WCLPRICE', # Weighted Close Price + }, + 'Cycle Indicators': { + 'HT_DCPERIOD', # Hilbert Transform - Dominant Cycle Period + 'HT_DCPHASE', # Hilbert Transform - Dominant Cycle Phase + 'HT_PHASOR-0', # Hilbert Transform - Phasor Components + 'HT_PHASOR-1', # Hilbert Transform - Phasor Components + 'HT_SINE-0', # Hilbert Transform - SineWave + 'HT_SINE-1', # Hilbert Transform - SineWave + 'HT_TRENDMODE', # Hilbert Transform - Trend vs Cycle Mode + }, + 'Pattern Recognition': { + 'CDL2CROWS', # Two Crows + 'CDL3BLACKCROWS', # Three Black Crows + 'CDL3INSIDE', # Three Inside Up/Down + 'CDL3LINESTRIKE', # Three-Line Strike + 'CDL3OUTSIDE', # Three Outside Up/Down + 'CDL3STARSINSOUTH', # Three Stars In The South + 'CDL3WHITESOLDIERS', # Three Advancing White Soldiers + 'CDLABANDONEDBABY', # Abandoned Baby + 'CDLADVANCEBLOCK', # Advance Block + 'CDLBELTHOLD', # Belt-hold + 'CDLBREAKAWAY', # Breakaway + 'CDLCLOSINGMARUBOZU', # Closing Marubozu + 'CDLCONCEALBABYSWALL', # Concealing Baby Swallow + 'CDLCOUNTERATTACK', # Counterattack + 'CDLDARKCLOUDCOVER', # Dark Cloud Cover + 'CDLDOJI', # Doji + 'CDLDOJISTAR', # Doji Star + 'CDLDRAGONFLYDOJI', # Dragonfly Doji + 'CDLENGULFING', # Engulfing Pattern + 'CDLEVENINGDOJISTAR', # Evening Doji Star + 'CDLEVENINGSTAR', # Evening Star + 'CDLGAPSIDESIDEWHITE', # Up/Down-gap side-by-side white lines + 'CDLGRAVESTONEDOJI', # Gravestone Doji + 'CDLHAMMER', # Hammer + 'CDLHANGINGMAN', # Hanging Man + 'CDLHARAMI', # Harami Pattern + 'CDLHARAMICROSS', # Harami Cross Pattern + 'CDLHIGHWAVE', # High-Wave Candle + 'CDLHIKKAKE', # Hikkake Pattern + 'CDLHIKKAKEMOD', # Modified Hikkake Pattern + 'CDLHOMINGPIGEON', # Homing Pigeon + 'CDLIDENTICAL3CROWS', # Identical Three Crows + 'CDLINNECK', # In-Neck Pattern + 'CDLINVERTEDHAMMER', # Inverted Hammer + 'CDLKICKING', # Kicking + 'CDLKICKINGBYLENGTH', # Kicking - bull/bear determined by the longer marubozu + 'CDLLADDERBOTTOM', # Ladder Bottom + 'CDLLONGLEGGEDDOJI', # Long Legged Doji + 'CDLLONGLINE', # Long Line Candle + 'CDLMARUBOZU', # Marubozu + 'CDLMATCHINGLOW', # Matching Low + 'CDLMATHOLD', # Mat Hold + 'CDLMORNINGDOJISTAR', # Morning Doji Star + 'CDLMORNINGSTAR', # Morning Star + 'CDLONNECK', # On-Neck Pattern + 'CDLPIERCING', # Piercing Pattern + 'CDLRICKSHAWMAN', # Rickshaw Man + 'CDLRISEFALL3METHODS', # Rising/Falling Three Methods + 'CDLSEPARATINGLINES', # Separating Lines + 'CDLSHOOTINGSTAR', # Shooting Star + 'CDLSHORTLINE', # Short Line Candle + 'CDLSPINNINGTOP', # Spinning Top + 'CDLSTALLEDPATTERN', # Stalled Pattern + 'CDLSTICKSANDWICH', # Stick Sandwich + # Takuri (Dragonfly Doji with very long lower shadow) + 'CDLTAKURI', + 'CDLTASUKIGAP', # Tasuki Gap + 'CDLTHRUSTING', # Thrusting Pattern + 'CDLTRISTAR', # Tristar Pattern + 'CDLUNIQUE3RIVER', # Unique 3 River + 'CDLUPSIDEGAP2CROWS', # Upside Gap Two Crows + 'CDLXSIDEGAP3METHODS', # Upside/Downside Gap Three Methods + + }, + 'Statistic Functions': { + 'BETA', # Beta + 'CORREL', # Pearson's Correlation Coefficient (r) + 'LINEARREG', # Linear Regression + 'LINEARREG_ANGLE', # Linear Regression Angle + 'LINEARREG_INTERCEPT', # Linear Regression Intercept + 'LINEARREG_SLOPE', # Linear Regression Slope + 'STDDEV', # Standard Deviation + 'TSF', # Time Series Forecast + 'VAR', # Variance + } + +} +god_genes = set() +########################### SETTINGS ############################## + +# god_genes = {'SMA'} +god_genes |= all_god_genes['Overlap Studies'] +god_genes |= all_god_genes['Momentum Indicators'] +god_genes |= all_god_genes['Volume Indicators'] +god_genes |= all_god_genes['Volatility Indicators'] +god_genes |= all_god_genes['Price Transform'] +god_genes |= all_god_genes['Cycle Indicators'] +god_genes |= all_god_genes['Pattern Recognition'] +god_genes |= all_god_genes['Statistic Functions'] + +#timeperiods = [5, 6, 12, 15, 50, 55, 100, 110] +timeperiods = [5, 10, 20, 50, 100] + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD3_9(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 10, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '5m' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + sell_percent = DecimalParameter(0, 0.02, decimals=3, default=0, space='sell') + sell_percent3 = DecimalParameter(0, 0.03, decimals=3, default=0, space='sell') + sell_percent5 = DecimalParameter(0, 0.04, decimals=3, default=0, space='sell') + + # Sell Hyperoptable Parameters/Spaces. + sell_crossed_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="CDLSHOOTINGSTAR-150", space='sell') + # sell_crossed_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="MAMA-1-100", space='sell') + # sell_crossed_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLMATHOLD-6", space='sell') + + sell_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="CDLUPSIDEGAP2CROWS-5", space='sell') + # sell_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="CDLHARAMICROSS-150", space='sell') + # sell_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDL2CROWS-5", space='sell') + + sell_operator0 = CategoricalParameter(operators, default=" float: + fee = 1.0007 + profit = ((current*fee) - (price*fee)) + + return float(f"{profit:.8f}") + + def calc_percentage_lower(self, price: float, current: float) -> float: + fee = 1.0007 + price = price*fee + current = current*fee + lowerpercent = ((price-current)/(price*fee))*100 + return float(f"{lowerpercent:.8f}") + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": self.protection_cooldown.value + }, + # { + # "method": "MaxDrawdown", + # "lookback_period_candles": self.lookback.value, + # "trade_limit": self.trade_limit.value, + # "stop_duration_candles": self.protection_stop.value, + # "max_allowed_drawdown": self.protection_max_allowed_dd.value, + # "only_per_pair": False + # }, + # { + # "method": "StoplossGuard", + # "lookback_period_candles": self.lookback_stoploss.value, + # "trade_limit": self.trade_limit_stoploss.value, + # "stop_duration_candles": self.protection_stoploss_stop.value, + # "only_per_pair": False + # }, + ] + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + if (current_profit > 0) & ( + (last_candle['percent5'] <= -0.015) + | (last_candle['percent10'] <= -0.015) + | (last_candle['percent20'] <= -0.015) + ) \ + & ((current_time - trade.open_date_utc).seconds >= 2400): + return "quick_lost" + + if self.profit_quick_lost: + if (current_profit >= 0) & (last_candle['percent3'] < -0.01) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 2400): + return "quick_lost_1H" + + if self.profit_no_change: + if (current_profit > 0.015) & (last_candle['percent20'] < 0.0) & \ + (last_candle['sma10'] < 0.0) & ((current_time - trade.open_date_utc).seconds >= 2400): + return "no_change" + + #if (current_profit > 0.01) & (last_candle['rsi'] < 30): + # return "small_rsi" + if self.profit_quick_gain_3: + if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600): + return "quick_gain_3" + if self.profit_quick_gain: + if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0): #& ((current_time - trade.open_date_utc).seconds <= 3600) + return "quick_gain" + + if self.profit_sma10: + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10'] * 1.005) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20: + if (current_profit > 0.005) & (last_candle['percent5'] < 0) & (last_candle['sma20_pente'] < -0.015)\ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20'] * 1.005) & + ((last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + # if self.profit_old_sma10: + # if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).days >= 3) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'old_sma10' + # if self.profit_very_old_sma10: + # if (current_profit > -0.01) \ + # & ((current_time - trade.open_date_utc).days >= 6) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0) | (last_candle['percent5'] < 0)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'very_old_sma10' + + if self.profit_over_rsi: + if (current_profit > 0) \ + & (last_candle['rsi'] > 88): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if self.profit_short_loss: + if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + # if (current_profit > 0) \ + # & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_rsi_2' + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe['profit'] = 0 + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)): + # dataframe["percent_ok"] = new dataframe() + # else: + # dataframe["percent_ok"] = 0 + + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max20'] = ta.MAX(dataframe['close'], timeperiod=20) + dataframe['max50'] = ta.MAX(dataframe['close'], timeperiod=50) + + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe["bb_lower_pente"] = 100 * (dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1)) / dataframe["bb_lowerband"] + dataframe["sma10_pente"] = 100 * (dataframe["sma10"] - dataframe["sma10"].shift(1)) / dataframe["sma10"] + dataframe["sma20_pente"] = 100 * (dataframe["sma20"] - dataframe["sma20"].shift(1)) / dataframe["sma20"] + + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["HT_PHASOR-0-100"] = gene_calculator(dataframe, "HT_PHASOR-0-100") + dataframe["CDLIDENTICAL3CROWS-100"] = gene_calculator(dataframe, "CDLIDENTICAL3CROWS-100") + dataframe["DEMA-10"] = gene_calculator(dataframe, "DEMA-10") + dataframe["CDLMORNINGDOJISTAR-20"] = gene_calculator(dataframe, "CDLMORNINGDOJISTAR-20") + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + # dataframe["q_0.1"] = dataframe.rolling(50).quantile(.1)['close'] + # dataframe["q_0.25"] = dataframe.rolling(50).quantile(.25)['close'] + # dataframe["q_0.33"] = dataframe.rolling(50).quantile(.33)['close'] + + dataframe["MACD-2-10"] = gene_calculator(dataframe, "MACD-2-10") + dataframe["CDLIDENTICAL3CROWS-10"] = gene_calculator(dataframe, "CDLIDENTICAL3CROWS-10") + dataframe["PPO-10"] = gene_calculator(dataframe, "PPO-10") + dataframe["MINUS_DM-10"] = gene_calculator(dataframe, "MINUS_DM-10") + dataframe["MOM-10"] = gene_calculator(dataframe, "MOM-10") + dataframe["MACDEXT-0-100"] = gene_calculator(dataframe, "MACDEXT-0-100") + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + + percent_lower = False + current_price = dataframe['close'].iloc[-1] + + dataframe['should_sell'] = False + dataframe['should_buy'] = False + + # Get the previous trade + # open_trades = Trade.get_trades_proxy(is_open=True) + # print(open_trades) + + trade = Trade.get_trades_proxy(is_open=False, pair=metadata['pair']) + if trade: + trade = trade[-1] + lsp = trade.close_rate + if lsp: + percent_lower = self.calc_percentage_lower(price=lsp, current=current_price) + # Found a bug? When force selling it doesnt close it + else: + lsp = trade.open_rate + if lsp: + percent_lower = self.calc_percentage_lower(price=lsp, current=current_price) + else: + lsp = 0.00 + + # Get the current Trade + trade = Trade.get_trades_proxy(is_open=True, pair=metadata['pair']) + if trade: + trade = trade[-1] + lbp = trade.open_rate + open_trade = True + profit = self.calc_profit(price=lbp, current=current_price) + profit_percent = (profit/lbp)*100 + else: + lbp = 0.00 + open_trade = False + profit = False + profit_percent = False + # print("------------") + # print("Last Sold For:", lsp) + + # if open_trade: + # print("Bought for: ", lbp) + # print("Current Price: ", current_price) + # if profit: + # print("Current Profit: ", profit, " ", float(f"{profit_percent:.8f}"), "%") + # if percent_lower and not open_trade: + # print("Percent Lower: ", float(f"{percent_lower:.8f}"), "%") + + # Should we Sell? + if profit_percent: + if profit_percent > 1: + dataframe['should_sell'] = True + + # Should we buy? + if not open_trade: + if (lsp == 0.00) & (lbp == 0.00): + dataframe['should_buy'] = True + # Is the percentage of what we sold for and the current price 2% lower + if percent_lower > 2: + dataframe['should_buy'] = True + + dataframe['last_sell_price'] = lsp + dataframe['last_buy_price'] = lbp + dataframe['current_price'] = current_price + dataframe['profit_percent'] = profit_percent + # print("Current Dataframe:") + # print(dataframe.tail(1)) + + # ## Base Timeframe / Pair + # + # # Kaufmann Adaptive Moving Average + # dataframe['kama'] = ta.KAMA(dataframe, length=233) + # + # # RMI: https://www.tradingview.com/script/kwIt9OgQ-Relative-Momentum-Index/ + # dataframe['rmi'] = csa.RMI(dataframe, length=24, mom=5) + # + # # Momentum Pinball: https://www.tradingview.com/script/fBpVB1ez-Momentum-Pinball-Indicator/ + # dataframe['roc-mp'] = ta.ROC(dataframe, timeperiod=1) + # dataframe['mp'] = ta.RSI(dataframe['roc-mp'], timeperiod=3) + # + # # MA Streak: https://www.tradingview.com/script/Yq1z7cIv-MA-Streak-Can-Show-When-a-Run-Is-Getting-Long-in-the-Tooth/ + # dataframe['mastreak'] = csa.mastreak(dataframe, period=4) + # + # # Percent Change Channel: https://www.tradingview.com/script/6wwAWXA1-MA-Streak-Change-Channel/ + # upper, mid, lower = csa.pcc(dataframe, period=40, mult=3) + # dataframe['pcc-lowerband'] = lower + # dataframe['pcc-upperband'] = upper + # + # lookup_idxs = dataframe.index.values - (abs(dataframe['mastreak'].values) + 1) + # valid_lookups = lookup_idxs >= 0 + # dataframe['sbc'] = np.nan + # dataframe.loc[valid_lookups, 'sbc'] = dataframe['close'].to_numpy()[lookup_idxs[valid_lookups].astype(int)] + # + # dataframe['streak-roc'] = 100 * (dataframe['close'] - dataframe['sbc']) / dataframe['sbc'] + # + # # Trends, Peaks and Crosses + # dataframe['candle-up'] = np.where(dataframe['close'] >= dataframe['open'], 1, 0) + # dataframe['candle-up-trend'] = np.where(dataframe['candle-up'].rolling(5).sum() >= 3, 1, 0) + # + # dataframe['rmi-up'] = np.where(dataframe['rmi'] >= dataframe['rmi'].shift(), 1, 0) + # dataframe['rmi-up-trend'] = np.where(dataframe['rmi-up'].rolling(5).sum() >= 3, 1, 0) + # + # dataframe['rmi-dn'] = np.where(dataframe['rmi'] <= dataframe['rmi'].shift(), 1, 0) + # dataframe['rmi-dn-count'] = dataframe['rmi-dn'].rolling(8).sum() + # + # dataframe['streak-bo'] = np.where(dataframe['streak-roc'] < dataframe['pcc-lowerband'], 1, 0) + # dataframe['streak-bo-count'] = dataframe['streak-bo'].rolling(8).sum() + # + # # Indicators used only for ROI and Custom Stoploss + # ssldown, sslup = csa.SSLChannels_ATR(dataframe, length=21) + # dataframe['sroc'] = csa.SROC(dataframe, roclen=21, emalen=13, smooth=21) + # dataframe['ssl-dir'] = np.where(sslup > ssldown, 'up', 'down') + # + # Base pair informative timeframe indicators + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_timeframe) + # + # # Get the "average day range" between the 1d high and 1d low to set up guards + # informative['1d-high'] = informative['close'].rolling(24).max() + # informative['1d-low'] = informative['close'].rolling(24).min() + # informative['adr'] = informative['1d-high'] - informative['1d-low'] + # + # # Base pair informative timeframe indicators + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_timeframe) + # + # # Get the "average day range" between the 1d high and 1d low to set up guards + # informative['1d-high'] = informative['close'].rolling(24).max() + # informative['1d-low'] = informative['close'].rolling(24).min() + # informative['adr'] = informative['1d-high'] - informative['1d-low'] + # + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, self.inf_timeframe, ffill=True) + # # + # # Other stake specific informative indicators + # # e.g if stake is BTC and current coin is XLM (pair: XLM/BTC) + # if self.config['stake_currency'] in ('BTC', 'ETH'): + # coin, stake = metadata['pair'].split('/') + # fiat = self.custom_fiat + # coin_fiat = f"{coin}/{fiat}" + # stake_fiat = f"{stake}/{fiat}" + # + # # Informative COIN/FIAT e.g. XLM/USD - Base Timeframe + # coin_fiat_tf = self.dp.get_pair_dataframe(pair=coin_fiat, timeframe=self.timeframe) + # dataframe[f"{fiat}_rmi"] = csa.RMI(coin_fiat_tf, length=55, mom=5) + # + # # Informative STAKE/FIAT e.g. BTC/USD - Base Timeframe + # stake_fiat_tf = self.dp.get_pair_dataframe(pair=stake_fiat, timeframe=self.timeframe) + # dataframe[f"{stake}_rmi"] = csa.RMI(stake_fiat_tf, length=55, mom=5) + # + # # Informatives for BTC/STAKE if not in whitelist + # else: + # pairs = self.dp.current_whitelist() + # btc_stake = f"BTC/{self.config['stake_currency']}" + # if not btc_stake in pairs: + # self.custom_btc_inf = True + # # BTC/STAKE - Base Timeframe + # btc_stake_tf = self.dp.get_pair_dataframe(pair=btc_stake, timeframe=self.timeframe) + # dataframe['BTC_rmi'] = csa.RMI(btc_stake_tf, length=55, mom=5) + # dataframe['BTC_close'] = btc_stake_tf['close'] + # dataframe['BTC_kama'] = ta.KAMA(btc_stake_tf, length=144) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + conditions2 = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + 0.7 #self.buy_real_num0.value + ) + conditions.append(condition1) + + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (dataframe['volume10'] * dataframe['close'] / 1000 >= 10) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['low'] < dataframe['min20']) + & (dataframe['percent'] > 0) + # & (dataframe['max'] < dataframe['close'] * self.buy_signal_condition_high.value) + ), ['buy', 'buy_tag']] = (1, 'buy_signal_condition') + + #################################################################### + + condition2, dataframe = condition_generator(dataframe, "/=R", "CMO-10", "HT_DCPERIOD-20", 0.37) + conditions2.append(condition2) + + dataframe.loc[ + (reduce(lambda x, y: x & y, conditions2) + # & (dataframe['volume10'] * dataframe['close'] / 1000 >= 10) + & (dataframe['close'].shift(9) < dataframe['bb_lowerband'].shift(9)) + # & (dataframe['max'] < dataframe['close'] * self.buy_signal_bb_width.value) + ), ['buy', 'buy_tag']] = (1, 'buy_bb_width_1') + + #################################################################### + conditions = list() + + condition, dataframe = condition_generator(dataframe, "/= 10) + & (dataframe['sma10_pente'] > self.buy_signal_sma10_pente.value) + & (dataframe['sma10'].shift(1) < dataframe['sma10'] * self.buy_signal_sma10.value) + & (dataframe['sma20'].shift(1) < dataframe['sma20']) + & (dataframe['sma50'].shift(1) < dataframe['sma50']) + & (dataframe['close'] < dataframe['bb_upperband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['percent50'] < self.buy_signal_sma_percent50.value) + # & (dataframe['max'] < dataframe['close'] * self.buy_signal_sma_max.value) + # & (dataframe['close'] < dataframe['min'] * self.buy_signal_sma_min.value) + # & (dataframe['close'] > dataframe['1d-low_1h'] * self.buy_signal_sma_min.value) + # & (dataframe['close'] < dataframe['1d-high_1h'] * self.buy_signal_sma_max.value) + ), + ['buy', 'buy_tag']] = (1, 'buy_sma') + # print(len(dataframe.keys())) + + #################################################################### + # conditions3 = list() + # + # condition3, dataframe = condition_generator(dataframe, "D", "MACD-2-10", "CDLIDENTICAL3CROWS-10", 0.88) + # conditions3.append(condition3) + # + # condition3, dataframe = condition_generator(dataframe, "/", "MOM-10", "MACDEXT-0-100", 0.87) + # #conditions3.append(condition3) + # + # if conditions3: + # dataframe.loc[ + # (reduce(lambda x, y: x & y, conditions3) + # # & (dataframe['volume10'] * dataframe['close'] / 1000 >= 10) + # & (dataframe['close'].shift(7) < dataframe['bb_lowerband'].shift(7)) + # & (dataframe['max'] < dataframe['close'] * self.buy_signal_minus.value) + # ), + # ['buy', 'buy_tag']] = (1, 'buy_minus_dm') + # print(len(dataframe.keys())) + + # ################################################################### + # + # dataframe.loc[ + # (dataframe['close'] > dataframe['bb_upperband']) + # & (dataframe['percent'] > 0.01) + # & (dataframe['percent20'] < 0.035) + # & (dataframe['cond1'] > 13) + # & (dataframe['bb_width'] > 0.02) + # & (dataframe['cond1'].shift(4) < 8) + # & (dataframe['bb_width'].shift(4) < 0.012) + # , ['buy', 'buy_tag']] = (1, 'buy_cond1') + # + # #################################################################### + # + # dataframe.loc[ + # (dataframe['bb_lower_pente'].shift(4) < -0.50) + # & (dataframe['sma10_pente'] > -0.2) + # & (dataframe['sma10_pente'].shift(4) < -0.4) + # & (dataframe['percent5'] < 0.01) + # , ['buy', 'buy_tag']] = (1, 'buy_lower_pente') + # + # #################################################################### + # + # dataframe.loc[ + # (dataframe['percent50'].shift(4) < -0.05) + # & (dataframe['sma10_pente'].shift(1) < 0) + # & (dataframe['sma10_pente'] >= 0) + # & (dataframe['open'] < dataframe['sma10']) + # , ['buy', 'buy_tag']] = (1, 'buy_sma50') + # + # pandas.set_option('display.max_rows', dataframe.shape[0] + 1) + # pandas.set_option('display.max_columns', 30) + # print(condition1) + # print(dataframe["q_0.1"]) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # conditions = list() + # + # condition1, dataframe = condition_generator( + # dataframe, + # self.sell_operator0.value, + # self.sell_indicator0.value, + # self.sell_crossed_indicator0.value, + # self.sell_real_num0.value + # ) + # conditions.append(condition1) + # + # # print(self.sell_percent.value, ' ', + # # self.sell_percent.value + self.sell_percent3.value, ' ', + # # self.sell_percent.value + self.sell_percent3.value + self.sell_percent5.value) + # dataframe.loc[ + # ( + # (reduce(lambda x, y: x & y, conditions)) & + # (dataframe['percent'] < - self.sell_percent.value) & + # (dataframe['percent3'] < - (self.sell_percent3.value + self.sell_percent.value)) & + # (dataframe['percent5'] < - (self.sell_percent5.value + self.sell_percent3.value + self.sell_percent.value)) & + # (dataframe['close'] < dataframe['open']) + # # (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + # ), 'sell'] = 1 + return dataframe + diff --git a/GodStraJD4.py b/GodStraJD4.py new file mode 100644 index 0000000..5b602d3 --- /dev/null +++ b/GodStraJD4.py @@ -0,0 +1,848 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from freqtrade import data +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle +# TODO: this gene is removed 'MAVP' cuz or error on periods +all_god_genes = { + 'Overlap Studies': { + 'BBANDS-0', # Bollinger Bands + 'BBANDS-1', # Bollinger Bands + 'BBANDS-2', # Bollinger Bands + 'DEMA', # Double Exponential Moving Average + 'EMA', # Exponential Moving Average + 'HT_TRENDLINE', # Hilbert Transform - Instantaneous Trendline + 'KAMA', # Kaufman Adaptive Moving Average + 'MA', # Moving average + 'MAMA-0', # MESA Adaptive Moving Average + 'MAMA-1', # MESA Adaptive Moving Average + # TODO: Fix this + # 'MAVP', # Moving average with variable period + 'MIDPOINT', # MidPoint over period + 'MIDPRICE', # Midpoint Price over period + 'SAR', # Parabolic SAR + 'SAREXT', # Parabolic SAR - Extended + 'SMA', # Simple Moving Average + 'T3', # Triple Exponential Moving Average (T3) + 'TEMA', # Triple Exponential Moving Average + 'TRIMA', # Triangular Moving Average + 'WMA', # Weighted Moving Average + }, + 'Momentum Indicators': { + 'ADX', # Average Directional Movement Index + 'ADXR', # Average Directional Movement Index Rating + 'APO', # Absolute Price Oscillator + 'AROON-0', # Aroon + 'AROON-1', # Aroon + 'AROONOSC', # Aroon Oscillator + 'BOP', # Balance Of Power + 'CCI', # Commodity Channel Index + 'CMO', # Chande Momentum Oscillator + 'DX', # Directional Movement Index + 'MACD-0', # Moving Average Convergence/Divergence + 'MACD-1', # Moving Average Convergence/Divergence + 'MACD-2', # Moving Average Convergence/Divergence + 'MACDEXT-0', # MACD with controllable MA type + 'MACDEXT-1', # MACD with controllable MA type + 'MACDEXT-2', # MACD with controllable MA type + 'MACDFIX-0', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-1', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-2', # Moving Average Convergence/Divergence Fix 12/26 + 'MFI', # Money Flow Index + 'MINUS_DI', # Minus Directional Indicator + 'MINUS_DM', # Minus Directional Movement + 'MOM', # Momentum + 'PLUS_DI', # Plus Directional Indicator + 'PLUS_DM', # Plus Directional Movement + 'PPO', # Percentage Price Oscillator + 'ROC', # Rate of change : ((price/prevPrice)-1)*100 + # Rate of change Percentage: (price-prevPrice)/prevPrice + 'ROCP', + 'ROCR', # Rate of change ratio: (price/prevPrice) + # Rate of change ratio 100 scale: (price/prevPrice)*100 + 'ROCR100', + 'RSI', # Relative Strength Index + 'STOCH-0', # Stochastic + 'STOCH-1', # Stochastic + 'STOCHF-0', # Stochastic Fast + 'STOCHF-1', # Stochastic Fast + 'STOCHRSI-0', # Stochastic Relative Strength Index + 'STOCHRSI-1', # Stochastic Relative Strength Index + # 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA + 'TRIX', + 'ULTOSC', # Ultimate Oscillator + 'WILLR', # Williams' %R + }, + 'Volume Indicators': { + 'AD', # Chaikin A/D Line + 'ADOSC', # Chaikin A/D Oscillator + 'OBV', # On Balance Volume + }, + 'Volatility Indicators': { + 'ATR', # Average True Range + 'NATR', # Normalized Average True Range + 'TRANGE', # True Range + }, + 'Price Transform': { + 'AVGPRICE', # Average Price + 'MEDPRICE', # Median Price + 'TYPPRICE', # Typical Price + 'WCLPRICE', # Weighted Close Price + }, + 'Cycle Indicators': { + 'HT_DCPERIOD', # Hilbert Transform - Dominant Cycle Period + 'HT_DCPHASE', # Hilbert Transform - Dominant Cycle Phase + 'HT_PHASOR-0', # Hilbert Transform - Phasor Components + 'HT_PHASOR-1', # Hilbert Transform - Phasor Components + 'HT_SINE-0', # Hilbert Transform - SineWave + 'HT_SINE-1', # Hilbert Transform - SineWave + 'HT_TRENDMODE', # Hilbert Transform - Trend vs Cycle Mode + }, + 'Pattern Recognition': { + 'CDL2CROWS', # Two Crows + 'CDL3BLACKCROWS', # Three Black Crows + 'CDL3INSIDE', # Three Inside Up/Down + 'CDL3LINESTRIKE', # Three-Line Strike + 'CDL3OUTSIDE', # Three Outside Up/Down + 'CDL3STARSINSOUTH', # Three Stars In The South + 'CDL3WHITESOLDIERS', # Three Advancing White Soldiers + 'CDLABANDONEDBABY', # Abandoned Baby + 'CDLADVANCEBLOCK', # Advance Block + 'CDLBELTHOLD', # Belt-hold + 'CDLBREAKAWAY', # Breakaway + 'CDLCLOSINGMARUBOZU', # Closing Marubozu + 'CDLCONCEALBABYSWALL', # Concealing Baby Swallow + 'CDLCOUNTERATTACK', # Counterattack + 'CDLDARKCLOUDCOVER', # Dark Cloud Cover + 'CDLDOJI', # Doji + 'CDLDOJISTAR', # Doji Star + 'CDLDRAGONFLYDOJI', # Dragonfly Doji + 'CDLENGULFING', # Engulfing Pattern + 'CDLEVENINGDOJISTAR', # Evening Doji Star + 'CDLEVENINGSTAR', # Evening Star + 'CDLGAPSIDESIDEWHITE', # Up/Down-gap side-by-side white lines + 'CDLGRAVESTONEDOJI', # Gravestone Doji + 'CDLHAMMER', # Hammer + 'CDLHANGINGMAN', # Hanging Man + 'CDLHARAMI', # Harami Pattern + 'CDLHARAMICROSS', # Harami Cross Pattern + 'CDLHIGHWAVE', # High-Wave Candle + 'CDLHIKKAKE', # Hikkake Pattern + 'CDLHIKKAKEMOD', # Modified Hikkake Pattern + 'CDLHOMINGPIGEON', # Homing Pigeon + 'CDLIDENTICAL3CROWS', # Identical Three Crows + 'CDLINNECK', # In-Neck Pattern + 'CDLINVERTEDHAMMER', # Inverted Hammer + 'CDLKICKING', # Kicking + 'CDLKICKINGBYLENGTH', # Kicking - bull/bear determined by the longer marubozu + 'CDLLADDERBOTTOM', # Ladder Bottom + 'CDLLONGLEGGEDDOJI', # Long Legged Doji + 'CDLLONGLINE', # Long Line Candle + 'CDLMARUBOZU', # Marubozu + 'CDLMATCHINGLOW', # Matching Low + 'CDLMATHOLD', # Mat Hold + 'CDLMORNINGDOJISTAR', # Morning Doji Star + 'CDLMORNINGSTAR', # Morning Star + 'CDLONNECK', # On-Neck Pattern + 'CDLPIERCING', # Piercing Pattern + 'CDLRICKSHAWMAN', # Rickshaw Man + 'CDLRISEFALL3METHODS', # Rising/Falling Three Methods + 'CDLSEPARATINGLINES', # Separating Lines + 'CDLSHOOTINGSTAR', # Shooting Star + 'CDLSHORTLINE', # Short Line Candle + 'CDLSPINNINGTOP', # Spinning Top + 'CDLSTALLEDPATTERN', # Stalled Pattern + 'CDLSTICKSANDWICH', # Stick Sandwich + # Takuri (Dragonfly Doji with very long lower shadow) + 'CDLTAKURI', + 'CDLTASUKIGAP', # Tasuki Gap + 'CDLTHRUSTING', # Thrusting Pattern + 'CDLTRISTAR', # Tristar Pattern + 'CDLUNIQUE3RIVER', # Unique 3 River + 'CDLUPSIDEGAP2CROWS', # Upside Gap Two Crows + 'CDLXSIDEGAP3METHODS', # Upside/Downside Gap Three Methods + + }, + 'Statistic Functions': { + 'BETA', # Beta + 'CORREL', # Pearson's Correlation Coefficient (r) + 'LINEARREG', # Linear Regression + 'LINEARREG_ANGLE', # Linear Regression Angle + 'LINEARREG_INTERCEPT', # Linear Regression Intercept + 'LINEARREG_SLOPE', # Linear Regression Slope + 'STDDEV', # Standard Deviation + 'TSF', # Time Series Forecast + 'VAR', # Variance + } + +} +god_genes = set() +########################### SETTINGS ############################## + +# god_genes = {'SMA'} +god_genes |= all_god_genes['Overlap Studies'] +god_genes |= all_god_genes['Momentum Indicators'] +god_genes |= all_god_genes['Volume Indicators'] +god_genes |= all_god_genes['Volatility Indicators'] +god_genes |= all_god_genes['Price Transform'] +god_genes |= all_god_genes['Cycle Indicators'] +god_genes |= all_god_genes['Pattern Recognition'] +god_genes |= all_god_genes['Statistic Functions'] + +timeperiods = [5, 6, 12, 15, 50, 55, 100, 110] +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator( + dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD4(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 0.598, + "644": 0.166, + "3269": 0.115, + "7289": 0 + } + + # Stoploss: + stoploss = -0.128 + # Buy hypers + timeframe = '4h' + + # Trailing stoploss + trailing_stop = True + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + buy_crossed_indicator0 = CategoricalParameter( + god_genes_with_timeperiod, default="ADD-20", space='buy') + buy_crossed_indicator1 = CategoricalParameter( + god_genes_with_timeperiod, default="ASIN-6", space='buy') + buy_crossed_indicator2 = CategoricalParameter( + god_genes_with_timeperiod, default="CDLEVENINGSTAR-50", space='buy') + + buy_indicator0 = CategoricalParameter( + god_genes_with_timeperiod, default="SMA-100", space='buy') + buy_indicator1 = CategoricalParameter( + god_genes_with_timeperiod, default="WILLR-50", space='buy') + buy_indicator2 = CategoricalParameter( + god_genes_with_timeperiod, default="CDLHANGINGMAN-20", space='buy') + + buy_operator0 = CategoricalParameter(operators, default="/ DataFrame: + ''' + It's good to calculate all indicators in all time periods here and so optimize the strategy. + But this strategy can take much time to generate anything that may not use in his optimization. + I just calculate the specific indicators in specific time period inside buy and sell strategy populator methods if needed. + Also, this method (populate_indicators) just calculates default value of hyperoptable params + so using this method have not big benefits instade of calculating useable things inside buy and sell trand populators + ''' + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + # TODO: Its not dry code! + buy_indicator = self.buy_indicator0.value + buy_crossed_indicator = self.buy_crossed_indicator0.value + buy_operator = self.buy_operator0.value + buy_real_num = self.buy_real_num0.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + # backup + buy_indicator = self.buy_indicator1.value + buy_crossed_indicator = self.buy_crossed_indicator1.value + buy_operator = self.buy_operator1.value + buy_real_num = self.buy_real_num1.value + + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + buy_indicator = self.buy_indicator2.value + buy_crossed_indicator = self.buy_crossed_indicator2.value + buy_operator = self.buy_operator2.value + buy_real_num = self.buy_real_num2.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['bb_lowerband']) + & (dataframe['bb_width'] >= 0.14) + & (dataframe['volume'] > 0) + ) | + reduce(lambda x, y: x & y, conditions), + 'buy']=1 + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + # TODO: Its not dry code! + sell_indicator = self.sell_indicator0.value + sell_crossed_indicator = self.sell_crossed_indicator0.value + sell_operator = self.sell_operator0.value + sell_real_num = self.sell_real_num0.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator1.value + sell_crossed_indicator = self.sell_crossed_indicator1.value + sell_operator = self.sell_operator1.value + sell_real_num = self.sell_real_num1.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator2.value + sell_crossed_indicator = self.sell_crossed_indicator2.value + sell_operator = self.sell_operator2.value + sell_real_num = self.sell_real_num2.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + #dataframe.loc[(dataframe['close'] < dataframe['bb_lowerband']),'sell']=1 + + if conditions: + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + ), + 'sell']=1 + return dataframe + + +class StrategyHelperLocal: + """ + simple helper class to predefine a couple of patterns for our + strategy + """ + + @staticmethod + def two_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) + # (dataframe['open'].shift(2) < dataframe['close'].shift(2)) + ) + + @staticmethod + def no_more_three_green_candles(dataframe): + """ + evaluates if we are having not more than 3 green candles in a row + :param self: + :param dataframe: + :return: + """ + return not ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) + ) + + @staticmethod + def seven_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) + ) + + @staticmethod + def eight_green_candles(dataframe): + """ + evaluates if we are having 8 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) & + (dataframe['open'].shift(8) < dataframe['close'].shift(8)) + ) + + @staticmethod + def two_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) + # (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def four_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) & + (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + (dataframe['open'].shift(3 + shift) > dataframe['close'].shift(3 + shift)) & + (dataframe['open'].shift(4 + shift) > dataframe['close'].shift(4 + shift)) + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def two_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) + ) + + @staticmethod + def four_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] > dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) + ) + + @staticmethod + def four_red_one_green_candle(dataframe): + """ + evaluates if we are having a green candle and 4 previous red + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) > dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) > dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) > dataframe['close'].shift(4)) + ) diff --git a/GodStraJD5.py b/GodStraJD5.py new file mode 100644 index 0000000..981cabd --- /dev/null +++ b/GodStraJD5.py @@ -0,0 +1,828 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from freqtrade import data +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle +# TODO: this gene is removed 'MAVP' cuz or error on periods +all_god_genes = { + 'Overlap Studies': { + 'BBANDS-0', # Bollinger Bands + 'BBANDS-1', # Bollinger Bands + 'BBANDS-2', # Bollinger Bands + 'DEMA', # Double Exponential Moving Average + 'EMA', # Exponential Moving Average + 'HT_TRENDLINE', # Hilbert Transform - Instantaneous Trendline + 'KAMA', # Kaufman Adaptive Moving Average + 'MA', # Moving average + 'MAMA-0', # MESA Adaptive Moving Average + 'MAMA-1', # MESA Adaptive Moving Average + # TODO: Fix this + # 'MAVP', # Moving average with variable period + 'MIDPOINT', # MidPoint over period + 'MIDPRICE', # Midpoint Price over period + 'SAR', # Parabolic SAR + 'SAREXT', # Parabolic SAR - Extended + 'SMA', # Simple Moving Average + 'T3', # Triple Exponential Moving Average (T3) + 'TEMA', # Triple Exponential Moving Average + 'TRIMA', # Triangular Moving Average + 'WMA', # Weighted Moving Average + }, + 'Momentum Indicators': { + 'ADX', # Average Directional Movement Index + 'ADXR', # Average Directional Movement Index Rating + 'APO', # Absolute Price Oscillator + 'AROON-0', # Aroon + 'AROON-1', # Aroon + 'AROONOSC', # Aroon Oscillator + 'BOP', # Balance Of Power + 'CCI', # Commodity Channel Index + 'CMO', # Chande Momentum Oscillator + 'DX', # Directional Movement Index + 'MACD-0', # Moving Average Convergence/Divergence + 'MACD-1', # Moving Average Convergence/Divergence + 'MACD-2', # Moving Average Convergence/Divergence + 'MACDEXT-0', # MACD with controllable MA type + 'MACDEXT-1', # MACD with controllable MA type + 'MACDEXT-2', # MACD with controllable MA type + 'MACDFIX-0', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-1', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-2', # Moving Average Convergence/Divergence Fix 12/26 + 'MFI', # Money Flow Index + 'MINUS_DI', # Minus Directional Indicator + 'MINUS_DM', # Minus Directional Movement + 'MOM', # Momentum + 'PLUS_DI', # Plus Directional Indicator + 'PLUS_DM', # Plus Directional Movement + 'PPO', # Percentage Price Oscillator + 'ROC', # Rate of change : ((price/prevPrice)-1)*100 + # Rate of change Percentage: (price-prevPrice)/prevPrice + 'ROCP', + 'ROCR', # Rate of change ratio: (price/prevPrice) + # Rate of change ratio 100 scale: (price/prevPrice)*100 + 'ROCR100', + 'RSI', # Relative Strength Index + 'STOCH-0', # Stochastic + 'STOCH-1', # Stochastic + 'STOCHF-0', # Stochastic Fast + 'STOCHF-1', # Stochastic Fast + 'STOCHRSI-0', # Stochastic Relative Strength Index + 'STOCHRSI-1', # Stochastic Relative Strength Index + # 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA + 'TRIX', + 'ULTOSC', # Ultimate Oscillator + 'WILLR', # Williams' %R + }, + 'Volume Indicators': { + 'AD', # Chaikin A/D Line + 'ADOSC', # Chaikin A/D Oscillator + 'OBV', # On Balance Volume + }, + 'Volatility Indicators': { + 'ATR', # Average True Range + 'NATR', # Normalized Average True Range + 'TRANGE', # True Range + }, + 'Price Transform': { + 'AVGPRICE', # Average Price + 'MEDPRICE', # Median Price + 'TYPPRICE', # Typical Price + 'WCLPRICE', # Weighted Close Price + }, + 'Cycle Indicators': { + 'HT_DCPERIOD', # Hilbert Transform - Dominant Cycle Period + 'HT_DCPHASE', # Hilbert Transform - Dominant Cycle Phase + 'HT_PHASOR-0', # Hilbert Transform - Phasor Components + 'HT_PHASOR-1', # Hilbert Transform - Phasor Components + 'HT_SINE-0', # Hilbert Transform - SineWave + 'HT_SINE-1', # Hilbert Transform - SineWave + 'HT_TRENDMODE', # Hilbert Transform - Trend vs Cycle Mode + }, + 'Pattern Recognition': { + 'CDL2CROWS', # Two Crows + 'CDL3BLACKCROWS', # Three Black Crows + 'CDL3INSIDE', # Three Inside Up/Down + 'CDL3LINESTRIKE', # Three-Line Strike + 'CDL3OUTSIDE', # Three Outside Up/Down + 'CDL3STARSINSOUTH', # Three Stars In The South + 'CDL3WHITESOLDIERS', # Three Advancing White Soldiers + 'CDLABANDONEDBABY', # Abandoned Baby + 'CDLADVANCEBLOCK', # Advance Block + 'CDLBELTHOLD', # Belt-hold + 'CDLBREAKAWAY', # Breakaway + 'CDLCLOSINGMARUBOZU', # Closing Marubozu + 'CDLCONCEALBABYSWALL', # Concealing Baby Swallow + 'CDLCOUNTERATTACK', # Counterattack + 'CDLDARKCLOUDCOVER', # Dark Cloud Cover + 'CDLDOJI', # Doji + 'CDLDOJISTAR', # Doji Star + 'CDLDRAGONFLYDOJI', # Dragonfly Doji + 'CDLENGULFING', # Engulfing Pattern + 'CDLEVENINGDOJISTAR', # Evening Doji Star + 'CDLEVENINGSTAR', # Evening Star + 'CDLGAPSIDESIDEWHITE', # Up/Down-gap side-by-side white lines + 'CDLGRAVESTONEDOJI', # Gravestone Doji + 'CDLHAMMER', # Hammer + 'CDLHANGINGMAN', # Hanging Man + 'CDLHARAMI', # Harami Pattern + 'CDLHARAMICROSS', # Harami Cross Pattern + 'CDLHIGHWAVE', # High-Wave Candle + 'CDLHIKKAKE', # Hikkake Pattern + 'CDLHIKKAKEMOD', # Modified Hikkake Pattern + 'CDLHOMINGPIGEON', # Homing Pigeon + 'CDLIDENTICAL3CROWS', # Identical Three Crows + 'CDLINNECK', # In-Neck Pattern + 'CDLINVERTEDHAMMER', # Inverted Hammer + 'CDLKICKING', # Kicking + 'CDLKICKINGBYLENGTH', # Kicking - bull/bear determined by the longer marubozu + 'CDLLADDERBOTTOM', # Ladder Bottom + 'CDLLONGLEGGEDDOJI', # Long Legged Doji + 'CDLLONGLINE', # Long Line Candle + 'CDLMARUBOZU', # Marubozu + 'CDLMATCHINGLOW', # Matching Low + 'CDLMATHOLD', # Mat Hold + 'CDLMORNINGDOJISTAR', # Morning Doji Star + 'CDLMORNINGSTAR', # Morning Star + 'CDLONNECK', # On-Neck Pattern + 'CDLPIERCING', # Piercing Pattern + 'CDLRICKSHAWMAN', # Rickshaw Man + 'CDLRISEFALL3METHODS', # Rising/Falling Three Methods + 'CDLSEPARATINGLINES', # Separating Lines + 'CDLSHOOTINGSTAR', # Shooting Star + 'CDLSHORTLINE', # Short Line Candle + 'CDLSPINNINGTOP', # Spinning Top + 'CDLSTALLEDPATTERN', # Stalled Pattern + 'CDLSTICKSANDWICH', # Stick Sandwich + # Takuri (Dragonfly Doji with very long lower shadow) + 'CDLTAKURI', + 'CDLTASUKIGAP', # Tasuki Gap + 'CDLTHRUSTING', # Thrusting Pattern + 'CDLTRISTAR', # Tristar Pattern + 'CDLUNIQUE3RIVER', # Unique 3 River + 'CDLUPSIDEGAP2CROWS', # Upside Gap Two Crows + 'CDLXSIDEGAP3METHODS', # Upside/Downside Gap Three Methods + + }, + 'Statistic Functions': { + 'BETA', # Beta + 'CORREL', # Pearson's Correlation Coefficient (r) + 'LINEARREG', # Linear Regression + 'LINEARREG_ANGLE', # Linear Regression Angle + 'LINEARREG_INTERCEPT', # Linear Regression Intercept + 'LINEARREG_SLOPE', # Linear Regression Slope + 'STDDEV', # Standard Deviation + 'TSF', # Time Series Forecast + 'VAR', # Variance + } + +} +god_genes = set() +########################### SETTINGS ############################## + +# god_genes = {'SMA'} +god_genes |= all_god_genes['Overlap Studies'] +god_genes |= all_god_genes['Momentum Indicators'] +god_genes |= all_god_genes['Volume Indicators'] +god_genes |= all_god_genes['Volatility Indicators'] +god_genes |= all_god_genes['Price Transform'] +god_genes |= all_god_genes['Cycle Indicators'] +god_genes |= all_god_genes['Pattern Recognition'] +god_genes |= all_god_genes['Statistic Functions'] + +timeperiods = [5, 6, 12, 15, 50, 55, 100, 110] +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator( + dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD5(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 0.598, + "644": 0.166, + "3269": 0.115, + "7289": 0 + } + + # Stoploss: + stoploss = -0.128 + # Buy hypers + timeframe = '4h' + + # Trailing stoploss + trailing_stop = True + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + buy_crossed_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="ADD-20", space='buy') + buy_crossed_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="ASIN-6", space='buy') + buy_crossed_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLEVENINGSTAR-50", space='buy') + + buy_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="SMA-100", space='buy') + buy_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="WILLR-50", space='buy') + buy_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLHANGINGMAN-20", space='buy') + + buy_operator0 = CategoricalParameter(operators, default="/ DataFrame: + ''' + It's good to calculate all indicators in all time periods here and so optimize the strategy. + But this strategy can take much time to generate anything that may not use in his optimization. + I just calculate the specific indicators in specific time period inside buy and sell strategy populator methods if needed. + Also, this method (populate_indicators) just calculates default value of hyperoptable params + so using this method have not big benefits instade of calculating useable things inside buy and sell trand populators + ''' + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + # TODO: Its not dry code! + buy_indicator = self.buy_indicator0.value + buy_crossed_indicator = self.buy_crossed_indicator0.value + buy_operator = self.buy_operator0.value + buy_real_num = self.buy_real_num0.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + # backup + buy_indicator = self.buy_indicator1.value + buy_crossed_indicator = self.buy_crossed_indicator1.value + buy_operator = self.buy_operator1.value + buy_real_num = self.buy_real_num1.value + + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + buy_indicator = self.buy_indicator2.value + buy_crossed_indicator = self.buy_crossed_indicator2.value + buy_operator = self.buy_operator2.value + buy_real_num = self.buy_real_num2.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['bb_lowerband']) + & (dataframe['bb_width'] >= 0.14) + & (dataframe['volume'] > 0) + ) | + reduce(lambda x, y: x & y, conditions), + 'buy']=1 + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + # TODO: Its not dry code! + sell_indicator = self.sell_indicator0.value + sell_crossed_indicator = self.sell_crossed_indicator0.value + sell_operator = self.sell_operator0.value + sell_real_num = self.sell_real_num0.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator1.value + sell_crossed_indicator = self.sell_crossed_indicator1.value + sell_operator = self.sell_operator1.value + sell_real_num = self.sell_real_num1.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator2.value + sell_crossed_indicator = self.sell_crossed_indicator2.value + sell_operator = self.sell_operator2.value + sell_real_num = self.sell_real_num2.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + #dataframe.loc[(dataframe['close'] < dataframe['bb_lowerband']),'sell']=1 + + if conditions: + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + ), + 'sell']=1 + return dataframe + + +class StrategyHelperLocal: + """ + simple helper class to predefine a couple of patterns for our + strategy + """ + + @staticmethod + def two_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) + # (dataframe['open'].shift(2) < dataframe['close'].shift(2)) + ) + + @staticmethod + def no_more_three_green_candles(dataframe): + """ + evaluates if we are having not more than 3 green candles in a row + :param self: + :param dataframe: + :return: + """ + return not ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) + ) + + @staticmethod + def seven_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) + ) + + @staticmethod + def eight_green_candles(dataframe): + """ + evaluates if we are having 8 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) & + (dataframe['open'].shift(8) < dataframe['close'].shift(8)) + ) + + @staticmethod + def two_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) + # (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def four_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) & + (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + (dataframe['open'].shift(3 + shift) > dataframe['close'].shift(3 + shift)) & + (dataframe['open'].shift(4 + shift) > dataframe['close'].shift(4 + shift)) + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def two_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) + ) + + @staticmethod + def four_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] > dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) + ) + + @staticmethod + def four_red_one_green_candle(dataframe): + """ + evaluates if we are having a green candle and 4 previous red + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) > dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) > dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) > dataframe['close'].shift(4)) + ) diff --git a/GodStraJD_P.py b/GodStraJD_P.py new file mode 100644 index 0000000..177feb0 --- /dev/null +++ b/GodStraJD_P.py @@ -0,0 +1,673 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from freqtrade import data +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle +# TODO: this gene is removed 'MAVP' cuz or error on periods +all_god_genes = { + 'Overlap Studies': { + 'BBANDS-0', # Bollinger Bands + 'BBANDS-1', # Bollinger Bands + 'BBANDS-2', # Bollinger Bands + 'DEMA', # Double Exponential Moving Average + 'EMA', # Exponential Moving Average + 'HT_TRENDLINE', # Hilbert Transform - Instantaneous Trendline + 'KAMA', # Kaufman Adaptive Moving Average + 'MA', # Moving average + 'MAMA-0', # MESA Adaptive Moving Average + 'MAMA-1', # MESA Adaptive Moving Average + # TODO: Fix this + # 'MAVP', # Moving average with variable period + 'MIDPOINT', # MidPoint over period + 'MIDPRICE', # Midpoint Price over period + 'SAR', # Parabolic SAR + 'SAREXT', # Parabolic SAR - Extended + 'SMA', # Simple Moving Average + 'T3', # Triple Exponential Moving Average (T3) + 'TEMA', # Triple Exponential Moving Average + 'TRIMA', # Triangular Moving Average + 'WMA', # Weighted Moving Average + }, + 'Momentum Indicators': { + 'ADX', # Average Directional Movement Index + 'ADXR', # Average Directional Movement Index Rating + 'APO', # Absolute Price Oscillator + 'AROON-0', # Aroon + 'AROON-1', # Aroon + 'AROONOSC', # Aroon Oscillator + 'BOP', # Balance Of Power + 'CCI', # Commodity Channel Index + 'CMO', # Chande Momentum Oscillator + 'DX', # Directional Movement Index + 'MACD-0', # Moving Average Convergence/Divergence + 'MACD-1', # Moving Average Convergence/Divergence + 'MACD-2', # Moving Average Convergence/Divergence + 'MACDEXT-0', # MACD with controllable MA type + 'MACDEXT-1', # MACD with controllable MA type + 'MACDEXT-2', # MACD with controllable MA type + 'MACDFIX-0', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-1', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-2', # Moving Average Convergence/Divergence Fix 12/26 + 'MFI', # Money Flow Index + 'MINUS_DI', # Minus Directional Indicator + 'MINUS_DM', # Minus Directional Movement + 'MOM', # Momentum + 'PLUS_DI', # Plus Directional Indicator + 'PLUS_DM', # Plus Directional Movement + 'PPO', # Percentage Price Oscillator + 'ROC', # Rate of change : ((price/prevPrice)-1)*100 + # Rate of change Percentage: (price-prevPrice)/prevPrice + 'ROCP', + 'ROCR', # Rate of change ratio: (price/prevPrice) + # Rate of change ratio 100 scale: (price/prevPrice)*100 + 'ROCR100', + 'RSI', # Relative Strength Index + 'STOCH-0', # Stochastic + 'STOCH-1', # Stochastic + 'STOCHF-0', # Stochastic Fast + 'STOCHF-1', # Stochastic Fast + 'STOCHRSI-0', # Stochastic Relative Strength Index + 'STOCHRSI-1', # Stochastic Relative Strength Index + # 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA + 'TRIX', + 'ULTOSC', # Ultimate Oscillator + 'WILLR', # Williams' %R + }, + 'Volume Indicators': { + 'AD', # Chaikin A/D Line + 'ADOSC', # Chaikin A/D Oscillator + 'OBV', # On Balance Volume + }, + 'Volatility Indicators': { + 'ATR', # Average True Range + 'NATR', # Normalized Average True Range + 'TRANGE', # True Range + }, + 'Price Transform': { + 'AVGPRICE', # Average Price + 'MEDPRICE', # Median Price + 'TYPPRICE', # Typical Price + 'WCLPRICE', # Weighted Close Price + }, + 'Cycle Indicators': { + 'HT_DCPERIOD', # Hilbert Transform - Dominant Cycle Period + 'HT_DCPHASE', # Hilbert Transform - Dominant Cycle Phase + 'HT_PHASOR-0', # Hilbert Transform - Phasor Components + 'HT_PHASOR-1', # Hilbert Transform - Phasor Components + 'HT_SINE-0', # Hilbert Transform - SineWave + 'HT_SINE-1', # Hilbert Transform - SineWave + 'HT_TRENDMODE', # Hilbert Transform - Trend vs Cycle Mode + }, + 'Pattern Recognition': { + 'CDL2CROWS', # Two Crows + 'CDL3BLACKCROWS', # Three Black Crows + 'CDL3INSIDE', # Three Inside Up/Down + 'CDL3LINESTRIKE', # Three-Line Strike + 'CDL3OUTSIDE', # Three Outside Up/Down + 'CDL3STARSINSOUTH', # Three Stars In The South + 'CDL3WHITESOLDIERS', # Three Advancing White Soldiers + 'CDLABANDONEDBABY', # Abandoned Baby + 'CDLADVANCEBLOCK', # Advance Block + 'CDLBELTHOLD', # Belt-hold + 'CDLBREAKAWAY', # Breakaway + 'CDLCLOSINGMARUBOZU', # Closing Marubozu + 'CDLCONCEALBABYSWALL', # Concealing Baby Swallow + 'CDLCOUNTERATTACK', # Counterattack + 'CDLDARKCLOUDCOVER', # Dark Cloud Cover + 'CDLDOJI', # Doji + 'CDLDOJISTAR', # Doji Star + 'CDLDRAGONFLYDOJI', # Dragonfly Doji + 'CDLENGULFING', # Engulfing Pattern + 'CDLEVENINGDOJISTAR', # Evening Doji Star + 'CDLEVENINGSTAR', # Evening Star + 'CDLGAPSIDESIDEWHITE', # Up/Down-gap side-by-side white lines + 'CDLGRAVESTONEDOJI', # Gravestone Doji + 'CDLHAMMER', # Hammer + 'CDLHANGINGMAN', # Hanging Man + 'CDLHARAMI', # Harami Pattern + 'CDLHARAMICROSS', # Harami Cross Pattern + 'CDLHIGHWAVE', # High-Wave Candle + 'CDLHIKKAKE', # Hikkake Pattern + 'CDLHIKKAKEMOD', # Modified Hikkake Pattern + 'CDLHOMINGPIGEON', # Homing Pigeon + 'CDLIDENTICAL3CROWS', # Identical Three Crows + 'CDLINNECK', # In-Neck Pattern + 'CDLINVERTEDHAMMER', # Inverted Hammer + 'CDLKICKING', # Kicking + 'CDLKICKINGBYLENGTH', # Kicking - bull/bear determined by the longer marubozu + 'CDLLADDERBOTTOM', # Ladder Bottom + 'CDLLONGLEGGEDDOJI', # Long Legged Doji + 'CDLLONGLINE', # Long Line Candle + 'CDLMARUBOZU', # Marubozu + 'CDLMATCHINGLOW', # Matching Low + 'CDLMATHOLD', # Mat Hold + 'CDLMORNINGDOJISTAR', # Morning Doji Star + 'CDLMORNINGSTAR', # Morning Star + 'CDLONNECK', # On-Neck Pattern + 'CDLPIERCING', # Piercing Pattern + 'CDLRICKSHAWMAN', # Rickshaw Man + 'CDLRISEFALL3METHODS', # Rising/Falling Three Methods + 'CDLSEPARATINGLINES', # Separating Lines + 'CDLSHOOTINGSTAR', # Shooting Star + 'CDLSHORTLINE', # Short Line Candle + 'CDLSPINNINGTOP', # Spinning Top + 'CDLSTALLEDPATTERN', # Stalled Pattern + 'CDLSTICKSANDWICH', # Stick Sandwich + # Takuri (Dragonfly Doji with very long lower shadow) + 'CDLTAKURI', + 'CDLTASUKIGAP', # Tasuki Gap + 'CDLTHRUSTING', # Thrusting Pattern + 'CDLTRISTAR', # Tristar Pattern + 'CDLUNIQUE3RIVER', # Unique 3 River + 'CDLUPSIDEGAP2CROWS', # Upside Gap Two Crows + 'CDLXSIDEGAP3METHODS', # Upside/Downside Gap Three Methods + + }, + 'Statistic Functions': { + 'BETA', # Beta + 'CORREL', # Pearson's Correlation Coefficient (r) + 'LINEARREG', # Linear Regression + 'LINEARREG_ANGLE', # Linear Regression Angle + 'LINEARREG_INTERCEPT', # Linear Regression Intercept + 'LINEARREG_SLOPE', # Linear Regression Slope + 'STDDEV', # Standard Deviation + 'TSF', # Time Series Forecast + 'VAR', # Variance + } + +} +god_genes = set() +########################### SETTINGS ############################## + +# god_genes = {'SMA'} +god_genes |= all_god_genes['Overlap Studies'] +god_genes |= all_god_genes['Momentum Indicators'] +god_genes |= all_god_genes['Volume Indicators'] +god_genes |= all_god_genes['Volatility Indicators'] +god_genes |= all_god_genes['Price Transform'] +god_genes |= all_god_genes['Cycle Indicators'] +god_genes |= all_god_genes['Pattern Recognition'] +god_genes |= all_god_genes['Statistic Functions'] + +timeperiods = [5, 6, 12, 15, 50, 55, 100, 110] +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator( + dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraJD_P(IStrategy): + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 0.598, + "644": 0.166, + "3269": 0.115, + "7289": 0 + } + + # Stoploss: + stoploss = -0.128 + # Buy hypers + timeframe = '4h' + + # Trailing stoploss + trailing_stop = True + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + buy_crossed_indicator0 = CategoricalParameter( + god_genes_with_timeperiod, default="ADD-20", space='buy') + buy_crossed_indicator1 = CategoricalParameter( + god_genes_with_timeperiod, default="ASIN-6", space='buy') + buy_crossed_indicator2 = CategoricalParameter( + god_genes_with_timeperiod, default="CDLEVENINGSTAR-50", space='buy') + + buy_indicator0 = CategoricalParameter( + god_genes_with_timeperiod, default="SMA-100", space='buy') + buy_indicator1 = CategoricalParameter( + god_genes_with_timeperiod, default="WILLR-50", space='buy') + buy_indicator2 = CategoricalParameter( + god_genes_with_timeperiod, default="CDLHANGINGMAN-20", space='buy') + + buy_operator0 = CategoricalParameter(operators, default="/ DataFrame: + ''' + It's good to calculate all indicators in all time periods here and so optimize the strategy. + But this strategy can take much time to generate anything that may not use in his optimization. + I just calculate the specific indicators in specific time period inside buy and sell strategy populator methods if needed. + Also, this method (populate_indicators) just calculates default value of hyperoptable params + so using this method have not big benefits instade of calculating useable things inside buy and sell trand populators + ''' + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + # TODO: Its not dry code! + buy_indicator = self.buy_indicator0.value + buy_crossed_indicator = self.buy_crossed_indicator0.value + buy_operator = self.buy_operator0.value + buy_real_num = self.buy_real_num0.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + # backup + buy_indicator = self.buy_indicator1.value + buy_crossed_indicator = self.buy_crossed_indicator1.value + buy_operator = self.buy_operator1.value + buy_real_num = self.buy_real_num1.value + + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + buy_indicator = self.buy_indicator2.value + buy_crossed_indicator = self.buy_crossed_indicator2.value + buy_operator = self.buy_operator2.value + buy_real_num = self.buy_real_num2.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['bb_lowerband']) + & (dataframe['bb_width'] >= 0.10) + & (dataframe['volume'] > 0) + ) | + ( + (dataframe['close'] < dataframe['bb_lowerband']) + & (dataframe['bb_width'] >= 0.15) + & (dataframe['volume'] > 0) + ) | + reduce(lambda x, y: x & y, conditions), + 'buy']=1 + + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + # TODO: Its not dry code! + sell_indicator = self.sell_indicator0.value + sell_crossed_indicator = self.sell_crossed_indicator0.value + sell_operator = self.sell_operator0.value + sell_real_num = self.sell_real_num0.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator1.value + sell_crossed_indicator = self.sell_crossed_indicator1.value + sell_operator = self.sell_operator1.value + sell_real_num = self.sell_real_num1.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator2.value + sell_crossed_indicator = self.sell_crossed_indicator2.value + sell_operator = self.sell_operator2.value + sell_real_num = self.sell_real_num2.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'sell']=1 + return dataframe diff --git a/GodStraJD_Test.json b/GodStraJD_Test.json new file mode 100644 index 0000000..9e47fe9 --- /dev/null +++ b/GodStraJD_Test.json @@ -0,0 +1,51 @@ +{ + "strategy_name": "GodStraJD", + "params": { + "buy": { + "buy_crossed_indicator0": "ADD-20", + "buy_crossed_indicator1": "ASIN-6", + "buy_crossed_indicator2": "CDLEVENINGSTAR-50", + "buy_indicator0": "SMA-100", + "buy_indicator1": "WILLR-50", + "buy_indicator2": "CDLHANGINGMAN-20", + "buy_operator0": "/R", + "sell_real_num0": 0.1, + "sell_real_num1": 0.8, + "sell_real_num2": 0.9 + }, + "protection": {}, + "roi": { + "0": 0.259, + "33": 0.097, + "93": 0.033, + "193": 0 + }, + "stoploss": { + "stoploss": -0.164 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.243, + "trailing_stop_positive_offset": 0.262, + "trailing_only_offset_is_reached": false + } + }, + "ft_stratparam_v": 1, + "export_time": "2021-11-25 17:47:44.984352+00:00" +} \ No newline at end of file diff --git a/GodStraNew.json b/GodStraNew.json new file mode 100644 index 0000000..60d74e7 --- /dev/null +++ b/GodStraNew.json @@ -0,0 +1,51 @@ +{ + "strategy_name": "GodStraNew", + "params": { + "roi": { + "0": 0.598, + "644": 0.166, + "3269": 0.115, + "7289": 0 + }, + "stoploss": { + "stoploss": -0.128 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": null, + "trailing_stop_positive_offset": 0.0, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_crossed_indicator0": "SAREXT-12", + "buy_crossed_indicator1": "BOP-5", + "buy_crossed_indicator2": "OBV-5", + "buy_indicator0": "AROONOSC-110", + "buy_indicator1": "CDLHIKKAKE-50", + "buy_indicator2": "CDLSHORTLINE-110", + "buy_operator0": "R", + "sell_real_num0": 0.1, + "sell_real_num1": 0.8, + "sell_real_num2": 0.9 + }, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-05 06:15:28.024140+00:00" +} \ No newline at end of file diff --git a/GodStraNew.py b/GodStraNew.py new file mode 100644 index 0000000..71c2e7c --- /dev/null +++ b/GodStraNew.py @@ -0,0 +1,639 @@ +# GodStraNew Strategy +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew +# --- Do not remove these libs --- +from freqtrade import data +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame + +# -------------------------------- + +# Add your lib to import here +# TODO: talib is fast but have not more indicators +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from random import shuffle +# TODO: this gene is removed 'MAVP' cuz or error on periods +all_god_genes = { + 'Overlap Studies': { + 'BBANDS-0', # Bollinger Bands + 'BBANDS-1', # Bollinger Bands + 'BBANDS-2', # Bollinger Bands + 'DEMA', # Double Exponential Moving Average + 'EMA', # Exponential Moving Average + 'HT_TRENDLINE', # Hilbert Transform - Instantaneous Trendline + 'KAMA', # Kaufman Adaptive Moving Average + 'MA', # Moving average + 'MAMA-0', # MESA Adaptive Moving Average + 'MAMA-1', # MESA Adaptive Moving Average + # TODO: Fix this + # 'MAVP', # Moving average with variable period + 'MIDPOINT', # MidPoint over period + 'MIDPRICE', # Midpoint Price over period + 'SAR', # Parabolic SAR + 'SAREXT', # Parabolic SAR - Extended + 'SMA', # Simple Moving Average + 'T3', # Triple Exponential Moving Average (T3) + 'TEMA', # Triple Exponential Moving Average + 'TRIMA', # Triangular Moving Average + 'WMA', # Weighted Moving Average + }, + 'Momentum Indicators': { + 'ADX', # Average Directional Movement Index + 'ADXR', # Average Directional Movement Index Rating + 'APO', # Absolute Price Oscillator + 'AROON-0', # Aroon + 'AROON-1', # Aroon + 'AROONOSC', # Aroon Oscillator + 'BOP', # Balance Of Power + 'CCI', # Commodity Channel Index + 'CMO', # Chande Momentum Oscillator + 'DX', # Directional Movement Index + 'MACD-0', # Moving Average Convergence/Divergence + 'MACD-1', # Moving Average Convergence/Divergence + 'MACD-2', # Moving Average Convergence/Divergence + 'MACDEXT-0', # MACD with controllable MA type + 'MACDEXT-1', # MACD with controllable MA type + 'MACDEXT-2', # MACD with controllable MA type + 'MACDFIX-0', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-1', # Moving Average Convergence/Divergence Fix 12/26 + 'MACDFIX-2', # Moving Average Convergence/Divergence Fix 12/26 + 'MFI', # Money Flow Index + 'MINUS_DI', # Minus Directional Indicator + 'MINUS_DM', # Minus Directional Movement + 'MOM', # Momentum + 'PLUS_DI', # Plus Directional Indicator + 'PLUS_DM', # Plus Directional Movement + 'PPO', # Percentage Price Oscillator + 'ROC', # Rate of change : ((price/prevPrice)-1)*100 + # Rate of change Percentage: (price-prevPrice)/prevPrice + 'ROCP', + 'ROCR', # Rate of change ratio: (price/prevPrice) + # Rate of change ratio 100 scale: (price/prevPrice)*100 + 'ROCR100', + 'RSI', # Relative Strength Index + 'STOCH-0', # Stochastic + 'STOCH-1', # Stochastic + 'STOCHF-0', # Stochastic Fast + 'STOCHF-1', # Stochastic Fast + 'STOCHRSI-0', # Stochastic Relative Strength Index + 'STOCHRSI-1', # Stochastic Relative Strength Index + # 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA + 'TRIX', + 'ULTOSC', # Ultimate Oscillator + 'WILLR', # Williams' %R + }, + 'Volume Indicators': { + 'AD', # Chaikin A/D Line + 'ADOSC', # Chaikin A/D Oscillator + 'OBV', # On Balance Volume + }, + 'Volatility Indicators': { + 'ATR', # Average True Range + 'NATR', # Normalized Average True Range + 'TRANGE', # True Range + }, + 'Price Transform': { + 'AVGPRICE', # Average Price + 'MEDPRICE', # Median Price + 'TYPPRICE', # Typical Price + 'WCLPRICE', # Weighted Close Price + }, + 'Cycle Indicators': { + 'HT_DCPERIOD', # Hilbert Transform - Dominant Cycle Period + 'HT_DCPHASE', # Hilbert Transform - Dominant Cycle Phase + 'HT_PHASOR-0', # Hilbert Transform - Phasor Components + 'HT_PHASOR-1', # Hilbert Transform - Phasor Components + 'HT_SINE-0', # Hilbert Transform - SineWave + 'HT_SINE-1', # Hilbert Transform - SineWave + 'HT_TRENDMODE', # Hilbert Transform - Trend vs Cycle Mode + }, + 'Pattern Recognition': { + 'CDL2CROWS', # Two Crows + 'CDL3BLACKCROWS', # Three Black Crows + 'CDL3INSIDE', # Three Inside Up/Down + 'CDL3LINESTRIKE', # Three-Line Strike + 'CDL3OUTSIDE', # Three Outside Up/Down + 'CDL3STARSINSOUTH', # Three Stars In The South + 'CDL3WHITESOLDIERS', # Three Advancing White Soldiers + 'CDLABANDONEDBABY', # Abandoned Baby + 'CDLADVANCEBLOCK', # Advance Block + 'CDLBELTHOLD', # Belt-hold + 'CDLBREAKAWAY', # Breakaway + 'CDLCLOSINGMARUBOZU', # Closing Marubozu + 'CDLCONCEALBABYSWALL', # Concealing Baby Swallow + 'CDLCOUNTERATTACK', # Counterattack + 'CDLDARKCLOUDCOVER', # Dark Cloud Cover + 'CDLDOJI', # Doji + 'CDLDOJISTAR', # Doji Star + 'CDLDRAGONFLYDOJI', # Dragonfly Doji + 'CDLENGULFING', # Engulfing Pattern + 'CDLEVENINGDOJISTAR', # Evening Doji Star + 'CDLEVENINGSTAR', # Evening Star + 'CDLGAPSIDESIDEWHITE', # Up/Down-gap side-by-side white lines + 'CDLGRAVESTONEDOJI', # Gravestone Doji + 'CDLHAMMER', # Hammer + 'CDLHANGINGMAN', # Hanging Man + 'CDLHARAMI', # Harami Pattern + 'CDLHARAMICROSS', # Harami Cross Pattern + 'CDLHIGHWAVE', # High-Wave Candle + 'CDLHIKKAKE', # Hikkake Pattern + 'CDLHIKKAKEMOD', # Modified Hikkake Pattern + 'CDLHOMINGPIGEON', # Homing Pigeon + 'CDLIDENTICAL3CROWS', # Identical Three Crows + 'CDLINNECK', # In-Neck Pattern + 'CDLINVERTEDHAMMER', # Inverted Hammer + 'CDLKICKING', # Kicking + 'CDLKICKINGBYLENGTH', # Kicking - bull/bear determined by the longer marubozu + 'CDLLADDERBOTTOM', # Ladder Bottom + 'CDLLONGLEGGEDDOJI', # Long Legged Doji + 'CDLLONGLINE', # Long Line Candle + 'CDLMARUBOZU', # Marubozu + 'CDLMATCHINGLOW', # Matching Low + 'CDLMATHOLD', # Mat Hold + 'CDLMORNINGDOJISTAR', # Morning Doji Star + 'CDLMORNINGSTAR', # Morning Star + 'CDLONNECK', # On-Neck Pattern + 'CDLPIERCING', # Piercing Pattern + 'CDLRICKSHAWMAN', # Rickshaw Man + 'CDLRISEFALL3METHODS', # Rising/Falling Three Methods + 'CDLSEPARATINGLINES', # Separating Lines + 'CDLSHOOTINGSTAR', # Shooting Star + 'CDLSHORTLINE', # Short Line Candle + 'CDLSPINNINGTOP', # Spinning Top + 'CDLSTALLEDPATTERN', # Stalled Pattern + 'CDLSTICKSANDWICH', # Stick Sandwich + # Takuri (Dragonfly Doji with very long lower shadow) + 'CDLTAKURI', + 'CDLTASUKIGAP', # Tasuki Gap + 'CDLTHRUSTING', # Thrusting Pattern + 'CDLTRISTAR', # Tristar Pattern + 'CDLUNIQUE3RIVER', # Unique 3 River + 'CDLUPSIDEGAP2CROWS', # Upside Gap Two Crows + 'CDLXSIDEGAP3METHODS', # Upside/Downside Gap Three Methods + + }, + 'Statistic Functions': { + 'BETA', # Beta + 'CORREL', # Pearson's Correlation Coefficient (r) + 'LINEARREG', # Linear Regression + 'LINEARREG_ANGLE', # Linear Regression Angle + 'LINEARREG_INTERCEPT', # Linear Regression Intercept + 'LINEARREG_SLOPE', # Linear Regression Slope + 'STDDEV', # Standard Deviation + 'TSF', # Time Series Forecast + 'VAR', # Variance + } + +} +god_genes = set() +########################### SETTINGS ############################## + +# god_genes = {'SMA'} +god_genes |= all_god_genes['Overlap Studies'] +god_genes |= all_god_genes['Momentum Indicators'] +god_genes |= all_god_genes['Volume Indicators'] +god_genes |= all_god_genes['Volatility Indicators'] +god_genes |= all_god_genes['Price Transform'] +god_genes |= all_god_genes['Cycle Indicators'] +god_genes |= all_god_genes['Pattern Recognition'] +god_genes |= all_god_genes['Statistic Functions'] + +timeperiods = [5, 6, 12, 15, 50, 55, 100, 110] +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator( + dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator( + dataframe, indicator_trend_sma) + + if operator == ">": + condition = ( + dataframe[indicator] > dataframe[crossed_indicator] + ) + elif operator == "=": + condition = ( + np.isclose(dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "<": + condition = ( + dataframe[indicator] < dataframe[crossed_indicator] + ) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) | + (qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator])) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == "CB": + condition = ( + qtpylib.crossed_below( + dataframe[indicator], dataframe[crossed_indicator]) + ) + elif operator == ">R": + condition = ( + dataframe[indicator] > real_num + ) + elif operator == "=R": + condition = ( + np.isclose(dataframe[indicator], real_num) + ) + elif operator == "R": + condition = ( + dataframe[indicator].div(dataframe[crossed_indicator]) > real_num + ) + elif operator == "/=R": + condition = ( + np.isclose(dataframe[indicator].div( + dataframe[crossed_indicator]), real_num) + ) + elif operator == "/ dataframe[indicator_trend_sma] + ) + elif operator == "DT": + condition = ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + elif operator == "OT": + condition = ( + + np.isclose(dataframe[indicator], dataframe[indicator_trend_sma]) + ) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] > dataframe[indicator_trend_sma] + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) & + ( + dataframe[indicator] < dataframe[indicator_trend_sma] + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) | + ( + qtpylib.crossed_above( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) & + ( + np.isclose( + dataframe[indicator], + dataframe[indicator_trend_sma] + ) + ) + ) + + return condition, dataframe + + +class GodStraNew(IStrategy): + + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 0.598, + "644": 0.166, + "3269": 0.115, + "7289": 0 + } + + # Stoploss: + stoploss = -0.128 + # Buy hypers + timeframe = '4h' + + + # #################### END OF RESULT PLACE #################### + + # TODO: Its not dry code! + # Buy Hyperoptable Parameters/Spaces. + buy_crossed_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="ADD-20", space='buy') + buy_crossed_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="ASIN-6", space='buy') + buy_crossed_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLEVENINGSTAR-50", space='buy') + + buy_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="SMA-100", space='buy') + buy_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="WILLR-50", space='buy') + buy_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLHANGINGMAN-20", space='buy') + + buy_operator0 = CategoricalParameter(operators, default="/ DataFrame: + ''' + It's good to calculate all indicators in all time periods here and so optimize the strategy. + But this strategy can take much time to generate anything that may not use in his optimization. + I just calculate the specific indicators in specific time period inside buy and sell strategy populator methods if needed. + Also, this method (populate_indicators) just calculates default value of hyperoptable params + so using this method have not big benefits instade of calculating useable things inside buy and sell trand populators + ''' + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + + # TODO: Its not dry code! + buy_indicator = self.buy_indicator0.value + buy_crossed_indicator = self.buy_crossed_indicator0.value + buy_operator = self.buy_operator0.value + buy_real_num = self.buy_real_num0.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + # backup + buy_indicator = self.buy_indicator1.value + buy_crossed_indicator = self.buy_crossed_indicator1.value + buy_operator = self.buy_operator1.value + buy_real_num = self.buy_real_num1.value + + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + buy_indicator = self.buy_indicator2.value + buy_crossed_indicator = self.buy_crossed_indicator2.value + buy_operator = self.buy_operator2.value + buy_real_num = self.buy_real_num2.value + condition, dataframe = condition_generator( + dataframe, + buy_operator, + buy_indicator, + buy_crossed_indicator, + buy_real_num + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'buy']=1 + + # print(len(dataframe.keys())) + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + conditions = list() + # TODO: Its not dry code! + sell_indicator = self.sell_indicator0.value + sell_crossed_indicator = self.sell_crossed_indicator0.value + sell_operator = self.sell_operator0.value + sell_real_num = self.sell_real_num0.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator1.value + sell_crossed_indicator = self.sell_crossed_indicator1.value + sell_operator = self.sell_operator1.value + sell_real_num = self.sell_real_num1.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + sell_indicator = self.sell_indicator2.value + sell_crossed_indicator = self.sell_crossed_indicator2.value + sell_operator = self.sell_operator2.value + sell_real_num = self.sell_real_num2.value + condition, dataframe = condition_generator( + dataframe, + sell_operator, + sell_indicator, + sell_crossed_indicator, + sell_real_num + ) + conditions.append(condition) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'sell']=1 + return dataframe + + diff --git a/GodStraNew.txt b/GodStraNew.txt new file mode 100644 index 0000000..24bcaac --- /dev/null +++ b/GodStraNew.txt @@ -0,0 +1,72 @@ +Result for strategy GodStraNew +=========================================================== BACKTESTING REPORT =========================================================== +| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | Avg Duration | Win Draw Loss Win% | +|-----------+--------+----------------+----------------+-------------------+----------------+------------------+-------------------------| +| SAND/USDT | 10 | 13.46 | 134.59 | 134.726 | 13.47 | 1 day, 17:48:00 | 8 0 2 80.0 | +| XRP/USDT | 7 | 17.80 | 124.59 | 124.713 | 12.47 | 1 day, 4:14:00 | 6 0 1 85.7 | +| GALA/USDT | 2 | 38.16 | 76.32 | 76.400 | 7.64 | 11:02:00 | 2 0 0 100 | +| BNB/USDT | 11 | 6.83 | 75.08 | 75.156 | 7.52 | 2 days, 22:45:00 | 9 0 2 81.8 | +| TRX/USDT | 6 | 9.74 | 58.44 | 58.499 | 5.85 | 3 days, 13:46:00 | 6 0 0 100 | +| ZEC/USDT | 3 | 14.89 | 44.66 | 44.700 | 4.47 | 1 day, 21:40:00 | 3 0 0 100 | +| IOTX/USDT | 8 | 5.40 | 43.18 | 43.222 | 4.32 | 3 days, 4:43:00 | 5 1 2 62.5 | +| EGLD/USDT | 3 | 13.35 | 40.04 | 40.076 | 4.01 | 2 days, 1:53:00 | 3 0 0 100 | +| ETH/USDT | 2 | 20.01 | 40.03 | 40.066 | 4.01 | 1 day, 7:05:00 | 2 0 0 100 | +| SOL/USDT | 4 | 9.19 | 36.78 | 36.813 | 3.68 | 19:50:00 | 3 0 1 75.0 | +| CELR/USDT | 3 | 11.94 | 35.81 | 35.848 | 3.58 | 2 days, 13:58:00 | 3 0 0 100 | +| AVAX/USDT | 2 | 16.58 | 33.17 | 33.200 | 3.32 | 2 days, 2:22:00 | 2 0 0 100 | +| ADA/USDT | 4 | 7.92 | 31.68 | 31.713 | 3.17 | 1 day, 10:49:00 | 3 0 1 75.0 | +| ROSE/USDT | 4 | 6.65 | 26.59 | 26.613 | 2.66 | 2 days, 14:16:00 | 3 0 1 75.0 | +| BTC/USDT | 5 | 3.79 | 18.94 | 18.955 | 1.90 | 3 days, 10:25:00 | 4 0 1 80.0 | +| TOTAL | 74 | 11.08 | 819.88 | 820.697 | 82.07 | 2 days, 6:51:00 | 62 1 11 83.8 | +=========================================================== BUY TAG STATS =========================================================== +| TAG | Buys | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | Avg Duration | Win Draw Loss Win% | +|-------+--------+----------------+----------------+-------------------+----------------+-----------------+-------------------------| +| TOTAL | 74 | 11.08 | 819.88 | 820.697 | 82.07 | 2 days, 6:51:00 | 62 1 11 83.8 | +===================================================== SELL REASON STATS ===================================================== +| Sell Reason | Sells | Win Draws Loss Win% | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | +|---------------+---------+--------------------------+----------------+----------------+-------------------+----------------| +| roi | 63 | 62 1 0 100 | 15.28 | 962.59 | 963.556 | 64.17 | +| stop_loss | 11 | 0 0 11 0 | -12.97 | -142.72 | -142.859 | -9.51 | +====================================================== LEFT OPEN TRADES REPORT ====================================================== +| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | Avg Duration | Win Draw Loss Win% | +|--------+--------+----------------+----------------+-------------------+----------------+----------------+-------------------------| +| TOTAL | 0 | 0.00 | 0.00 | 0.000 | 0.00 | 0:00 | 0 0 0 0 | +=============== SUMMARY METRICS ================ +| Metric | Value | +|------------------------+---------------------| +| Backtesting from | 2021-01-01 00:00:00 | +| Backtesting to | 2021-11-20 00:00:00 | +| Max open trades | 15 | +| | | +| Total/Daily Avg Trades | 74 / 0.23 | +| Starting balance | 1000.000 USDT | +| Final balance | 1820.697 USDT | +| Absolute profit | 820.697 USDT | +| Total profit % | 82.07% | +| Trades per day | 0.23 | +| Avg. daily profit % | 0.25% | +| Avg. stake amount | 100.000 USDT | +| Total trade volume | 7400.000 USDT | +| | | +| Best Pair | SAND/USDT 134.59% | +| Worst Pair | BTC/USDT 18.94% | +| Best trade | SAND/USDT 59.74% | +| Worst trade | SOL/USDT -12.97% | +| Best day | 77.900 USDT | +| Worst day | -38.962 USDT | +| Days win/draw/lose | 32 / 224 / 2 | +| Avg. Duration Winners | 2 days, 6:59:00 | +| Avg. Duration Loser | 1 day, 22:00:00 | +| Rejected Buy signals | 0 | +| | | +| Min balance | 1016.600 USDT | +| Max balance | 1820.697 USDT | +| Drawdown | 51.90% | +| Drawdown | 51.949 USDT | +| Drawdown high | 394.815 USDT | +| Drawdown low | 342.866 USDT | +| Drawdown Start | 2021-01-10 05:20:00 | +| Drawdown End | 2021-01-11 06:40:00 | +| Market change | 2716.79% | +================================================ + diff --git a/Heracles.py b/Heracles.py new file mode 100644 index 0000000..e5c7d3d --- /dev/null +++ b/Heracles.py @@ -0,0 +1,127 @@ +# Heracles Strategy: Strongest Son of GodStra +# ( With just 1 Genome! its a bacteria :D ) +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT:Add to your pairlists inside config.json (Under StaticPairList): +# { +# "method": "AgeFilter", +# "min_days_listed": 100 +# }, +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces roi buy --strategy Heracles +# ###################################################################### +# --- Do not remove these libs --- +from freqtrade.strategy.parameters import IntParameter, DecimalParameter +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame +# -------------------------------- +# Add your lib to import here +# import talib.abstract as ta +import pandas as pd +import ta +from ta.utils import dropna +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np + + +class Heracles(IStrategy): + ########################################## RESULT PASTE PLACE ########################################## + # 10/100: 25 trades. 18/4/3 Wins/Draws/Losses. Avg profit 5.92%. Median profit 6.33%. Total profit 0.04888306 BTC ( 48.88Σ%). Avg duration 4 days, 6:24:00 min. Objective: -11.42103 + + # Buy hyperspace params: + buy_params = { + "buy_crossed_indicator_shift": 9, + "buy_div_max": 0.75, + "buy_div_min": 0.16, + "buy_indicator_shift": 15, + } + + # Sell hyperspace params: + sell_params = { + } + + # ROI table: + minimal_roi = { + "0": 0.598, + "644": 0.166, + "3269": 0.115, + "7289": 0 + } + + # Stoploss: + stoploss = -0.256 + + # Optimal timeframe use it in your config + timeframe = '4h' + + # Trailing stoploss + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.015 + trailing_only_offset_is_reached = True + + ########################################## END RESULT PASTE PLACE ###################################### + + # buy params + buy_div_min = DecimalParameter(0, 1, default=0.16, decimals=2, space='buy') + buy_div_max = DecimalParameter(0, 1, default=0.75, decimals=2, space='buy') + buy_indicator_shift = IntParameter(0, 20, default=16, space='buy') + buy_crossed_indicator_shift = IntParameter(0, 20, default=9, space='buy') + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe = dropna(dataframe) + + dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + dataframe['high'], + dataframe['low'], + dataframe['close'], + window=20, + window_atr=10, + fillna=False, + original_version=True + ) + + dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + dataframe['high'], + dataframe['low'], + dataframe['close'], + window=10, + offset=0, + fillna=False + ) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Buy strategy Hyperopt will build and use. + """ + conditions = [] + + IND = 'volatility_dcp' + CRS = 'volatility_kcw' + DFIND = dataframe[IND] + DFCRS = dataframe[CRS] + + d = DFIND.shift(self.buy_indicator_shift.value).div( + DFCRS.shift(self.buy_crossed_indicator_shift.value)) + + # print(d.min(), "\t", d.max()) + conditions.append( + d.between(self.buy_div_min.value, self.buy_div_max.value)) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'buy']=1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Sell strategy Hyperopt will build and use. + """ + dataframe.loc[:, 'sell'] = 0 + return dataframe diff --git a/Heracles.txt b/Heracles.txt new file mode 100644 index 0000000..47f3017 --- /dev/null +++ b/Heracles.txt @@ -0,0 +1,87 @@ +Result for strategy Heracles +=========================================================== BACKTESTING REPORT =========================================================== +| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | Avg Duration | Win Draw Loss Win% | +|-----------+--------+----------------+----------------+-------------------+----------------+------------------+-------------------------| +| SAND/USDT | 100 | 6.54 | 653.55 | 654.205 | 65.42 | 3 days, 4:19:00 | 72 7 21 72.0 | +| SOL/USDT | 91 | 6.05 | 550.81 | 551.356 | 55.14 | 3 days, 11:25:00 | 67 11 13 73.6 | +| IOTX/USDT | 87 | 5.47 | 475.86 | 476.334 | 47.63 | 3 days, 15:17:00 | 53 13 21 60.9 | +| AVAX/USDT | 97 | 4.42 | 428.92 | 429.347 | 42.93 | 3 days, 7:05:00 | 66 11 20 68.0 | +| CELR/USDT | 98 | 4.04 | 396.36 | 396.759 | 39.68 | 3 days, 6:15:00 | 68 8 22 69.4 | +| EGLD/USDT | 82 | 3.32 | 272.19 | 272.459 | 27.25 | 3 days, 21:22:00 | 58 7 17 70.7 | +| ROSE/USDT | 89 | 2.76 | 245.45 | 245.694 | 24.57 | 3 days, 14:27:00 | 54 15 20 60.7 | +| ADA/USDT | 74 | 3.24 | 239.60 | 239.837 | 23.98 | 4 days, 8:01:00 | 51 8 15 68.9 | +| BNB/USDT | 78 | 2.95 | 230.00 | 230.229 | 23.02 | 4 days, 2:22:00 | 49 14 15 62.8 | +| GALA/USDT | 23 | 7.37 | 169.53 | 169.704 | 16.97 | 2 days, 21:36:00 | 16 1 6 69.6 | +| ETH/USDT | 64 | 2.64 | 168.86 | 169.030 | 16.90 | 4 days, 23:19:00 | 45 10 9 70.3 | +| XRP/USDT | 73 | 2.23 | 162.52 | 162.686 | 16.27 | 4 days, 8:38:00 | 47 12 14 64.4 | +| TRX/USDT | 70 | 1.90 | 133.05 | 133.185 | 13.32 | 4 days, 13:53:00 | 44 13 13 62.9 | +| ZEC/USDT | 68 | 1.38 | 93.76 | 93.851 | 9.39 | 4 days, 16:14:00 | 42 12 14 61.8 | +| BTC/USDT | 42 | 1.55 | 65.16 | 65.227 | 6.52 | 7 days, 13:52:00 | 22 15 5 52.4 | +| TOTAL | 1136 | 3.77 | 4285.62 | 4289.906 | 428.99 | 3 days, 23:42:00 | 754 157 225 66.4 | +=========================================================== BUY TAG STATS ============================================================ +| TAG | Buys | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | Avg Duration | Win Draw Loss Win% | +|-------+--------+----------------+----------------+-------------------+----------------+------------------+-------------------------| +| TOTAL | 1136 | 3.77 | 4285.62 | 4289.906 | 428.99 | 3 days, 23:42:00 | 754 157 225 66.4 | +===================================================== SELL REASON STATS ===================================================== +| Sell Reason | Sells | Win Draws Loss Win% | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | +|---------------+---------+--------------------------+----------------+----------------+-------------------+----------------| +| roi | 905 | 748 157 0 100 | 10.95 | 9908.73 | 9918.64 | 660.58 | +| stop_loss | 216 | 0 0 216 0 | -25.75 | -5561.71 | -5567.27 | -370.78 | +| force_sell | 15 | 6 0 9 40.0 | -4.09 | -61.4 | -61.462 | -4.09 | +========================================================= LEFT OPEN TRADES REPORT ========================================================= +| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | Avg Duration | Win Draw Loss Win% | +|-----------+--------+----------------+----------------+-------------------+----------------+-------------------+-------------------------| +| SOL/USDT | 1 | 10.52 | 10.52 | 10.526 | 1.05 | 1 day, 7:15:00 | 1 0 0 100 | +| ADA/USDT | 1 | 8.67 | 8.67 | 8.680 | 0.87 | 1 day, 6:45:00 | 1 0 0 100 | +| EGLD/USDT | 1 | 6.62 | 6.62 | 6.625 | 0.66 | 12:20:00 | 1 0 0 100 | +| ZEC/USDT | 1 | 5.44 | 5.44 | 5.450 | 0.54 | 1 day, 15:50:00 | 1 0 0 100 | +| SAND/USDT | 1 | 3.56 | 3.56 | 3.568 | 0.36 | 1 day, 11:50:00 | 1 0 0 100 | +| AVAX/USDT | 1 | 2.05 | 2.05 | 2.055 | 0.21 | 11:25:00 | 1 0 0 100 | +| ETH/USDT | 1 | -7.97 | -7.97 | -7.976 | -0.80 | 8 days, 18:30:00 | 0 0 1 0 | +| CELR/USDT | 1 | -8.43 | -8.43 | -8.443 | -0.84 | 1 day, 22:35:00 | 0 0 1 0 | +| ROSE/USDT | 1 | -8.76 | -8.76 | -8.773 | -0.88 | 2:35:00 | 0 0 1 0 | +| BTC/USDT | 1 | -10.15 | -10.15 | -10.158 | -1.02 | 6 days, 9:10:00 | 0 0 1 0 | +| GALA/USDT | 1 | -10.40 | -10.40 | -10.408 | -1.04 | 1 day, 12:15:00 | 0 0 1 0 | +| TRX/USDT | 1 | -10.65 | -10.65 | -10.662 | -1.07 | 5 days, 3:40:00 | 0 0 1 0 | +| BNB/USDT | 1 | -11.18 | -11.18 | -11.188 | -1.12 | 9 days, 11:25:00 | 0 0 1 0 | +| XRP/USDT | 1 | -14.78 | -14.78 | -14.791 | -1.48 | 10 days, 18:25:00 | 0 0 1 0 | +| IOTX/USDT | 1 | -15.95 | -15.95 | -15.966 | -1.60 | 3 days, 2:45:00 | 0 0 1 0 | +| TOTAL | 15 | -4.09 | -61.40 | -61.462 | -6.15 | 3 days, 14:19:00 | 6 0 9 40.0 | +=============== SUMMARY METRICS ================ +| Metric | Value | +|------------------------+---------------------| +| Backtesting from | 2021-01-01 00:00:00 | +| Backtesting to | 2021-11-20 00:00:00 | +| Max open trades | 15 | +| | | +| Total/Daily Avg Trades | 1136 / 3.52 | +| Starting balance | 1000.000 USDT | +| Final balance | 5289.906 USDT | +| Absolute profit | 4289.906 USDT | +| Total profit % | 428.99% | +| Trades per day | 3.52 | +| Avg. daily profit % | 1.33% | +| Avg. stake amount | 100.000 USDT | +| Total trade volume | 113600.000 USDT | +| | | +| Best Pair | SAND/USDT 653.55% | +| Worst Pair | BTC/USDT 65.16% | +| Best trade | SAND/USDT 59.74% | +| Worst trade | XRP/USDT -25.75% | +| Best day | 281.693 USDT | +| Worst day | -524.662 USDT | +| Days win/draw/lose | 232 / 38 / 54 | +| Avg. Duration Winners | 2 days, 18:54:00 | +| Avg. Duration Loser | 4 days, 23:38:00 | +| Rejected Buy signals | 0 | +| | | +| Min balance | 1018.871 USDT | +| Max balance | 5380.309 USDT | +| Drawdown | 1230.25% | +| Drawdown | 1231.483 USDT | +| Drawdown high | 3146.373 USDT | +| Drawdown low | 1914.890 USDT | +| Drawdown Start | 2021-05-07 16:05:00 | +| Drawdown End | 2021-06-22 13:45:00 | +| Market change | 2716.79% | +================================================ diff --git a/Heracles_2.json b/Heracles_2.json new file mode 100644 index 0000000..62cb7df --- /dev/null +++ b/Heracles_2.json @@ -0,0 +1,33 @@ +{ + "strategy_name": "Heracles_2", + "params": { + "stoploss": { + "stoploss": -1.0 + }, + "buy": { + "buy_bb_lowerband": 1.04, + "buy_bb_width": 0.03, + "buy_crossed_indicator_shift": 2, + "buy_div_max": 0.65, + "buy_div_min": 0.52, + "buy_indicator_shift": 17, + "buy_max_min": 0.01 + }, + "sell": {}, + "protection": {}, + "roi": { + "0": 0.22599999999999998, + "39": 0.078, + "61": 0.025, + "148": 0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.16, + "trailing_stop_positive_offset": 0.20600000000000002, + "trailing_only_offset_is_reached": true + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-31 20:36:56.362578+00:00" +} \ No newline at end of file diff --git a/Heracles_2.py b/Heracles_2.py new file mode 100644 index 0000000..53b6119 --- /dev/null +++ b/Heracles_2.py @@ -0,0 +1,274 @@ +# Heracles Strategy: Strongest Son of GodStra +# ( With just 1 Genome! its a bacteria :D ) +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT:Add to your pairlists inside config.json (Under StaticPairList): +# { +# "method": "AgeFilter", +# "min_days_listed": 100 +# }, +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces roi buy --strategy Heracles +# ###################################################################### +# --- Do not remove these libs --- +from freqtrade.strategy.parameters import IntParameter, DecimalParameter +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame +# -------------------------------- +# Add your lib to import here +# import talib.abstract as ta +import pandas as pd +import talib.abstract as talib +import ta +from ta.utils import dropna +import freqtrade.vendor.qtpylib.indicators as qtpylib +from functools import reduce +import numpy as np +from freqtrade.strategy.strategy_helper import merge_informative_pair + + +class Heracles_2(IStrategy): + ########################################## RESULT PASTE PLACE ########################################## + # 10/100: 25 trades. 18/4/3 Wins/Draws/Losses. Avg profit 5.92%. Median profit 6.33%. Total profit 0.04888306 BTC ( 48.88Σ%). Avg duration 4 days, 6:24:00 min. Objective: -11.42103 + + # Buy hyperspace params: + buy_params = { + "buy_crossed_indicator_shift": 9, + "buy_div_max": 0.75, + "buy_div_min": 0.16, + "buy_indicator_shift": 15, + } + + # Sell hyperspace params: + sell_params = { + } + + # ROI table: + minimal_roi = { + "0": 0.598, + "644": 0.166, + "3269": 0.115, + "7289": 0 + } + + # Stoploss: + stoploss = -0.256 + + # Optimal timeframe use it in your config + timeframe = '4h' + + # Trailing stoploss + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.015 + trailing_only_offset_is_reached = True + + plot_config = { + "main_plot": { + "max50": { + "color": "#dd1384", + "type": "line" + }, + "min50": { + "color": "#850678", + "type": "line" + }, + "min3_1d": { + "color": "#30c310", + "type": "line" + }, + "max3_1d": { + "color": "#e64062", + "type": "line" + }, + "min200": { + "color": "#86c932", + "type": "line" + } + }, + "subplots": { + "Vol": { + "volume10": { + "color": "#7b53f5", + "type": "line" + } + }, + "Percent": { + "max_min": { + "color": "#74effc", + "type": "line" + } + } + } +} + + ########################################## END RESULT PASTE PLACE ###################################### + + # buy params + buy_div_min = DecimalParameter(0, 1, default=0.16, decimals=2, space='buy') + buy_div_max = DecimalParameter(0, 1, default=0.75, decimals=2, space='buy') + buy_indicator_shift = IntParameter(0, 20, default=16, space='buy') + buy_crossed_indicator_shift = IntParameter(0, 20, default=9, space='buy') + + #buy_rsi_min = IntParameter(10, 40, default=10, space='buy') + #buy_rsi_max = IntParameter(50, 98, default=90, space='buy') + + buy_max_min = DecimalParameter(0, 0.05, default=0.02, decimals=2, space='buy') + buy_bb_lowerband = DecimalParameter(1, 1.05, default=1, decimals=2, space='buy') + buy_bb_width = DecimalParameter(0.01, 0.15, default=0.065, decimals=2, space='buy') + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + if (current_profit > 0.01) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if (current_profit > 0.01) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # Assign tf to each pair so they can be downloaded and cached for strategy. + # informative_pairs = [(pair, "5m") for pair in pairs] + informative_pairs = [(pair, '1d') for pair in pairs] + # informative_pairs += [(pair, '1w') for pair in pairs] + + # Optionally Add additional "static" pairs + # informative_pairs = [("BTC/USDT", "1w"), ("BTC/USDT", "1d"), ("BTC/USDT", "5m")] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe = dropna(dataframe) + + dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + dataframe['high'], + dataframe['low'], + dataframe['close'], + window=20, + window_atr=10, + fillna=False, + original_version=True + ) + + dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + dataframe['high'], + dataframe['low'], + dataframe['close'], + window=10, + offset=0, + fillna=False + ) + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max_min'] = (dataframe['max50'] - dataframe['min50']) / dataframe['min50'] + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + dataframe['volume_cond'] = (dataframe["volume10"] * dataframe['close'] / 1000 >= 19) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Buy strategy Hyperopt will build and use. + """ + conditions = [] + + IND = 'volatility_dcp' + CRS = 'volatility_kcw' + DFIND = dataframe[IND] + DFCRS = dataframe[CRS] + + d = DFIND.shift(self.buy_indicator_shift.value).div( + DFCRS.shift(self.buy_crossed_indicator_shift.value)) + + # print(d.min(), "\t", d.max()) + conditions.append( + d.between(self.buy_div_min.value, self.buy_div_max.value)) + + if conditions: + dataframe.loc[ + ( + (dataframe['volume10'] * dataframe['close'] / 1000 >= 19) + & (reduce(lambda x, y: x & y, conditions)) + & (dataframe['close_1d'] <= dataframe['open_1d']) + & (dataframe['max_min'] >= self.buy_max_min.value) + & (dataframe['close'] <= dataframe['min200'] * 1.005) + & (dataframe['close'].shift(1) <= dataframe['min200'].shift(1) * 1.005) + & (dataframe['min200'].shift(1) == dataframe['min200']) + & (dataframe['min50'] <= dataframe['min3_1d'] * 1.005) + & (dataframe['close'].shift(1) < dataframe['bb_lowerband'] * self.buy_bb_lowerband.value) + & (dataframe['bb_width'].shift(1) >= self.buy_bb_width.value) + ), + 'buy']=1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Sell strategy Hyperopt will build and use. + """ + # dataframe.loc[ + # (dataframe['close'] >= dataframe['max50'] * 0.998) + # , 'sell'] = 1 + return dataframe diff --git a/HourBasedStrategy.py b/HourBasedStrategy.py new file mode 100644 index 0000000..c8c30db --- /dev/null +++ b/HourBasedStrategy.py @@ -0,0 +1,105 @@ +# Hour Strategy +# In this strategy we try to find the best hours to buy and sell in a day.(in hourly timeframe) +# Because of that you should just use 1h timeframe on this strategy. +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# Requires hyperopt before running. +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --strategy HourBasedStrategy -e 200 + + +from freqtrade.strategy import IntParameter, IStrategy +from pandas import DataFrame + +# -------------------------------- +# Add your lib to import here +# No need to These imports. just for who want to add more conditions: +# import talib.abstract as ta +# import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class HourBasedStrategy(IStrategy): + # SHIB/USDT, 1000$x1:100days + # 158/1000: 51 trades. 29/19/3 Wins/Draws/Losses. Avg profit 4.02%. Median profit 2.48%. Total profit 4867.53438466 USDT ( 486.75%). Avg duration 1 day, 19:38:00 min. Objective: -4.17276 + # buy_params = {"buy_hour_max": 18,"buy_hour_min": 7,} + # sell_params = {"sell_hour_max": 9,"sell_hour_min": 21,} + # minimal_roi = {"0": 0.18,"171": 0.155,"315": 0.075,"1035": 0} + # stoploss = -0.292 + + # SHIB/USDT, 1000$x1:100days + # 36/1000: 113 trades. 55/14/44 Wins/Draws/Losses. Avg profit 2.06%. Median profit 0.00%. Total profit 5126.14785426 USDT ( 512.61%). Avg duration 16:48:00 min. Objective: -4.57837 + # buy_params = {"buy_hour_max": 21,"buy_hour_min": 6,} + # sell_params = {"sell_hour_max": 6,"sell_hour_min": 4,} + # minimal_roi = {"0": 0.247,"386": 0.186,"866": 0.052,"1119": 0} + # stoploss = -0.302 + + # SAND/USDT, 1000$x1:100days + # 72/1000: 158 trades. 67/13/78 Wins/Draws/Losses. Avg profit 1.37%. Median profit 0.00%. Total profit 4274.73622346 USDT ( 427.47%). Avg duration 13:50:00 min. Objective: -4.87331 + # buy_params = {"buy_hour_max": 23,"buy_hour_min": 4,} + # sell_params = {"sell_hour_max": 23,"sell_hour_min": 3,} + # minimal_roi = {"0": 0.482,"266": 0.191,"474": 0.09,"1759": 0} + # stoploss = -0.05 + + # KDA/USDT, 1000$x1:100days + # 7/1000: 65 trades. 40/23/2 Wins/Draws/Losses. Avg profit 6.42%. Median profit 7.59%. Total profit 41120.00939125 USDT ( 4112.00%). Avg duration 1 day, 9:40:00 min. Objective: -8.46089 + # buy_params = {"buy_hour_max": 22,"buy_hour_min": 9,} + # sell_params = {"sell_hour_max": 1,"sell_hour_min": 7,} + # minimal_roi = {"0": 0.517,"398": 0.206,"1003": 0.076,"1580": 0} + # stoploss = -0.338 + + # {KDA/USDT, BTC/USDT, DOGE/USDT, SAND/USDT, ETH/USDT, SOL/USDT}, 1000$x1:100days, ShuffleFilter42 + # 56/1000: 63 trades. 41/19/3 Wins/Draws/Losses. Avg profit 4.60%. Median profit 8.89%. Total profit 11596.50333022 USDT ( 1159.65%). Avg duration 1 day, 14:46:00 min. Objective: -5.76694 + + # Buy hyperspace params: + buy_params = { + "buy_hour_max": 24, + "buy_hour_min": 4, + } + + # Sell hyperspace params: + sell_params = { + "sell_hour_max": 21, + "sell_hour_min": 22, + } + + # ROI table: + minimal_roi = { + "0": 0.528, + "169": 0.113, + "528": 0.089, + "1837": 0 + } + + # Stoploss: + stoploss = -0.10 + + # Optimal timeframe + timeframe = '1h' + + buy_hour_min = IntParameter(0, 24, default=1, space='buy') + buy_hour_max = IntParameter(0, 24, default=0, space='buy') + + sell_hour_min = IntParameter(0, 24, default=1, space='sell') + sell_hour_max = IntParameter(0, 24, default=0, space='sell') + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['hour'] = dataframe['date'].dt.hour + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + min, max = self.buy_hour_min.value, self.buy_hour_max.value + dataframe.loc[ + ( + (dataframe['hour'].between(min, max)) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + min, max = self.sell_hour_min.value, self.sell_hour_max.value + dataframe.loc[ + ( + (dataframe['hour'].between(min, max)) + ), + 'buy'] = 1 + return dataframe diff --git a/HourBasedStrategy.txt b/HourBasedStrategy.txt new file mode 100644 index 0000000..32f1550 --- /dev/null +++ b/HourBasedStrategy.txt @@ -0,0 +1,87 @@ +Result for strategy HourBasedStrategy +=========================================================== BACKTESTING REPORT ========================================================== +| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | Avg Duration | Win Draw Loss Win% | +|-----------+--------+----------------+----------------+-------------------+----------------+-----------------+-------------------------| +| SAND/USDT | 256 | 1.81 | 462.34 | 462.800 | 46.28 | 1 day, 2:59:00 | 125 26 105 48.8 | +| SOL/USDT | 248 | 1.74 | 432.15 | 432.585 | 43.26 | 1 day, 4:33:00 | 142 14 92 57.3 | +| IOTX/USDT | 229 | 1.81 | 414.71 | 415.123 | 41.51 | 1 day, 7:16:00 | 110 26 93 48.0 | +| AVAX/USDT | 239 | 1.48 | 354.65 | 355.007 | 35.50 | 1 day, 5:52:00 | 129 17 93 54.0 | +| BNB/USDT | 196 | 1.66 | 325.65 | 325.975 | 32.60 | 1 day, 12:46:00 | 118 17 61 60.2 | +| ADA/USDT | 194 | 1.62 | 314.28 | 314.595 | 31.46 | 1 day, 13:10:00 | 112 18 64 57.7 | +| CELR/USDT | 282 | 1.10 | 309.01 | 309.315 | 30.93 | 1 day, 1:13:00 | 142 16 124 50.4 | +| ROSE/USDT | 223 | 1.34 | 298.59 | 298.886 | 29.89 | 1 day, 8:12:00 | 121 16 86 54.3 | +| EGLD/USDT | 200 | 1.23 | 245.40 | 245.642 | 24.56 | 1 day, 11:11:00 | 111 20 69 55.5 | +| TRX/USDT | 175 | 1.13 | 197.06 | 197.253 | 19.73 | 1 day, 17:16:00 | 104 17 54 59.4 | +| ETH/USDT | 177 | 1.01 | 179.05 | 179.230 | 17.92 | 1 day, 16:57:00 | 104 23 50 58.8 | +| XRP/USDT | 209 | 0.74 | 155.56 | 155.719 | 15.57 | 1 day, 10:32:00 | 106 24 79 50.7 | +| GALA/USDT | 53 | 2.74 | 145.04 | 145.188 | 14.52 | 1 day, 4:33:00 | 27 2 24 50.9 | +| ZEC/USDT | 209 | 0.47 | 99.11 | 99.204 | 9.92 | 1 day, 10:22:00 | 114 16 79 54.5 | +| BTC/USDT | 131 | 0.24 | 31.06 | 31.096 | 3.11 | 2 days, 6:12:00 | 76 25 30 58.0 | +| TOTAL | 3021 | 1.31 | 3963.65 | 3967.618 | 396.76 | 1 day, 9:36:00 | 1641 277 1103 54.3 | +========================================================== BUY TAG STATS =========================================================== +| TAG | Buys | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | Avg Duration | Win Draw Loss Win% | +|-------+--------+----------------+----------------+-------------------+----------------+----------------+-------------------------| +| TOTAL | 3021 | 1.31 | 3963.65 | 3967.618 | 396.76 | 1 day, 9:36:00 | 1641 277 1103 54.3 | +===================================================== SELL REASON STATS ===================================================== +| Sell Reason | Sells | Win Draws Loss Win% | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | +|---------------+---------+--------------------------+----------------+----------------+-------------------+----------------| +| roi | 1906 | 1629 277 0 100 | 7.92 | 15090.2 | 15105.3 | 1006.02 | +| stop_loss | 1100 | 0 0 1100 0 | -10.18 | -11197.8 | -11209 | -746.52 | +| force_sell | 15 | 12 0 3 80.0 | 4.75 | 71.23 | 71.301 | 4.75 | +======================================================== LEFT OPEN TRADES REPORT ======================================================== +| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | Avg Duration | Win Draw Loss Win% | +|-----------+--------+----------------+----------------+-------------------+----------------+-----------------+-------------------------| +| AVAX/USDT | 1 | 15.62 | 15.62 | 15.633 | 1.56 | 1 day, 7:35:00 | 1 0 0 100 | +| SOL/USDT | 1 | 10.52 | 10.52 | 10.532 | 1.05 | 1 day, 8:35:00 | 1 0 0 100 | +| EGLD/USDT | 1 | 10.01 | 10.01 | 10.016 | 1.00 | 1 day, 19:55:00 | 1 0 0 100 | +| ZEC/USDT | 1 | 8.03 | 8.03 | 8.040 | 0.80 | 1 day, 8:40:00 | 1 0 0 100 | +| IOTX/USDT | 1 | 6.84 | 6.84 | 6.851 | 0.69 | 1 day, 6:45:00 | 1 0 0 100 | +| CELR/USDT | 1 | 6.39 | 6.39 | 6.392 | 0.64 | 1 day, 8:35:00 | 1 0 0 100 | +| XRP/USDT | 1 | 6.32 | 6.32 | 6.327 | 0.63 | 1 day, 6:45:00 | 1 0 0 100 | +| BNB/USDT | 1 | 5.14 | 5.14 | 5.144 | 0.51 | 1 day, 8:30:00 | 1 0 0 100 | +| ROSE/USDT | 1 | 3.86 | 3.86 | 3.867 | 0.39 | 19:55:00 | 1 0 0 100 | +| SAND/USDT | 1 | 2.72 | 2.72 | 2.721 | 0.27 | 19:55:00 | 1 0 0 100 | +| TRX/USDT | 1 | 2.19 | 2.19 | 2.193 | 0.22 | 1 day, 7:25:00 | 1 0 0 100 | +| ETH/USDT | 1 | 0.04 | 0.04 | 0.036 | 0.00 | 1 day, 19:55:00 | 1 0 0 100 | +| ADA/USDT | 1 | -1.42 | -1.42 | -1.419 | -0.14 | 1 day, 19:55:00 | 0 0 1 0 | +| GALA/USDT | 1 | -1.75 | -1.75 | -1.754 | -0.18 | 19:55:00 | 0 0 1 0 | +| BTC/USDT | 1 | -3.27 | -3.27 | -3.277 | -0.33 | 1 day, 19:55:00 | 0 0 1 0 | +| TOTAL | 15 | 4.75 | 71.23 | 71.301 | 7.13 | 1 day, 8:41:00 | 12 0 3 80.0 | +=============== SUMMARY METRICS ================ +| Metric | Value | +|------------------------+---------------------| +| Backtesting from | 2021-01-01 00:00:00 | +| Backtesting to | 2021-11-20 00:00:00 | +| Max open trades | 15 | +| | | +| Total/Daily Avg Trades | 3021 / 9.35 | +| Starting balance | 1000.000 USDT | +| Final balance | 4967.618 USDT | +| Absolute profit | 3967.618 USDT | +| Total profit % | 396.76% | +| Trades per day | 9.35 | +| Avg. daily profit % | 1.23% | +| Avg. stake amount | 100.000 USDT | +| Total trade volume | 302100.000 USDT | +| | | +| Best Pair | SAND/USDT 462.34% | +| Worst Pair | BTC/USDT 31.06% | +| Best trade | SAND/USDT 120.20% | +| Worst trade | ZEC/USDT -10.18% | +| Best day | 351.483 USDT | +| Worst day | -604.011 USDT | +| Days win/draw/lose | 226 / 6 / 92 | +| Avg. Duration Winners | 1 day, 11:00:00 | +| Avg. Duration Loser | 1 day, 1:56:00 | +| Rejected Buy signals | 0 | +| | | +| Min balance | 989.810 USDT | +| Max balance | 4972.649 USDT | +| Drawdown | 1128.21% | +| Drawdown | 1129.342 USDT | +| Drawdown high | 3121.246 USDT | +| Drawdown low | 1991.905 USDT | +| Drawdown Start | 2021-05-09 00:05:00 | +| Drawdown End | 2021-06-22 13:50:00 | +| Market change | 2716.79% | +================================================ diff --git a/InformativeSample.py b/InformativeSample.py new file mode 100644 index 0000000..8e00856 --- /dev/null +++ b/InformativeSample.py @@ -0,0 +1,131 @@ + +# --- Do not remove these libs --- +from freqtrade.strategy import IStrategy, merge_informative_pair +from typing import Dict, List +from functools import reduce +from pandas import DataFrame +# -------------------------------- + +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class InformativeSample(IStrategy): + """ + Sample strategy implementing Informative Pairs - compares stake_currency with USDT. + Not performing very well - but should serve as an example how to use a referential pair against USDT. + author@: xmatthias + github@: https://github.com/freqtrade/freqtrade-strategies + + How to use it? + > python3 freqtrade -s InformativeSample + """ + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi" + minimal_roi = { + "60": 0.01, + "30": 0.03, + "20": 0.04, + "0": 0.05 + } + + # Optimal stoploss designed for the strategy + # This attribute will be overridden if the config file contains "stoploss" + stoploss = -0.10 + + # Optimal timeframe for the strategy + timeframe = '5m' + + # trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.02 + trailing_stop_positive_offset = 0.04 + + # run "populate_indicators" only for new candle + ta_on_candle = False + + # Experimental settings (configuration will overide these if set) + use_sell_signal = True + sell_profit_only = True + ignore_roi_if_buy_signal = False + + # Optional order type mapping + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [(f"BTC/USDT", '15m')] + + 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. + """ + + dataframe['ema20'] = ta.EMA(dataframe, timeperiod=20) + dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + if self.dp: + # Get ohlcv data for informative pair at 15m interval. + inf_tf = '15m' + informative = self.dp.get_pair_dataframe(pair=f"BTC/USDT", + timeframe=inf_tf) + + # calculate SMA20 on informative pair + informative['sma20'] = informative['close'].rolling(20).mean() + + # Combine the 2 dataframe + # This will result in a column named 'closeETH' or 'closeBTC' - depending on stake_currency. + dataframe = merge_informative_pair(dataframe, informative, + self.timeframe, inf_tf, ffill=True) + + 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 + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (dataframe['ema20'] > dataframe['ema50']) & + # stake/USDT above sma(stake/USDT, 20) + (dataframe['close_15m'] > dataframe['sma20_15m']) + ), + '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 + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (dataframe['ema20'] < dataframe['ema50']) & + # stake/USDT below sma(stake/USDT, 20) + (dataframe['close_15m'] < dataframe['sma20_15m']) + ), + 'sell'] = 1 + return dataframe diff --git a/Ishimoku_1.json b/Ishimoku_1.json new file mode 100644 index 0000000..ab8e00f --- /dev/null +++ b/Ishimoku_1.json @@ -0,0 +1,70 @@ +{ + "strategy_name": "Ishimoku_1", + "params": { + "roi": { + "0": 10 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_base": 0.05, + "buy_rsi": 42 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2, + "sell_h_too_old_percent": 0.01 + }, + "protection": {}, + "stoploss": { + "stoploss": -1 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-04-28 22:33:13.383313+00:00" +} diff --git a/Ishimoku_1.py b/Ishimoku_1.py new file mode 100644 index 0000000..63c16e0 --- /dev/null +++ b/Ishimoku_1.py @@ -0,0 +1,554 @@ +# +# +# +from datetime import timedelta, datetime +from typing import Optional +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame +import logging +# -------------------------------- + +# Add your lib to import here +import ta +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +logger = logging.getLogger(__name__) + + +class Ishimoku_1(IStrategy): + + # ROI table: + minimal_roi = { + "0": 0.564, + "567": 0.273, + "2814": 0.12, + "7675": 0 + } + + # Stoploss: + stoploss = -0.256 + + # Buy hypers + timeframe = '4h' + + stop_buying = False + max_open_trades = 5 + + plot_config = { + "main_plot": { + "min200": { + "color": "#86c932" + }, + "max50": { + "color": "white" + }, + "max200": { + "color": "yellow" + }, + "sma3_1d": { + "color": "pink" + }, + "sma5_1d": { + "color": "blue" + }, + "sma10_1d": { + "color": "orange" + }, + "close_1d": { + "color": "#73e233", + }, + "bb_lowerband": { + "color": "#da59a6"}, + "bb_upperband": { + "color": "#da59a6", + }, + "sar": { + "color": "#4f9f51", + } + }, + "subplots": { + "Ind": { + "trend_ichimoku_base": { + "color": "#dd1384" + }, + "trend_kst_diff": { + "color": "#850678" + } + }, + "BB": { + "bb_width": { + "color": "white" + }, + # "bb_lower_5": { + # "color": "yellow" + # } + }, + # "Cond": { + # "cond1": { + # "color": "yellow" + # } + # }, + "Rsi": { + "rsi": { + "color": "pink" + }, + # "rsi_1d": { + # "color": "yellow" + # } + }, + # "Percent": { + # "max_min": { + # "color": "#74effc" + # }, + # "pct_change_1_1d": { + # "color": "green" + # }, + # "pct_change_3_1d": { + # "color": "orange" + # }, + # "pct_change_5_1d": { + # "color": "red" + # } + # } + } + } + trades = list() + + buy_base = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + buy_rsi = IntParameter(20, 60, default=45, space='buy') + + profit_b_no_change = BooleanParameter(default=True, space="sell") + profit_b_quick_lost = BooleanParameter(default=True, space="sell") + profit_b_sma5 = BooleanParameter(default=True, space="sell") + profit_b_sma10 = BooleanParameter(default=True, space="sell") + profit_b_sma20 = BooleanParameter(default=True, space="sell") + profit_b_quick_gain = BooleanParameter(default=True, space="sell") + profit_b_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_b_old_sma10 = BooleanParameter(default=True, space="sell") + profit_b_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_b_over_rsi = BooleanParameter(default=True, space="sell") + profit_b_short_loss = BooleanParameter(default=True, space="sell") + + sell_b_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_b_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_b_candels = IntParameter(0, 48, default=12, space='sell') + + sell_b_too_old_day = IntParameter(0, 10, default=5, space='sell') + sell_b_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + sell_b_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') + sell_b_profit_percent10 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') + + sell_b_RSI = IntParameter(70, 98, default=88, space='sell') + sell_b_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_b_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_b_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + # sell_b_expected_profit = DecimalParameter(0, 0.01, decimals=3, default=0.01, space='sell') + + profit_h_no_change = BooleanParameter(default=True, space="sell") + profit_h_quick_lost = BooleanParameter(default=True, space="sell") + profit_h_sma5 = BooleanParameter(default=True, space="sell") + profit_h_sma10 = BooleanParameter(default=True, space="sell") + profit_h_sma20 = BooleanParameter(default=True, space="sell") + profit_h_quick_gain = BooleanParameter(default=True, space="sell") + profit_h_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_h_old_sma10 = BooleanParameter(default=True, space="sell") + profit_h_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_h_over_rsi = BooleanParameter(default=True, space="sell") + profit_h_short_loss = BooleanParameter(default=True, space="sell") + + sell_h_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_candels = IntParameter(0, 48, default=12, space='sell') + + sell_h_too_old_day = IntParameter(0, 10, default=5, space='sell') + sell_h_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + sell_h_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') + sell_h_profit_percent10 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') + + sell_h_RSI = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_h_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + # protection_max_allowed_dd = DecimalParameter(0, 1, decimals=2, default=0.04, space='protection') + # protection_stop = IntParameter(0, 100, default=48, space='protection') + # protection_stoploss_stop = IntParameter(0, 100, default=48, space='protection') + # lookback = IntParameter(0, 200, default=48, space='protection') + # trade_limit = IntParameter(0, 10, default=2, space='protection') + + protection_down_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='protection') + protection_down_percent3 = DecimalParameter(0, 0.05, decimals=2, default=0.02, space='protection') + protection_down_percent5 = DecimalParameter(0, 0.05, decimals=2, default=0.03, space='protection') + protection_up_percent = DecimalParameter(-0.02, 0.02, decimals=3, default=0.0, space='protection') + protection_up_percent3 = DecimalParameter(-0.02, 0.05, decimals=2, default=0.0, space='protection') + protection_up_percent5 = DecimalParameter(-0.02, 0.05, decimals=2, default=0.0, space='protection') + + # def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + # proposed_stake: float, min_stake: float, max_stake: float, + # **kwargs) -> float: + # + # informative, _ = self.dp.get_analyzed_dataframe(pair='BTC/USDT', timeframe=self.timeframe) + # # current_candle = informative.iloc[-1].squeeze() + # + # current = informative.tail(1).iloc[0]['close'] + # # 50000 => 2 30000 => 20 + # if current > 50000: + # self.max_open_trades = 2 + # proposed_stake = self.config['stake_amount'] / 2 + # else: + # if current > 32000: + # self.max_open_trades = 2 + int((50000 - current) / 1000) + # proposed_stake = self.config['stake_amount'] / 2 \ + # + self.config['stake_amount'] * self.max_open_trades / self.config['max_open_trades'] + # else: + # self.max_open_trades = self.config['max_open_trades'] + # proposed_stake = self.config['stake_amount'] + # + # return min(max_stake, proposed_stake) + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + # { + # "method": "MaxDrawdown", + # "lookback_period_candles": self.lookback.value, + # "trade_limit": self.trade_limit.value, + # "stop_duration_candles": self.protection_stop.value, + # "max_allowed_drawdown": self.protection_max_allowed_dd.value, + # "only_per_pair": False + # } + ] + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + allow_to_buy = True + + informative, _ = self.dp.get_analyzed_dataframe(pair='BTC/USDT', timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + + if (self.stop_buying is True) & ( + (info_last_candle['percent'] > self.protection_up_percent.value) + | (info_last_candle['percent3'] > self.protection_up_percent3.value) + | (info_last_candle['percent5'] > self.protection_up_percent5.value)): + # print("Enable buying") + self.stop_buying = False + + if self.stop_buying: + allow_to_buy = False + return allow_to_buy + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + expected_profit = 0.01 + days = (current_time - trade.open_date_utc).days + ###### + + informative, _ = self.dp.get_analyzed_dataframe(pair='BTC/USDT', timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + + if self.stop_buying is True: + if (info_last_candle['percent'] > 0) | (info_last_candle['percent3'] > 0) | ( + info_last_candle['percent5'] > 0): + # print("Enable buying") + self.stop_buying = False + else: + if self.stop_buying is False: + if (info_last_candle['percent'] < - self.protection_down_percent.value) \ + | (info_last_candle['percent3'] < - self.protection_down_percent3.value) \ + | (info_last_candle['percent5'] < - self.protection_down_percent5.value): + self.stop_buying = True + # print("Disable buying", info_last_candle['percent'], info_last_candle['percent3'], info_last_candle['percent5']) + # return 'send_all' + + if (last_candle['pct_change_1_1h'] < 0): + if (current_profit > 0.015) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | ( + last_candle['percent5'] < -0.005)): + return 'b_percent_quick' + + if (current_profit > last_candle['bb_width'] / 2) & (previous_last_candle['close'] > previous_last_candle['bb_upperband'])\ + & (last_candle['percent'] < -0.005) & ((current_time - trade.open_date_utc).seconds <= 3600): + return 'b_bb_width' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value) \ + & (days < self.sell_b_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2) \ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + if (current_profit > 0.025) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | ( + last_candle['percent5'] < -0.005)): + return 'h_percent_quick' + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value) \ + & (days < self.sell_h_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2) \ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_h_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle[ + 'max200']): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + informative_pairs = [('BTC/USDT', '1h')] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['rsi'] = talib.RSI(dataframe) + + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib - tib.min()) / (tib.max() - tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd - tkd.min()) / (tkd.max() - tkd.min()) + + ################### INFORMATIVE BTC 1H + informative = self.dp.get_pair_dataframe(pair='BTC/USDT', timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative['sma7'] = talib.SMA(informative, timeperiod=7) + informative['pct_change_1'] = informative['close'].pct_change(1) + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= self.buy_base.value) + & (dataframe['rsi'] < self.buy_rsi.value) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['sma7_1h'].shift(1) <= dataframe['sma7_1h']) + & (dataframe['close_1h'] <= dataframe['bb_middleband_1h']) + + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe diff --git a/Ishimoku_2.json b/Ishimoku_2.json new file mode 100644 index 0000000..5911ef5 --- /dev/null +++ b/Ishimoku_2.json @@ -0,0 +1,70 @@ +{ + "strategy_name": "Ishimoku_2", + "params": { + "roi": { + "0": 10 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_base": 0.05, + "buy_rsi": 42 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2, + "sell_h_too_old_percent": 0.01 + }, + "protection": {}, + "stoploss": { + "stoploss": -1 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-04-28 22:33:13.383313+00:00" +} diff --git a/Ishimoku_2.py b/Ishimoku_2.py new file mode 100644 index 0000000..cb97aa2 --- /dev/null +++ b/Ishimoku_2.py @@ -0,0 +1,554 @@ +# +# +# +from datetime import timedelta, datetime +from typing import Optional +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame +import logging +# -------------------------------- + +# Add your lib to import here +import ta +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +logger = logging.getLogger(__name__) + + +class Ishimoku_2(IStrategy): + + # ROI table: + minimal_roi = { + "0": 0.564, + "567": 0.273, + "2814": 0.12, + "7675": 0 + } + + # Stoploss: + stoploss = -0.256 + + # Buy hypers + timeframe = '4h' + + stop_buying = False + max_open_trades = 5 + + plot_config = { + "main_plot": { + "min200": { + "color": "#86c932" + }, + "max50": { + "color": "white" + }, + "max200": { + "color": "yellow" + }, + "sma3_1d": { + "color": "pink" + }, + "sma5_1d": { + "color": "blue" + }, + "sma10_1d": { + "color": "orange" + }, + "close_1d": { + "color": "#73e233", + }, + "bb_lowerband": { + "color": "#da59a6"}, + "bb_upperband": { + "color": "#da59a6", + }, + "sar": { + "color": "#4f9f51", + } + }, + "subplots": { + "Ind": { + "trend_ichimoku_base": { + "color": "#dd1384" + }, + "trend_kst_diff": { + "color": "#850678" + } + }, + "BB": { + "bb_width": { + "color": "white" + }, + # "bb_lower_5": { + # "color": "yellow" + # } + }, + # "Cond": { + # "cond1": { + # "color": "yellow" + # } + # }, + "Rsi": { + "rsi": { + "color": "pink" + }, + # "rsi_1d": { + # "color": "yellow" + # } + }, + # "Percent": { + # "max_min": { + # "color": "#74effc" + # }, + # "pct_change_1_1d": { + # "color": "green" + # }, + # "pct_change_3_1d": { + # "color": "orange" + # }, + # "pct_change_5_1d": { + # "color": "red" + # } + # } + } + } + trades = list() + + buy_base = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + buy_rsi = IntParameter(20, 60, default=45, space='buy') + + profit_b_no_change = BooleanParameter(default=True, space="sell") + profit_b_quick_lost = BooleanParameter(default=True, space="sell") + profit_b_sma5 = BooleanParameter(default=True, space="sell") + profit_b_sma10 = BooleanParameter(default=True, space="sell") + profit_b_sma20 = BooleanParameter(default=True, space="sell") + profit_b_quick_gain = BooleanParameter(default=True, space="sell") + profit_b_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_b_old_sma10 = BooleanParameter(default=True, space="sell") + profit_b_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_b_over_rsi = BooleanParameter(default=True, space="sell") + profit_b_short_loss = BooleanParameter(default=True, space="sell") + + sell_b_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_b_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_b_candels = IntParameter(0, 48, default=12, space='sell') + + sell_b_too_old_day = IntParameter(0, 10, default=5, space='sell') + sell_b_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + sell_b_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') + sell_b_profit_percent10 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') + + sell_b_RSI = IntParameter(70, 98, default=88, space='sell') + sell_b_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_b_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_b_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + # sell_b_expected_profit = DecimalParameter(0, 0.01, decimals=3, default=0.01, space='sell') + + profit_h_no_change = BooleanParameter(default=True, space="sell") + profit_h_quick_lost = BooleanParameter(default=True, space="sell") + profit_h_sma5 = BooleanParameter(default=True, space="sell") + profit_h_sma10 = BooleanParameter(default=True, space="sell") + profit_h_sma20 = BooleanParameter(default=True, space="sell") + profit_h_quick_gain = BooleanParameter(default=True, space="sell") + profit_h_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_h_old_sma10 = BooleanParameter(default=True, space="sell") + profit_h_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_h_over_rsi = BooleanParameter(default=True, space="sell") + profit_h_short_loss = BooleanParameter(default=True, space="sell") + + sell_h_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_candels = IntParameter(0, 48, default=12, space='sell') + + sell_h_too_old_day = IntParameter(0, 10, default=5, space='sell') + sell_h_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + sell_h_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') + sell_h_profit_percent10 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') + + sell_h_RSI = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_h_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + # protection_max_allowed_dd = DecimalParameter(0, 1, decimals=2, default=0.04, space='protection') + # protection_stop = IntParameter(0, 100, default=48, space='protection') + # protection_stoploss_stop = IntParameter(0, 100, default=48, space='protection') + # lookback = IntParameter(0, 200, default=48, space='protection') + # trade_limit = IntParameter(0, 10, default=2, space='protection') + + protection_down_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='protection') + protection_down_percent3 = DecimalParameter(0, 0.05, decimals=2, default=0.02, space='protection') + protection_down_percent5 = DecimalParameter(0, 0.05, decimals=2, default=0.03, space='protection') + protection_up_percent = DecimalParameter(-0.02, 0.02, decimals=3, default=0.0, space='protection') + protection_up_percent3 = DecimalParameter(-0.02, 0.05, decimals=2, default=0.0, space='protection') + protection_up_percent5 = DecimalParameter(-0.02, 0.05, decimals=2, default=0.0, space='protection') + + # def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + # proposed_stake: float, min_stake: float, max_stake: float, + # **kwargs) -> float: + # + # informative, _ = self.dp.get_analyzed_dataframe(pair='BTC/USDT', timeframe=self.timeframe) + # # current_candle = informative.iloc[-1].squeeze() + # + # current = informative.tail(1).iloc[0]['close'] + # # 50000 => 2 30000 => 20 + # if current > 50000: + # self.max_open_trades = 2 + # proposed_stake = self.config['stake_amount'] / 2 + # else: + # if current > 32000: + # self.max_open_trades = 2 + int((50000 - current) / 1000) + # proposed_stake = self.config['stake_amount'] / 2 \ + # + self.config['stake_amount'] * self.max_open_trades / self.config['max_open_trades'] + # else: + # self.max_open_trades = self.config['max_open_trades'] + # proposed_stake = self.config['stake_amount'] + # + # return min(max_stake, proposed_stake) + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + # { + # "method": "MaxDrawdown", + # "lookback_period_candles": self.lookback.value, + # "trade_limit": self.trade_limit.value, + # "stop_duration_candles": self.protection_stop.value, + # "max_allowed_drawdown": self.protection_max_allowed_dd.value, + # "only_per_pair": False + # } + ] + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + allow_to_buy = True + + informative, _ = self.dp.get_analyzed_dataframe(pair='BTC/USDT', timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + + if (self.stop_buying is True) & ( + (info_last_candle['percent'] > self.protection_up_percent.value) + | (info_last_candle['percent3'] > self.protection_up_percent3.value) + | (info_last_candle['percent5'] > self.protection_up_percent5.value)): + # print("Enable buying") + self.stop_buying = False + + if self.stop_buying: + allow_to_buy = False + return allow_to_buy + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + expected_profit = 0.01 + days = (current_time - trade.open_date_utc).days + ###### + + informative, _ = self.dp.get_analyzed_dataframe(pair='BTC/USDT', timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + + if self.stop_buying is True: + if (info_last_candle['percent'] > 0) | (info_last_candle['percent3'] > 0) | ( + info_last_candle['percent5'] > 0): + # print("Enable buying") + self.stop_buying = False + else: + if self.stop_buying is False: + if (info_last_candle['percent'] < - self.protection_down_percent.value) \ + | (info_last_candle['percent3'] < - self.protection_down_percent3.value) \ + | (info_last_candle['percent5'] < - self.protection_down_percent5.value): + self.stop_buying = True + # print("Disable buying", info_last_candle['percent'], info_last_candle['percent3'], info_last_candle['percent5']) + # return 'send_all' + + if (last_candle['pct_change_1_1h'] < 0): + if (current_profit > 0.015) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | ( + last_candle['percent5'] < -0.005)): + return 'b_percent_quick' + + if (current_profit > last_candle['bb_width'] / 2) & (previous_last_candle['close'] > previous_last_candle['bb_upperband'])\ + & (last_candle['percent'] < -0.005) & ((current_time - trade.open_date_utc).seconds <= 3600): + return 'b_bb_width' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value) \ + & (days < self.sell_b_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2) \ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + if (current_profit > 0.025) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | ( + last_candle['percent5'] < -0.005)): + return 'h_percent_quick' + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value) \ + & (days < self.sell_h_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2) \ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_h_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle[ + 'max200']): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + informative_pairs = [('BTC/USDT', '1h')] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['rsi'] = talib.RSI(dataframe) + + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib - tib.min()) / (tib.max() - tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd - tkd.min()) / (tkd.max() - tkd.min()) + + ################### INFORMATIVE BTC 1H + informative = self.dp.get_pair_dataframe(pair='BTC/USDT', timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative['sma7'] = talib.SMA(informative, timeperiod=7) + informative['pct_change_1'] = informative['close'].pct_change(1) + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= self.buy_base.value) + & (dataframe['rsi'] < self.buy_rsi.value) + #& (dataframe['close'] < dataframe['sma10']) + #& (dataframe['sma7_1h'].shift(1) <= dataframe['sma7_1h']) + #& (dataframe['close_1h'] <= dataframe['bb_middleband_1h']) + + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe diff --git a/Ishimoku_3.json b/Ishimoku_3.json new file mode 100644 index 0000000..7a2f7e5 --- /dev/null +++ b/Ishimoku_3.json @@ -0,0 +1,70 @@ +{ + "strategy_name": "Ishimoku_3", + "params": { + "roi": { + "0": 10 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_base": 0.05, + "buy_rsi": 42 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2, + "sell_h_too_old_percent": 0.01 + }, + "protection": {}, + "stoploss": { + "stoploss": -1 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-04-28 22:33:13.383313+00:00" +} diff --git a/Ishimoku_3.py b/Ishimoku_3.py new file mode 100644 index 0000000..4292a7c --- /dev/null +++ b/Ishimoku_3.py @@ -0,0 +1,554 @@ +# +# +# +from datetime import timedelta, datetime +from typing import Optional +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame +import logging +# -------------------------------- + +# Add your lib to import here +import ta +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +logger = logging.getLogger(__name__) + + +class Ishimoku_3(IStrategy): + + # ROI table: + minimal_roi = { + "0": 0.564, + "567": 0.273, + "2814": 0.12, + "7675": 0 + } + + # Stoploss: + stoploss = -0.256 + + # Buy hypers + timeframe = '4h' + + stop_buying = False + max_open_trades = 5 + + plot_config = { + "main_plot": { + "min200": { + "color": "#86c932" + }, + "max50": { + "color": "white" + }, + "max200": { + "color": "yellow" + }, + "sma3_1d": { + "color": "pink" + }, + "sma5_1d": { + "color": "blue" + }, + "sma10_1d": { + "color": "orange" + }, + "close_1d": { + "color": "#73e233", + }, + "bb_lowerband": { + "color": "#da59a6"}, + "bb_upperband": { + "color": "#da59a6", + }, + "sar": { + "color": "#4f9f51", + } + }, + "subplots": { + "Ind": { + "trend_ichimoku_base": { + "color": "#dd1384" + }, + "trend_kst_diff": { + "color": "#850678" + } + }, + "BB": { + "bb_width": { + "color": "white" + }, + # "bb_lower_5": { + # "color": "yellow" + # } + }, + # "Cond": { + # "cond1": { + # "color": "yellow" + # } + # }, + "Rsi": { + "rsi": { + "color": "pink" + }, + # "rsi_1d": { + # "color": "yellow" + # } + }, + # "Percent": { + # "max_min": { + # "color": "#74effc" + # }, + # "pct_change_1_1d": { + # "color": "green" + # }, + # "pct_change_3_1d": { + # "color": "orange" + # }, + # "pct_change_5_1d": { + # "color": "red" + # } + # } + } + } + trades = list() + + buy_base = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + buy_rsi = IntParameter(20, 60, default=45, space='buy') + + profit_b_no_change = BooleanParameter(default=True, space="sell") + profit_b_quick_lost = BooleanParameter(default=True, space="sell") + profit_b_sma5 = BooleanParameter(default=True, space="sell") + profit_b_sma10 = BooleanParameter(default=True, space="sell") + profit_b_sma20 = BooleanParameter(default=True, space="sell") + profit_b_quick_gain = BooleanParameter(default=True, space="sell") + profit_b_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_b_old_sma10 = BooleanParameter(default=True, space="sell") + profit_b_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_b_over_rsi = BooleanParameter(default=True, space="sell") + profit_b_short_loss = BooleanParameter(default=True, space="sell") + + sell_b_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_b_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_b_candels = IntParameter(0, 48, default=12, space='sell') + + sell_b_too_old_day = IntParameter(0, 10, default=5, space='sell') + sell_b_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + sell_b_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') + sell_b_profit_percent10 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') + + sell_b_RSI = IntParameter(70, 98, default=88, space='sell') + sell_b_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_b_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_b_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + # sell_b_expected_profit = DecimalParameter(0, 0.01, decimals=3, default=0.01, space='sell') + + profit_h_no_change = BooleanParameter(default=True, space="sell") + profit_h_quick_lost = BooleanParameter(default=True, space="sell") + profit_h_sma5 = BooleanParameter(default=True, space="sell") + profit_h_sma10 = BooleanParameter(default=True, space="sell") + profit_h_sma20 = BooleanParameter(default=True, space="sell") + profit_h_quick_gain = BooleanParameter(default=True, space="sell") + profit_h_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_h_old_sma10 = BooleanParameter(default=True, space="sell") + profit_h_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_h_over_rsi = BooleanParameter(default=True, space="sell") + profit_h_short_loss = BooleanParameter(default=True, space="sell") + + sell_h_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_candels = IntParameter(0, 48, default=12, space='sell') + + sell_h_too_old_day = IntParameter(0, 10, default=5, space='sell') + sell_h_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + sell_h_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') + sell_h_profit_percent10 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') + + sell_h_RSI = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_h_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + # protection_max_allowed_dd = DecimalParameter(0, 1, decimals=2, default=0.04, space='protection') + # protection_stop = IntParameter(0, 100, default=48, space='protection') + # protection_stoploss_stop = IntParameter(0, 100, default=48, space='protection') + # lookback = IntParameter(0, 200, default=48, space='protection') + # trade_limit = IntParameter(0, 10, default=2, space='protection') + + protection_down_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='protection') + protection_down_percent3 = DecimalParameter(0, 0.05, decimals=2, default=0.02, space='protection') + protection_down_percent5 = DecimalParameter(0, 0.05, decimals=2, default=0.03, space='protection') + protection_up_percent = DecimalParameter(-0.02, 0.02, decimals=3, default=0.0, space='protection') + protection_up_percent3 = DecimalParameter(-0.02, 0.05, decimals=2, default=0.0, space='protection') + protection_up_percent5 = DecimalParameter(-0.02, 0.05, decimals=2, default=0.0, space='protection') + + # def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + # proposed_stake: float, min_stake: float, max_stake: float, + # **kwargs) -> float: + # + # informative, _ = self.dp.get_analyzed_dataframe(pair='BTC/USDT', timeframe=self.timeframe) + # # current_candle = informative.iloc[-1].squeeze() + # + # current = informative.tail(1).iloc[0]['close'] + # # 50000 => 2 30000 => 20 + # if current > 50000: + # self.max_open_trades = 2 + # proposed_stake = self.config['stake_amount'] / 2 + # else: + # if current > 32000: + # self.max_open_trades = 2 + int((50000 - current) / 1000) + # proposed_stake = self.config['stake_amount'] / 2 \ + # + self.config['stake_amount'] * self.max_open_trades / self.config['max_open_trades'] + # else: + # self.max_open_trades = self.config['max_open_trades'] + # proposed_stake = self.config['stake_amount'] + # + # return min(max_stake, proposed_stake) + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + # { + # "method": "MaxDrawdown", + # "lookback_period_candles": self.lookback.value, + # "trade_limit": self.trade_limit.value, + # "stop_duration_candles": self.protection_stop.value, + # "max_allowed_drawdown": self.protection_max_allowed_dd.value, + # "only_per_pair": False + # } + ] + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + allow_to_buy = True + + informative, _ = self.dp.get_analyzed_dataframe(pair='BTC/USDT', timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + + if (self.stop_buying is True) & ( + (info_last_candle['percent'] > self.protection_up_percent.value) + | (info_last_candle['percent3'] > self.protection_up_percent3.value) + | (info_last_candle['percent5'] > self.protection_up_percent5.value)): + # print("Enable buying") + self.stop_buying = False + + if self.stop_buying: + allow_to_buy = False + return allow_to_buy + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + expected_profit = 0.01 + days = (current_time - trade.open_date_utc).days + ###### + + informative, _ = self.dp.get_analyzed_dataframe(pair='BTC/USDT', timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + + if self.stop_buying is True: + if (info_last_candle['percent'] > 0) | (info_last_candle['percent3'] > 0) | ( + info_last_candle['percent5'] > 0): + # print("Enable buying") + self.stop_buying = False + else: + if self.stop_buying is False: + if (info_last_candle['percent'] < - self.protection_down_percent.value) \ + | (info_last_candle['percent3'] < - self.protection_down_percent3.value) \ + | (info_last_candle['percent5'] < - self.protection_down_percent5.value): + self.stop_buying = True + # print("Disable buying", info_last_candle['percent'], info_last_candle['percent3'], info_last_candle['percent5']) + # return 'send_all' + + if (last_candle['pct_change_1_1h'] < 0): + if (current_profit > 0.015) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | ( + last_candle['percent5'] < -0.005)): + return 'b_percent_quick' + + if (current_profit > last_candle['bb_width'] / 2) & (previous_last_candle['close'] > previous_last_candle['bb_upperband'])\ + & (last_candle['percent'] < -0.005) & ((current_time - trade.open_date_utc).seconds <= 3600): + return 'b_bb_width' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value) \ + & (days < self.sell_b_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2) \ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + if (current_profit > 0.025) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | ( + last_candle['percent5'] < -0.005)): + return 'h_percent_quick' + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value) \ + & (days < self.sell_h_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2) \ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_h_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle[ + 'max200']): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + informative_pairs = [('BTC/USDT', '1h')] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['rsi'] = talib.RSI(dataframe) + + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib - tib.min()) / (tib.max() - tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd - tkd.min()) / (tkd.max() - tkd.min()) + + ################### INFORMATIVE BTC 1H + informative = self.dp.get_pair_dataframe(pair='BTC/USDT', timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative['sma7'] = talib.SMA(informative, timeperiod=7) + informative['pct_change_1'] = informative['close'].pct_change(1) + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= self.buy_base.value) + & (dataframe['rsi'] < self.buy_rsi.value) + #& (dataframe['close'] < dataframe['sma10']) + #& (dataframe['sma7_1h'].shift(1) <= dataframe['sma7_1h']) + & (dataframe['close_1h'] <= dataframe['bb_middleband_1h']) + + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe diff --git a/Ishimoku_4.json b/Ishimoku_4.json new file mode 100644 index 0000000..5e7cc9a --- /dev/null +++ b/Ishimoku_4.json @@ -0,0 +1,70 @@ +{ + "strategy_name": "Ishimoku_4", + "params": { + "roi": { + "0": 10 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_base": 0.05, + "buy_rsi": 42 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2, + "sell_h_too_old_percent": 0.01 + }, + "protection": {}, + "stoploss": { + "stoploss": -1 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-04-28 22:33:13.383313+00:00" +} diff --git a/Ishimoku_4.py b/Ishimoku_4.py new file mode 100644 index 0000000..5147e23 --- /dev/null +++ b/Ishimoku_4.py @@ -0,0 +1,554 @@ +# +# +# +from datetime import timedelta, datetime +from typing import Optional +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame +import logging +# -------------------------------- + +# Add your lib to import here +import ta +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +logger = logging.getLogger(__name__) + + +class Ishimoku_4(IStrategy): + + # ROI table: + minimal_roi = { + "0": 0.564, + "567": 0.273, + "2814": 0.12, + "7675": 0 + } + + # Stoploss: + stoploss = -0.256 + + # Buy hypers + timeframe = '4h' + + stop_buying = False + max_open_trades = 5 + + plot_config = { + "main_plot": { + "min200": { + "color": "#86c932" + }, + "max50": { + "color": "white" + }, + "max200": { + "color": "yellow" + }, + "sma3_1d": { + "color": "pink" + }, + "sma5_1d": { + "color": "blue" + }, + "sma10_1d": { + "color": "orange" + }, + "close_1d": { + "color": "#73e233", + }, + "bb_lowerband": { + "color": "#da59a6"}, + "bb_upperband": { + "color": "#da59a6", + }, + "sar": { + "color": "#4f9f51", + } + }, + "subplots": { + "Ind": { + "trend_ichimoku_base": { + "color": "#dd1384" + }, + "trend_kst_diff": { + "color": "#850678" + } + }, + "BB": { + "bb_width": { + "color": "white" + }, + # "bb_lower_5": { + # "color": "yellow" + # } + }, + # "Cond": { + # "cond1": { + # "color": "yellow" + # } + # }, + "Rsi": { + "rsi": { + "color": "pink" + }, + # "rsi_1d": { + # "color": "yellow" + # } + }, + # "Percent": { + # "max_min": { + # "color": "#74effc" + # }, + # "pct_change_1_1d": { + # "color": "green" + # }, + # "pct_change_3_1d": { + # "color": "orange" + # }, + # "pct_change_5_1d": { + # "color": "red" + # } + # } + } + } + trades = list() + + buy_base = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + buy_rsi = IntParameter(20, 60, default=45, space='buy') + + profit_b_no_change = BooleanParameter(default=True, space="sell") + profit_b_quick_lost = BooleanParameter(default=True, space="sell") + profit_b_sma5 = BooleanParameter(default=True, space="sell") + profit_b_sma10 = BooleanParameter(default=True, space="sell") + profit_b_sma20 = BooleanParameter(default=True, space="sell") + profit_b_quick_gain = BooleanParameter(default=True, space="sell") + profit_b_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_b_old_sma10 = BooleanParameter(default=True, space="sell") + profit_b_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_b_over_rsi = BooleanParameter(default=True, space="sell") + profit_b_short_loss = BooleanParameter(default=True, space="sell") + + sell_b_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_b_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_b_candels = IntParameter(0, 48, default=12, space='sell') + + sell_b_too_old_day = IntParameter(0, 10, default=5, space='sell') + sell_b_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + sell_b_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') + sell_b_profit_percent10 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') + + sell_b_RSI = IntParameter(70, 98, default=88, space='sell') + sell_b_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_b_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_b_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + # sell_b_expected_profit = DecimalParameter(0, 0.01, decimals=3, default=0.01, space='sell') + + profit_h_no_change = BooleanParameter(default=True, space="sell") + profit_h_quick_lost = BooleanParameter(default=True, space="sell") + profit_h_sma5 = BooleanParameter(default=True, space="sell") + profit_h_sma10 = BooleanParameter(default=True, space="sell") + profit_h_sma20 = BooleanParameter(default=True, space="sell") + profit_h_quick_gain = BooleanParameter(default=True, space="sell") + profit_h_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_h_old_sma10 = BooleanParameter(default=True, space="sell") + profit_h_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_h_over_rsi = BooleanParameter(default=True, space="sell") + profit_h_short_loss = BooleanParameter(default=True, space="sell") + + sell_h_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_candels = IntParameter(0, 48, default=12, space='sell') + + sell_h_too_old_day = IntParameter(0, 10, default=5, space='sell') + sell_h_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + sell_h_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') + sell_h_profit_percent10 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') + + sell_h_RSI = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_h_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + # protection_max_allowed_dd = DecimalParameter(0, 1, decimals=2, default=0.04, space='protection') + # protection_stop = IntParameter(0, 100, default=48, space='protection') + # protection_stoploss_stop = IntParameter(0, 100, default=48, space='protection') + # lookback = IntParameter(0, 200, default=48, space='protection') + # trade_limit = IntParameter(0, 10, default=2, space='protection') + + protection_down_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='protection') + protection_down_percent3 = DecimalParameter(0, 0.05, decimals=2, default=0.02, space='protection') + protection_down_percent5 = DecimalParameter(0, 0.05, decimals=2, default=0.03, space='protection') + protection_up_percent = DecimalParameter(-0.02, 0.02, decimals=3, default=0.0, space='protection') + protection_up_percent3 = DecimalParameter(-0.02, 0.05, decimals=2, default=0.0, space='protection') + protection_up_percent5 = DecimalParameter(-0.02, 0.05, decimals=2, default=0.0, space='protection') + + # def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + # proposed_stake: float, min_stake: float, max_stake: float, + # **kwargs) -> float: + # + # informative, _ = self.dp.get_analyzed_dataframe(pair='BTC/USDT', timeframe=self.timeframe) + # # current_candle = informative.iloc[-1].squeeze() + # + # current = informative.tail(1).iloc[0]['close'] + # # 50000 => 2 30000 => 20 + # if current > 50000: + # self.max_open_trades = 2 + # proposed_stake = self.config['stake_amount'] / 2 + # else: + # if current > 32000: + # self.max_open_trades = 2 + int((50000 - current) / 1000) + # proposed_stake = self.config['stake_amount'] / 2 \ + # + self.config['stake_amount'] * self.max_open_trades / self.config['max_open_trades'] + # else: + # self.max_open_trades = self.config['max_open_trades'] + # proposed_stake = self.config['stake_amount'] + # + # return min(max_stake, proposed_stake) + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + # { + # "method": "MaxDrawdown", + # "lookback_period_candles": self.lookback.value, + # "trade_limit": self.trade_limit.value, + # "stop_duration_candles": self.protection_stop.value, + # "max_allowed_drawdown": self.protection_max_allowed_dd.value, + # "only_per_pair": False + # } + ] + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + allow_to_buy = True + + informative, _ = self.dp.get_analyzed_dataframe(pair='BTC/USDT', timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + + if (self.stop_buying is True) & ( + (info_last_candle['percent'] > self.protection_up_percent.value) + | (info_last_candle['percent3'] > self.protection_up_percent3.value) + | (info_last_candle['percent5'] > self.protection_up_percent5.value)): + # print("Enable buying") + self.stop_buying = False + + if self.stop_buying: + allow_to_buy = False + return allow_to_buy + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + expected_profit = 0.01 + days = (current_time - trade.open_date_utc).days + ###### + + informative, _ = self.dp.get_analyzed_dataframe(pair='BTC/USDT', timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + + if self.stop_buying is True: + if (info_last_candle['percent'] > 0) | (info_last_candle['percent3'] > 0) | ( + info_last_candle['percent5'] > 0): + # print("Enable buying") + self.stop_buying = False + else: + if self.stop_buying is False: + if (info_last_candle['percent'] < - self.protection_down_percent.value) \ + | (info_last_candle['percent3'] < - self.protection_down_percent3.value) \ + | (info_last_candle['percent5'] < - self.protection_down_percent5.value): + self.stop_buying = True + # print("Disable buying", info_last_candle['percent'], info_last_candle['percent3'], info_last_candle['percent5']) + # return 'send_all' + + if (last_candle['pct_change_1_1h'] < 0): + if (current_profit > 0.015) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | ( + last_candle['percent5'] < -0.005)): + return 'b_percent_quick' + + if (current_profit > last_candle['bb_width'] / 2) & (previous_last_candle['close'] > previous_last_candle['bb_upperband'])\ + & (last_candle['percent'] < -0.005) & ((current_time - trade.open_date_utc).seconds <= 3600): + return 'b_bb_width' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value) \ + & (days < self.sell_b_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2) \ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + if (current_profit > 0.025) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | ( + last_candle['percent5'] < -0.005)): + return 'h_percent_quick' + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value) \ + & (days < self.sell_h_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2) \ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_h_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle[ + 'max200']): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + informative_pairs = [('BTC/USDT', '1h')] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['rsi'] = talib.RSI(dataframe) + + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib - tib.min()) / (tib.max() - tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd - tkd.min()) / (tkd.max() - tkd.min()) + + ################### INFORMATIVE BTC 1H + informative = self.dp.get_pair_dataframe(pair='BTC/USDT', timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative['sma7'] = talib.SMA(informative, timeperiod=7) + informative['pct_change_1'] = informative['close'].pct_change(1) + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= self.buy_base.value) + & (dataframe['rsi'] < self.buy_rsi.value) + #& (dataframe['close'] < dataframe['sma10']) + & (dataframe['sma7_1h'].shift(1) <= dataframe['sma7_1h']) + & (dataframe['close_1h'] <= dataframe['bb_middleband_1h']) + + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe diff --git a/Ishimoku_5.json b/Ishimoku_5.json new file mode 100644 index 0000000..7f9ffdd --- /dev/null +++ b/Ishimoku_5.json @@ -0,0 +1,70 @@ +{ + "strategy_name": "Ishimoku_5", + "params": { + "roi": { + "0": 10 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_base": 0.05, + "buy_rsi": 42 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2, + "sell_h_too_old_percent": 0.01 + }, + "protection": {}, + "stoploss": { + "stoploss": -1 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-04-28 22:33:13.383313+00:00" +} diff --git a/Ishimoku_5.py b/Ishimoku_5.py new file mode 100644 index 0000000..f94a2b1 --- /dev/null +++ b/Ishimoku_5.py @@ -0,0 +1,554 @@ +# +# +# +from datetime import timedelta, datetime +from typing import Optional +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame +import logging +# -------------------------------- + +# Add your lib to import here +import ta +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +logger = logging.getLogger(__name__) + + +class Ishimoku_5(IStrategy): + + # ROI table: + minimal_roi = { + "0": 0.564, + "567": 0.273, + "2814": 0.12, + "7675": 0 + } + + # Stoploss: + stoploss = -0.256 + + # Buy hypers + timeframe = '4h' + + stop_buying = False + max_open_trades = 5 + + plot_config = { + "main_plot": { + "min200": { + "color": "#86c932" + }, + "max50": { + "color": "white" + }, + "max200": { + "color": "yellow" + }, + "sma3_1d": { + "color": "pink" + }, + "sma5_1d": { + "color": "blue" + }, + "sma10_1d": { + "color": "orange" + }, + "close_1d": { + "color": "#73e233", + }, + "bb_lowerband": { + "color": "#da59a6"}, + "bb_upperband": { + "color": "#da59a6", + }, + "sar": { + "color": "#4f9f51", + } + }, + "subplots": { + "Ind": { + "trend_ichimoku_base": { + "color": "#dd1384" + }, + "trend_kst_diff": { + "color": "#850678" + } + }, + "BB": { + "bb_width": { + "color": "white" + }, + # "bb_lower_5": { + # "color": "yellow" + # } + }, + # "Cond": { + # "cond1": { + # "color": "yellow" + # } + # }, + "Rsi": { + "rsi": { + "color": "pink" + }, + # "rsi_1d": { + # "color": "yellow" + # } + }, + # "Percent": { + # "max_min": { + # "color": "#74effc" + # }, + # "pct_change_1_1d": { + # "color": "green" + # }, + # "pct_change_3_1d": { + # "color": "orange" + # }, + # "pct_change_5_1d": { + # "color": "red" + # } + # } + } + } + trades = list() + + buy_base = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + buy_rsi = IntParameter(20, 60, default=45, space='buy') + + profit_b_no_change = BooleanParameter(default=True, space="sell") + profit_b_quick_lost = BooleanParameter(default=True, space="sell") + profit_b_sma5 = BooleanParameter(default=True, space="sell") + profit_b_sma10 = BooleanParameter(default=True, space="sell") + profit_b_sma20 = BooleanParameter(default=True, space="sell") + profit_b_quick_gain = BooleanParameter(default=True, space="sell") + profit_b_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_b_old_sma10 = BooleanParameter(default=True, space="sell") + profit_b_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_b_over_rsi = BooleanParameter(default=True, space="sell") + profit_b_short_loss = BooleanParameter(default=True, space="sell") + + sell_b_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_b_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_b_candels = IntParameter(0, 48, default=12, space='sell') + + sell_b_too_old_day = IntParameter(0, 10, default=5, space='sell') + sell_b_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + sell_b_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') + sell_b_profit_percent10 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') + + sell_b_RSI = IntParameter(70, 98, default=88, space='sell') + sell_b_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_b_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_b_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + # sell_b_expected_profit = DecimalParameter(0, 0.01, decimals=3, default=0.01, space='sell') + + profit_h_no_change = BooleanParameter(default=True, space="sell") + profit_h_quick_lost = BooleanParameter(default=True, space="sell") + profit_h_sma5 = BooleanParameter(default=True, space="sell") + profit_h_sma10 = BooleanParameter(default=True, space="sell") + profit_h_sma20 = BooleanParameter(default=True, space="sell") + profit_h_quick_gain = BooleanParameter(default=True, space="sell") + profit_h_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_h_old_sma10 = BooleanParameter(default=True, space="sell") + profit_h_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_h_over_rsi = BooleanParameter(default=True, space="sell") + profit_h_short_loss = BooleanParameter(default=True, space="sell") + + sell_h_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_candels = IntParameter(0, 48, default=12, space='sell') + + sell_h_too_old_day = IntParameter(0, 10, default=5, space='sell') + sell_h_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + sell_h_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') + sell_h_profit_percent10 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') + + sell_h_RSI = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_h_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + # protection_max_allowed_dd = DecimalParameter(0, 1, decimals=2, default=0.04, space='protection') + # protection_stop = IntParameter(0, 100, default=48, space='protection') + # protection_stoploss_stop = IntParameter(0, 100, default=48, space='protection') + # lookback = IntParameter(0, 200, default=48, space='protection') + # trade_limit = IntParameter(0, 10, default=2, space='protection') + + protection_down_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='protection') + protection_down_percent3 = DecimalParameter(0, 0.05, decimals=2, default=0.02, space='protection') + protection_down_percent5 = DecimalParameter(0, 0.05, decimals=2, default=0.03, space='protection') + protection_up_percent = DecimalParameter(-0.02, 0.02, decimals=3, default=0.0, space='protection') + protection_up_percent3 = DecimalParameter(-0.02, 0.05, decimals=2, default=0.0, space='protection') + protection_up_percent5 = DecimalParameter(-0.02, 0.05, decimals=2, default=0.0, space='protection') + + # def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + # proposed_stake: float, min_stake: float, max_stake: float, + # **kwargs) -> float: + # + # informative, _ = self.dp.get_analyzed_dataframe(pair='BTC/USDT', timeframe=self.timeframe) + # # current_candle = informative.iloc[-1].squeeze() + # + # current = informative.tail(1).iloc[0]['close'] + # # 50000 => 2 30000 => 20 + # if current > 50000: + # self.max_open_trades = 2 + # proposed_stake = self.config['stake_amount'] / 2 + # else: + # if current > 32000: + # self.max_open_trades = 2 + int((50000 - current) / 1000) + # proposed_stake = self.config['stake_amount'] / 2 \ + # + self.config['stake_amount'] * self.max_open_trades / self.config['max_open_trades'] + # else: + # self.max_open_trades = self.config['max_open_trades'] + # proposed_stake = self.config['stake_amount'] + # + # return min(max_stake, proposed_stake) + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + # { + # "method": "MaxDrawdown", + # "lookback_period_candles": self.lookback.value, + # "trade_limit": self.trade_limit.value, + # "stop_duration_candles": self.protection_stop.value, + # "max_allowed_drawdown": self.protection_max_allowed_dd.value, + # "only_per_pair": False + # } + ] + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + allow_to_buy = True + + # informative, _ = self.dp.get_analyzed_dataframe(pair='BTC/USDT', timeframe=self.timeframe) + # info_last_candle = informative.iloc[-1].squeeze() + # + # if (self.stop_buying is True) & ( + # (info_last_candle['percent'] > self.protection_up_percent.value) + # | (info_last_candle['percent3'] > self.protection_up_percent3.value) + # | (info_last_candle['percent5'] > self.protection_up_percent5.value)): + # # print("Enable buying") + # self.stop_buying = False + # + # if self.stop_buying: + # allow_to_buy = False + return allow_to_buy + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + expected_profit = 0.01 + days = (current_time - trade.open_date_utc).days + ###### + + # informative, _ = self.dp.get_analyzed_dataframe(pair='BTC/USDT', timeframe=self.timeframe) + # info_last_candle = informative.iloc[-1].squeeze() + # + # if self.stop_buying is True: + # if (info_last_candle['percent'] > 0) | (info_last_candle['percent3'] > 0) | ( + # info_last_candle['percent5'] > 0): + # # print("Enable buying") + # self.stop_buying = False + # else: + # if self.stop_buying is False: + # if (info_last_candle['percent'] < - self.protection_down_percent.value) \ + # | (info_last_candle['percent3'] < - self.protection_down_percent3.value) \ + # | (info_last_candle['percent5'] < - self.protection_down_percent5.value): + # self.stop_buying = True + # # print("Disable buying", info_last_candle['percent'], info_last_candle['percent3'], info_last_candle['percent5']) + # return 'send_all' + + if (last_candle['pct_change_1_1h'] < 0): + if (current_profit > 0.015) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | ( + last_candle['percent5'] < -0.005)): + return 'b_percent_quick' + + if (current_profit > last_candle['bb_width'] / 2) & (previous_last_candle['close'] > previous_last_candle['bb_upperband'])\ + & (last_candle['percent'] < -0.005) & ((current_time - trade.open_date_utc).seconds <= 3600): + return 'b_bb_width' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value) \ + & (days < self.sell_b_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2) \ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + if (current_profit > 0.025) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | ( + last_candle['percent5'] < -0.005)): + return 'h_percent_quick' + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value) \ + & (days < self.sell_h_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2) \ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_h_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle[ + 'max200']): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + informative_pairs = [('BTC/USDT', '1h')] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['rsi'] = talib.RSI(dataframe) + + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib - tib.min()) / (tib.max() - tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd - tkd.min()) / (tkd.max() - tkd.min()) + + ################### INFORMATIVE BTC 1H + informative = self.dp.get_pair_dataframe(pair='BTC/USDT', timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative['sma7'] = talib.SMA(informative, timeperiod=7) + informative['pct_change_1'] = informative['close'].pct_change(1) + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= self.buy_base.value) + & (dataframe['rsi'] < self.buy_rsi.value) + & (dataframe['close'] < dataframe['sma10']) + # & (dataframe['sma7_1h'].shift(1) <= dataframe['sma7_1h']) + # & (dataframe['close_1h'] <= dataframe['bb_middleband_1h']) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe diff --git a/Ishimoku_6.json b/Ishimoku_6.json new file mode 100644 index 0000000..950bd05 --- /dev/null +++ b/Ishimoku_6.json @@ -0,0 +1,77 @@ +{ + "strategy_name": "Ishimoku_6", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_base": 0.08, + "buy_rsi": 70 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": true, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": true, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 85, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2, + "sell_h_too_old_percent": 0.01, + "sell_rsi5_1h": 41 + }, + "protection": { + "btc_allow_to_buy": 0.0, + "btc_fall_1": -0.02, + "btc_fall_3": -0.1, + "btc_fall_5": -0.04, + "btc_sell_all_profit": -0.42 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-05-13 11:54:30.328274+00:00" +} \ No newline at end of file diff --git a/Ishimoku_6.py b/Ishimoku_6.py new file mode 100644 index 0000000..8821562 --- /dev/null +++ b/Ishimoku_6.py @@ -0,0 +1,728 @@ +# +# +# +from datetime import timedelta, datetime +from typing import Optional +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame +import logging +from technical import pivots_points +# -------------------------------- + +# Add your lib to import here +import ta +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +logger = logging.getLogger(__name__) + + +class Ishimoku_6(IStrategy): + # ROI table: + minimal_roi = { + "0": 0.564, + "567": 0.273, + "2814": 0.12, + "7675": 0 + } + + # Stoploss: + stoploss = -0.256 + + # Buy hypers + timeframe = '4h' + + stop_buying = {} + max_open_trades = 5 + stop_buy_for_all = False + plot_config = { + "main_plot": { + "min200": { + "color": "#86c932" + }, + "max50": { + "color": "white" + }, + "max200": { + "color": "yellow" + }, + "bb_lowerband": { + "color": "#da59a6" + }, + "bb_upperband": { + "color": "#da59a6" + }, + "sar": { + "color": "#4f9f51" + }, + "enter_tag": { + "color": "#b400c7", + "type": "line" + }, + "sma10": { + "color": "#271db5", + "type": "line" + }, + "r1": { + "color": "#3181d9", + "type": "line" + }, + "r2": { + "color": "#e31837", + "type": "line" + }, + "r3": { + "color": "#a0f99b", + "type": "line" + }, + "s1": { + "color": "#ee7d7d", + "type": "line" + }, + "s2": { + "color": "#740346", + "type": "line" + }, + "s3": { + "color": "#ff2a10", + "type": "line" + } + }, + "subplots": { + "Ind": { + "trend_ichimoku_base": { + "color": "#dd1384" + }, + "trend_kst_diff": { + "color": "#850678" + }, + "ichimoku_conversion_line": { + "color": "#83a12a", + "type": "line" + } + }, + "BB": { + "ichimoku_cut_below_1h": { + "color": "#9ee22c", + "type": "line" + }, + "ichimoku_cut_above_1h": { + "color": "#aa2a1f", + "type": "line" + } + }, + "Cond": { + "cond1": { + "color": "yellow" + } + }, + "Rsi": { + "rsi": { + "color": "pink" + }, + "rsi_5_1h": { + "color": "#34e97f", + "type": "line" + }, + "rsi_1h": { + "color": "#3ce8ea", + "type": "line" + }, + "rsi5": { + "color": "#47f377", + "type": "line" + } + }, + "Percent": { + "percent5": { + "color": "#2250f1", + "type": "line" + }, + "percent10": { + "color": "#e02bbb", + "type": "line" + } + } + } + } + trades = list() + + buy_base = DecimalParameter(0, 0.2, decimals=2, default=0.10, space='buy') + buy_rsi = IntParameter(20, 80, default=70, space='buy') + + profit_b_no_change = BooleanParameter(default=True, space="sell") + profit_b_quick_lost = BooleanParameter(default=True, space="sell") + profit_b_sma5 = BooleanParameter(default=True, space="sell") + profit_b_sma10 = BooleanParameter(default=True, space="sell") + profit_b_sma20 = BooleanParameter(default=True, space="sell") + profit_b_quick_gain = BooleanParameter(default=True, space="sell") + profit_b_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_b_old_sma10 = BooleanParameter(default=True, space="sell") + profit_b_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_b_over_rsi = BooleanParameter(default=True, space="sell") + profit_b_short_loss = BooleanParameter(default=True, space="sell") + + sell_b_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_b_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_b_candels = IntParameter(0, 48, default=12, space='sell') + + sell_b_too_old_day = IntParameter(0, 10, default=5, space='sell') + sell_b_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + sell_b_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') + sell_b_profit_percent10 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') + + sell_b_RSI = IntParameter(70, 98, default=88, space='sell') + sell_b_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_b_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_b_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_rsi5_1h = IntParameter(30, 80, default=41, space='sell') + # sell_b_expected_profit = DecimalParameter(0, 0.01, decimals=3, default=0.01, space='sell') + + profit_h_no_change = BooleanParameter(default=True, space="sell") + profit_h_quick_lost = BooleanParameter(default=True, space="sell") + profit_h_sma5 = BooleanParameter(default=True, space="sell") + profit_h_sma10 = BooleanParameter(default=True, space="sell") + profit_h_sma20 = BooleanParameter(default=True, space="sell") + profit_h_quick_gain = BooleanParameter(default=True, space="sell") + profit_h_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_h_old_sma10 = BooleanParameter(default=True, space="sell") + profit_h_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_h_over_rsi = BooleanParameter(default=True, space="sell") + profit_h_short_loss = BooleanParameter(default=True, space="sell") + + sell_h_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_candels = IntParameter(0, 48, default=12, space='sell') + + sell_h_too_old_day = IntParameter(0, 10, default=5, space='sell') + sell_h_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + sell_h_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') + sell_h_profit_percent10 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') + + sell_h_RSI = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_h_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + btc_fall_1 = DecimalParameter(-0.1, 0, decimals=2, default=0.0, space='protection') + btc_fall_3 = DecimalParameter(-0.1, 0, decimals=2, default=0.0, space='protection') + btc_fall_5 = DecimalParameter(-0.1, 0, decimals=2, default=0.0, space='protection') + btc_sell_all_profit = DecimalParameter(-0.5, 0, decimals=2, default=0.0, space='protection') + btc_allow_to_buy = DecimalParameter(-0.02, 0.05, decimals=2, default=0.0, space='protection') + + def bot_loop_start(self, **kwargs) -> None: + inf_tf = '5m' + pairs = self.dp.current_whitelist() + print("Calcul des pairs informatives") + for pairname in pairs: + self.stop_buying[pairname] = True + print("Fin Calcul des pairs informatives") + + # def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + # proposed_stake: float, min_stake: float, max_stake: float, + # **kwargs) -> float: + # + # informative, _ = self.dp.get_analyzed_dataframe(pair='BTC/USDT', timeframe=self.timeframe) + # # current_candle = informative.iloc[-1].squeeze() + # + # current = informative.tail(1).iloc[0]['close'] + # # 50000 => 2 30000 => 20 + # if current > 50000: + # self.max_open_trades = 2 + # proposed_stake = self.config['stake_amount'] / 2 + # else: + # if current > 32000: + # self.max_open_trades = 2 + int((50000 - current) / 1000) + # proposed_stake = self.config['stake_amount'] / 2 \ + # + self.config['stake_amount'] * self.max_open_trades / self.config['max_open_trades'] + # else: + # self.max_open_trades = self.config['max_open_trades'] + # proposed_stake = self.config['stake_amount'] + # + # return min(max_stake, proposed_stake) + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + # { + # "method": "MaxDrawdown", + # "lookback_period_candles": self.lookback.value, + # "trade_limit": self.trade_limit.value, + # "stop_duration_candles": self.protection_stop.value, + # "max_allowed_drawdown": self.protection_max_allowed_dd.value, + # "only_per_pair": False + # } + ] + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + allow_to_buy = True + informative, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + info_previous_last_candle = informative.iloc[-2].squeeze() + + if self.stop_buy_for_all is True: + if info_last_candle['pct_change_1_1d'] > self.btc_allow_to_buy.value: + self.stop_buy_for_all = False + logger.info("1 -BUYING IS ENABLED %s date %s", pair, info_last_candle['date']) + else: + logger.info("1 -BUYING IS BLOCKED BY BTC FALL %s date %s", pair, info_last_candle['date']) + return False + + if self.stop_buying.get(pair, None) is None: + print("enable buying tag", pair) + self.stop_buying[pair] = False + # if (info_last_candle['rsi_1h'] < self.protection_RSI_enable.value) & (self.stop_buying[pair] is True): + # print("Enable buying", pair) + # self.stop_buying[pair] = False + + # if self.stop_buying[pair] is True: + # if (info_last_candle['rsi_5_1h'] > 20) & (info_previous_last_candle['rsi_5_1h'] <= info_last_candle['rsi_5_1h']) \ + # & (info_last_candle['max_rsi_1h'] < 50): + # print("2 - Enable buying", pair, info_last_candle['date'], info_last_candle['rsi_5_1h'], info_last_candle['max_rsi_1h']) + # self.stop_buying[pair] = False + + if self.stop_buying[pair] is True: + if (info_last_candle['rsi5'] > 20) & (info_last_candle['rsi'] > 30): + # print("1 - Enable buying ", pair, info_last_candle['date'], info_last_candle['rsi5']) + logger.info("1 - Enable buying %s date %s", pair, info_last_candle['date']) + + self.stop_buying[pair] = False + + if self.stop_buying[pair]: + allow_to_buy = False + logger.info("3 - cancel buying %s date %s", pair, info_last_candle['date']) + else: + logger.info("3 - accept buying %s date %s", pair, info_last_candle['date']) + # , (info_last_candle['trend_ichimoku_base'], + # (info_last_candle['close'] < info_last_candle['sma10']), + # (info_previous_last_candle['sma10'], info_last_candle['sma10']))) + + return allow_to_buy + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + if (self.stop_buy_for_all is True) & (current_profit < self.btc_sell_all_profit.value): + return "btc_fall" + + expected_profit = 0.01 + days = (current_time - trade.open_date_utc).days + ###### + + btc, _ = self.dp.get_analyzed_dataframe(pair="BTC/USDT", timeframe=self.timeframe) + btc_last_candle = btc.iloc[-1].squeeze() + btc_previous_last_candle = btc.iloc[-2].squeeze() + + informative, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + info_previous_last_candle = informative.iloc[-2].squeeze() + + if self.stop_buying.get(pair, None) is None: + print("enable buying tag", pair) + self.stop_buying[pair] = False + + if self.stop_buying[pair] is False: + if (last_candle['rsi5'] < 16): + logger.info("0 - Disable buying %s date=%s rsi=%s", pair, last_candle['date'], last_candle['rsi5']) + self.stop_buying[pair] = True + if (current_profit > 0): + return "stop_buying" + + if self.stop_buying[pair] is True: + if (last_candle['rsi5'] > 20) & (last_candle['percent10'] > 0): + logger.info("1 - Enable buying %s date=%s rsi=%s", pair, last_candle['date'], last_candle['rsi5']) + # print("1 - Enable buying ", pair, last_candle['date'], last_candle['rsi5']) + self.stop_buying[pair] = False + + + if (btc_last_candle['percent5'] < self.btc_fall_5.value) | (btc_last_candle['percent3'] < self.btc_fall_3.value) | ( + btc_last_candle['percent'] < self.btc_fall_1.value): + self.stop_buy_for_all = True + return "btc_fall" + + if last_candle['rsi_5_1h'] < self.sell_rsi5_1h.value: + if (current_profit > 0.01) & ((last_candle['percent'] < -0.003) | (last_candle['percent3'] < -0.003) | ( + last_candle['percent5'] < -0.003) | (last_candle['rsi5'] < 41)): + return 'b_percent_quick' + + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value) \ + & (days < self.sell_b_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2) \ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > last_candle['bb_width'] / 1.3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle['percent'] < self.sell_b_RSI2_percent.value): + # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) \ + & (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + if (current_profit > 0.025) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | ( + last_candle['percent5'] < -0.005)): + return 'h_percent_quick' + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value) \ + & (days < self.sell_h_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2) \ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > last_candle['bb_width'] / 0.8) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): + # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent'] < - self.sell_h_RSI2_percent.value): + # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle[ + 'max200']): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + informative_pairs = [('BTC/USDT', '1d')] + pairs = self.dp.current_whitelist() + informative_pairs += [(pair, '1h') for pair in pairs] + # self.stop_buying[pair] = [(pair, False) for pair in pairs] + # print(self.stop_buying) + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['ichimoku_conversion_line'] = ta.trend.ichimoku_conversion_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min5'] = talib.MIN(dataframe['close'], timeperiod=5) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma5_ma_2'] = dataframe['sma5'].pct_change(2) + dataframe['sma5_ma_5'] = dataframe['sma5'].pct_change(5) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + # # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + dataframe['ichimoku_conversion_line_1'] = dataframe['ichimoku_conversion_line'] + dataframe['trend_ichimoku_base_1'] = dataframe['trend_ichimoku_base'] + dataframe['trend_kst_diff_1'] = dataframe['trend_kst_diff'] + + tib = dataframe['ichimoku_conversion_line'] + dataframe['ichimoku_conversion_line'] = (tib - tib.min()) / (tib.max() - tib.min()) + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib - tib.min()) / (tib.max() - tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd - tkd.min()) / (tkd.max() - tkd.min()) + + pivot = pivots_points.pivots_points(dataframe, 72, 3) + dataframe['r1'] = pivot['r1'] + dataframe['r2'] = pivot['r2'] + dataframe['r3'] = pivot['r3'] + dataframe['s1'] = pivot['s1'] + dataframe['s2'] = pivot['s2'] + dataframe['s3'] = pivot['s3'] + + ################### INFORMATIVE BTC 1H + informative = self.dp.get_pair_dataframe(pair="BTC/USDT", timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["rsi_5"] = talib.RSI(informative, timeperiod=5) + informative['pct_change_1'] = informative['close'].pct_change(1) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ################### INFORMATIVE BTC 1H + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative["rsi_5"] = talib.RSI(informative, timeperiod=5) + informative["rsi_ma"] = informative["rsi"].rolling(5).mean() + informative['max_rsi'] = talib.MAX(informative['rsi'], timeperiod=10) + informative['min_rsi'] = talib.MIN(informative['rsi'], timeperiod=10) + informative['sma7'] = talib.SMA(informative, timeperiod=7) + informative['pct_change_1'] = informative['close'].pct_change(1) + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + informative['ichimoku_conversion_line'] = ta.trend.ichimoku_conversion_line( + informative['high'], + informative['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + + informative['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + informative['high'], + informative['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + informative['ichimoku_conversion_line_1'] = informative['ichimoku_conversion_line'] + informative['trend_ichimoku_base_1'] = informative['trend_ichimoku_base'] + + informative['ichimoku_cut_below'] = qtpylib.crossed_below(informative['ichimoku_conversion_line_1'], + informative['trend_ichimoku_base_1']) + informative['ichimoku_cut_above'] = qtpylib.crossed_above(informative['ichimoku_conversion_line_1'], + informative['trend_ichimoku_base_1']) + + tib = informative['ichimoku_conversion_line'] + informative['ichimoku_conversion_line'] = (tib - tib.min()) / (tib.max() - tib.min()) + tib = informative['trend_ichimoku_base'] + informative['trend_ichimoku_base'] = (tib - tib.min()) / (tib.max() - tib.min()) + # tkd = informative['trend_kst_diff'] + # informative['trend_kst_diff'] = (tkd - tkd.min()) / (tkd.max() - tkd.min()) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['trend_ichimoku_base'] <= 0.03) #self.buy_base.value) + # & (dataframe['rsi'] < self.buy_rsi.value) + # & (dataframe['close'] < dataframe['sma10']) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + # & (dataframe['pct_change_1_1h'] < 0) + # # & (dataframe['close'] <= dataframe['min50'] * (1 + dataframe['bb_width'] / 1.8)) + # ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku_b') + + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= 0.08) # self.buy_base.value) + # & (dataframe['rsi'] < 42) #self.buy_rsi.value) + # & (dataframe['sma5_ma_2'] > - 0.002) + & (dataframe['close'].shift(2) < dataframe['sma10'].shift(2)) + & (dataframe['close'].shift(2) < dataframe['sar'].shift(2)) + & (dataframe['open'].shift(2) < dataframe['sar'].shift(2)) + # & (dataframe['close'] < dataframe['bb_lowerband'] + dataframe['bb_width'] / 3) + & (dataframe['sma10'].shift(1) * 1.001 < dataframe['sma10']) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + # & (dataframe['rsi_5_1h'] > 35) + # & (dataframe['close'] <= dataframe['min50'] * (1 + dataframe['bb_width'] / 1.8)) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku_h') + + # dataframe.loc[ + # ( + # (dataframe['trend_ichimoku_base'] <= 0.08) + # & (dataframe['open'] == dataframe['min50']) + # & (dataframe['min50'].shift(4) == dataframe['min50']) + # ), ['buy', 'buy_tag']] = (1, 'buy_test') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe diff --git a/Ishimoku_6Best.json b/Ishimoku_6Best.json new file mode 100644 index 0000000..2b8c44f --- /dev/null +++ b/Ishimoku_6Best.json @@ -0,0 +1,81 @@ +{ + "strategy_name": "Ishimoku_6", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_base": 0.05, + "buy_rsi": 42 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2, + "sell_h_too_old_percent": 0.01 + }, + "protection": { + "bb_width_force_sell": 1.0, + "my_stoploss_percent": 0.38, + "my_stoploss_profit": 0.28, + "protection_down_percent": 0.019, + "protection_down_percent3": 0.05, + "protection_down_percent5": 0.02, + "protection_up_percent": -0.013, + "protection_up_percent3": -0.02, + "protection_up_percent5": -0.01, + "send_all_percent": 0.46 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-04-30 14:34:22.357711+00:00" +} \ No newline at end of file diff --git a/Ishimoku_7.json b/Ishimoku_7.json new file mode 100644 index 0000000..198d8e6 --- /dev/null +++ b/Ishimoku_7.json @@ -0,0 +1,70 @@ +{ + "strategy_name": "Ishimoku_7", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_base": 0.08, + "buy_rsi": 78 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": true, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": true, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 85, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2, + "sell_h_too_old_percent": 0.01 + }, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2022-05-07 18:58:24.628568+00:00" +} \ No newline at end of file diff --git a/Ishimoku_7.py b/Ishimoku_7.py new file mode 100644 index 0000000..0610082 --- /dev/null +++ b/Ishimoku_7.py @@ -0,0 +1,752 @@ +# +# +# +from datetime import timedelta, datetime +from typing import Optional +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame +import logging +from technical import pivots_points +# -------------------------------- + +# Add your lib to import here +import ta +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +logger = logging.getLogger(__name__) + + +class Ishimoku_7(IStrategy): + + # ROI table: + minimal_roi = { + "0": 0.564, + "567": 0.273, + "2814": 0.12, + "7675": 0 + } + + # Stoploss: + stoploss = -0.256 + + # Buy hypers + timeframe = '4h' + + stop_buying = {} + max_open_trades = 5 + + plot_config = { + "main_plot": { + "min200": { + "color": "#86c932" + }, + "max50": { + "color": "white" + }, + "max200": { + "color": "yellow" + }, + "bb_lowerband": { + "color": "#da59a6" + }, + "bb_upperband": { + "color": "#da59a6" + }, + "sar": { + "color": "#4f9f51" + }, + "enter_tag": { + "color": "#b400c7", + "type": "line" + }, + "sma10": { + "color": "#271db5", + "type": "line" + }, + "r1": { + "color": "#3181d9", + "type": "line" + }, + "r2": { + "color": "#e31837", + "type": "line" + }, + "r3": { + "color": "#a0f99b", + "type": "line" + }, + "s1": { + "color": "#ee7d7d", + "type": "line" + }, + "s2": { + "color": "#740346", + "type": "line" + }, + "s3": { + "color": "#ff2a10", + "type": "line" + } + }, + "subplots": { + "Ind": { + "trend_ichimoku_base": { + "color": "#dd1384" + }, + "trend_kst_diff": { + "color": "#850678" + }, + "ichimoku_conversion_line": { + "color": "#83a12a", + "type": "line" + } + }, + "BB": { + "ichimoku_cut_below_1h": { + "color": "#9ee22c", + "type": "line" + }, + "ichimoku_cut_above_1h": { + "color": "#aa2a1f", + "type": "line" + } + }, + "Cond": { + "cond1": { + "color": "yellow" + } + }, + "Rsi": { + "rsi": { + "color": "pink" + }, + "rsi_5_1h": { + "color": "#34e97f", + "type": "line" + }, + "rsi_1h": { + "color": "#3ce8ea", + "type": "line" + }, + "rsi5": { + "color": "#47f377", + "type": "line" + } + }, + "Percent": { + "percent5": { + "color": "#2250f1", + "type": "line" + }, + "percent10": { + "color": "#e02bbb", + "type": "line" + } + } + } +} + trades = list() + + buy_base = DecimalParameter(0, 0.2, decimals=2, default=0.10, space='buy') + buy_rsi = IntParameter(20, 80, default=70, space='buy') + + profit_b_no_change = BooleanParameter(default=True, space="sell") + profit_b_quick_lost = BooleanParameter(default=True, space="sell") + profit_b_sma5 = BooleanParameter(default=True, space="sell") + profit_b_sma10 = BooleanParameter(default=True, space="sell") + profit_b_sma20 = BooleanParameter(default=True, space="sell") + profit_b_quick_gain = BooleanParameter(default=True, space="sell") + profit_b_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_b_old_sma10 = BooleanParameter(default=True, space="sell") + profit_b_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_b_over_rsi = BooleanParameter(default=True, space="sell") + profit_b_short_loss = BooleanParameter(default=True, space="sell") + + sell_b_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_b_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_b_candels = IntParameter(0, 48, default=12, space='sell') + + sell_b_too_old_day = IntParameter(0, 10, default=5, space='sell') + sell_b_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + sell_b_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') + sell_b_profit_percent10 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') + + sell_b_RSI = IntParameter(70, 98, default=88, space='sell') + sell_b_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_b_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_b_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_rsi5_1h = IntParameter(30, 80, default=41, space='sell') + # sell_b_expected_profit = DecimalParameter(0, 0.01, decimals=3, default=0.01, space='sell') + + profit_h_no_change = BooleanParameter(default=True, space="sell") + profit_h_quick_lost = BooleanParameter(default=True, space="sell") + profit_h_sma5 = BooleanParameter(default=True, space="sell") + profit_h_sma10 = BooleanParameter(default=True, space="sell") + profit_h_sma20 = BooleanParameter(default=True, space="sell") + profit_h_quick_gain = BooleanParameter(default=True, space="sell") + profit_h_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_h_old_sma10 = BooleanParameter(default=True, space="sell") + profit_h_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_h_over_rsi = BooleanParameter(default=True, space="sell") + profit_h_short_loss = BooleanParameter(default=True, space="sell") + + sell_h_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_candels = IntParameter(0, 48, default=12, space='sell') + + sell_h_too_old_day = IntParameter(0, 10, default=5, space='sell') + sell_h_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + sell_h_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') + sell_h_profit_percent10 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') + + sell_h_RSI = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_h_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + # protection_max_allowed_dd = DecimalParameter(0, 1, decimals=2, default=0.04, space='protection') + # protection_stop = IntParameter(0, 100, default=48, space='protection') + # protection_stoploss_stop = IntParameter(0, 100, default=48, space='protection') + # lookback = IntParameter(0, 200, default=48, space='protection') + # trade_limit = IntParameter(0, 10, default=2, space='protection') + + # protection_down_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='protection') + # protection_down_percent3 = DecimalParameter(0, 0.05, decimals=2, default=0.02, space='protection') + # protection_down_percent5 = DecimalParameter(0, 0.05, decimals=2, default=0.03, space='protection') + # protection_up_percent = DecimalParameter(-0.02, 0.02, decimals=3, default=0.0, space='protection') + # protection_up_percent3 = DecimalParameter(-0.02, 0.05, decimals=2, default=0.0, space='protection') + # protection_up_percent5 = DecimalParameter(-0.02, 0.05, decimals=2, default=0.0, space='protection') + + # protection_RSI_enable = IntParameter(10, 50, default=30, space='protection') + # protection_RSI_disable = IntParameter(50, 90, default=65, space='protection') + + # send_all_percent = DecimalParameter(0, 0.5, decimals=2, default=0.0, space='protection') + # bb_width_force_sell = DecimalParameter(1, 3, decimals=1, default=0.0, space='protection') + # my_stoploss_percent = DecimalParameter(0, 0.5, decimals=2, default=0.01, space='protection') + # my_stoploss_profit = DecimalParameter(0, 0.5, decimals=2, default=0, space='protection') + + def bot_loop_start(self, **kwargs) -> None: + inf_tf = '5m' + pairs = self.dp.current_whitelist() + # print( "Calcul des pairs informatives") + for pairname in pairs: + self.stop_buying[pairname] = True + # print( "Fin Calcul des pairs informatives") + + # def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + # proposed_stake: float, min_stake: float, max_stake: float, + # **kwargs) -> float: + # + # informative, _ = self.dp.get_analyzed_dataframe(pair='BTC/USDT', timeframe=self.timeframe) + # # current_candle = informative.iloc[-1].squeeze() + # + # current = informative.tail(1).iloc[0]['close'] + # # 50000 => 2 30000 => 20 + # if current > 50000: + # self.max_open_trades = 2 + # proposed_stake = self.config['stake_amount'] / 2 + # else: + # if current > 32000: + # self.max_open_trades = 2 + int((50000 - current) / 1000) + # proposed_stake = self.config['stake_amount'] / 2 \ + # + self.config['stake_amount'] * self.max_open_trades / self.config['max_open_trades'] + # else: + # self.max_open_trades = self.config['max_open_trades'] + # proposed_stake = self.config['stake_amount'] + # + # return min(max_stake, proposed_stake) + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 10 + }, + # { + # "method": "MaxDrawdown", + # "lookback_period_candles": self.lookback.value, + # "trade_limit": self.trade_limit.value, + # "stop_duration_candles": self.protection_stop.value, + # "max_allowed_drawdown": self.protection_max_allowed_dd.value, + # "only_per_pair": False + # } + ] + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + allow_to_buy = True + + informative, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + info_previous_last_candle = informative.iloc[-2].squeeze() + + if self.stop_buying.get(pair, None) is None: + # print("enable buying tag", pair) + self.stop_buying[pair] = False + # if (info_last_candle['rsi_1h'] < self.protection_RSI_enable.value) & (self.stop_buying[pair] is True): + # print("Enable buying", pair) + # self.stop_buying[pair] = False + + #if self.stop_buying[pair] is True: + # if (info_last_candle['rsi_5_1h'] > 20) & (info_previous_last_candle['rsi_5_1h'] <= info_last_candle['rsi_5_1h']) \ + # & (info_last_candle['max_rsi_1h'] < 50): + # print("2 - Enable buying", pair, info_last_candle['date'], info_last_candle['rsi_5_1h'], info_last_candle['max_rsi_1h']) + # self.stop_buying[pair] = False + + if self.stop_buying[pair] is True: + if (info_last_candle['rsi5'] > 20) & (info_last_candle['rsi'] > 30): + # print("1 - Enable buying ", pair, info_last_candle['date'], info_last_candle['rsi5']) + self.stop_buying[pair] = False + + if self.stop_buying[pair]: + allow_to_buy = False + # print("3 - cancel buying", pair, info_last_candle['date']) + # else: + # print("3 - accept buying", pair, info_last_candle['date'], (info_last_candle['trend_ichimoku_base'], + # (info_last_candle['close'] < info_last_candle['sma10']), + # (info_previous_last_candle['sma10'], info_last_candle['sma10']))) + + return allow_to_buy + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + expected_profit = 0.01 + days = (current_time - trade.open_date_utc).days + ###### + + btc, _ = self.dp.get_analyzed_dataframe(pair="BTC/USDT", timeframe=self.timeframe) + btc_last_candle = btc.iloc[-1].squeeze() + btc_previous_last_candle = btc.iloc[-2].squeeze() + + informative, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + info_previous_last_candle = informative.iloc[-2].squeeze() + + if self.stop_buying.get(pair, None) is None: + # print("enable buying tag", pair) + self.stop_buying[pair] = False + + if self.stop_buying[pair] is False: + if (last_candle['rsi5'] < 16): + # print("0 - Disable buying", pair, last_candle['date'], last_candle['rsi5']) + self.stop_buying[pair] = True + if (current_profit > 0): + return "stop_buying" + + if self.stop_buying[pair] is True: + if (last_candle['rsi5'] > 20) & (last_candle['percent10'] > 0): + # print("1 - Enable buying ", pair, last_candle['date'], last_candle['rsi5']) + self.stop_buying[pair] = False + + # if self.stop_buying[pair] is False: + # if ((info_previous_last_candle['rsi_5_1h'] > 78) & (info_last_candle['pct_change_1_1h'] < - 0.005)) | \ + # (info_last_candle['rsi_5_1h'] > 88): + # print("0 - Disable buying", pair, info_last_candle['date'], info_previous_last_candle['rsi_5_1h'], info_last_candle['rsi_5_1h'], info_last_candle['max_rsi_1h']) + # self.stop_buying[pair] = True + # + # if self.stop_buying[pair] is True: + # if (info_last_candle['rsi_5_1h'] < 30) & (info_previous_last_candle['rsi_5_1h'] <= info_last_candle['rsi_5_1h']): + # print("1 - Enable buying ", pair, info_last_candle['date'], info_previous_last_candle['rsi_5_1h'], info_last_candle['rsi_5_1h'], info_last_candle['max_rsi_1h']) + # self.stop_buying[pair] = False + + # if self.stop_buying is True: + # if (info_last_candle['percent'] > self.protection_up_percent.value) \ + # & (info_last_candle['percent3'] > self.protection_up_percent3.value): + # # print("Enable buying") + # self.stop_buying = False + # else: + # if self.stop_buying is False: + # if (info_last_candle['percent'] < - self.protection_down_percent.value) \ + # | (info_last_candle['percent3'] < - self.protection_down_percent3.value) \ + # | (info_last_candle['percent5'] < - self.protection_down_percent5.value): + # self.stop_buying = True + # #print("Disable buying", info_last_candle['percent'], info_last_candle['percent3'], info_last_candle['percent5']) + # # if (current_profit < - self.send_all_percent.value): + # return 'send_all' + + #if (current_profit < -0.08) & (last_candle['rsi5'] < 10) & (btc_last_candle['close'] > 30000): + # return "send_rsi5"; + + # if (current_profit < - self.my_stoploss_profit.value) & (info_last_candle['percent20'] < - self.my_stoploss_percent.value): + # return 'my_stoploss' + + if last_candle['rsi_5_1h'] < self.sell_rsi5_1h.value: + if (current_profit > 0.01) & ((last_candle['percent'] < -0.003) | (last_candle['percent3'] < -0.003) | ( + last_candle['percent5'] < -0.003) | (last_candle['rsi5'] < 41)): + return 'b_percent_quick' + + #if (current_profit > last_candle['bb_width'] / self.bb_width_force_sell.value)\ + # & (last_candle['percent5'] < 0): #& ((current_time - trade.open_date_utc).seconds <= 3600): + # return 'b_bb_width' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value) \ + & (days < self.sell_b_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2) \ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > last_candle['bb_width'] / 1.3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle['percent'] < - self.sell_b_RSI2_percent.value): + # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) \ + & (last_candle['percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + if (current_profit > 0.025) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | ( + last_candle['percent5'] < -0.005)): + return 'h_percent_quick' + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value) \ + & (days < self.sell_h_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2) \ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > last_candle['bb_width'] / 0.8) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): + # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent'] < - self.sell_h_RSI2_percent.value): + # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # informative_pairs = [('BTC/USDT', '1h')] + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1h') for pair in pairs] + #self.stop_buying[pair] = [(pair, False) for pair in pairs] + #print(self.stop_buying) + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['ichimoku_conversion_line'] = ta.trend.ichimoku_conversion_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min5'] = talib.MIN(dataframe['close'], timeperiod=5) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + dataframe['ichimoku_conversion_line_1'] = dataframe['ichimoku_conversion_line'] + dataframe['trend_ichimoku_base_1'] = dataframe['trend_ichimoku_base'] + dataframe['trend_kst_diff_1'] = dataframe['trend_kst_diff'] + + tib = dataframe['ichimoku_conversion_line'] + dataframe['ichimoku_conversion_line'] = (tib - tib.min()) / (tib.max() - tib.min()) + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib - tib.min()) / (tib.max() - tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd - tkd.min()) / (tkd.max() - tkd.min()) + + pivot = pivots_points.pivots_points(dataframe, 72, 3) + dataframe['r1'] = pivot['r1'] + dataframe['r2'] = pivot['r2'] + dataframe['r3'] = pivot['r3'] + dataframe['s1'] = pivot['s1'] + dataframe['s2'] = pivot['s2'] + dataframe['s3'] = pivot['s3'] + + ################### INFORMATIVE BTC 1H + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative["rsi_5"] = talib.RSI(informative, timeperiod=5) + informative["rsi_ma"] = informative["rsi"].rolling(5).mean() + informative['max_rsi'] = talib.MAX(informative['rsi'], timeperiod=10) + informative['min_rsi'] = talib.MIN(informative['rsi'], timeperiod=10) + informative['sma7'] = talib.SMA(informative, timeperiod=7) + informative['pct_change_1'] = informative['close'].pct_change(1) + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + informative['ichimoku_conversion_line'] = ta.trend.ichimoku_conversion_line( + informative['high'], + informative['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + + informative['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + informative['high'], + informative['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + informative['ichimoku_conversion_line_1'] = informative['ichimoku_conversion_line'] + informative['trend_ichimoku_base_1'] = informative['trend_ichimoku_base'] + + informative['ichimoku_cut_below'] = qtpylib.crossed_below(informative['ichimoku_conversion_line_1'], informative['trend_ichimoku_base_1']) + informative['ichimoku_cut_above'] = qtpylib.crossed_above(informative['ichimoku_conversion_line_1'], informative['trend_ichimoku_base_1']) + + tib = informative['ichimoku_conversion_line'] + informative['ichimoku_conversion_line'] = (tib - tib.min()) / (tib.max() - tib.min()) + tib = informative['trend_ichimoku_base'] + informative['trend_ichimoku_base'] = (tib - tib.min()) / (tib.max() - tib.min()) + # tkd = informative['trend_kst_diff'] + # informative['trend_kst_diff'] = (tkd - tkd.min()) / (tkd.max() - tkd.min()) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['trend_ichimoku_base'] <= 0.03) #self.buy_base.value) + # & (dataframe['rsi'] < self.buy_rsi.value) + # & (dataframe['close'] < dataframe['sma10']) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + # & (dataframe['pct_change_1_1h'] < 0) + # # & (dataframe['close'] <= dataframe['min50'] * (1 + dataframe['bb_width'] / 1.8)) + # ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku_b') + + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= self.buy_base.value) + & (dataframe['rsi_1h'] < self.buy_rsi.value) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['sma10'].shift(1) * 1.001 < dataframe['sma10']) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + # & (dataframe['rsi_5_1h'] > 35) + # & (dataframe['close'] <= dataframe['min50'] * (1 + dataframe['bb_width'] / 1.8)) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku_h') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # conditions = [] + # IND = 'trend_kst_diff' + # REAL = self.sell_real.value + # OPR = self.sell_cat.value + # DFIND = dataframe[IND] + # # print(DFIND.mean()) + # + # if OPR == ">R": + # conditions.append(DFIND > REAL) + # elif OPR == "=R": + # conditions.append(np.isclose(DFIND, REAL)) + # elif OPR == " DataFrame: + + # We will dinamicly generate the indicators + # cuz this method just run one time in hyperopts + # if you have static timeframes you can move first loop of buy and sell trends populators inside this method + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + for i in self.buy_ma_count.range: + dataframe[f"buy-ma-{i+1}"] = ta.SMA( + dataframe, timeperiod=int((i + 1) * self.buy_ma_gap.value) + ) + + conditions = [] + + for i in self.buy_ma_count.range: + if i > 1: + shift = self.buy_ma_shift.value + for shift in self.buy_ma_shift.range: + conditions.append( + dataframe[f"buy-ma-{i}"].shift(shift) + > dataframe[f"buy-ma-{i-1}"].shift(shift) + ) + if conditions: + dataframe.loc[reduce(lambda x, y: x & y, conditions), "buy"] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + for i in self.sell_ma_count.range: + dataframe[f"sell-ma-{i+1}"] = ta.SMA( + dataframe, timeperiod=int((i + 1) * self.sell_ma_gap.value) + ) + + conditions = [] + + for i in self.sell_ma_count.range: + if i > 1: + shift = self.sell_ma_shift.value + for shift in self.sell_ma_shift.range: + conditions.append( + dataframe[f"sell-ma-{i}"].shift(shift) + < dataframe[f"sell-ma-{i-1}"].shift(shift) + ) + if conditions: + dataframe.loc[reduce(lambda x, y: x & y, conditions), "sell"] = 1 + return dataframe diff --git a/MyStrategy.py b/MyStrategy.py new file mode 100644 index 0000000..f428d75 --- /dev/null +++ b/MyStrategy.py @@ -0,0 +1,362 @@ +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame +# -------------------------------- + +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +import numpy as np # noqa + + +def activate(x): + return np.tanh(x) # tanh + +params = { + '0-0-0-w': -0.53814, + '0-0-bias': -0.96407, + '1-0-0-w': -0.49249, + '10-0-0-w': 0.08845, + '11-0-0-w': -0.14317, + '12-0-0-w': 0.00923, + '13-0-0-w': 0.30464, + '14-0-0-w': -0.35835, + '15-0-0-w': -0.49712, + '16-0-0-w': 0.76135, + '17-0-0-w': -0.75257, + '18-0-0-w': -0.04622, + '19-0-0-w': 0.10012, + '2-0-0-w': -0.23534, + '20-0-0-w': -0.04553, + '21-0-0-w': -0.35334, + '22-0-0-w': 0.17952, + '23-0-0-w': 0.44446, + '24-0-0-w': -0.15875, + '25-0-0-w': 0.97565, + '26-0-0-w': -0.89948, + '27-0-0-w': 0.61777, + '28-0-0-w': -0.60204, + '29-0-0-w': -0.85229, + '3-0-0-w': 0.47262, + '30-0-0-w': -0.52791, + '31-0-0-w': 0.98494, + '4-0-0-w': -0.54942, + '5-0-0-w': 0.40523, + '6-0-0-w': 0.4723, + '7-0-0-w': 0.63297, + '8-0-0-w': 0.07159, + '9-0-0-w': -0.86791, + 'adx-bias': -0.48719, + 'ao-bias': -0.87518, + 'aroonosc-bias': -0.56096, + 'bb_percent-bias': -0.98703, + 'bb_width-bias': -0.73742, + 'cci-bias': 0.47039, + 'end-0-w': -0.81658, + 'end-bias': 0.74656, + 'fastd-bias': -0.2793, + 'fisher_rsi_norm-bias': -0.36065, + 'kc_percent-bias': 0.76707, + 'kc_width-bias': 0.5489, + 'macd-bias': 0.55448, + 'macdhist-bias': -0.83133, + 'macdsignal-bias': 0.30828, + 'mfi-bias': -0.13097, + 'roc-bias': -0.78885, + 'rsi-bias': 0.9856, + 'sar-bias': 0.43812, + 'sma10-bias': -0.39019, + 'sma100-bias': 0.03558, + 'sma21-bias': 0.07457, + 'sma3-bias': 0.93633, + 'sma5-bias': -0.93329, + 'sma50-bias': -0.60637, + 'tema10-bias': -0.45946, + 'tema100-bias': 0.1662, + 'tema21-bias': 0.68466, + 'tema3-bias': 0.25368, + 'tema5-bias': -0.88818, + 'tema50-bias': 0.3019, + 'uo-bias': 0.71019, + 'wbb_percent-bias': -0.55964, + 'wbb_width-bias': 0.23523, + + 's-0-0-0-w': 0.85409, + 's-0-0-bias': -0.04613, + 's-1-0-0-w': -0.14997, + 's-10-0-0-w': -0.67008, + 's-11-0-0-w': -0.40221, + 's-12-0-0-w': 0.64553, + 's-13-0-0-w': 0.22838, + 's-14-0-0-w': 0.99977, + 's-15-0-0-w': 0.89363, + 's-16-0-0-w': -0.88212, + 's-17-0-0-w': -0.71813, + 's-18-0-0-w': 0.41602, + 's-19-0-0-w': -0.48389, + 's-2-0-0-w': 0.09649, + 's-20-0-0-w': 0.64273, + 's-21-0-0-w': -0.31671, + 's-22-0-0-w': 0.9663, + 's-23-0-0-w': 0.00229, + 's-24-0-0-w': 0.96244, + 's-25-0-0-w': -0.24513, + 's-26-0-0-w': 0.52312, + 's-27-0-0-w': 0.44742, + 's-28-0-0-w': -0.03916, + 's-29-0-0-w': 0.88882, + 's-3-0-0-w': -0.32112, + 's-30-0-0-w': -0.70886, + 's-31-0-0-w': -0.42672, + 's-4-0-0-w': -0.55265, + 's-5-0-0-w': 0.56105, + 's-6-0-0-w': 0.47436, + 's-7-0-0-w': 0.58136, + 's-8-0-0-w': -0.48308, + 's-9-0-0-w': -0.16024, + 's-adx-bias': -0.4091, + 's-ao-bias': 0.76889, + 's-aroonosc-bias': 0.16228, + 's-bb_percent-bias': 0.19407, + 's-bb_width-bias': 0.11795, + 's-cci-bias': 0.8379, + 's-end-0-w': -0.14648, + 's-end-bias': -0.85697, + 's-fastd-bias': -0.00581, + 's-fisher_rsi_norm-bias': -0.05253, + 's-kc_percent-bias': -0.3562, + 's-kc_width-bias': 0.67451, + 's-macd-bias': -0.17742, + 's-macdhist-bias': -0.58328, + 's-macdsignal-bias': -0.79847, + 's-mfi-bias': -0.48236, + 's-roc-bias': -0.5914, + 's-rsi-bias': -0.9618, + 's-sar-bias': 0.57033, + 's-sma10-bias': 0.14349, + 's-sma100-bias': 0.02401, + 's-sma21-bias': 0.78191, + 's-sma3-bias': 0.72279, + 's-sma5-bias': -0.19383, + 's-sma50-bias': 0.63697, + 's-tema10-bias': 0.96837, + 's-tema100-bias': 0.77171, + 's-tema21-bias': 0.67279, + 's-tema3-bias': -0.24583, + 's-tema5-bias': -0.08997, + 's-tema50-bias': 0.65532, + 's-uo-bias': 0.67701, + 's-wbb_percent-bias': -0.658, + 's-wbb_width-bias': -0.71056 +} + +network_shape = [1] + +class MyStrategy(IStrategy): + # ROI table: + minimal_roi = { + "0": 0.21029, + "11": 0.05876, + "57": 0.02191, + "281": 0 + } + + # Stoploss: + stoploss = -0.07693 + + # Optimal ticker interval for the strategy + ticker_interval = '2h' + + # Trailing stop: + trailing_only_offset_is_reached = False + trailing_stop = True + trailing_stop_positive = 0.01019 + trailing_stop_positive_offset = 0.01164 + + # run "populate_indicators" only for new candle + process_only_new_candles = True + + # Experimental settings (configuration will overide these if set) + use_sell_signal = True + sell_profit_only = True + ignore_roi_if_buy_signal = True + + startup_candle_count = 100 + + 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. + """ + + # Momentum Indicators + # ------------------------------------ + + # ADX + dataframe['adx'] = ta.ADX(dataframe) / 100 + + # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # # Minus Directional Indicator / Movement + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + dataframe['aroonosc'] = ta.AROONOSC(dataframe) / 100 + + # # Awesome Oscillator + dataframe['ao'] = ((qtpylib.awesome_oscillator(dataframe) > 0).astype(int) - 0.5) * 2 + + # # Keltner Channel + keltner = qtpylib.keltner_channel(dataframe) + dataframe["kc_upperband"] = keltner["upper"] + dataframe["kc_lowerband"] = keltner["lower"] + dataframe["kc_middleband"] = keltner["mid"] + dataframe["kc_percent"] = ( + (dataframe["close"] - dataframe["kc_lowerband"]) / + (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) + ) + dataframe["kc_width"] = ( + (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) / dataframe["kc_middleband"] + ) + + # # Ultimate Oscillator + dataframe['uo'] = ta.ULTOSC(dataframe) / 100 + + # # Commodity Channel Index: values [Oversold:-100, Overbought:100] + dataframe['cci'] = ta.CCI(dataframe) / 200 + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) / 100 + + # # Inverse Fisher transform on RSI: values [-1.0, 1.0] (https://goo.gl/2JGGoy) + rsi = 0.1 * (dataframe['rsi'] * 100 - 50) + fisher_rsi = (np.exp(2 * rsi) - 1) / (np.exp(2 * rsi) + 1) + + # # Inverse Fisher transform on RSI normalized: values [0.0, 100.0] (https://goo.gl/2JGGoy) + dataframe['fisher_rsi_norm'] = 50 * (fisher_rsi + 1) / 100 + + # # Stochastic Slow + # stoch = ta.STOCH(dataframe) + # dataframe['slowd'] = stoch['slowd'] + # dataframe['slowk'] = stoch['slowk'] + + # Stochastic Fast + stoch_fast = ta.STOCHF(dataframe) + dataframe['fastd'] = stoch_fast['fastd'] / 100 + + # # Stochastic RSI + # stoch_rsi = ta.STOCHRSI(dataframe) + # dataframe['fastd_rsi'] = stoch_rsi['fastd'] + # dataframe['fastk_rsi'] = stoch_rsi['fastk'] + + # MACD + macd = ta.MACD(dataframe) + dataframe['macd'] = macd['macd'] + dataframe['macdsignal'] = macd['macdsignal'] + dataframe['macdhist'] = macd['macdhist'] + + # MFI + dataframe['mfi'] = ta.MFI(dataframe) / 100 + + # # ROC + dataframe['roc'] = ta.ROC(dataframe) / 100 + + # Overlap Studies + # ------------------------------------ + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + # # SMA - Simple Moving Average + dataframe['sma3'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=3) - 1 + dataframe['sma5'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=5) - 1 + dataframe['sma10'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=10) - 1 + dataframe['sma21'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=21) - 1 + dataframe['sma50'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=50) - 1 + dataframe['sma100'] = dataframe['close'] / ta.SMA(dataframe, timeperiod=100) - 1 + + # Parabolic SAR + dataframe['sar'] = dataframe['close'] / ta.SAR(dataframe) - 1 + + # TEMA - Triple Exponential Moving Average + dataframe['tema3'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=3) - 1 + dataframe['tema5'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=5) - 1 + dataframe['tema10'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=10) - 1 + dataframe['tema21'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=21) - 1 + dataframe['tema50'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=50) - 1 + dataframe['tema100'] = dataframe['close'] / ta.TEMA(dataframe, timeperiod=100) - 1 + + 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 + :return: DataFrame with buy column + """ + indicators = ['aroonosc', 'ao', 'uo', 'cci', 'rsi', 'fisher_rsi_norm', 'sar', + 'sma3', 'sma5', 'sma10', 'sma21', 'sma50', 'sma100', + 'tema3', 'tema5', 'tema10', 'tema21', 'tema50', 'tema100', + 'fastd', 'adx', 'bb_percent', 'bb_width', 'macd', 'macdsignal', 'macdhist', 'mfi', + 'wbb_percent', 'wbb_width', 'roc', 'kc_percent', 'kc_width'] + inputs = [] + for indicator in indicators: + inputs.append(dataframe[indicator] + params[indicator + '-bias']) + + for index, layer_size in enumerate(network_shape): + outputs = [] + for n in range(layer_size): + weight = 0 + for i, input in enumerate(inputs): + weight += params['{}-{}-{}-w'.format(i, index, n)] * input + weight += params['{}-{}-bias'.format(index, n)] + outputs.append(activate(weight)) + inputs = outputs + + weight = 0 + for i, input in enumerate(inputs): + weight += params['end-{}-w'.format(i)] * input + weight += params['end-bias'] + + dataframe.loc[activate(weight) > 0, 'buy'] = 1 + + # Check that the candle had volume + dataframe.loc[dataframe['volume'] <= 0, 'buy'] = 0 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe diff --git a/NotAnotherSMAOffsetStrategy.py b/NotAnotherSMAOffsetStrategy.py new file mode 100644 index 0000000..4d2d11e --- /dev/null +++ b/NotAnotherSMAOffsetStrategy.py @@ -0,0 +1,224 @@ +# --- Do not remove these libs --- +# --- Do not remove these libs --- +from freqtrade.strategy.interface import IStrategy +from typing import Dict, List +from functools import reduce +from pandas import DataFrame +# -------------------------------- +import talib.abstract as ta +import numpy as np +import freqtrade.vendor.qtpylib.indicators as qtpylib +import datetime +from technical.util import resample_to_interval, resampled_merge +from datetime import datetime, timedelta +from freqtrade.persistence import Trade +from freqtrade.strategy import stoploss_from_open, merge_informative_pair, DecimalParameter, IntParameter, CategoricalParameter +import technical.indicators as ftt + +# @Rallipanos + +# Buy hyperspace params: +buy_params = { + "base_nb_candles_buy": 14, + "ewo_high": 2.327, + "ewo_high_2": -2.327, + "ewo_low": -20.988, + "low_offset": 0.975, + "low_offset_2": 0.955, + "rsi_buy": 69 + } + +# Sell hyperspace params: +sell_params = { + "base_nb_candles_sell": 24, + "high_offset": 0.991, + "high_offset_2": 0.997 + } + +def EWO(dataframe, ema_length=5, ema2_length=35): + df = dataframe.copy() + ema1 = ta.EMA(df, timeperiod=ema_length) + ema2 = ta.EMA(df, timeperiod=ema2_length) + emadif = (ema1 - ema2) / df['low'] * 100 + return emadif + + + +class NotAnotherSMAOffsetStrategy(IStrategy): + INTERFACE_VERSION = 2 + + # ROI table: + minimal_roi = { + "0": 0.215, + "40": 0.032, + "87": 0.016, + "201": 0 + } + + # Stoploss: + stoploss = -0.35 + + # SMAOffset + base_nb_candles_buy = IntParameter( + 5, 80, default=buy_params['base_nb_candles_buy'], space='buy', optimize=True) + base_nb_candles_sell = IntParameter( + 5, 80, default=sell_params['base_nb_candles_sell'], space='sell', optimize=True) + low_offset = DecimalParameter( + 0.9, 0.99, default=buy_params['low_offset'], space='buy', optimize=True) + low_offset_2 = DecimalParameter( + 0.9, 0.99, default=buy_params['low_offset_2'], space='buy', optimize=True) + high_offset = DecimalParameter( + 0.95, 1.1, default=sell_params['high_offset'], space='sell', optimize=True) + high_offset_2 = DecimalParameter( + 0.99, 1.5, default=sell_params['high_offset_2'], space='sell', optimize=True) + + # Protection + fast_ewo = 50 + slow_ewo = 200 + ewo_low = DecimalParameter(-20.0, -8.0, + default=buy_params['ewo_low'], space='buy', optimize=True) + ewo_high = DecimalParameter( + 2.0, 12.0, default=buy_params['ewo_high'], space='buy', optimize=True) + + ewo_high_2 = DecimalParameter( + -6.0, 12.0, default=buy_params['ewo_high_2'], space='buy', optimize=True) + + rsi_buy = IntParameter(30, 70, default=buy_params['rsi_buy'], space='buy', optimize=True) + + # Trailing stop: + trailing_stop = True + trailing_stop_positive = 0.005 + trailing_stop_positive_offset = 0.03 + trailing_only_offset_is_reached = True + + # Sell signal + use_sell_signal = True + sell_profit_only = False + sell_profit_offset = 0.01 + ignore_roi_if_buy_signal = False + + ## Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'ioc' + } + + # Optimal timeframe for the strategy + timeframe = '5m' + inf_1h = '1h' + + process_only_new_candles = True + startup_candle_count = 200 + + plot_config = { + 'main_plot': { + 'ma_buy': {'color': 'orange'}, + 'ma_sell': {'color': 'orange'}, + }, + } + def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, + rate: float, time_in_force: str, sell_reason: str, + current_time: datetime, **kwargs) -> bool: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1] + + + if (last_candle is not None): + if (sell_reason in ['sell_signal']): + if (last_candle['hma_50']*1.149 > last_candle['ema_100']) and (last_candle['close'] < last_candle['ema_100']*0.951): #*1.2 + return False + return True + + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # Calculate all ma_buy values + for val in self.base_nb_candles_buy.range: + dataframe[f'ma_buy_{val}'] = ta.EMA(dataframe, timeperiod=val) + + # Calculate all ma_sell values + for val in self.base_nb_candles_sell.range: + dataframe[f'ma_sell_{val}'] = ta.EMA(dataframe, timeperiod=val) + + dataframe['hma_50'] = qtpylib.hull_moving_average(dataframe['close'], window=50) + dataframe['ema_100'] = ta.EMA(dataframe, timeperiod=100) + + dataframe['sma_9'] = ta.SMA(dataframe, timeperiod=9) + # Elliot + dataframe['EWO'] = EWO(dataframe, self.fast_ewo, self.slow_ewo) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) + dataframe['rsi_fast'] = ta.RSI(dataframe, timeperiod=4) + dataframe['rsi_slow'] = ta.RSI(dataframe, timeperiod=20) + + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + (dataframe['rsi_fast'] <35)& + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & + (dataframe['EWO'] > self.ewo_high.value) & + (dataframe['rsi'] < self.rsi_buy.value) & + (dataframe['volume'] > 0)& + (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) + ), + ['buy', 'buy_tag']] = (1, 'ewo1') + + + dataframe.loc[ + ( + (dataframe['rsi_fast'] <35)& + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset_2.value)) & + (dataframe['EWO'] > self.ewo_high_2.value) & + (dataframe['rsi'] < self.rsi_buy.value) & + (dataframe['volume'] > 0)& + (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value))& + (dataframe['rsi']<25) + ), + ['buy', 'buy_tag']] = (1, 'ewo2') + + + dataframe.loc[ + ( + (dataframe['rsi_fast'] < 35)& + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & + (dataframe['EWO'] < self.ewo_low.value) & + (dataframe['volume'] > 0)& + (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) + ), + ['buy', 'buy_tag']] = (1, 'ewolow') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + + conditions.append( + ( (dataframe['close']>dataframe['sma_9'])& + (dataframe['close'] > (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset_2.value)) & + (dataframe['rsi']>50)& + (dataframe['volume'] > 0)& + (dataframe['rsi_fast']>dataframe['rsi_slow']) + ) + | + ( + (dataframe['close'] (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) & + (dataframe['volume'] > 0)& + (dataframe['rsi_fast']>dataframe['rsi_slow']) + ) + + ) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x | y, conditions), + 'sell' + ]=1 + + return dataframe diff --git a/NotAnotherSMAOffsetStrategyHO.json b/NotAnotherSMAOffsetStrategyHO.json new file mode 100644 index 0000000..0631ad1 --- /dev/null +++ b/NotAnotherSMAOffsetStrategyHO.json @@ -0,0 +1,37 @@ +{ + "strategy_name": "NotAnotherSMAOffsetStrategyHO", + "params": { + "roi": { + "0": 0.289, + "25": 0.105, + "80": 0.038, + "140": 0 + }, + "stoploss": { + "stoploss": -0.35 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.005, + "trailing_stop_positive_offset": 0.03, + "trailing_only_offset_is_reached": true + }, + "buy": { + "base_nb_candles_buy": 16, + "ewo_high": 3.422, + "ewo_low": -8.562, + "low_offset": 0.966, + "ewo_high_2": -5.793, + "low_offset_2": 0.955, + "rsi_buy": 64 + }, + "sell": { + "high_offset": 1.014, + "base_nb_candles_sell": 18, + "high_offset_2": 1.008 + }, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2022-07-06 18:58:29.710728+00:00" +} \ No newline at end of file diff --git a/NotAnotherSMAOffsetStrategyHO.py b/NotAnotherSMAOffsetStrategyHO.py new file mode 100644 index 0000000..9ad05e5 --- /dev/null +++ b/NotAnotherSMAOffsetStrategyHO.py @@ -0,0 +1,301 @@ +# --- Do not remove these libs --- +# --- Do not remove these libs --- +from freqtrade.strategy.interface import IStrategy +from typing import Dict, List +from functools import reduce +from pandas import DataFrame +# -------------------------------- +import talib.abstract as ta +import numpy as np +import freqtrade.vendor.qtpylib.indicators as qtpylib +import datetime +from technical.util import resample_to_interval, resampled_merge +from datetime import datetime, timedelta +from freqtrade.persistence import Trade +from freqtrade.strategy import stoploss_from_open, merge_informative_pair, DecimalParameter, IntParameter, CategoricalParameter +import technical.indicators as ftt + +# @Rallipanos + +# # Buy hyperspace params: +# buy_params = { +# "base_nb_candles_buy": 14, +# "ewo_high": 2.327, +# "ewo_high_2": -2.327, +# "ewo_low": -20.988, +# "low_offset": 0.975, +# "low_offset_2": 0.955, +# "rsi_buy": 69 +# } + +# # Buy hyperspace params: +# buy_params = { +# "base_nb_candles_buy": 18, +# "ewo_high": 3.422, +# "ewo_high_2": -3.436, +# "ewo_low": -8.562, +# "low_offset": 0.966, +# "low_offset_2": 0.959, +# "rsi_buy": 66, +# } + +# # # Sell hyperspace params: +# # sell_params = { +# # "base_nb_candles_sell": 17, +# # "high_offset": 0.997, +# # "high_offset_2": 1.01, +# # } + +# # Sell hyperspace params: +# sell_params = { +# "base_nb_candles_sell": 7, +# "high_offset": 1.014, +# "high_offset_2": 0.995, +# } + +# # Buy hyperspace params: +# buy_params = { +# "ewo_high_2": -5.642, +# "low_offset_2": 0.951, +# "rsi_buy": 54, +# "base_nb_candles_buy": 16, # value loaded from strategy +# "ewo_high": 3.422, # value loaded from strategy +# "ewo_low": -8.562, # value loaded from strategy +# "low_offset": 0.966, # value loaded from strategy +# } + +# # Sell hyperspace params: +# sell_params = { +# "base_nb_candles_sell": 8, +# "high_offset_2": 1.002, +# "high_offset": 1.014, # value loaded from strategy +# } + +# Buy hyperspace params: +buy_params = { + "ewo_high_2": -4.58, + "low_offset_2": 0.951, + "rsi_buy": 52, + "base_nb_candles_buy": 16, # value loaded from strategy + "ewo_high": 3.422, # value loaded from strategy + "ewo_low": -8.562, # value loaded from strategy + "low_offset": 0.966, # value loaded from strategy +} + +# Sell hyperspace params: +sell_params = { + "base_nb_candles_sell": 10, + "high_offset_2": 1.002, + "high_offset": 1.014, # value loaded from strategy +} + + +def EWO(dataframe, ema_length=5, ema2_length=35): + df = dataframe.copy() + ema1 = ta.EMA(df, timeperiod=ema_length) + ema2 = ta.EMA(df, timeperiod=ema2_length) + emadif = (ema1 - ema2) / df['low'] * 100 + return emadif + + +class NotAnotherSMAOffsetStrategyHO(IStrategy): + INTERFACE_VERSION = 2 + + # ROI table: + minimal_roi = { + "0": 0.289, + "25": 0.105, + "80": 0.038, + "140": 0 + } + + # Stoploss: + stoploss = -0.35 + + # SMAOffset + base_nb_candles_buy = IntParameter( + 5, 80, default=buy_params['base_nb_candles_buy'], space='buy', optimize=False) + base_nb_candles_sell = IntParameter( + 5, 80, default=sell_params['base_nb_candles_sell'], space='sell', optimize=True) + low_offset = DecimalParameter( + 0.9, 0.99, default=buy_params['low_offset'], space='buy', optimize=False) + low_offset_2 = DecimalParameter( + 0.9, 0.99, default=buy_params['low_offset_2'], space='buy', optimize=True) + high_offset = DecimalParameter( + 0.95, 1.1, default=sell_params['high_offset'], space='sell', optimize=False) + high_offset_2 = DecimalParameter( + 0.99, 1.5, default=sell_params['high_offset_2'], space='sell', optimize=True) + + # Protection + fast_ewo = 50 + slow_ewo = 200 + ewo_low = DecimalParameter(-20.0, -8.0, + default=buy_params['ewo_low'], space='buy', optimize=False) + ewo_high = DecimalParameter( + 2.0, 12.0, default=buy_params['ewo_high'], space='buy', optimize=False) + + ewo_high_2 = DecimalParameter( + -6.0, 12.0, default=buy_params['ewo_high_2'], space='buy', optimize=True) + + rsi_buy = IntParameter(30, 70, default=buy_params['rsi_buy'], space='buy', optimize=True) + + # Trailing stop: + trailing_stop = True + trailing_stop_positive = 0.005 + trailing_stop_positive_offset = 0.03 + trailing_only_offset_is_reached = True + + # Sell signal + use_sell_signal = True + sell_profit_only = False + sell_profit_offset = 0.01 + ignore_roi_if_buy_signal = False + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'ioc' + } + + # Optimal timeframe for the strategy + timeframe = '5m' + inf_1h = '1h' + + process_only_new_candles = True + startup_candle_count = 200 + + plot_config = { + 'main_plot': { + 'ma_buy': {'color': 'orange'}, + 'ma_sell': {'color': 'orange'}, + }, + } + + slippage_protection = { + 'retries': 3, + 'max_slippage': -0.02 + } + + buy_signals = {} + + def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, + rate: float, time_in_force: str, sell_reason: str, + current_time: datetime, **kwargs) -> bool: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1] + + if (last_candle is not None): + if (sell_reason in ['sell_signal']): + if (last_candle['hma_50']*1.149 > last_candle['ema_100']) and (last_candle['close'] < last_candle['ema_100']*0.951): # *1.2 + return False + + # slippage + try: + state = self.slippage_protection['__pair_retries'] + except KeyError: + state = self.slippage_protection['__pair_retries'] = {} + + candle = dataframe.iloc[-1].squeeze() + + slippage = (rate / candle['close']) - 1 + if slippage < self.slippage_protection['max_slippage']: + pair_retries = state.get(pair, 0) + if pair_retries < self.slippage_protection['retries']: + state[pair] = pair_retries + 1 + return False + + state[pair] = 0 + + return True + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # Calculate all ma_buy values + for val in self.base_nb_candles_buy.range: + dataframe[f'ma_buy_{val}'] = ta.EMA(dataframe, timeperiod=val) + + # Calculate all ma_sell values + for val in self.base_nb_candles_sell.range: + dataframe[f'ma_sell_{val}'] = ta.EMA(dataframe, timeperiod=val) + + dataframe['hma_50'] = qtpylib.hull_moving_average(dataframe['close'], window=50) + dataframe['ema_100'] = ta.EMA(dataframe, timeperiod=100) + + dataframe['sma_9'] = ta.SMA(dataframe, timeperiod=9) + # Elliot + dataframe['EWO'] = EWO(dataframe, self.fast_ewo, self.slow_ewo) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) + dataframe['rsi_fast'] = ta.RSI(dataframe, timeperiod=4) + dataframe['rsi_slow'] = ta.RSI(dataframe, timeperiod=20) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + (dataframe['rsi_fast'] < 35) & + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & + (dataframe['EWO'] > self.ewo_high.value) & + (dataframe['rsi'] < self.rsi_buy.value) & + (dataframe['volume'] > 0) & + (dataframe['close'] < ( + dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) + ), + ['buy', 'buy_tag']] = (1, 'ewo1') + + dataframe.loc[ + ( + (dataframe['rsi_fast'] < 35) & + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset_2.value)) & + (dataframe['EWO'] > self.ewo_high_2.value) & + (dataframe['rsi'] < self.rsi_buy.value) & + (dataframe['volume'] > 0) & + (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) & + (dataframe['rsi'] < 25) + ), + ['buy', 'buy_tag']] = (1, 'ewo2') + + dataframe.loc[ + ( + (dataframe['rsi_fast'] < 35) & + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & + (dataframe['EWO'] < self.ewo_low.value) & + (dataframe['volume'] > 0) & + (dataframe['close'] < ( + dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) + ), + ['buy', 'buy_tag']] = (1, 'ewolow') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + + conditions.append( + ((dataframe['close'] > dataframe['sma_9']) & + (dataframe['close'] > (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset_2.value)) & + (dataframe['rsi'] > 50) & + (dataframe['volume'] > 0) & + (dataframe['rsi_fast'] > dataframe['rsi_slow']) + ) + | + ( + (dataframe['close'] < dataframe['hma_50']) & + (dataframe['close'] > (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) & + (dataframe['volume'] > 0) & + (dataframe['rsi_fast'] > dataframe['rsi_slow']) + ) + + ) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x | y, conditions), + 'sell' + ]=1 + + return dataframe diff --git a/NotAnotherSMAOffsetStrategyX1.py b/NotAnotherSMAOffsetStrategyX1.py new file mode 100644 index 0000000..80037ab --- /dev/null +++ b/NotAnotherSMAOffsetStrategyX1.py @@ -0,0 +1,299 @@ +# --- Do not remove these libs --- +# --- Do not remove these libs --- +from freqtrade.strategy.interface import IStrategy +from typing import Dict, List +from functools import reduce +from pandas import DataFrame +# -------------------------------- +import talib.abstract as ta +import numpy as np +import freqtrade.vendor.qtpylib.indicators as qtpylib +import datetime +from technical.util import resample_to_interval, resampled_merge +from datetime import datetime, timedelta +from freqtrade.persistence import Trade +from freqtrade.strategy import stoploss_from_open, merge_informative_pair, DecimalParameter, IntParameter, CategoricalParameter +import technical.indicators as ftt +import pandas_ta as pta + +# @Rallipanos + + +def EWO(dataframe, ema_length=5, ema2_length=35): + df = dataframe.copy() + ema1 = ta.EMA(df, timeperiod=ema_length) + ema2 = ta.EMA(df, timeperiod=ema2_length) + # emadif = (ema1 - ema2) / df['low'] * 100 + emadif = (ema1 - ema2) / df['close'] * 100 + return emadif + + + +class NotAnotherSMAOffsetStrategyX1(IStrategy): + INTERFACE_VERSION = 2 + + # Buy hyperspace params: + buy_params = { + "base_nb_candles_buy": 14, + "ewo_high": 2.327, + "ewo_high_2": -2.327, + "ewo_low": -19.988, + "low_offset": 0.975, + "low_offset_2": 0.955, + "rsi_buy": 69, + } + + # Sell hyperspace params: + sell_params = { + "base_nb_candles_sell": 24, + "high_offset": 0.991, + "high_offset_2": 0.997, + "pHSL": -0.99, + "pPF_1": 0.022, + "pSL_1": 0.021, + "pPF_2": 0.08, + "pSL_2": 0.04, + } + + # ROI table: + minimal_roi = { + "0": 0.215, + "40": 0.032, + "87": 0.016, + "201": 0 + } + + # Stoploss: + stoploss = -0.35 + + # SMAOffset + base_nb_candles_buy = IntParameter( + 5, 80, default=buy_params['base_nb_candles_buy'], space='buy', optimize=True) + base_nb_candles_sell = IntParameter( + 5, 80, default=sell_params['base_nb_candles_sell'], space='sell', optimize=True) + low_offset = DecimalParameter( + 0.9, 0.99, default=buy_params['low_offset'], space='buy', optimize=True) + low_offset_2 = DecimalParameter( + 0.9, 0.99, default=buy_params['low_offset_2'], space='buy', optimize=True) + high_offset = DecimalParameter( + 0.95, 1.1, default=sell_params['high_offset'], space='sell', optimize=True) + high_offset_2 = DecimalParameter( + 0.99, 1.5, default=sell_params['high_offset_2'], space='sell', optimize=True) + + # Protection + fast_ewo = 50 + slow_ewo = 200 + ewo_low = DecimalParameter(-20.0, -8.0, + default=buy_params['ewo_low'], space='buy', optimize=True) + ewo_high = DecimalParameter( + 2.0, 12.0, default=buy_params['ewo_high'], space='buy', optimize=True) + + ewo_high_2 = DecimalParameter( + -6.0, 12.0, default=buy_params['ewo_high_2'], space='buy', optimize=True) + + rsi_buy = IntParameter(30, 70, default=buy_params['rsi_buy'], space='buy', optimize=True) + + # trailing stoploss hyperopt parameters + # hard stoploss profit + pHSL = DecimalParameter(-0.200, -0.040, default=-0.08, decimals=3, space='sell', optimize=False, load=True) + # profit threshold 1, trigger point, SL_1 is used + pPF_1 = DecimalParameter(0.008, 0.020, default=0.016, decimals=3, space='sell', optimize=True, load=True) + pSL_1 = DecimalParameter(0.008, 0.020, default=0.011, decimals=3, space='sell', optimize=True, load=True) + + # profit threshold 2, SL_2 is used + pPF_2 = DecimalParameter(0.040, 0.100, default=0.080, decimals=3, space='sell', optimize=True, load=True) + pSL_2 = DecimalParameter(0.020, 0.070, default=0.040, decimals=3, space='sell', optimize=True, load=True) + + protections = [ + # { + # "method": "StoplossGuard", + # "lookback_period_candles": 12, + # "trade_limit": 1, + # "stop_duration_candles": 6, + # "only_per_pair": True + # }, + # { + # "method": "StoplossGuard", + # "lookback_period_candles": 12, + # "trade_limit": 2, + # "stop_duration_candles": 6, + # "only_per_pair": False + # }, + { + "method": "LowProfitPairs", + "lookback_period_candles": 60, + "trade_limit": 1, + "stop_duration": 60, + "required_profit": -0.05 + }, + { + "method": "CooldownPeriod", + "stop_duration_candles": 2 + } + ] + + # Trailing stop: + trailing_stop = False + trailing_stop_positive = 0.005 + trailing_stop_positive_offset = 0.03 + trailing_only_offset_is_reached = True + + # Custom stoploss + use_custom_stoploss = True + + + # Sell signal + use_sell_signal = True + sell_profit_only = False + sell_profit_offset = 0.01 + ignore_roi_if_buy_signal = False + + # Optimal timeframe for the strategy + timeframe = '5m' + inf_1h = '1h' + + process_only_new_candles = True + startup_candle_count = 400 + + plot_config = { + 'main_plot': { + 'ma_buy': {'color': 'orange'}, + 'ma_sell': {'color': 'orange'}, + }, + } + + + def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, + rate: float, time_in_force: str, sell_reason: str, + current_time: datetime, **kwargs) -> bool: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1] + + + if (last_candle is not None): + if (sell_reason in ['sell_signal']): + if (last_candle['hma_50']*1.149 > last_candle['ema_100']) and (last_candle['close'] < last_candle['ema_100']*0.951): #*1.2 + return False + return True + + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # Calculate all ma_buy values + for val in self.base_nb_candles_buy.range: + dataframe[f'ma_buy_{val}'] = ta.EMA(dataframe, timeperiod=val) + + # Calculate all ma_sell values + for val in self.base_nb_candles_sell.range: + dataframe[f'ma_sell_{val}'] = ta.EMA(dataframe, timeperiod=val) + + # dataframe['hma_50'] = qtpylib.hull_moving_average(dataframe['close'], window=50) + dataframe['hma_50'] = pta.hma(dataframe['close'], 50) + dataframe['ema_100'] = ta.EMA(dataframe, timeperiod=100) + + dataframe['sma_9'] = ta.SMA(dataframe, timeperiod=9) + # Elliot + dataframe['EWO'] = EWO(dataframe, self.fast_ewo, self.slow_ewo) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) + dataframe['rsi_fast'] = ta.RSI(dataframe, timeperiod=4) + dataframe['rsi_slow'] = ta.RSI(dataframe, timeperiod=20) + + + return dataframe + + def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, + current_rate: float, current_profit: float, **kwargs) -> float: + + # hard stoploss profit + HSL = self.pHSL.value + PF_1 = self.pPF_1.value + SL_1 = self.pSL_1.value + PF_2 = self.pPF_2.value + SL_2 = self.pSL_2.value + + # For profits between PF_1 and PF_2 the stoploss (sl_profit) used is linearly interpolated + # between the values of SL_1 and SL_2. For all profits above PL_2 the sl_profit value + # rises linearly with current profit, for profits below PF_1 the hard stoploss profit is used. + + if (current_profit > PF_2): + sl_profit = SL_2 + (current_profit - PF_2) + elif (current_profit > PF_1): + sl_profit = SL_1 + ((current_profit - PF_1)*(SL_2 - SL_1)/(PF_2 - PF_1)) + else: + sl_profit = HSL + + return stoploss_from_open(sl_profit, current_profit) + + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + (dataframe['rsi_fast'] <35)& + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & + (dataframe['EWO'] > self.ewo_high.value) & + (dataframe['rsi'] < self.rsi_buy.value) & + (dataframe['volume'] > 0)& + (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) + ), + ['buy', 'buy_tag']] = (1, 'ewo1') + + + """ + dataframe.loc[ + ( + (dataframe['rsi_fast'] <35)& + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset_2.value)) & + (dataframe['EWO'] > self.ewo_high_2.value) & + (dataframe['rsi'] < self.rsi_buy.value) & + (dataframe['volume'] > 0)& + (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value))& + (dataframe['rsi']<25) + ), + ['buy', 'buy_tag']] = (1, 'ewo2') + """ + + dataframe.loc[ + ( + (dataframe['rsi_fast'] < 35)& + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & + (dataframe['EWO'] < self.ewo_low.value) & + (dataframe['volume'] > 0)& + (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) + ), + ['buy', 'buy_tag']] = (1, 'ewolow') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + + conditions.append( + ( + (dataframe['close']>dataframe['hma_50'])& + #(dataframe['close']>dataframe['sma_9'])& + (dataframe['close'] > (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset_2.value)) & + (dataframe['rsi']>50)& + (dataframe['volume'] > 0)& + (dataframe['rsi_fast']>dataframe['rsi_slow']) + ) + | + ( + (dataframe['close'] (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) & + (dataframe['volume'] > 0)& + (dataframe['rsi_fast']>dataframe['rsi_slow']) + ) + + ) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x | y, conditions), + 'sell' + ]=1 + + return dataframe diff --git a/NotAnotherSMAOffsetStrategy_uzi2.py b/NotAnotherSMAOffsetStrategy_uzi2.py new file mode 100644 index 0000000..e5257d7 --- /dev/null +++ b/NotAnotherSMAOffsetStrategy_uzi2.py @@ -0,0 +1,269 @@ +# --- Do not remove these libs --- +from freqtrade.strategy.interface import IStrategy +from functools import reduce +from pandas import DataFrame +# -------------------------------- +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +import datetime +from datetime import datetime +from freqtrade.persistence import Trade +from freqtrade.strategy import DecimalParameter, IntParameter +import numpy as np + +# @Rallipanos mod. Uzirox + + +def zlema2(dataframe, fast): + df = dataframe.copy() + zema1=ta.EMA(df['close'], fast) + zema2=ta.EMA(zema1, fast) + d1=zema1-zema2 + df['zlema2']=zema1+d1 + return df['zlema2'] + + + +# Buy hyperspace params: +buy_params = { + "base_nb_candles_buy": 14, + "ewo_high": 2.327, + "ewo_high_2": -2.327, + "ewo_low": -20.988, + "low_offset": 0.975, + "low_offset_2": 0.955, + "rsi_buy": 69 + } + +# Sell hyperspace params: +sell_params = { + "base_nb_candles_sell": 24, + "high_offset": 0.991, + "high_offset_2": 0.997 + } + +order_types = { + 'buy': 'limit', + 'sell': 'market', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + +def EWO(dataframe, ema_length=5, ema2_length=35): + df = dataframe.copy() + ema1 = ta.EMA(df, timeperiod=ema_length) + ema2 = ta.EMA(df, timeperiod=ema2_length) + emadif = (ema1 - ema2) / df['close'] * 100 + return emadif + + + +class NotAnotherSMAOffsetStrategy_uzi2(IStrategy): + INTERFACE_VERSION = 2 + + # ROI table: + minimal_roi = { + "0": 0.215, + "40": 0.032, + "87": 0.016, + "201": 0 + } + + # Stoploss: + stoploss = -0.1 + + # SMAOffset + base_nb_candles_buy = IntParameter(5, 80, default=buy_params['base_nb_candles_buy'], space='buy', optimize=True) + base_nb_candles_sell = IntParameter(5, 80, default=sell_params['base_nb_candles_sell'], space='sell', optimize=True) + low_offset = DecimalParameter(0.9, 0.99, default=buy_params['low_offset'], space='buy', optimize=True) + low_offset_2 = DecimalParameter(0.9, 0.99, default=buy_params['low_offset_2'], space='buy', optimize=True) + high_offset = DecimalParameter(0.95, 1.1, default=sell_params['high_offset'], space='sell', optimize=True) + high_offset_2 = DecimalParameter(0.99, 1.5, default=sell_params['high_offset_2'], space='sell', optimize=True) + + # Protection + fast_ewo = 50 + slow_ewo = 200 + ewo_low = DecimalParameter(-20.0, -8.0,default=buy_params['ewo_low'], space='buy', optimize=True) + ewo_high = DecimalParameter(2.0, 12.0, default=buy_params['ewo_high'], space='buy', optimize=True) + ewo_high_2 = DecimalParameter(-6.0, 12.0, default=buy_params['ewo_high_2'], space='buy', optimize=True) + rsi_buy = IntParameter(30, 70, default=buy_params['rsi_buy'], space='buy', optimize=True) + + # Trailing stop: + trailing_stop = True + trailing_stop_positive = 0.005 + trailing_stop_positive_offset = 0.03 + trailing_only_offset_is_reached = True + + # Sell signal + use_sell_signal = True + sell_profit_only = False + sell_profit_offset = 0.005 + ignore_roi_if_buy_signal = False + + # Optimal timeframe for the strategy + timeframe = '5m' + + process_only_new_candles = True + startup_candle_count = 400 + + + def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, + rate: float, time_in_force: str, sell_reason: str, + current_time: datetime, **kwargs) -> bool: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1] + + + if (last_candle is not None): + if (sell_reason in ['sell_signal']): + if (last_candle['hma_50']*1.149 > last_candle['ema_100']) and (last_candle['close'] < last_candle['ema_100']*0.951): #*1.2 + return False + return True + + + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # Calculate all ma_buy values + for val in self.base_nb_candles_buy.range: + dataframe[f'ma_buy_{val}'] = ta.EMA(dataframe, timeperiod=val) + + # Calculate all ma_sell values + for val in self.base_nb_candles_sell.range: + dataframe[f'ma_sell_{val}'] = ta.EMA(dataframe, timeperiod=val) + + + # *MAs + dataframe['hma_50'] = qtpylib.hull_moving_average(dataframe['close'], window=50) + dataframe['ema_100'] = ta.EMA(dataframe, timeperiod = 100) + dataframe['ema_10'] = zlema2(dataframe, 10) + dataframe['sma_9'] = ta.SMA(dataframe, timeperiod = 9) + + # Elliot + dataframe['EWO'] = EWO(dataframe, self.fast_ewo, self.slow_ewo) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) + dataframe['rsi_fast'] = ta.RSI(dataframe, timeperiod=4) + dataframe['rsi_slow'] = ta.RSI(dataframe, timeperiod=20) + + # strategy BinHV45 + bb_40 = qtpylib.bollinger_bands(dataframe['close'], window=40, stds=2) + dataframe['lower'] = bb_40['lower'] + dataframe['mid'] = bb_40['mid'] + dataframe['bbdelta'] = (bb_40['mid'] - dataframe['lower']).abs() + dataframe['closedelta'] = (dataframe['close'] - dataframe['close'].shift()).abs() + dataframe['tail'] = (dataframe['close'] - dataframe['low']).abs() + + # strategy ClucMay72018 + 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'] + dataframe['ema_slow'] = ta.EMA(dataframe, timeperiod=50) + dataframe['volume_mean_slow'] = dataframe['volume'].rolling(window=30).mean() + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + (dataframe['rsi_fast'] <35)& + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & + (dataframe['EWO'] > self.ewo_high.value) & + (dataframe['rsi'] < self.rsi_buy.value) & + (dataframe['volume'] > 0) & + (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) + ), + ['buy', 'buy_tag']] = (1, 'ewo1') + + + dataframe.loc[ + ( + (dataframe['rsi_fast'] <35)& + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset_2.value)) & + (dataframe['EWO'] > self.ewo_high_2.value) & + (dataframe['rsi'] < self.rsi_buy.value) & + (dataframe['volume'] > 0) & + (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value))& + (dataframe['rsi']<25) + ), + ['buy', 'buy_tag']] = (1, 'ewo2') + + dataframe.loc[ + ( + (dataframe['rsi_fast'] < 35)& + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & + (dataframe['EWO'] < self.ewo_low.value) & + (dataframe['volume'] > 0) & + (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) + ), + ['buy', 'buy_tag']] = (1, 'ewolow') + + + # buy in bull market + dataframe.loc[ + ( + (dataframe['ema_10'].rolling(10).mean() > dataframe['ema_100'].rolling(10).mean()) & + (dataframe['lower'].shift().gt(0)) & + (dataframe['bbdelta'].gt(dataframe['close'] * 0.031)) & + (dataframe['closedelta'].gt(dataframe['close'] * 0.018)) & + (dataframe['tail'].lt(dataframe['bbdelta'] * 0.233)) & + (dataframe['close'].lt(dataframe['lower'].shift())) & + (dataframe['close'].le(dataframe['close'].shift())) & + (dataframe['volume'] > 0) + ) + | + ( + (dataframe['ema_10'].rolling(10).mean() > dataframe['ema_100'].rolling(10).mean()) & + (dataframe['close'] > dataframe['ema_100']) & + (dataframe['close'] < dataframe['ema_slow']) & + (dataframe['close'] < 0.993 * dataframe['bb_lowerband']) & + (dataframe['volume'] < (dataframe['volume_mean_slow'].shift(1) * 21)) & + (dataframe['volume'] > 0) + ), + ['buy', 'buy_tag']] = (1, 'bb_bull') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + + conditions.append( + ( + + (dataframe['close'] > dataframe['sma_9']) & + (dataframe['close'] > (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset_2.value)) & + (dataframe['rsi']>50) & + (dataframe['volume'] > 0) & + (dataframe['rsi_fast'] > dataframe['rsi_slow']) + + ) + | + ( + (dataframe['sma_9'] > (dataframe['sma_9'].shift(1) + dataframe['sma_9'].shift(1)*0.005)) & + (dataframe['close'] < dataframe['hma_50']) & + (dataframe['close'] > (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) & + (dataframe['volume'] > 0) & + (dataframe['rsi_fast']>dataframe['rsi_slow']) + ) + + ) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x | y, conditions), + 'sell' + ]=1 + + return dataframe + + plot_config = { + 'main_plot':{ + 'ema_100':{}, + 'ema_10':{}, + 'sma_9':{} + } + } \ No newline at end of file diff --git a/Pierrick_20211201.py b/Pierrick_20211201.py new file mode 100644 index 0000000..73682d3 --- /dev/null +++ b/Pierrick_20211201.py @@ -0,0 +1,132 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + +# This class is a sample. Feel free to customize it. +class S000(IStrategy): + + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + + # ROI table: + minimal_roi = { + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 #0.015 + trailing_only_offset_is_reached = True + + #max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + 'main_plot': { + 'tema': {}, + 'sar': {'color': 'white'}, + }, + 'subplots': { + "MACD": { + 'macd': {'color': 'blue'}, + 'macdsignal': {'color': 'orange'}, + }, + "RSI": { + 'rsi': {'color': 'red'}, + } + } + } + + def informative_pairs(self): + + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # MACD + #macd = ta.MACD(dataframe) + #dataframe['macd'] = macd['macd'] + #dataframe['macdsignal'] = macd['macdsignal'] + #dataframe['macdhist'] = macd['macdhist'] + + # RSI + #dataframe['rsi'] = ta.RSI(dataframe) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + ( + (dataframe['close'] < dataframe['bb_lowerband'] * 0.998) + & (dataframe['bb_width'] >= 0.065) + & (dataframe['volume'] > 0) + ) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + + ), + + 'sell'] = 1 + return dataframe diff --git a/PremiereStrategie.py b/PremiereStrategie.py new file mode 100644 index 0000000..b565510 --- /dev/null +++ b/PremiereStrategie.py @@ -0,0 +1,552 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# flake8: noqa: F401 + +# --- Do not remove these libs --- +import operator +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame + +from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, + IStrategy, IntParameter) + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class PremiereStrategie(IStrategy): + """ + This is a strategy template to get you started. + More information in https://www.freqtrade.io/en/latest/strategy-customization/ + + You can: + :return: a Dataframe with all mandatory indicators for the strategies + - Rename the class name (Do not forget to update class_name) + - Add any methods you want to build your strategy + - Add any lib you need to build your strategy + + You must keep: + - the lib in the section "Do not remove these libs" + - the methods: populate_indicators, populate_buy_trend, populate_sell_trend + You should keep: + - timeframe, minimal_roi, stoploss, trailing_* + """ + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi". + minimal_roi = { + # "240": 0.002, + # "120": 0.005, + # "60": 0.01, + # "30": 0.015, + # "15": 0.02, + "0": 0.015 + } + + # minimal_roi = { + # "0": 0.015 + # } + + # Optimal stoploss designed for the strategy. + # This attribute will be overridden if the config file contains "stoploss". + stoploss = -0.10 + + # Trailing stoploss + trailing_stop = False + # trailing_only_offset_is_reached = False + # trailing_stop_positive = 0.01 + # trailing_stop_positive_offset = 0.0 # Disabled / not configured + + # Optimal timeframe for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = True + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'tema': {}, + 'sar': {'color': 'white'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "MACD": { + 'macd': {'color': 'blue'}, + 'macdsignal': {'color': 'orange'}, + }, + "RSI": { + 'rsi': {'color': 'red'}, + } + } + } + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + + 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: Dataframe with data from the exchange + :param metadata: Additional information, like the currently traded pair + :return: a Dataframe with all mandatory indicators for the strategies + """ + + # Momentum Indicators + # ------------------------------------ + + # # ADX + # dataframe['adx'] = ta.ADX(dataframe) + # + # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + # + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # # Awesome Oscillator + # dataframe['ao'] = qtpylib.awesome_oscillator(dataframe) + + # # Keltner Channel + # keltner = qtpylib.keltner_channel(dataframe) + # dataframe["kc_upperband"] = keltner["upper"] + # dataframe["kc_lowerband"] = keltner["lower"] + # dataframe["kc_middleband"] = keltner["mid"] + # dataframe["kc_percent"] = ( + # (dataframe["close"] - dataframe["kc_lowerband"]) / + # (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) + # ) + # dataframe["kc_width"] = ( + # (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) / dataframe["kc_middleband"] + # ) + + # # Ultimate Oscillator + # dataframe['uo'] = ta.ULTOSC(dataframe) + + # # Commodity Channel Index: values [Oversold:-100, Overbought:100] + # dataframe['cci'] = ta.CCI(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # Inverse Fisher transform on RSI: values [-1.0, 1.0] (https://goo.gl/2JGGoy) + # rsi = 0.1 * (dataframe['rsi'] - 50) + # dataframe['fisher_rsi'] = (np.exp(2 * rsi) - 1) / (np.exp(2 * rsi) + 1) + + # # Inverse Fisher transform on RSI normalized: values [0.0, 100.0] (https://goo.gl/2JGGoy) + # dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1) + + # # Stochastic Slow + # stoch = ta.STOCH(dataframe) + # dataframe['slowd'] = stoch['slowd'] + # dataframe['slowk'] = stoch['slowk'] + + # # Stochastic Fast + # stoch_fast = ta.STOCHF(dataframe) + # dataframe['fastd'] = stoch_fast['fastd'] + # dataframe['fastk'] = stoch_fast['fastk'] + + # # Stochastic RSI + # Please read https://github.com/freqtrade/freqtrade/issues/2961 before using this. + # STOCHRSI is NOT aligned with tradingview, which may result in non-expected results. + # stoch_rsi = ta.STOCHRSI(dataframe) + # dataframe['fastd_rsi'] = stoch_rsi['fastd'] + # dataframe['fastk_rsi'] = stoch_rsi['fastk'] + + # MACD + macd = ta.MACD(dataframe) + dataframe['macd'] = macd['macd'] + dataframe['macdsignal'] = macd['macdsignal'] + dataframe['macdhist'] = macd['macdhist'] + + # MFI + dataframe['mfi'] = ta.MFI(dataframe) + + # # ROC + # dataframe['roc'] = ta.ROC(dataframe) + + # Overlap Studies + # ------------------------------------ + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + # dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + + # Parabolic SAR + dataframe['sar'] = ta.SAR(dataframe) + + # 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 + # # ------------------------------------ + # # Heikin Ashi Strategy + # heikinashi = qtpylib.heikinashi(dataframe) + # dataframe['ha_open'] = heikinashi['open'] + # dataframe['ha_close'] = heikinashi['close'] + # dataframe['ha_high'] = heikinashi['high'] + # dataframe['ha_low'] = heikinashi['low'] + + # Retrieve best bid and best ask from the orderbook + # ------------------------------------ + """ + # first check if dataprovider is available + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + ob = self.dp.orderbook(metadata['pair'], 1) + dataframe['best_bid'] = ob['bids'][0][0] + dataframe['best_ask'] = ob['asks'][0][0] + """ + + 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 populated with indicators + :param metadata: Additional information, like the currently traded pair + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + # (qtpylib.crossed_above(dataframe['rsi'], 30)) & # Signal: RSI crosses above 30 + # (dataframe['tema'] <= dataframe['bb_middleband']) & # Guard: tema below BB middle + # (dataframe['tema'] > dataframe['tema'].shift(1)) & # Guard: tema is raising + # (dataframe['volume'] > 0) # Make sure Volume is not 0 + # ) | ( + (dataframe['macd'] > dataframe['macdsignal']) & + (dataframe['macd'] > dataframe['macd'].shift(1)) & # Guard: macd is raising + # (dataframe['tema'] > dataframe['tema'].shift(1)) & # Guard: tema is raising + # (dataframe['macd'].shift(1) > dataframe['macd'].shift(2)) & + (StrategyHelperLocal.two_green_one_red_candle(dataframe)) & + # (StrategyHelperLocal.no_more_three_green_candles(dataframe)) & + (dataframe['volume'] > 0) # Make sure Volume is not 0 + ), + '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 populated with indicators + :param metadata: Additional information, like the currently traded pair + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (qtpylib.crossed_above(dataframe['rsi'], 70)) & # Signal: RSI crosses above 70 + (dataframe['tema'] > dataframe['bb_middleband']) & # Guard: tema above BB middle + # (dataframe['tema'] < dataframe['tema'].shift(1)) & # Guard: tema is falling + (dataframe['volume'] > 0) # Make sure Volume is not 0 + # ) | ( + # (StrategyHelperLocal.four_green_one_red_candle(dataframe)) & + # (dataframe['volume'] > 0) # Make sure Volume is not 0 + # ) | ( + # (StrategyHelperLocal.two_red_candles(dataframe)) & + # (dataframe['tema'] < dataframe['tema'].shift(1)) & # Guard: tema is falling + # (dataframe['volume'] > 0) # Make sure Volume is not 0 + ), + 'sell'] = 1 + return dataframe + + +class StrategyHelperLocal: + """ + simple helper class to predefine a couple of patterns for our + strategy + """ + + @staticmethod + def two_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) + # (dataframe['open'].shift(2) < dataframe['close'].shift(2)) + ) + + @staticmethod + def no_more_three_green_candles(dataframe): + """ + evaluates if we are having not more than 3 green candles in a row + :param self: + :param dataframe: + :return: + """ + return not ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) + ) + + @staticmethod + def seven_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) + ) + + @staticmethod + def eight_green_candles(dataframe): + """ + evaluates if we are having 8 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) & + (dataframe['open'].shift(8) < dataframe['close'].shift(8)) + ) + + @staticmethod + def two_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) + # (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def four_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) & + (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + (dataframe['open'].shift(3 + shift) > dataframe['close'].shift(3 + shift)) & + (dataframe['open'].shift(4 + shift) > dataframe['close'].shift(4 + shift)) + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def two_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) + ) + + @staticmethod + def four_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] > dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) + ) + + @staticmethod + def four_red_one_green_candle(dataframe): + """ + evaluates if we are having a green candle and 4 previous red + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) > dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) > dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) > dataframe['close'].shift(4)) + ) diff --git a/RalliV1.py b/RalliV1.py new file mode 100644 index 0000000..e17c8f4 --- /dev/null +++ b/RalliV1.py @@ -0,0 +1,292 @@ +# --- Do not remove these libs --- +# --- Do not remove these libs --- +from freqtrade.strategy.interface import IStrategy +from typing import Dict, List +from functools import reduce +from pandas import DataFrame +# -------------------------------- +import talib.abstract as ta +import numpy as np +import freqtrade.vendor.qtpylib.indicators as qtpylib +import datetime +from technical.util import resample_to_interval, resampled_merge +from datetime import datetime, timedelta +from freqtrade.persistence import Trade +from freqtrade.strategy import stoploss_from_open, merge_informative_pair, DecimalParameter, IntParameter, CategoricalParameter +import technical.indicators as ftt + +# @Rallipanos + +# Buy hyperspace params: +buy_params = { + "base_nb_candles_buy": 14, + "ewo_high": 2.327, + "ewo_high_2": -2.327, + "ewo_low": -20.988, + "low_offset": 0.975, + "low_offset_2": 0.955, + "rsi_buy": 60, + "rsi_buy_2": 45 + } + +# Sell hyperspace params: +sell_params = { + "base_nb_candles_sell": 24, + "high_offset": 0.991, + "high_offset_2": 0.997 + } + +def EWO(dataframe, ema_length=5, ema2_length=35): + df = dataframe.copy() + ema1 = ta.EMA(df, timeperiod=ema_length) + ema2 = ta.EMA(df, timeperiod=ema2_length) + emadif = (ema1 - ema2) / df['low'] * 100 + return emadif + + + +class RalliV1(IStrategy): + INTERFACE_VERSION = 2 + + # ROI table: + minimal_roi = { + "0": 0.04, + "40": 0.032, + "87": 0.018, + "201": 0 + } + + # Stoploss: + stoploss = -0.3 + + # SMAOffset + base_nb_candles_buy = IntParameter( + 5, 80, default=buy_params['base_nb_candles_buy'], space='buy', optimize=True) + base_nb_candles_sell = IntParameter( + 5, 80, default=sell_params['base_nb_candles_sell'], space='sell', optimize=True) + low_offset = DecimalParameter( + 0.9, 0.99, default=buy_params['low_offset'], space='buy', optimize=True) + low_offset_2 = DecimalParameter( + 0.9, 0.99, default=buy_params['low_offset_2'], space='buy', optimize=True) + high_offset = DecimalParameter( + 0.95, 1.1, default=sell_params['high_offset'], space='sell', optimize=True) + high_offset_2 = DecimalParameter( + 0.99, 1.5, default=sell_params['high_offset_2'], space='sell', optimize=True) + + # Protection + fast_ewo = 50 + slow_ewo = 200 + ewo_low = DecimalParameter(-20.0, -8.0, + default=buy_params['ewo_low'], space='buy', optimize=True) + ewo_high = DecimalParameter( + 2.0, 12.0, default=buy_params['ewo_high'], space='buy', optimize=True) + + ewo_high_2 = DecimalParameter( + -6.0, 12.0, default=buy_params['ewo_high_2'], space='buy', optimize=True) + + rsi_buy = IntParameter(30, 70, default=buy_params['rsi_buy'], space='buy', optimize=True) + rsi_buy_2 = IntParameter(30, 70, default=buy_params['rsi_buy_2'], space='buy', optimize=True) + + # Trailing stop: + trailing_stop = False + trailing_stop_positive = 0.005 + trailing_stop_positive_offset = 0.03 + trailing_only_offset_is_reached = True + + # Sell signal + use_sell_signal = True + sell_profit_only = False + sell_profit_offset = 0.01 + ignore_roi_if_buy_signal = False + + ## Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + # Optimal timeframe for the strategy + timeframe = '5m' + inf_1h = '1h' + + process_only_new_candles = True + startup_candle_count = 200 + + plot_config = { + 'main_plot': { + 'ma_buy': {'color': 'orange'}, + 'ma_sell': {'color': 'orange'}, + }, + } + def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, + rate: float, time_in_force: str, sell_reason: str, + current_time: datetime, **kwargs) -> bool: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1] + + + if (last_candle is not None): + if (sell_reason in ['sell_signal']): + if (last_candle['rsi'] < 45 ) and (last_candle['hma_50'] > last_candle['ema_100']): #*1.2 + return False + return True + + use_custom_stoploss = True + + def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, + current_profit: float, **kwargs) -> float: + df, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + candle = df.iloc[-1].squeeze() + + if current_profit < 0.001 and current_time - timedelta(minutes=140) > trade.open_date_utc: + return -0.005 + + return 1 + + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # Calculate all ma_buy values + for val in self.base_nb_candles_buy.range: + dataframe[f'ma_buy_{val}'] = ta.EMA(dataframe, timeperiod=val) + + # Calculate all ma_sell values + for val in self.base_nb_candles_sell.range: + dataframe[f'ma_sell_{val}'] = ta.EMA(dataframe, timeperiod=val) + + dataframe['hma_50'] = qtpylib.hull_moving_average(dataframe['close'], window=50) + dataframe['hma_9'] = qtpylib.hull_moving_average(dataframe['close'], window=9) + dataframe['ema_100'] = ta.EMA(dataframe, timeperiod=100) + dataframe['ema_14'] = ta.EMA(dataframe, timeperiod=14) + dataframe['sma_9'] = ta.SMA(dataframe, timeperiod=9) + dataframe['ema_9'] = ta.EMA(dataframe, timeperiod=9) + # Elliot + dataframe['EWO'] = EWO(dataframe, self.fast_ewo, self.slow_ewo) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) + dataframe['rsi_fast'] = ta.RSI(dataframe, timeperiod=4) + dataframe['rsi_slow'] = ta.RSI(dataframe, timeperiod=20) + + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + + conditions.append( + ( + (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] < dataframe['ema_100'])& + (dataframe['sma_9'] < dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'])& + (dataframe['rsi_fast'] <35)& + (dataframe['rsi_fast'] >4)& + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & + (dataframe['EWO'] > self.ewo_high.value) & + (dataframe['rsi'] < self.rsi_buy_2.value) & + (dataframe['volume'] > 0)& + (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) + ) + ) + + conditions.append( + ( + (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] < dataframe['ema_100'])& + (dataframe['sma_9'] < dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'])& + (dataframe['rsi_fast'] <35)& + (dataframe['rsi_fast'] >4)& + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset_2.value)) & + (dataframe['EWO'] > self.ewo_high_2.value) & + (dataframe['rsi'] < self.rsi_buy_2.value) & + (dataframe['volume'] > 0)& + (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value))& + (dataframe['rsi']<25) + ) + ) + + conditions.append( + ( + (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] < dataframe['ema_100'])& + (dataframe['sma_9'] < dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'])& + (dataframe['rsi_fast'] < 35)& + (dataframe['rsi_fast'] >4)& + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & + (dataframe['EWO'] < self.ewo_low.value) & + (dataframe['volume'] > 0)& + (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) + ) + ) + + + conditions.append( + ( + (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] > dataframe['ema_100'])& + (dataframe['rsi_fast'] <35)& + (dataframe['rsi_fast'] >4)& + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & + (dataframe['EWO'] > self.ewo_high.value) & + (dataframe['rsi'] < self.rsi_buy.value) & + (dataframe['volume'] > 0)& + (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) + ) + ) + + conditions.append( + ( + (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] > dataframe['ema_100'])& + (dataframe['rsi_fast'] <35)& + (dataframe['rsi_fast'] >4)& + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset_2.value)) & + (dataframe['EWO'] > self.ewo_high_2.value) & + (dataframe['rsi'] < self.rsi_buy.value) & + (dataframe['volume'] > 0)& + (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value))& + (dataframe['rsi']<25) + ) + ) + + conditions.append( + ( (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] > dataframe['ema_100'])& + (dataframe['rsi_fast'] < 35)& + (dataframe['rsi_fast'] >4)& + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & + (dataframe['EWO'] < self.ewo_low.value) & + (dataframe['volume'] > 0)& + (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) + ) + ) + if conditions: + dataframe.loc[ + reduce(lambda x, y: x | y, conditions), + 'buy' + ]=1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + + conditions.append( + ( (dataframe['hma_50']>dataframe['ema_100'])& + (dataframe['close']>dataframe['sma_9'])& + (dataframe['close'] > (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset_2.value)) & + (dataframe['volume'] > 0)& + (dataframe['rsi_fast']>dataframe['rsi_slow']) + ) + | + ( + (dataframe['close'] (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) & + (dataframe['volume'] > 0)& + (dataframe['rsi_fast']>dataframe['rsi_slow']) + ) + + ) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x | y, conditions), + 'sell' + ]=1 + + return dataframe diff --git a/RalliV1_disable56.py b/RalliV1_disable56.py new file mode 100644 index 0000000..d0008b0 --- /dev/null +++ b/RalliV1_disable56.py @@ -0,0 +1,292 @@ +# --- Do not remove these libs --- +# --- Do not remove these libs --- +from freqtrade.strategy.interface import IStrategy +from typing import Dict, List +from functools import reduce +from pandas import DataFrame +# -------------------------------- +import talib.abstract as ta +import numpy as np +import freqtrade.vendor.qtpylib.indicators as qtpylib +import datetime +from technical.util import resample_to_interval, resampled_merge +from datetime import datetime, timedelta +from freqtrade.persistence import Trade +from freqtrade.strategy import stoploss_from_open, merge_informative_pair, DecimalParameter, IntParameter, CategoricalParameter +import technical.indicators as ftt + +# @Rallipanos + +# Buy hyperspace params: +buy_params = { + "base_nb_candles_buy": 14, + "ewo_high": 2.327, + "ewo_high_2": -2.327, + "ewo_low": -20.988, + "low_offset": 0.975, + "low_offset_2": 0.955, + "rsi_buy": 60, + "rsi_buy_2": 45 + } + +# Sell hyperspace params: +sell_params = { + "base_nb_candles_sell": 24, + "high_offset": 0.991, + "high_offset_2": 0.997 + } + +def EWO(dataframe, ema_length=5, ema2_length=35): + df = dataframe.copy() + ema1 = ta.EMA(df, timeperiod=ema_length) + ema2 = ta.EMA(df, timeperiod=ema2_length) + emadif = (ema1 - ema2) / df['low'] * 100 + return emadif + + + +class RalliV1_disable56(IStrategy): + INTERFACE_VERSION = 2 + + # ROI table: + minimal_roi = { + "0": 0.04, + "40": 0.032, + "87": 0.018, + "201": 0 + } + + # Stoploss: + stoploss = -0.3 + + # SMAOffset + base_nb_candles_buy = IntParameter( + 5, 80, default=buy_params['base_nb_candles_buy'], space='buy', optimize=True) + base_nb_candles_sell = IntParameter( + 5, 80, default=sell_params['base_nb_candles_sell'], space='sell', optimize=True) + low_offset = DecimalParameter( + 0.9, 0.99, default=buy_params['low_offset'], space='buy', optimize=True) + low_offset_2 = DecimalParameter( + 0.9, 0.99, default=buy_params['low_offset_2'], space='buy', optimize=True) + high_offset = DecimalParameter( + 0.95, 1.1, default=sell_params['high_offset'], space='sell', optimize=True) + high_offset_2 = DecimalParameter( + 0.99, 1.5, default=sell_params['high_offset_2'], space='sell', optimize=True) + + # Protection + fast_ewo = 50 + slow_ewo = 200 + ewo_low = DecimalParameter(-20.0, -8.0, + default=buy_params['ewo_low'], space='buy', optimize=True) + ewo_high = DecimalParameter( + 2.0, 12.0, default=buy_params['ewo_high'], space='buy', optimize=True) + + ewo_high_2 = DecimalParameter( + -6.0, 12.0, default=buy_params['ewo_high_2'], space='buy', optimize=True) + + rsi_buy = IntParameter(30, 70, default=buy_params['rsi_buy'], space='buy', optimize=True) + rsi_buy_2 = IntParameter(30, 70, default=buy_params['rsi_buy_2'], space='buy', optimize=True) + + # Trailing stop: + trailing_stop = False + trailing_stop_positive = 0.005 + trailing_stop_positive_offset = 0.03 + trailing_only_offset_is_reached = True + + # Sell signal + use_sell_signal = True + sell_profit_only = False + sell_profit_offset = 0.01 + ignore_roi_if_buy_signal = False + + ## Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + # Optimal timeframe for the strategy + timeframe = '5m' + inf_1h = '1h' + + process_only_new_candles = True + startup_candle_count = 200 + + plot_config = { + 'main_plot': { + 'ma_buy': {'color': 'orange'}, + 'ma_sell': {'color': 'orange'}, + }, + } + def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, + rate: float, time_in_force: str, sell_reason: str, + current_time: datetime, **kwargs) -> bool: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1] + + + if (last_candle is not None): + if (sell_reason in ['sell_signal']): + if (last_candle['rsi'] < 45 ) and (last_candle['hma_50'] > last_candle['ema_100']): #*1.2 + return False + return True + + use_custom_stoploss = True + + def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, + current_profit: float, **kwargs) -> float: + df, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + candle = df.iloc[-1].squeeze() + + if current_profit < 0.001 and current_time - timedelta(minutes=140) > trade.open_date_utc: + return -0.005 + + return 1 + + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # Calculate all ma_buy values + for val in self.base_nb_candles_buy.range: + dataframe[f'ma_buy_{val}'] = ta.EMA(dataframe, timeperiod=val) + + # Calculate all ma_sell values + for val in self.base_nb_candles_sell.range: + dataframe[f'ma_sell_{val}'] = ta.EMA(dataframe, timeperiod=val) + + dataframe['hma_50'] = qtpylib.hull_moving_average(dataframe['close'], window=50) + dataframe['hma_9'] = qtpylib.hull_moving_average(dataframe['close'], window=9) + dataframe['ema_100'] = ta.EMA(dataframe, timeperiod=100) + dataframe['ema_14'] = ta.EMA(dataframe, timeperiod=14) + dataframe['sma_9'] = ta.SMA(dataframe, timeperiod=9) + dataframe['ema_9'] = ta.EMA(dataframe, timeperiod=9) + # Elliot + dataframe['EWO'] = EWO(dataframe, self.fast_ewo, self.slow_ewo) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) + dataframe['rsi_fast'] = ta.RSI(dataframe, timeperiod=4) + dataframe['rsi_slow'] = ta.RSI(dataframe, timeperiod=20) + + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + + conditions.append( + ( + (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] < dataframe['ema_100'])& + (dataframe['sma_9'] < dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'])& + (dataframe['rsi_fast'] <35)& + (dataframe['rsi_fast'] >4)& + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & + (dataframe['EWO'] > self.ewo_high.value) & + (dataframe['rsi'] < self.rsi_buy_2.value) & + (dataframe['volume'] > 0)& + (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) + ) + ) + + conditions.append( + ( + (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] < dataframe['ema_100'])& + (dataframe['sma_9'] < dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'])& + (dataframe['rsi_fast'] <35)& + (dataframe['rsi_fast'] >4)& + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset_2.value)) & + (dataframe['EWO'] > self.ewo_high_2.value) & + (dataframe['rsi'] < self.rsi_buy_2.value) & + (dataframe['volume'] > 0)& + (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value))& + (dataframe['rsi']<25) + ) + ) + + conditions.append( + ( + (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] < dataframe['ema_100'])& + (dataframe['sma_9'] < dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'])& + (dataframe['rsi_fast'] < 35)& + (dataframe['rsi_fast'] >4)& + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & + (dataframe['EWO'] < self.ewo_low.value) & + (dataframe['volume'] > 0)& + (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) + ) + ) + + + conditions.append( + ( + (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] > dataframe['ema_100'])& + (dataframe['rsi_fast'] <35)& + (dataframe['rsi_fast'] >4)& + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & + (dataframe['EWO'] > self.ewo_high.value) & + (dataframe['rsi'] < self.rsi_buy.value) & + (dataframe['volume'] > 0)& + (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) + ) + ) + + # conditions.append( + # ( + # (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] > dataframe['ema_100'])& + # (dataframe['rsi_fast'] <35)& + # (dataframe['rsi_fast'] >4)& + # (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset_2.value)) & + # (dataframe['EWO'] > self.ewo_high_2.value) & + # (dataframe['rsi'] < self.rsi_buy.value) & + # (dataframe['volume'] > 0)& + # (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value))& + # (dataframe['rsi']<25) + # ) + # ) + + # conditions.append( + # ( (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] > dataframe['ema_100'])& + # (dataframe['rsi_fast'] < 35)& + # (dataframe['rsi_fast'] >4)& + # (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & + # (dataframe['EWO'] < self.ewo_low.value) & + # (dataframe['volume'] > 0)& + # (dataframe['close'] < (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) + # ) + # ) + if conditions: + dataframe.loc[ + reduce(lambda x, y: x | y, conditions), + 'buy' + ]=1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + + conditions.append( + ( (dataframe['hma_50']>dataframe['ema_100'])& + (dataframe['close']>dataframe['sma_9'])& + (dataframe['close'] > (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset_2.value)) & + (dataframe['volume'] > 0)& + (dataframe['rsi_fast']>dataframe['rsi_slow']) + ) + | + ( + (dataframe['close'] (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) & + (dataframe['volume'] > 0)& + (dataframe['rsi_fast']>dataframe['rsi_slow']) + ) + + ) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x | y, conditions), + 'sell' + ]=1 + + return dataframe diff --git a/Reco_1.py b/Reco_1.py new file mode 100644 index 0000000..63bb6a1 --- /dev/null +++ b/Reco_1.py @@ -0,0 +1,129 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +import numpy as np # noqa +import pandas as pd # noqa + +from freqtrade.strategy import IStrategy, merge_informative_pair +from pandas import DataFrame + +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + +class Reco_1(IStrategy): + + minimal_roi = { + "0": 1 + } + + # Stoploss + stoploss = -0.05 + + # Trailing stoploss + trailing_stop = True + trailing_only_offset_is_reached = True + trailing_stop_positive = 0.015 + trailing_stop_positive_offset = 0.02 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = False + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Optional order type mapping. + order_types = { + 'buy': 'market', + 'sell': 'market', + 'stoploss': 'market', + 'stoploss_on_exchange': True + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + def informative_pairs(self): + + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # Assign tf to each pair so they can be downloaded and cached for strategy. + informative_pairs = [(pair, '6h') for pair in pairs] + # Optionally Add additional "static" pairs + informative_pairs += [("ETH/USDT", "5m"), + ("BTC/TUSD", "5m"), + ("ALGO/BTC", "5m"), + ("ATOM/BTC", "5m"), + ("BAT/BTC", "5m"), + ("BCH/BTC", "5m"), + ("BRD/BTC", "5m"), + ("EOS/BTC", "5m"), + ("ETH/BTC", "5m"), + ("IOTA/BTC", "5m"), + ("LINK/BTC", "5m"), + ("LTC/BTC", "5m"), + ("NEO/BTC", "5m"), + ("NXS/BTC", "5m"), + ("XMR/BTC", "5m"), + ("XRP/BTC", "5m"), + ("XTZ/BTC", "5m"), + ] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + if not self.dp: + # Don't do anything if DataProvider is not available. + return dataframe + + inf_tf = '6h' + # Get the informative pair + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe = inf_tf) + # Get the 18 hours 3WHITESOLDIERS + informative['CDL3WHITESOLDIERS'] = ta.CDL3WHITESOLDIERS(informative, timeperiod=3) + + # Use the helper function merge_informative_pair to safely merge the pair + # Automatically renames the columns and merges a shorter timeframe dataframe and a longer timeframe informative pair + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, inf_tf, ffill = True) + + # Calculate 3WHITESOLDIERS of the original dataframe (5m timeframe) + dataframe['CDL3WHITESOLDIERS'] = ta.CDL3WHITESOLDIERS(dataframe, timeperiod=3) + + + # Momentum Indicators + # MFI + dataframe['mfi'] = ta.MFI(dataframe) + + # # Chart type + # # Heikin Ashi Strategy + 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: + + dataframe.loc[ + ( + (qtpylib.crossed_above(dataframe['mfi'] > 18)) & + (dataframe['CDL3WHITESOLDIERS_6h'] == True) & + (dataframe['volume'] > 0) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + ), + 'sell'] = 1 + + return dataframe diff --git a/SMAOG.py b/SMAOG.py new file mode 100644 index 0000000..4f10568 --- /dev/null +++ b/SMAOG.py @@ -0,0 +1,109 @@ +from datetime import datetime, timedelta +import talib.abstract as ta +from freqtrade.persistence import Trade +from freqtrade.strategy import CategoricalParameter +from freqtrade.strategy import DecimalParameter, IntParameter +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame + +# og @tirail +# author @Jooopieeert#0239 + +ma_types = { + 'SMA': ta.SMA, + 'EMA': ta.EMA, +} +class SMAOG(IStrategy): + INTERFACE_VERSION = 2 + buy_params = { + "base_nb_candles_buy": 26, + "buy_trigger": "SMA", + "low_offset": 0.968, + "pair_is_bad_0_threshold": 0.555, + "pair_is_bad_1_threshold": 0.172, + "pair_is_bad_2_threshold": 0.198, + } + sell_params = { + "base_nb_candles_sell": 28, + "high_offset": 0.985, + "sell_trigger": "EMA", + } + base_nb_candles_buy = IntParameter(16, 45, default=buy_params['base_nb_candles_buy'], space='buy', optimize=False, load=True) + base_nb_candles_sell = IntParameter(16, 45, default=sell_params['base_nb_candles_sell'], space='sell', optimize=False, load=True) + low_offset = DecimalParameter(0.8, 0.99, default=buy_params['low_offset'], space='buy', optimize=False, load=True) + high_offset = DecimalParameter(0.8, 1.1, default=sell_params['high_offset'], space='sell', optimize=False, load=True) + buy_trigger = CategoricalParameter(ma_types.keys(), default=buy_params['buy_trigger'], space='buy', optimize=False, load=True) + sell_trigger = CategoricalParameter(ma_types.keys(), default=sell_params['sell_trigger'], space='sell', optimize=False, load=True) + pair_is_bad_0_threshold = DecimalParameter(0.0, 0.600, default=0.220, space='buy', optimize=True, load=True) + pair_is_bad_1_threshold = DecimalParameter(0.0, 0.350, default=0.090, space='buy', optimize=True, load=True) + pair_is_bad_2_threshold = DecimalParameter(0.0, 0.200, default=0.060, space='buy', optimize=True, load=True) + + timeframe = '5m' + stoploss = -0.23 + minimal_roi = {"0": 10,} + trailing_stop = True + trailing_only_offset_is_reached = True + trailing_stop_positive = 0.01 + trailing_stop_positive_offset = 0.02 + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + process_only_new_candles = True + startup_candle_count = 400 + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + if not self.config['runmode'].value == 'hyperopt': + dataframe['ma_offset_buy'] = ma_types[self.buy_trigger.value](dataframe, int(self.base_nb_candles_buy.value)) * self.low_offset.value + dataframe['ma_offset_sell'] = ma_types[self.sell_trigger.value](dataframe, int(self.base_nb_candles_sell.value)) * self.high_offset.value + dataframe['pair_is_bad'] = ( + (((dataframe['open'].rolling(144).min() - dataframe['close']) / dataframe[ + 'close']) >= self.pair_is_bad_0_threshold.value) | + (((dataframe['open'].rolling(12).min() - dataframe['close']) / dataframe[ + 'close']) >= self.pair_is_bad_1_threshold.value) | + (((dataframe['open'].rolling(2).min() - dataframe['close']) / dataframe[ + 'close']) >= self.pair_is_bad_2_threshold.value)).astype('int') + dataframe['ema_50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema_200'] = ta.EMA(dataframe, timeperiod=200) + dataframe['rsi_exit'] = ta.RSI(dataframe, timeperiod=2) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + if self.config['runmode'].value == 'hyperopt': + dataframe['ma_offset_buy'] = ma_types[self.buy_trigger.value](dataframe, int(self.base_nb_candles_buy.value)) * self.low_offset.value + dataframe['pair_is_bad'] = ( + (((dataframe['open'].rolling(144).min() - dataframe['close']) / dataframe[ + 'close']) >= self.pair_is_bad_0_threshold.value) | + (((dataframe['open'].rolling(12).min() - dataframe['close']) / dataframe[ + 'close']) >= self.pair_is_bad_1_threshold.value) | + (((dataframe['open'].rolling(2).min() - dataframe['close']) / dataframe[ + 'close']) >= self.pair_is_bad_2_threshold.value)).astype('int') + dataframe['ema_50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema_200'] = ta.EMA(dataframe, timeperiod=200) + dataframe['rsi_exit'] = ta.RSI(dataframe, timeperiod=2) + + dataframe.loc[ + ( + (dataframe['ema_50'] > dataframe['ema_200']) & + (dataframe['close'] > dataframe['ema_200']) & + (dataframe['pair_is_bad'] < 1) & + (dataframe['close'] < dataframe['ma_offset_buy']) & + (dataframe['volume'] > 0) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + if self.config['runmode'].value == 'hyperopt': + dataframe['ma_offset_sell'] = ta.EMA(dataframe, int(self.base_nb_candles_sell.value)) * self.high_offset.value + dataframe.loc[ + ( + (dataframe['close'] > dataframe['ma_offset_sell']) & + ( + (dataframe['open'] < dataframe['open'].shift(1)) | + (dataframe['rsi_exit'] < 50) | + (dataframe['rsi_exit'] < dataframe['rsi_exit'].shift(1)) + ) & + (dataframe['volume'] > 0) + ), + 'sell'] = 1 + return dataframe diff --git a/SMAOffsetProtectOptV1Mod2.py b/SMAOffsetProtectOptV1Mod2.py new file mode 100644 index 0000000..7529d04 --- /dev/null +++ b/SMAOffsetProtectOptV1Mod2.py @@ -0,0 +1,191 @@ +# --- Do not remove these libs --- +from freqtrade.strategy.interface import IStrategy +from typing import Dict, List +from functools import reduce +from pandas import DataFrame +# -------------------------------- + +import talib.abstract as ta +import numpy as np +import freqtrade.vendor.qtpylib.indicators as qtpylib +import datetime +from technical.util import resample_to_interval, resampled_merge +from datetime import datetime, timedelta +from freqtrade.persistence import Trade +from freqtrade.strategy import stoploss_from_open, merge_informative_pair, DecimalParameter, IntParameter, CategoricalParameter +import technical.indicators as ftt + +######################################## Warning ######################################## +# You won't get a lot of benefits by simply changing to this strategy # +# with the HyperOpt values changed. # +# # +# You should test it closely, trying backtesting and dry running, and we recommend # +# customizing the terms of sale and purchase as well. # +# # +# You should always be careful in real trading! # +######################################################################################### + +# Modified Buy / Sell params - 20210619 +# Buy hyperspace params: +buy_params = { + "base_nb_candles_buy": 16, + "ewo_high": 5.672, + "ewo_low": -19.931, + "low_offset": 0.973, + "rsi_buy": 59, +} + +# Sell hyperspace params: +sell_params = { + "base_nb_candles_sell": 20, + "high_offset": 1.010, +} + +def EWO(dataframe, ema_length=5, ema2_length=35): + df = dataframe.copy() + ema1 = ta.EMA(df, timeperiod=ema_length) + ema2 = ta.EMA(df, timeperiod=ema2_length) + emadif = (ema1 - ema2) / df['close'] * 100 + return emadif + + +class SMAOffsetProtectOptV1Mod2(IStrategy): + INTERFACE_VERSION = 2 + + # Modified ROI - 20210620 + # ROI table: + minimal_roi = { + "0": 0.028, + "10": 0.018, + "30": 0.010, + "40": 0.005 + } + + # Stoploss: + stoploss = -0.5 + + # SMAOffset + base_nb_candles_buy = IntParameter( + 5, 80, default=buy_params['base_nb_candles_buy'], space='buy', optimize=True) + base_nb_candles_sell = IntParameter( + 5, 80, default=sell_params['base_nb_candles_sell'], space='sell', optimize=True) + low_offset = DecimalParameter( + 0.9, 0.99, default=buy_params['low_offset'], space='buy', optimize=True) + high_offset = DecimalParameter( + 0.99, 1.1, default=sell_params['high_offset'], space='sell', optimize=True) + + # Protection + fast_ewo = 50 + slow_ewo = 200 + ewo_low = DecimalParameter(-20.0, -8.0, + default=buy_params['ewo_low'], space='buy', optimize=True) + ewo_high = DecimalParameter( + 2.0, 12.0, default=buy_params['ewo_high'], space='buy', optimize=True) + rsi_buy = IntParameter(30, 70, default=buy_params['rsi_buy'], space='buy', optimize=True) + + + # Trailing stop: + trailing_stop = False + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.01 + trailing_only_offset_is_reached = True + + # Sell signal + use_sell_signal = True + sell_profit_only = False + sell_profit_offset = 0.01 + ignore_roi_if_buy_signal = False + + # Optimal timeframe for the strategy + timeframe = '5m' + informative_timeframe = '1h' + + process_only_new_candles = True + startup_candle_count: int = 30 + + plot_config = { + 'main_plot': { + 'ma_buy': {'color': 'orange'}, + 'ma_sell': {'color': 'orange'}, + }, + } + + use_custom_stoploss = False + + def informative_pairs(self): + + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, self.informative_timeframe) for pair in pairs] + + return informative_pairs + + def get_informative_indicators(self, metadata: dict): + + dataframe = self.dp.get_pair_dataframe( + pair=metadata['pair'], timeframe=self.informative_timeframe) + + return dataframe + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # Calculate all ma_buy values + for val in self.base_nb_candles_buy.range: + dataframe[f'ma_buy_{val}'] = ta.EMA(dataframe, timeperiod=val) + + # Calculate all ma_sell values + for val in self.base_nb_candles_sell.range: + dataframe[f'ma_sell_{val}'] = ta.EMA(dataframe, timeperiod=val) + + # Elliot + dataframe['EWO'] = EWO(dataframe, self.fast_ewo, self.slow_ewo) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + + conditions.append( + ( + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & + (dataframe['EWO'] > self.ewo_high.value) & + (dataframe['rsi'] < self.rsi_buy.value) & + (dataframe['volume'] > 0) + ) + ) + + conditions.append( + ( + (dataframe['close'] < (dataframe[f'ma_buy_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) & + (dataframe['EWO'] < self.ewo_low.value) & + (dataframe['volume'] > 0) + ) + ) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x | y, conditions), + 'buy' + ]=1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + + conditions.append( + ( + (dataframe['close'] > (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) & + (dataframe['volume'] > 0) + ) + ) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x | y, conditions), + 'sell' + ]=1 + + return dataframe diff --git a/Scalp.py b/Scalp.py new file mode 100644 index 0000000..31701d5 --- /dev/null +++ b/Scalp.py @@ -0,0 +1,75 @@ +# --- Do not remove these libs --- +from freqtrade.strategy.interface import IStrategy +from typing import Dict, List +from functools import reduce +from pandas import DataFrame +# -------------------------------- +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class Scalp(IStrategy): + """ + this strategy is based around the idea of generating a lot of potentatils buys and make tiny profits on each trade + + we recommend to have at least 60 parallel trades at any time to cover non avoidable losses. + + Recommended is to only sell based on ROI for this strategy + """ + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi" + minimal_roi = { + "0": 0.01 + } + # Optimal stoploss designed for the strategy + # This attribute will be overridden if the config file contains "stoploss" + # should not be below 3% loss + + stoploss = -0.04 + # Optimal timeframe for the strategy + # the shorter the better + timeframe = '1m' + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['ema_high'] = ta.EMA(dataframe, timeperiod=5, price='high') + dataframe['ema_close'] = ta.EMA(dataframe, timeperiod=5, price='close') + dataframe['ema_low'] = ta.EMA(dataframe, timeperiod=5, price='low') + stoch_fast = ta.STOCHF(dataframe, 5, 3, 0, 3, 0) + dataframe['fastd'] = stoch_fast['fastd'] + dataframe['fastk'] = stoch_fast['fastk'] + dataframe['adx'] = ta.ADX(dataframe) + + # required for graphing + bollinger = qtpylib.bollinger_bands(dataframe['close'], window=20, stds=2) + dataframe['bb_lowerband'] = bollinger['lower'] + dataframe['bb_upperband'] = bollinger['upper'] + dataframe['bb_middleband'] = bollinger['mid'] + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['open'] < dataframe['ema_low']) & + (dataframe['adx'] > 30) & + ( + (dataframe['fastk'] < 30) & + (dataframe['fastd'] < 30) & + (qtpylib.crossed_above(dataframe['fastk'], dataframe['fastd'])) + ) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['open'] >= dataframe['ema_high']) + ) | + ( + (qtpylib.crossed_above(dataframe['fastk'], 70)) | + (qtpylib.crossed_above(dataframe['fastd'], 70)) + ), + 'sell'] = 1 + return dataframe diff --git a/SecondeStrategie.py b/SecondeStrategie.py new file mode 100644 index 0000000..5ebc510 --- /dev/null +++ b/SecondeStrategie.py @@ -0,0 +1,554 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# flake8: noqa: F401 + +# --- Do not remove these libs --- +import operator +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame + +from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, + IStrategy, IntParameter) + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class SecondeStrategie(IStrategy): + """ + This is a strategy template to get you started. + More information in https://www.freqtrade.io/en/latest/strategy-customization/ + + You can: + :return: a Dataframe with all mandatory indicators for the strategies + - Rename the class name (Do not forget to update class_name) + - Add any methods you want to build your strategy + - Add any lib you need to build your strategy + + You must keep: + - the lib in the section "Do not remove these libs" + - the methods: populate_indicators, populate_buy_trend, populate_sell_trend + You should keep: + - timeframe, minimal_roi, stoploss, trailing_* + """ + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi". + # minimal_roi = { + # "240": 0.002, + # "120": 0.005, + # "60": 0.01, + # "30": 0.015, + # "15": 0.02, + # "0": 0.03 + # } + + minimal_roi = { + "0": 0.015 + } + + # Optimal stoploss designed for the strategy. + # This attribute will be overridden if the config file contains "stoploss". + stoploss = -0.03 + + # Trailing stoploss + trailing_stop = False + # trailing_only_offset_is_reached = False + # trailing_stop_positive = 0.01 + # trailing_stop_positive_offset = 0.0 # Disabled / not configured + + # Optimal timeframe for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'tema': {}, + 'sar': {'color': 'white'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "MACD": { + 'macd': {'color': 'blue'}, + 'macdsignal': {'color': 'orange'}, + }, + "RSI": { + 'rsi': {'color': 'red'}, + } + } + } + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + + 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: Dataframe with data from the exchange + :param metadata: Additional information, like the currently traded pair + :return: a Dataframe with all mandatory indicators for the strategies + """ + + # Momentum Indicators + # ------------------------------------ + + # # ADX + # dataframe['adx'] = ta.ADX(dataframe) + # + # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + # + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # Awesome Oscillator + dataframe['ao'] = qtpylib.awesome_oscillator(dataframe) + + # # Keltner Channel + # keltner = qtpylib.keltner_channel(dataframe) + # dataframe["kc_upperband"] = keltner["upper"] + # dataframe["kc_lowerband"] = keltner["lower"] + # dataframe["kc_middleband"] = keltner["mid"] + # dataframe["kc_percent"] = ( + # (dataframe["close"] - dataframe["kc_lowerband"]) / + # (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) + # ) + # dataframe["kc_width"] = ( + # (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) / dataframe["kc_middleband"] + # ) + + # # Ultimate Oscillator + # dataframe['uo'] = ta.ULTOSC(dataframe) + + # # Commodity Channel Index: values [Oversold:-100, Overbought:100] + # dataframe['cci'] = ta.CCI(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # Inverse Fisher transform on RSI: values [-1.0, 1.0] (https://goo.gl/2JGGoy) + # rsi = 0.1 * (dataframe['rsi'] - 50) + # dataframe['fisher_rsi'] = (np.exp(2 * rsi) - 1) / (np.exp(2 * rsi) + 1) + + # # Inverse Fisher transform on RSI normalized: values [0.0, 100.0] (https://goo.gl/2JGGoy) + # dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1) + + # # Stochastic Slow + # stoch = ta.STOCH(dataframe) + # dataframe['slowd'] = stoch['slowd'] + # dataframe['slowk'] = stoch['slowk'] + + # Stochastic Fast + stoch_fast = ta.STOCHF(dataframe) + dataframe['fastd'] = stoch_fast['fastd'] + dataframe['fastk'] = stoch_fast['fastk'] + + # # Stochastic RSI + # Please read https://github.com/freqtrade/freqtrade/issues/2961 before using this. + # STOCHRSI is NOT aligned with tradingview, which may result in non-expected results. + # stoch_rsi = ta.STOCHRSI(dataframe) + # dataframe['fastd_rsi'] = stoch_rsi['fastd'] + # dataframe['fastk_rsi'] = stoch_rsi['fastk'] + + # MACD + macd = ta.MACD(dataframe) + dataframe['macd'] = macd['macd'] + dataframe['macdsignal'] = macd['macdsignal'] + dataframe['macdhist'] = macd['macdhist'] + + # MFI + dataframe['mfi'] = ta.MFI(dataframe) + + # # ROC + # dataframe['roc'] = ta.ROC(dataframe) + + # Overlap Studies + # ------------------------------------ + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + # dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + + # Parabolic SAR + dataframe['sar'] = ta.SAR(dataframe) + + # 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 + # # ------------------------------------ + # # Heikin Ashi Strategy + # heikinashi = qtpylib.heikinashi(dataframe) + # dataframe['ha_open'] = heikinashi['open'] + # dataframe['ha_close'] = heikinashi['close'] + # dataframe['ha_high'] = heikinashi['high'] + # dataframe['ha_low'] = heikinashi['low'] + + # Retrieve best bid and best ask from the orderbook + # ------------------------------------ + """ + # first check if dataprovider is available + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + ob = self.dp.orderbook(metadata['pair'], 1) + dataframe['best_bid'] = ob['bids'][0][0] + dataframe['best_ask'] = ob['asks'][0][0] + """ + + 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 populated with indicators + :param metadata: Additional information, like the currently traded pair + :return: DataFrame with buy column + """ + dataframe.loc[ + # ( + # (qtpylib.crossed_above(dataframe['rsi'], 30)) & # Signal: RSI crosses above 30 + # (dataframe['tema'] <= dataframe['bb_middleband']) & # Guard: tema below BB middle + # (dataframe['tema'] > dataframe['tema'].shift(1)) & # Guard: tema is raising + # (dataframe['volume'] > 0) # Make sure Volume is not 0 + # ) + # | + ( + (dataframe['macd'] > dataframe['macdsignal']) & + (dataframe['macd'] > dataframe['macd'].shift(1)) & # Guard: macd is raising + (dataframe['tema'] > dataframe['tema'].shift(1)) & # Guard: tema is raising + # (dataframe['macd'].shift(1) > dataframe['macd'].shift(2)) & + (StrategyHelperLocal.two_green_one_red_candle(dataframe)) & + # (StrategyHelperLocal.no_more_three_green_candles(dataframe)) & + (dataframe['volume'] > 0) # Make sure Volume is not 0 + ), + '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 populated with indicators + :param metadata: Additional information, like the currently traded pair + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (qtpylib.crossed_above(dataframe['rsi'], 70)) & # Signal: RSI crosses above 70 + (dataframe['tema'] > dataframe['bb_middleband']) & # Guard: tema above BB middle + (dataframe['tema'] < dataframe['tema'].shift(1)) & # Guard: tema is falling + (dataframe['volume'] > 0) # Make sure Volume is not 0 + ) | ( + # # (StrategyHelperLocal.four_green_one_red_candle(dataframe)) & + # # (dataframe['volume'] > 0) # Make sure Volume is not 0 + # ) | ( + (StrategyHelperLocal.two_red_candles(dataframe)) & + (dataframe['tema'] < dataframe['tema'].shift(1)) & # Guard: tema is falling + (dataframe['volume'] > 0) # Make sure Volume is not 0 + ), + 'sell'] = 1 + return dataframe + + +class StrategyHelperLocal: + """ + simple helper class to predefine a couple of patterns for our + strategy + """ + + @staticmethod + def two_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) + # (dataframe['open'].shift(2) < dataframe['close'].shift(2)) + ) + + @staticmethod + def no_more_three_green_candles(dataframe): + """ + evaluates if we are having not more than 3 green candles in a row + :param self: + :param dataframe: + :return: + """ + return operator.not_( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) + ) + + @staticmethod + def seven_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) + ) + + @staticmethod + def eight_green_candles(dataframe): + """ + evaluates if we are having 8 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) & + (dataframe['open'].shift(8) < dataframe['close'].shift(8)) + ) + + @staticmethod + def two_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) + # (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def four_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) & + (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + (dataframe['open'].shift(3 + shift) > dataframe['close'].shift(3 + shift)) & + (dataframe['open'].shift(4 + shift) > dataframe['close'].shift(4 + shift)) + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def two_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) + ) + + @staticmethod + def four_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] > dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) + ) + + @staticmethod + def four_red_one_green_candle(dataframe): + """ + evaluates if we are having a green candle and 4 previous red + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) > dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) > dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) > dataframe['close'].shift(4)) + ) diff --git a/Solipsis5.json b/Solipsis5.json new file mode 100644 index 0000000..bf68e92 --- /dev/null +++ b/Solipsis5.json @@ -0,0 +1,49 @@ +{ + "strategy_name": "Solipsis5", + "params": { + "roi": { + "0": 100 + }, + "stoploss": { + "stoploss": -0.99 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": null, + "trailing_stop_positive_offset": 0.0, + "trailing_only_offset_is_reached": false + }, + "buy": { + "base_ma_streak": 4, + "base_mp": 48, + "base_rmi_max": 57, + "base_rmi_min": 22, + "base_rmi_streak": 5, + "base_trigger": "none", + "inf_pct_adr": 0.96, + "xbtc_base_rmi": 69, + "xbtc_guard": "lazy", + "xtra_base_fiat_rmi": 67, + "xtra_base_stake_rmi": 12 + }, + "sell": { + "csell_endtrend_respect_roi": false, + "csell_pullback": true, + "csell_pullback_amount": 0.025, + "csell_pullback_respect_roi": false, + "csell_roi_end": 0.005, + "csell_roi_start": 0.039, + "csell_roi_time": 1124, + "csell_roi_type": "static", + "csell_trend_type": "any", + "cstop_bail_how": "none", + "cstop_bail_roc": -3.211, + "cstop_bail_time": 949, + "cstop_bail_time_trend": true, + "cstop_loss_threshold": -0.042 + }, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2022-02-15 00:02:16.762786+00:00" +} \ No newline at end of file diff --git a/Solipsis5.py b/Solipsis5.py new file mode 100644 index 0000000..d9a7c38 --- /dev/null +++ b/Solipsis5.py @@ -0,0 +1,588 @@ +import numpy as np +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +import arrow + +from freqtrade.strategy import (IStrategy, merge_informative_pair, stoploss_from_open, + IntParameter, DecimalParameter, CategoricalParameter) + +from typing import Dict, List, Optional, Tuple, Union +from pandas import DataFrame, Series +from functools import reduce +from datetime import datetime, timedelta +from freqtrade.persistence import Trade + +# Get rid of pandas warnings during backtesting +import pandas as pd +pd.options.mode.chained_assignment = None # default='warn' + +# Strategy specific imports, files must reside in same folder as strategy +import sys +from pathlib import Path +sys.path.append(str(Path(__file__).parent)) + +# import custom_indicators as cta + +""" +Solipsis - By @werkkrew + +Credits - +@JimmyNixx for many of the ideas used throughout as well as helping me stay motivated throughout development! +@rk for submitting many PR's that have made this strategy possible! + +I ask for nothing in return except that if you make changes which bring you greater success than what has been provided, you share those ideas back to +the community. Also, please don't nag me with a million questions and especially don't blame me if you lose a ton of money using this. + +I take no responsibility for any success or failure you have using this strategy. + +VERSION: 5.2.1 +""" + + +""" +Misc. Helper Functions +""" +def same_length(bigger, shorter): + return np.concatenate((np.full((bigger.shape[0] - shorter.shape[0]), np.nan), shorter)) + +""" +Maths +""" +def linear_growth(start: float, end: float, start_time: int, end_time: int, trade_time: int) -> float: + """ + Simple linear growth function. Grows from start to end after end_time minutes (starts after start_time minutes) + """ + time = max(0, trade_time - start_time) + rate = (end - start) / (end_time - start_time) + + return min(end, start + (rate * time)) + +def linear_decay(start: float, end: float, start_time: int, end_time: int, trade_time: int) -> float: + """ + Simple linear decay function. Decays from start to end after end_time minutes (starts after start_time minutes) + """ + time = max(0, trade_time - start_time) + rate = (start - end) / (end_time - start_time) + + return max(end, start - (rate * time)) + +""" +TA Indicators +""" + +def zema(dataframe, period, field='close'): + """ + Source: https://github.com/freqtrade/technical/blob/master/technical/indicators/overlap_studies.py#L79 + Modified slightly to use ta.EMA instead of technical ema + """ + df = dataframe.copy() + + df['ema1'] = ta.EMA(df[field], timeperiod=period) + df['ema2'] = ta.EMA(df['ema1'], timeperiod=period) + df['d'] = df['ema1'] - df['ema2'] + df['zema'] = df['ema1'] + df['d'] + + return df['zema'] + +def RMI(dataframe, *, length=20, mom=5): + """ + Source: https://github.com/freqtrade/technical/blob/master/technical/indicators/indicators.py#L912 + """ + df = dataframe.copy() + + df['maxup'] = (df['close'] - df['close'].shift(mom)).clip(lower=0) + df['maxdown'] = (df['close'].shift(mom) - df['close']).clip(lower=0) + + df.fillna(0, inplace=True) + + df["emaInc"] = ta.EMA(df, price='maxup', timeperiod=length) + df["emaDec"] = ta.EMA(df, price='maxdown', timeperiod=length) + + df['RMI'] = np.where(df['emaDec'] == 0, 0, 100 - 100 / (1 + df["emaInc"] / df["emaDec"])) + + return df["RMI"] + +def mastreak(dataframe: DataFrame, period: int = 4, field='close') -> Series: + """ + MA Streak + Port of: https://www.tradingview.com/script/Yq1z7cIv-MA-Streak-Can-Show-When-a-Run-Is-Getting-Long-in-the-Tooth/ + """ + df = dataframe.copy() + + avgval = zema(df, period, field) + + arr = np.diff(avgval) + pos = np.clip(arr, 0, 1).astype(bool).cumsum() + neg = np.clip(arr, -1, 0).astype(bool).cumsum() + streak = np.where(arr >= 0, pos - np.maximum.accumulate(np.where(arr <= 0, pos, 0)), + -neg + np.maximum.accumulate(np.where(arr >= 0, neg, 0))) + + res = same_length(df['close'], streak) + + return res + +def pcc(dataframe: DataFrame, period: int = 20, mult: int = 2): + """ + Percent Change Channel + PCC is like KC unless it uses percentage changes in price to set channel distance. + https://www.tradingview.com/script/6wwAWXA1-MA-Streak-Change-Channel/ + """ + df = dataframe.copy() + + df['previous_close'] = df['close'].shift() + + df['close_change'] = (df['close'] - df['previous_close']) / df['previous_close'] * 100 + df['high_change'] = (df['high'] - df['close']) / df['close'] * 100 + df['low_change'] = (df['low'] - df['close']) / df['close'] * 100 + + df['delta'] = df['high_change'] - df['low_change'] + + mid = zema(df, period, 'close_change') + rangema = zema(df, period, 'delta') + + upper = mid + rangema * mult + lower = mid - rangema * mult + + return upper, rangema, lower + +def SSLChannels(dataframe, length=10, mode='sma'): + """ + Source: https://www.tradingview.com/script/xzIoaIJC-SSL-channel/ + Source: https://github.com/freqtrade/technical/blob/master/technical/indicators/indicators.py#L1025 + Usage: + dataframe['sslDown'], dataframe['sslUp'] = SSLChannels(dataframe, 10) + """ + if mode not in ('sma'): + raise ValueError(f"Mode {mode} not supported yet") + + df = dataframe.copy() + + if mode == 'sma': + df['smaHigh'] = df['high'].rolling(length).mean() + df['smaLow'] = df['low'].rolling(length).mean() + + df['hlv'] = np.where(df['close'] > df['smaHigh'], 1, + np.where(df['close'] < df['smaLow'], -1, np.NAN)) + df['hlv'] = df['hlv'].ffill() + + df['sslDown'] = np.where(df['hlv'] < 0, df['smaHigh'], df['smaLow']) + df['sslUp'] = np.where(df['hlv'] < 0, df['smaLow'], df['smaHigh']) + + return df['sslDown'], df['sslUp'] + +def SSLChannels_ATR(dataframe, length=7): + """ + SSL Channels with ATR: https://www.tradingview.com/script/SKHqWzql-SSL-ATR-channel/ + Credit to @JimmyNixx for python + """ + df = dataframe.copy() + + df['ATR'] = ta.ATR(df, timeperiod=14) + df['smaHigh'] = df['high'].rolling(length).mean() + df['ATR'] + df['smaLow'] = df['low'].rolling(length).mean() - df['ATR'] + df['hlv'] = np.where(df['close'] > df['smaHigh'], 1, np.where(df['close'] < df['smaLow'], -1, np.NAN)) + df['hlv'] = df['hlv'].ffill() + df['sslDown'] = np.where(df['hlv'] < 0, df['smaHigh'], df['smaLow']) + df['sslUp'] = np.where(df['hlv'] < 0, df['smaLow'], df['smaHigh']) + + return df['sslDown'], df['sslUp'] + +def WaveTrend(dataframe, chlen=10, avg=21, smalen=4): + """ + WaveTrend Ocillator by LazyBear + https://www.tradingview.com/script/2KE8wTuF-Indicator-WaveTrend-Oscillator-WT/ + """ + df = dataframe.copy() + + df['hlc3'] = (df['high'] + df['low'] + df['close']) / 3 + df['esa'] = ta.EMA(df['hlc3'], timeperiod=chlen) + df['d'] = ta.EMA((df['hlc3'] - df['esa']).abs(), timeperiod=chlen) + df['ci'] = (df['hlc3'] - df['esa']) / (0.015 * df['d']) + df['tci'] = ta.EMA(df['ci'], timeperiod=avg) + + df['wt1'] = df['tci'] + df['wt2'] = ta.SMA(df['wt1'], timeperiod=smalen) + df['wt1-wt2'] = df['wt1'] - df['wt2'] + + return df['wt1'], df['wt2'] + +def T3(dataframe, length=5): + """ + T3 Average by HPotter on Tradingview + https://www.tradingview.com/script/qzoC9H1I-T3-Average/ + """ + df = dataframe.copy() + + df['xe1'] = ta.EMA(df['close'], timeperiod=length) + df['xe2'] = ta.EMA(df['xe1'], timeperiod=length) + df['xe3'] = ta.EMA(df['xe2'], timeperiod=length) + df['xe4'] = ta.EMA(df['xe3'], timeperiod=length) + df['xe5'] = ta.EMA(df['xe4'], timeperiod=length) + df['xe6'] = ta.EMA(df['xe5'], timeperiod=length) + b = 0.7 + c1 = -b*b*b + c2 = 3*b*b+3*b*b*b + c3 = -6*b*b-3*b-3*b*b*b + c4 = 1+3*b+b*b*b+3*b*b + df['T3Average'] = c1 * df['xe6'] + c2 * df['xe5'] + c3 * df['xe4'] + c4 * df['xe3'] + + return df['T3Average'] + + +def SROC(dataframe, roclen=21, emalen=13, smooth=21): + df = dataframe.copy() + + roc = ta.ROC(df, timeperiod=roclen) + ema = ta.EMA(df, timeperiod=emalen) + sroc = ta.ROC(ema, timeperiod=smooth) + + return sroc + + +class Solipsis5(IStrategy): + + ## Buy Space Hyperopt Variables + + # Base Pair Params + base_mp = IntParameter(10, 50, default=30, space='buy', load=True, optimize=True) + base_rmi_max = IntParameter(30, 60, default=50, space='buy', load=True, optimize=True) + base_rmi_min = IntParameter(0, 30, default=20, space='buy', load=True, optimize=True) + base_ma_streak = IntParameter(1, 4, default=1, space='buy', load=True, optimize=True) + base_rmi_streak = IntParameter(3, 8, default=3, space='buy', load=True, optimize=True) + base_trigger = CategoricalParameter(['pcc', 'rmi', 'none'], default='rmi', space='buy', load=True, optimize=True) + inf_pct_adr = DecimalParameter(0.70, 0.99, default=0.80, space='buy', load=True, optimize=True) + # BTC Informative + xbtc_guard = CategoricalParameter(['strict', 'lazy', 'none'], default='lazy', space='buy', optimize=True) + xbtc_base_rmi = IntParameter(20, 70, default=40, space='buy', load=True, optimize=True) + # BTC / ETH Stake Parameters + xtra_base_stake_rmi = IntParameter(10, 50, default=50, space='buy', load=True, optimize=True) + xtra_base_fiat_rmi = IntParameter(30, 70, default=50, space='buy', load=True, optimize=True) + + ## Sell Space Params are being used for both custom_stoploss and custom_sell + + # Custom Sell Profit (formerly Dynamic ROI) + csell_roi_type = CategoricalParameter(['static', 'decay', 'step'], default='step', space='sell', load=True, optimize=True) + csell_roi_time = IntParameter(720, 1440, default=720, space='sell', load=True, optimize=True) + csell_roi_start = DecimalParameter(0.01, 0.05, default=0.01, space='sell', load=True, optimize=True) + csell_roi_end = DecimalParameter(0.0, 0.01, default=0, space='sell', load=True, optimize=True) + csell_trend_type = CategoricalParameter(['rmi', 'ssl', 'candle', 'any', 'none'], default='any', space='sell', load=True, optimize=True) + csell_pullback = CategoricalParameter([True, False], default=True, space='sell', load=True, optimize=True) + csell_pullback_amount = DecimalParameter(0.005, 0.03, default=0.01, space='sell', load=True, optimize=True) + csell_pullback_respect_roi = CategoricalParameter([True, False], default=False, space='sell', load=True, optimize=True) + csell_endtrend_respect_roi = CategoricalParameter([True, False], default=False, space='sell', load=True, optimize=True) + + # Custom Stoploss + cstop_loss_threshold = DecimalParameter(-0.05, -0.01, default=-0.03, space='sell', load=True, optimize=True) + cstop_bail_how = CategoricalParameter(['roc', 'time', 'any', 'none'], default='none', space='sell', load=True, optimize=True) + cstop_bail_roc = DecimalParameter(-5.0, -1.0, default=-3.0, space='sell', load=True, optimize=True) + cstop_bail_time = IntParameter(60, 1440, default=720, space='sell', load=True, optimize=True) + cstop_bail_time_trend = CategoricalParameter([True, False], default=True, space='sell', load=True, optimize=True) + + timeframe = '5m' + inf_timeframe = '1h' + + buy_params = {} + + sell_params = {} + + minimal_roi = { + "0": 100 + } + + stoploss = -0.99 + use_custom_stoploss = True + + # Recommended + use_sell_signal = True + sell_profit_only = True + ignore_roi_if_buy_signal = True + + # Required + startup_candle_count: int = 233 + process_only_new_candles = False + + # Strategy Specific Variable Storage + custom_trade_info = {} + custom_fiat = "USD" # Only relevant if stake is BTC or ETH + custom_btc_inf = False # Don't change this. + + """ + Informative Pair Definitions + """ + def informative_pairs(self): + # add all whitelisted pairs on informative timeframe + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, self.inf_timeframe) for pair in pairs] + + # add extra informative pairs if the stake is BTC or ETH + if self.config['stake_currency'] in ('BTC', 'ETH'): + for pair in pairs: + coin, stake = pair.split('/') + coin_fiat = f"{coin}/{self.custom_fiat}" + informative_pairs += [(coin_fiat, self.timeframe)] + + stake_fiat = f"{self.config['stake_currency']}/{self.custom_fiat}" + informative_pairs += [(stake_fiat, self.timeframe)] + # if BTC/STAKE is not in whitelist, add it as an informative pair on both timeframes + else: + btc_stake = f"BTC/{self.config['stake_currency']}" + if not btc_stake in pairs: + informative_pairs += [(btc_stake, self.timeframe)] + + return informative_pairs + + """ + Indicator Definitions + """ + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + if not metadata['pair'] in self.custom_trade_info: + self.custom_trade_info[metadata['pair']] = {} + if not 'had-trend' in self.custom_trade_info[metadata["pair"]]: + self.custom_trade_info[metadata['pair']]['had-trend'] = False + + ## Base Timeframe / Pair + + # Kaufmann Adaptive Moving Average + dataframe['kama'] = ta.KAMA(dataframe, length=233) + + # RMI: https://www.tradingview.com/script/kwIt9OgQ-Relative-Momentum-Index/ + dataframe['rmi'] = RMI(dataframe, length=24, mom=5) + + # Momentum Pinball: https://www.tradingview.com/script/fBpVB1ez-Momentum-Pinball-Indicator/ + dataframe['roc-mp'] = ta.ROC(dataframe, timeperiod=1) + dataframe['mp'] = ta.RSI(dataframe['roc-mp'], timeperiod=3) + + # MA Streak: https://www.tradingview.com/script/Yq1z7cIv-MA-Streak-Can-Show-When-a-Run-Is-Getting-Long-in-the-Tooth/ + dataframe['mastreak'] = mastreak(dataframe, period=4) + + # Percent Change Channel: https://www.tradingview.com/script/6wwAWXA1-MA-Streak-Change-Channel/ + upper, mid, lower = pcc(dataframe, period=40, mult=3) + dataframe['pcc-lowerband'] = lower + dataframe['pcc-upperband'] = upper + + lookup_idxs = dataframe.index.values - (abs(dataframe['mastreak'].values) + 1) + valid_lookups = lookup_idxs >= 0 + dataframe['sbc'] = np.nan + dataframe.loc[valid_lookups, 'sbc'] = dataframe['close'].to_numpy()[lookup_idxs[valid_lookups].astype(int)] + + dataframe['streak-roc'] = 100 * (dataframe['close'] - dataframe['sbc']) / dataframe['sbc'] + + # Trends, Peaks and Crosses + dataframe['candle-up'] = np.where(dataframe['close'] >= dataframe['open'],1,0) + dataframe['candle-up-trend'] = np.where(dataframe['candle-up'].rolling(5).sum() >= 3,1,0) + + dataframe['rmi-up'] = np.where(dataframe['rmi'] >= dataframe['rmi'].shift(),1,0) + dataframe['rmi-up-trend'] = np.where(dataframe['rmi-up'].rolling(5).sum() >= 3,1,0) + + dataframe['rmi-dn'] = np.where(dataframe['rmi'] <= dataframe['rmi'].shift(),1,0) + dataframe['rmi-dn-count'] = dataframe['rmi-dn'].rolling(8).sum() + + dataframe['streak-bo'] = np.where(dataframe['streak-roc'] < dataframe['pcc-lowerband'],1,0) + dataframe['streak-bo-count'] = dataframe['streak-bo'].rolling(8).sum() + + # Indicators used only for ROI and Custom Stoploss + ssldown, sslup = SSLChannels_ATR(dataframe, length=21) + dataframe['sroc'] = SROC(dataframe, roclen=21, emalen=13, smooth=21) + dataframe['ssl-dir'] = np.where(sslup > ssldown,'up','down') + + # Base pair informative timeframe indicators + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_timeframe) + + # Get the "average day range" between the 1d high and 1d low to set up guards + informative['1d-high'] = informative['close'].rolling(24).max() + informative['1d-low'] = informative['close'].rolling(24).min() + informative['adr'] = informative['1d-high'] - informative['1d-low'] + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, self.inf_timeframe, ffill=True) + + # Other stake specific informative indicators + # e.g if stake is BTC and current coin is XLM (pair: XLM/BTC) + if self.config['stake_currency'] in ('BTC', 'ETH'): + coin, stake = metadata['pair'].split('/') + fiat = self.custom_fiat + coin_fiat = f"{coin}/{fiat}" + stake_fiat = f"{stake}/{fiat}" + + # Informative COIN/FIAT e.g. XLM/USD - Base Timeframe + coin_fiat_tf = self.dp.get_pair_dataframe(pair=coin_fiat, timeframe=self.timeframe) + dataframe[f"{fiat}_rmi"] = RMI(coin_fiat_tf, length=55, mom=5) + + # Informative STAKE/FIAT e.g. BTC/USD - Base Timeframe + stake_fiat_tf = self.dp.get_pair_dataframe(pair=stake_fiat, timeframe=self.timeframe) + dataframe[f"{stake}_rmi"] = RMI(stake_fiat_tf, length=55, mom=5) + + # Informatives for BTC/STAKE if not in whitelist + else: + pairs = self.dp.current_whitelist() + btc_stake = f"BTC/{self.config['stake_currency']}" + if not btc_stake in pairs: + self.custom_btc_inf = True + # BTC/STAKE - Base Timeframe + btc_stake_tf = self.dp.get_pair_dataframe(pair=btc_stake, timeframe=self.timeframe) + dataframe['BTC_rmi'] = RMI(btc_stake_tf, length=55, mom=5) + dataframe['BTC_close'] = btc_stake_tf['close'] + dataframe['BTC_kama'] = ta.KAMA(btc_stake_tf, length=144) + + return dataframe + + """ + Buy Signal + """ + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + + # Informative Timeframe Guards + conditions.append( + (dataframe['close'] <= dataframe[f"1d-low_{self.inf_timeframe}"] + + (self.inf_pct_adr.value * dataframe[f"adr_{self.inf_timeframe}"])) + ) + + # Base Timeframe Guards + conditions.append( + (dataframe['rmi-dn-count'] >= self.base_rmi_streak.value) & + (dataframe['streak-bo-count'] >= self.base_ma_streak.value) & + (dataframe['rmi'] <= self.base_rmi_max.value) & + (dataframe['rmi'] >= self.base_rmi_min.value) & + (dataframe['mp'] <= self.base_mp.value) + ) + + # Base Timeframe Trigger + if self.base_trigger.value == 'pcc': + conditions.append(qtpylib.crossed_above(dataframe['streak-roc'], dataframe['pcc-lowerband'])) + + if self.base_trigger.value == 'rmi': + conditions.append(dataframe['rmi-up-trend'] == 1) + + # Extra conditions for */BTC and */ETH stakes on additional informative pairs + if self.config['stake_currency'] in ('BTC', 'ETH'): + conditions.append( + (dataframe[f"{self.custom_fiat}_rmi"] > self.xtra_base_fiat_rmi.value) | + (dataframe[f"{self.config['stake_currency']}_rmi"] < self.xtra_base_stake_rmi.value) + ) + # Extra conditions for BTC/STAKE if not in whitelist + else: + if self.custom_btc_inf: + if self.xbtc_guard.value == 'strict': + conditions.append( + ( + (dataframe['BTC_rmi'] > self.xbtc_base_rmi.value) & + (dataframe['BTC_close'] > dataframe['BTC_kama']) + ) + ) + if self.xbtc_guard.value == 'lazy': + conditions.append( + (dataframe['close'] > dataframe['kama']) | + ( + (dataframe['BTC_rmi'] > self.xbtc_base_rmi.value) & + (dataframe['BTC_close'] > dataframe['BTC_kama']) + ) + ) + + conditions.append(dataframe['volume'].gt(0)) + + if conditions: + dataframe.loc[ + reduce(lambda x, y: x & y, conditions), + 'buy'] = 1 + + return dataframe + + """ + Sell Signal + """ + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe['sell'] = 0 + + return dataframe + + """ + Custom Stoploss + """ + def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + trade_dur = int((current_time.timestamp() - trade.open_date_utc.timestamp()) // 60) + in_trend = self.custom_trade_info[trade.pair]['had-trend'] + + # Determine how we sell when we are in a loss + if current_profit < self.cstop_loss_threshold.value: + if self.cstop_bail_how.value == 'roc' or self.cstop_bail_how.value == 'any': + # Dynamic bailout based on rate of change + if last_candle['sroc'] <= self.cstop_bail_roc.value: + return 0.01 + if self.cstop_bail_how.value == 'time' or self.cstop_bail_how.value == 'any': + # Dynamic bailout based on time, unless time_trend is true and there is a potential reversal + if trade_dur > self.cstop_bail_time.value: + if self.cstop_bail_time_trend.value == True and in_trend == True: + return 1 + else: + return 0.01 + return 1 + + """ + Custom Sell + """ + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + + trade_dur = int((current_time.timestamp() - trade.open_date_utc.timestamp()) // 60) + max_profit = max(0, trade.calc_profit_ratio(trade.max_rate)) + pullback_value = max(0, (max_profit - self.csell_pullback_amount.value)) + in_trend = False + + # Determine our current ROI point based on the defined type + if self.csell_roi_type.value == 'static': + min_roi = self.csell_roi_start.value + elif self.csell_roi_type.value == 'decay': + min_roi = linear_decay(self.csell_roi_start.value, self.csell_roi_end.value, 0, self.csell_roi_time.value, trade_dur) + elif self.csell_roi_type.value == 'step': + if trade_dur < self.csell_roi_time.value: + min_roi = self.csell_roi_start.value + else: + min_roi = self.csell_roi_end.value + + # Determine if there is a trend + if self.csell_trend_type.value == 'rmi' or self.csell_trend_type.value == 'any': + if last_candle['rmi-up-trend'] == 1: + in_trend = True + if self.csell_trend_type.value == 'ssl' or self.csell_trend_type.value == 'any': + if last_candle['ssl-dir'] == 'up': + in_trend = True + if self.csell_trend_type.value == 'candle' or self.csell_trend_type.value == 'any': + if last_candle['candle-up-trend'] == 1: + in_trend = True + + # Don't sell if we are in a trend unless the pullback threshold is met + if in_trend == True and current_profit > 0: + # Record that we were in a trend for this trade/pair for a more useful sell message later + self.custom_trade_info[trade.pair]['had-trend'] = True + # If pullback is enabled and profit has pulled back allow a sell, maybe + if self.csell_pullback.value == True and (current_profit <= pullback_value): + if self.csell_pullback_respect_roi.value == True and current_profit > min_roi: + return 'intrend_pullback_roi' + elif self.csell_pullback_respect_roi.value == False: + if current_profit > min_roi: + return 'intrend_pullback_roi' + else: + return 'intrend_pullback_noroi' + # We are in a trend and pullback is disabled or has not happened or various criteria were not met, hold + return None + # If we are not in a trend, just use the roi value + elif in_trend == False: + if self.custom_trade_info[trade.pair]['had-trend']: + if current_profit > min_roi: + self.custom_trade_info[trade.pair]['had-trend'] = False + return 'trend_roi' + elif self.csell_endtrend_respect_roi.value == False: + self.custom_trade_info[trade.pair]['had-trend'] = False + return 'trend_noroi' + elif current_profit > min_roi: + return 'notrend_roi' + else: + return None + diff --git a/StJD01.py b/StJD01.py new file mode 100644 index 0000000..2747fa1 --- /dev/null +++ b/StJD01.py @@ -0,0 +1,506 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# flake8: noqa: F401 + +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame + +from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, + IStrategy, IntParameter) + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class StJD01(IStrategy): + """ + This is a strategy template to get you started. + More information in https://www.freqtrade.io/en/latest/strategy-customization/ + + You can: + :return: a Dataframe with all mandatory indicators for the strategies + - Rename the class name (Do not forget to update class_name) + - Add any methods you want to build your strategy + - Add any lib you need to build your strategy + + You must keep: + - the lib in the section "Do not remove these libs" + - the methods: populate_indicators, populate_buy_trend, populate_sell_trend + You should keep: + - timeframe, minimal_roi, stoploss, trailing_* + """ + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi". + minimal_roi = { + "0": 100 + } + + # Optimal stoploss designed for the strategy. + # This attribute will be overridden if the config file contains "stoploss". + stoploss = -1 + + # Trailing stoploss + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.015 + trailing_only_offset_is_reached = True + + # Optimal timeframe for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'tema': {}, + 'sar': {'color': 'white'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "MACD": { + 'macd': {'color': 'blue'}, + 'macdsignal': {'color': 'orange'}, + }, + "RSI": { + 'rsi': {'color': 'red'}, + } + } + } + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + + 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: Dataframe with data from the exchange + :param metadata: Additional information, like the currently traded pair + :return: a Dataframe with all mandatory indicators for the strategies + """ + + # Momentum Indicators + # ------------------------------------ + + # # ADX + # dataframe['adx'] = ta.ADX(dataframe) + # + # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + # + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # Awesome Oscillator + dataframe['ao'] = qtpylib.awesome_oscillator(dataframe) + + # # Keltner Channel + # keltner = qtpylib.keltner_channel(dataframe) + # dataframe["kc_upperband"] = keltner["upper"] + # dataframe["kc_lowerband"] = keltner["lower"] + # dataframe["kc_middleband"] = keltner["mid"] + # dataframe["kc_percent"] = ( + # (dataframe["close"] - dataframe["kc_lowerband"]) / + # (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) + # ) + # dataframe["kc_width"] = ( + # (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) / dataframe["kc_middleband"] + # ) + + # # Ultimate Oscillator + # dataframe['uo'] = ta.ULTOSC(dataframe) + + # # Commodity Channel Index: values [Oversold:-100, Overbought:100] + # dataframe['cci'] = ta.CCI(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # Inverse Fisher transform on RSI: values [-1.0, 1.0] (https://goo.gl/2JGGoy) + # rsi = 0.1 * (dataframe['rsi'] - 50) + # dataframe['fisher_rsi'] = (np.exp(2 * rsi) - 1) / (np.exp(2 * rsi) + 1) + + # # Inverse Fisher transform on RSI normalized: values [0.0, 100.0] (https://goo.gl/2JGGoy) + # dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1) + + # # Stochastic Slow + # stoch = ta.STOCH(dataframe) + # dataframe['slowd'] = stoch['slowd'] + # dataframe['slowk'] = stoch['slowk'] + + # Stochastic Fast + stoch_fast = ta.STOCHF(dataframe) + dataframe['fastd'] = stoch_fast['fastd'] + dataframe['fastk'] = stoch_fast['fastk'] + + # # Stochastic RSI + # Please read https://github.com/freqtrade/freqtrade/issues/2961 before using this. + # STOCHRSI is NOT aligned with tradingview, which may result in non-expected results. + # stoch_rsi = ta.STOCHRSI(dataframe) + # dataframe['fastd_rsi'] = stoch_rsi['fastd'] + # dataframe['fastk_rsi'] = stoch_rsi['fastk'] + + # MACD + macd = ta.MACD(dataframe) + dataframe['macd'] = macd['macd'] + dataframe['macdsignal'] = macd['macdsignal'] + dataframe['macdhist'] = macd['macdhist'] + + # MFI + dataframe['mfi'] = ta.MFI(dataframe) + + # # ROC + # dataframe['roc'] = ta.ROC(dataframe) + + # Overlap Studies + # ------------------------------------ + + + fast_period = 5 + slow_period = 50 + dataframe['fast_MA'] = ta.SMA(dataframe, timeperiod=fast_period) + dataframe['slow_MA'] = ta.SMA(dataframe, timeperiod=slow_period) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + # dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + + # Parabolic SAR + dataframe['sar'] = ta.SAR(dataframe) + + # 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 + # # ------------------------------------ + # # Heikin Ashi Strategy + # heikinashi = qtpylib.heikinashi(dataframe) + # dataframe['ha_open'] = heikinashi['open'] + # dataframe['ha_close'] = heikinashi['close'] + # dataframe['ha_high'] = heikinashi['high'] + # dataframe['ha_low'] = heikinashi['low'] + + # Retrieve best bid and best ask from the orderbook + # ------------------------------------ + """ + # first check if dataprovider is available + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + ob = self.dp.orderbook(metadata['pair'], 1) + dataframe['best_bid'] = ob['bids'][0][0] + dataframe['best_ask'] = ob['asks'][0][0] + """ + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + qtpylib.crossed_above(dataframe['fast_MA'], dataframe['slow_MA']) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + qtpylib.crossed_below(dataframe['fast_MA'], dataframe['slow_MA']) + ), + 'sell' + ] = 1 + + return dataframe + + +class StrategyHelperLocal: + """ + simple helper class to predefine a couple of patterns for our + strategy + """ + + @staticmethod + def two_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) # & + # (dataframe['open'].shift(2) < dataframe['close'].shift(2)) + ) + + @staticmethod + def three_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) + ) + + @staticmethod + def seven_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) + ) + + @staticmethod + def eight_green_candles(dataframe): + """ + evaluates if we are having 8 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) & + (dataframe['open'].shift(8) < dataframe['close'].shift(8)) + ) + + @staticmethod + def three_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) & + (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def four_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) & + (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + (dataframe['open'].shift(3 + shift) > dataframe['close'].shift(3 + shift)) & + (dataframe['open'].shift(4 + shift) > dataframe['close'].shift(4 + shift)) + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def four_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] > dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) + ) + + @staticmethod + def four_red_one_green_candle(dataframe): + """ + evaluates if we are having a green candle and 4 previous red + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) > dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) > dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) > dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) > dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) > dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) > dataframe['close'].shift(7)) + ) diff --git a/StJD02.py b/StJD02.py new file mode 100644 index 0000000..db17d9c --- /dev/null +++ b/StJD02.py @@ -0,0 +1,525 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# flake8: noqa: F401 + +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame + +from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, + IStrategy, IntParameter) + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class StJD02(IStrategy): + """ + This is a strategy template to get you started. + More information in https://www.freqtrade.io/en/latest/strategy-customization/ + + You can: + :return: a Dataframe with all mandatory indicators for the strategies + - Rename the class name (Do not forget to update class_name) + - Add any methods you want to build your strategy + - Add any lib you need to build your strategy + + You must keep: + - the lib in the section "Do not remove these libs" + - the methods: populate_indicators, populate_buy_trend, populate_sell_trend + You should keep: + - timeframe, minimal_roi, stoploss, trailing_* + """ + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi". + minimal_roi = { + "0": 0.015 + } + + # Optimal stoploss designed for the strategy. + # This attribute will be overridden if the config file contains "stoploss". + stoploss = -0.03 + + # Trailing stoploss + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.015 + trailing_only_offset_is_reached = True + + # Optimal timeframe for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'tema': {}, + 'sar': {'color': 'white'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "MACD": { + 'macd': {'color': 'blue'}, + 'macdsignal': {'color': 'orange'}, + }, + "RSI": { + 'rsi': {'color': 'red'}, + } + } + } + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + + 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: Dataframe with data from the exchange + :param metadata: Additional information, like the currently traded pair + :return: a Dataframe with all mandatory indicators for the strategies + """ + + # Momentum Indicators + # ------------------------------------ + + # # ADX + # dataframe['adx'] = ta.ADX(dataframe) + # + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + # + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # Awesome Oscillator + dataframe['ao'] = qtpylib.awesome_oscillator(dataframe) + + # # Keltner Channel + # keltner = qtpylib.keltner_channel(dataframe) + # dataframe["kc_upperband"] = keltner["upper"] + # dataframe["kc_lowerband"] = keltner["lower"] + # dataframe["kc_middleband"] = keltner["mid"] + # dataframe["kc_percent"] = ( + # (dataframe["close"] - dataframe["kc_lowerband"]) / + # (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) + # ) + # dataframe["kc_width"] = ( + # (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) / dataframe["kc_middleband"] + # ) + + # # Ultimate Oscillator + # dataframe['uo'] = ta.ULTOSC(dataframe) + + # # Commodity Channel Index: values [Oversold:-100, Overbought:100] + # dataframe['cci'] = ta.CCI(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # Inverse Fisher transform on RSI: values [-1.0, 1.0] (https://goo.gl/2JGGoy) + # rsi = 0.1 * (dataframe['rsi'] - 50) + # dataframe['fisher_rsi'] = (np.exp(2 * rsi) - 1) / (np.exp(2 * rsi) + 1) + + # # Inverse Fisher transform on RSI normalized: values [0.0, 100.0] (https://goo.gl/2JGGoy) + # dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1) + + # # Stochastic Slow + # stoch = ta.STOCH(dataframe) + # dataframe['slowd'] = stoch['slowd'] + # dataframe['slowk'] = stoch['slowk'] + + # Stochastic Fast + stoch_fast = ta.STOCHF(dataframe) + dataframe['fastd'] = stoch_fast['fastd'] + dataframe['fastk'] = stoch_fast['fastk'] + + # # Stochastic RSI + # Please read https://github.com/freqtrade/freqtrade/issues/2961 before using this. + # STOCHRSI is NOT aligned with tradingview, which may result in non-expected results. + # stoch_rsi = ta.STOCHRSI(dataframe) + # dataframe['fastd_rsi'] = stoch_rsi['fastd'] + # dataframe['fastk_rsi'] = stoch_rsi['fastk'] + + # MACD + macd = ta.MACD(dataframe) + dataframe['macd'] = macd['macd'] + dataframe['macdsignal'] = macd['macdsignal'] + dataframe['macdhist'] = macd['macdhist'] + + # MFI + dataframe['mfi'] = ta.MFI(dataframe) + + # # ROC + # dataframe['roc'] = ta.ROC(dataframe) + + # Overlap Studies + # ------------------------------------ + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + fast_period = 5 + slow_period = 50 + dataframe['fast_MA'] = ta.SMA(dataframe, timeperiod=fast_period) + dataframe['slow_MA'] = ta.SMA(dataframe, timeperiod=slow_period) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + # dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + + # Parabolic SAR + dataframe['sar'] = ta.SAR(dataframe) + + # 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 + # # ------------------------------------ + # # Heikin Ashi Strategy + # heikinashi = qtpylib.heikinashi(dataframe) + # dataframe['ha_open'] = heikinashi['open'] + # dataframe['ha_close'] = heikinashi['close'] + # dataframe['ha_high'] = heikinashi['high'] + # dataframe['ha_low'] = heikinashi['low'] + + # Linear Regression Angle + + #dataframe['angle'] = ta.LINEARREG_ANGLE(dataframe['close'], timeperiod=5) + dataframe['angle'] = ta.LINEARREG_ANGLE(dataframe['close'], timeperiod=30) + + # Linear Regression Line + dataframe['lr_middle'] = ta.LINEARREG(dataframe['close'], timeperiod=25) + dataframe['atr'] = ta.ATR(dataframe, timeperiod=25) + dataframe['lr_lower1.0'] = dataframe['lr_middle'] - dataframe['atr'] * 1 + + # Retrieve best bid and best ask from the orderbook + # ------------------------------------ + """ + # first check if dataprovider is available + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + ob = self.dp.orderbook(metadata['pair'], 1) + dataframe['best_bid'] = ob['bids'][0][0] + dataframe['best_ask'] = ob['asks'][0][0] + """ + + 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 populated with indicators + :param metadata: Additional information, like the currently traded pair + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + # (dataframe["close"] >= dataframe["bb_upperband"]) & + (dataframe["close"] < dataframe["bb_lowerband"]) & + (dataframe["angle"] > 0) & + (dataframe['volume'] > 0) # Make sure Volume is not 0 + ), + '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 populated with indicators + :param metadata: Additional information, like the currently traded pair + :return: DataFrame with buy column + dataframe.loc[ + ( + (StrategyHelperLocal.two_red_candles(dataframe)) & + (dataframe["bb_upperband"].shift(1) > dataframe["bb_upperband"]) & + (dataframe['volume'] > 0) # Make sure Volume is not 0 + ), + 'sell'] = 1 + """ + return dataframe + + +class StrategyHelperLocal: + """ + simple helper class to predefine a couple of patterns for our + strategy + """ + + @staticmethod + def two_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) # & + # (dataframe['open'].shift(2) < dataframe['close'].shift(2)) + ) + + @staticmethod + def three_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) + ) + + @staticmethod + def seven_green_candles(dataframe): + """ + evaluates if we are having 7 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) + ) + + @staticmethod + def eight_green_candles(dataframe): + """ + evaluates if we are having 8 green candles in a row + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) < dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) < dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) < dataframe['close'].shift(7)) & + (dataframe['open'].shift(8) < dataframe['close'].shift(8)) + ) + + @staticmethod + def two_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) + ) + + @staticmethod + def four_red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) & + (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + (dataframe['open'].shift(3 + shift) > dataframe['close'].shift(3 + shift)) & + (dataframe['open'].shift(4 + shift) > dataframe['close'].shift(4 + shift)) + # (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + # (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + # (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) & + # (dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift)) + ) + + @staticmethod + def four_green_one_red_candle(dataframe): + """ + evaluates if we are having a red candle and 4 previous green + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] > dataframe['close']) & + (dataframe['open'].shift(1) < dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) < dataframe['close'].shift(4)) + ) + + @staticmethod + def four_red_one_green_candle(dataframe): + """ + evaluates if we are having a green candle and 4 previous red + :param self: + :param dataframe: + :return: + """ + return ( + (dataframe['open'] < dataframe['close']) & + (dataframe['open'].shift(1) > dataframe['close'].shift(1)) & + (dataframe['open'].shift(2) > dataframe['close'].shift(2)) & + (dataframe['open'].shift(3) > dataframe['close'].shift(3)) & + (dataframe['open'].shift(4) > dataframe['close'].shift(4)) & + (dataframe['open'].shift(5) > dataframe['close'].shift(5)) & + (dataframe['open'].shift(6) > dataframe['close'].shift(6)) & + (dataframe['open'].shift(7) > dataframe['close'].shift(7)) + ) diff --git a/Strategy001.py b/Strategy001.py new file mode 100644 index 0000000..64be087 --- /dev/null +++ b/Strategy001.py @@ -0,0 +1,121 @@ + +# --- Do not remove these libs --- +from freqtrade.strategy import IStrategy +from typing import Dict, List +from functools import reduce +from pandas import DataFrame +# -------------------------------- + +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class Strategy001(IStrategy): + """ + Strategy 001 + author@: Gerald Lonlas + github@: https://github.com/freqtrade/freqtrade-strategies + + How to use it? + > python3 ./freqtrade/main.py -s Strategy001 + """ + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi" + minimal_roi = { + "60": 0.01, + "30": 0.03, + "20": 0.04, + "0": 0.05 + } + + # Optimal stoploss designed for the strategy + # This attribute will be overridden if the config file contains "stoploss" + stoploss = -0.10 + + # Optimal timeframe for the strategy + timeframe = '5m' + + # trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.01 + trailing_stop_positive_offset = 0.02 + + # run "populate_indicators" only for new candle + process_only_new_candles = False + + # Experimental settings (configuration will overide these if set) + use_sell_signal = True + sell_profit_only = True + ignore_roi_if_buy_signal = False + + # Optional order type mapping + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + + 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. + """ + + dataframe['ema20'] = ta.EMA(dataframe, timeperiod=20) + dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + heikinashi = qtpylib.heikinashi(dataframe) + dataframe['ha_open'] = heikinashi['open'] + dataframe['ha_close'] = heikinashi['close'] + + 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 + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + qtpylib.crossed_above(dataframe['ema20'], dataframe['ema50']) & + (dataframe['ha_close'] > dataframe['ema20']) & + (dataframe['ha_open'] < dataframe['ha_close']) # green bar + ), + '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 + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + qtpylib.crossed_above(dataframe['ema50'], dataframe['ema100']) & + (dataframe['ha_close'] < dataframe['ema20']) & + (dataframe['ha_open'] > dataframe['ha_close']) # red bar + ), + 'sell'] = 1 + return dataframe diff --git a/Strategy001_custom_sell.py b/Strategy001_custom_sell.py new file mode 100644 index 0000000..231d085 --- /dev/null +++ b/Strategy001_custom_sell.py @@ -0,0 +1,141 @@ + +# --- Do not remove these libs --- +from freqtrade.strategy.interface import IStrategy +from typing import Dict, List +from functools import reduce +from pandas import DataFrame +# -------------------------------- + +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + +class Strategy001_custom_sell(IStrategy): + + """ + Strategy 001_custom_sell + author@: Gerald Lonlas, froggleston + github@: https://github.com/freqtrade/freqtrade-strategies + + How to use it? + > python3 ./freqtrade/main.py -s Strategy001_custom_sell + """ + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi" + minimal_roi = { + "60": 0.01, + "30": 0.03, + "20": 0.04, + "0": 0.05 + } + + # Optimal stoploss designed for the strategy + # This attribute will be overridden if the config file contains "stoploss" + stoploss = -0.10 + + # Optimal timeframe for the strategy + timeframe = '5m' + + # trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.01 + trailing_stop_positive_offset = 0.02 + + # run "populate_indicators" only for new candle + process_only_new_candles = False + + # Experimental settings (configuration will overide these if set) + use_sell_signal = True + sell_profit_only = True + ignore_roi_if_buy_signal = False + + # Optional order type mapping + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + + 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. + """ + + dataframe['ema20'] = ta.EMA(dataframe, timeperiod=20) + dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + heikinashi = qtpylib.heikinashi(dataframe) + dataframe['ha_open'] = heikinashi['open'] + dataframe['ha_close'] = heikinashi['close'] + + dataframe['rsi'] = ta.RSI(dataframe, 14) + + 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 + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + qtpylib.crossed_above(dataframe['ema20'], dataframe['ema50']) & + (dataframe['ha_close'] > dataframe['ema20']) & + (dataframe['ha_open'] < dataframe['ha_close']) # green bar + ), + '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 + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + qtpylib.crossed_above(dataframe['ema50'], dataframe['ema100']) & + (dataframe['ha_close'] < dataframe['ema20']) & + (dataframe['ha_open'] > dataframe['ha_close']) # red bar + ), + 'sell'] = 1 + return dataframe + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, current_profit: float, **kwargs): + """ + Sell only when matching some criteria other than those used to generate the sell signal + :return: str sell_reason, if any, otherwise None + """ + # get dataframe + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + + # get the current candle + current_candle = dataframe.iloc[-1].squeeze() + + # if RSI greater than 70 and profit is positive, then sell + if (current_candle['rsi'] > 70) and (current_profit > 0): + return "rsi_profit_sell" + + # else, hold + return None \ No newline at end of file diff --git a/Strategy002.py b/Strategy002.py new file mode 100644 index 0000000..a7c0c98 --- /dev/null +++ b/Strategy002.py @@ -0,0 +1,135 @@ + +# --- Do not remove these libs --- +from freqtrade.strategy import IStrategy +from typing import Dict, List +from functools import reduce +from pandas import DataFrame +# -------------------------------- + +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +import numpy # noqa + + +class Strategy002(IStrategy): + """ + Strategy 002 + author@: Gerald Lonlas + github@: https://github.com/freqtrade/freqtrade-strategies + + How to use it? + > python3 ./freqtrade/main.py -s Strategy002 + """ + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi" + minimal_roi = { + "60": 0.01, + "30": 0.03, + "20": 0.04, + "0": 0.05 + } + + # Optimal stoploss designed for the strategy + # This attribute will be overridden if the config file contains "stoploss" + stoploss = -0.10 + + # Optimal timeframe for the strategy + timeframe = '5m' + + # trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.01 + trailing_stop_positive_offset = 0.02 + + # run "populate_indicators" only for new candle + process_only_new_candles = False + + # Experimental settings (configuration will overide these if set) + use_sell_signal = True + sell_profit_only = True + ignore_roi_if_buy_signal = False + + # Optional order type mapping + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + + 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. + """ + + # Stoch + stoch = ta.STOCH(dataframe) + dataframe['slowk'] = stoch['slowk'] + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy) + rsi = 0.1 * (dataframe['rsi'] - 50) + dataframe['fisher_rsi'] = (numpy.exp(2 * rsi) - 1) / (numpy.exp(2 * rsi) + 1) + + # Bollinger bands + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) + dataframe['bb_lowerband'] = bollinger['lower'] + + # SAR Parabol + dataframe['sar'] = ta.SAR(dataframe) + + # Hammer: values [0, 100] + dataframe['CDLHAMMER'] = ta.CDLHAMMER(dataframe) + + 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 + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (dataframe['rsi'] < 30) & + (dataframe['slowk'] < 20) & + (dataframe['bb_lowerband'] > dataframe['close']) & + (dataframe['CDLHAMMER'] == 100) + ), + '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 + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (dataframe['sar'] > dataframe['close']) & + (dataframe['fisher_rsi'] > 0.3) + ), + 'sell'] = 1 + return dataframe diff --git a/Strategy003.py b/Strategy003.py new file mode 100644 index 0000000..8d8630b --- /dev/null +++ b/Strategy003.py @@ -0,0 +1,152 @@ + +# --- Do not remove these libs --- +from freqtrade.strategy.interface import IStrategy +from typing import Dict, List +from functools import reduce +from pandas import DataFrame +# -------------------------------- + +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +import numpy # noqa + + +class Strategy003(IStrategy): + """ + Strategy 003 + author@: Gerald Lonlas + github@: https://github.com/freqtrade/freqtrade-strategies + + How to use it? + > python3 ./freqtrade/main.py -s Strategy003 + """ + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi" + minimal_roi = { + "60": 0.01, + "30": 0.03, + "20": 0.04, + "0": 0.05 + } + + # Optimal stoploss designed for the strategy + # This attribute will be overridden if the config file contains "stoploss" + stoploss = -0.10 + + # Optimal timeframe for the strategy + timeframe = '5m' + + # trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.01 + trailing_stop_positive_offset = 0.02 + + # run "populate_indicators" only for new candle + process_only_new_candles = False + + # Experimental settings (configuration will overide these if set) + use_sell_signal = True + sell_profit_only = True + ignore_roi_if_buy_signal = False + + # Optional order type mapping + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + + 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. + """ + + # MFI + dataframe['mfi'] = ta.MFI(dataframe) + + # Stoch fast + stoch_fast = ta.STOCHF(dataframe) + dataframe['fastd'] = stoch_fast['fastd'] + dataframe['fastk'] = stoch_fast['fastk'] + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy) + rsi = 0.1 * (dataframe['rsi'] - 50) + dataframe['fisher_rsi'] = (numpy.exp(2 * rsi) - 1) / (numpy.exp(2 * rsi) + 1) + + # Bollinger bands + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) + dataframe['bb_lowerband'] = bollinger['lower'] + + # EMA - Exponential Moving Average + 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) + + 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 + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (dataframe['rsi'] < 28) & + (dataframe['rsi'] > 0) & + (dataframe['close'] < dataframe['sma']) & + (dataframe['fisher_rsi'] < -0.94) & + (dataframe['mfi'] < 16.0) & + ( + (dataframe['ema50'] > dataframe['ema100']) | + (qtpylib.crossed_above(dataframe['ema5'], dataframe['ema10'])) + ) & + (dataframe['fastd'] > dataframe['fastk']) & + (dataframe['fastd'] > 0) + ), + '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 + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (dataframe['sar'] > dataframe['close']) & + (dataframe['fisher_rsi'] > 0.3) + ), + 'sell'] = 1 + return dataframe diff --git a/Strategy004.py b/Strategy004.py new file mode 100644 index 0000000..120aff1 --- /dev/null +++ b/Strategy004.py @@ -0,0 +1,154 @@ + +# --- Do not remove these libs --- +from freqtrade.strategy import IStrategy +from typing import Dict, List +from functools import reduce +from pandas import DataFrame +# -------------------------------- + +import talib.abstract as ta + + +class Strategy004(IStrategy): + + """ + Strategy 004 + author@: Gerald Lonlas + github@: https://github.com/freqtrade/freqtrade-strategies + + How to use it? + > python3 ./freqtrade/main.py -s Strategy004 + """ + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi" + minimal_roi = { + "60": 0.01, + "30": 0.03, + "20": 0.04, + "0": 0.05 + } + + # Optimal stoploss designed for the strategy + # This attribute will be overridden if the config file contains "stoploss" + stoploss = -0.10 + + # Optimal timeframe for the strategy + timeframe = '5m' + + # trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.01 + trailing_stop_positive_offset = 0.02 + + # run "populate_indicators" only for new candle + process_only_new_candles = False + + # Experimental settings (configuration will overide these if set) + use_sell_signal = True + sell_profit_only = True + ignore_roi_if_buy_signal = False + + # Optional order type mapping + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + + 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. + """ + + # ADX + dataframe['adx'] = ta.ADX(dataframe) + dataframe['slowadx'] = ta.ADX(dataframe, 35) + + # Commodity Channel Index: values Oversold:<-100, Overbought:>100 + dataframe['cci'] = ta.CCI(dataframe) + + # Stoch + stoch = ta.STOCHF(dataframe, 5) + dataframe['fastd'] = stoch['fastd'] + dataframe['fastk'] = stoch['fastk'] + dataframe['fastk-previous'] = dataframe.fastk.shift(1) + dataframe['fastd-previous'] = dataframe.fastd.shift(1) + + # Slow Stoch + slowstoch = ta.STOCHF(dataframe, 50) + dataframe['slowfastd'] = slowstoch['fastd'] + dataframe['slowfastk'] = slowstoch['fastk'] + dataframe['slowfastk-previous'] = dataframe.slowfastk.shift(1) + dataframe['slowfastd-previous'] = dataframe.slowfastd.shift(1) + + # EMA - Exponential Moving Average + dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5) + + dataframe['mean-volume'] = dataframe['volume'].mean() + + 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 + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + ( + (dataframe['adx'] > 50) | + (dataframe['slowadx'] > 26) + ) & + (dataframe['cci'] < -100) & + ( + (dataframe['fastk-previous'] < 20) & + (dataframe['fastd-previous'] < 20) + ) & + ( + (dataframe['slowfastk-previous'] < 30) & + (dataframe['slowfastd-previous'] < 30) + ) & + (dataframe['fastk-previous'] < dataframe['fastd-previous']) & + (dataframe['fastk'] > dataframe['fastd']) & + (dataframe['mean-volume'] > 0.75) & + (dataframe['close'] > 0.00000100) + ), + '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 + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (dataframe['slowadx'] < 25) & + ((dataframe['fastk'] > 70) | (dataframe['fastd'] > 70)) & + (dataframe['fastk-previous'] < dataframe['fastd-previous']) & + (dataframe['close'] > dataframe['ema5']) + ), + 'sell'] = 1 + return dataframe diff --git a/Strategy005.json b/Strategy005.json new file mode 100644 index 0000000..074584d --- /dev/null +++ b/Strategy005.json @@ -0,0 +1,36 @@ +{ + "strategy_name": "Strategy005", + "params": { + "roi": { + "0": 0.05, + "20": 0.04, + "40": 0.03, + "80": 0.02, + "1440": 0.01 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.01, + "trailing_stop_positive_offset": 0.02, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_fastd": 7, + "buy_fishRsiNorma": 80, + "buy_rsi": 31, + "buy_volumeAVG": 119 + }, + "sell": { + "sell_fishRsiNorma": 30, + "sell_minusDI": 4, + "sell_rsi": 74, + "sell_trigger": "rsi-macd-minusdi" + }, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-07 18:45:24.449663+00:00" +} \ No newline at end of file diff --git a/Strategy005.py b/Strategy005.py new file mode 100644 index 0000000..7c23f28 --- /dev/null +++ b/Strategy005.py @@ -0,0 +1,185 @@ + +# --- Do not remove these libs --- +from freqtrade.strategy import IStrategy +from freqtrade.strategy import CategoricalParameter, IntParameter +from functools import reduce +from pandas import DataFrame +# -------------------------------- + +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +import numpy # noqa + + +class Strategy005(IStrategy): + """ + Strategy 005 + author@: Gerald Lonlas + github@: https://github.com/freqtrade/freqtrade-strategies + + How to use it? + > python3 ./freqtrade/main.py -s Strategy005 + """ + INTERFACE_VERSION = 2 + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi" + minimal_roi = { + "1440": 0.01, + "80": 0.02, + "40": 0.03, + "20": 0.04, + "0": 0.05 + } + + # Optimal stoploss designed for the strategy + # This attribute will be overridden if the config file contains "stoploss" + stoploss = -1 + + # Optimal timeframe for the strategy + timeframe = '5m' + + # trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.01 + trailing_stop_positive_offset = 0.02 + + # run "populate_indicators" only for new candle + process_only_new_candles = False + + # Experimental settings (configuration will overide these if set) + use_sell_signal = True + sell_profit_only = True + ignore_roi_if_buy_signal = False + + # Optional order type mapping + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + buy_volumeAVG = IntParameter(low=50, high=300, default=70, space='buy', optimize=True) + buy_rsi = IntParameter(low=1, high=100, default=30, space='buy', optimize=True) + buy_fastd = IntParameter(low=1, high=100, default=30, space='buy', optimize=True) + buy_fishRsiNorma = IntParameter(low=1, high=100, default=30, space='buy', optimize=True) + + sell_rsi = IntParameter(low=1, high=100, default=70, space='sell', optimize=True) + sell_minusDI = IntParameter(low=1, high=100, default=50, space='sell', optimize=True) + sell_fishRsiNorma = IntParameter(low=1, high=100, default=50, space='sell', optimize=True) + sell_trigger = CategoricalParameter(["rsi-macd-minusdi", "sar-fisherRsi"], + default=30, space='sell', optimize=True) + + # Buy hyperspace params: + buy_params = { + "buy_fastd": 1, + "buy_fishRsiNorma": 5, + "buy_rsi": 26, + "buy_volumeAVG": 150, + } + + # Sell hyperspace params: + sell_params = { + "sell_fishRsiNorma": 30, + "sell_minusDI": 4, + "sell_rsi": 74, + "sell_trigger": "rsi-macd-minusdi", + } + + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + + 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. + """ + + # MACD + macd = ta.MACD(dataframe) + dataframe['macd'] = macd['macd'] + dataframe['macdsignal'] = macd['macdsignal'] + + # Minus Directional Indicator / Movement + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy) + rsi = 0.1 * (dataframe['rsi'] - 50) + dataframe['fisher_rsi'] = (numpy.exp(2 * rsi) - 1) / (numpy.exp(2 * rsi) + 1) + # 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 fast + stoch_fast = ta.STOCHF(dataframe) + dataframe['fastd'] = stoch_fast['fastd'] + dataframe['fastk'] = stoch_fast['fastk'] + + # Overlap Studies + # ------------------------------------ + + # SAR Parabol + dataframe['sar'] = ta.SAR(dataframe) + + # SMA - Simple Moving Average + dataframe['sma'] = ta.SMA(dataframe, timeperiod=40) + + 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 + :return: DataFrame with buy column + """ + dataframe.loc[ + # Prod + ( + (dataframe['close'] > 0.00000200) & + (dataframe['volume'] > dataframe['volume'].rolling(self.buy_volumeAVG.value).mean() * 4) & + (dataframe['close'] < dataframe['sma']) & + (dataframe['fastd'] > dataframe['fastk']) & + (dataframe['rsi'] > self.buy_rsi.value) & + (dataframe['fastd'] > self.buy_fastd.value) & + (dataframe['fisher_rsi_norma'] < self.buy_fishRsiNorma.value) + ), + '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 + :return: DataFrame with buy column + """ + + conditions = [] + if self.sell_trigger.value == 'rsi-macd-minusdi': + conditions.append(qtpylib.crossed_above(dataframe['rsi'], self.sell_rsi.value)) + conditions.append(dataframe['macd'] < 0) + conditions.append(dataframe['minus_di'] > self.sell_minusDI.value) + if self.sell_trigger.value == 'sar-fisherRsi': + conditions.append(dataframe['sar'] > dataframe['close']) + conditions.append(dataframe['fisher_rsi'] > self.sell_fishRsiNorma.value) + + if conditions: + dataframe.loc[reduce(lambda x, y: x & y, conditions), 'sell'] = 1 + + return dataframe diff --git a/StrategyJD.json b/StrategyJD.json new file mode 100644 index 0000000..7e0bf30 --- /dev/null +++ b/StrategyJD.json @@ -0,0 +1,29 @@ +{ + "strategy_name": "StrategyJD", + "params": { + "stoploss": { + "stoploss": -0.118 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.11, + "buy_decalage": 12, + "buy_sma_percent": 0.98 + }, + "sell": {}, + "protection": {}, + "roi": { + "0": 0.391, + "142": 0.134, + "347": 0.067, + "818": 0 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-09 15:16:01.241716+00:00" +} \ No newline at end of file diff --git a/StrategyJD.jsonPositive b/StrategyJD.jsonPositive new file mode 100644 index 0000000..7e0bf30 --- /dev/null +++ b/StrategyJD.jsonPositive @@ -0,0 +1,29 @@ +{ + "strategy_name": "StrategyJD", + "params": { + "stoploss": { + "stoploss": -0.118 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.11, + "buy_decalage": 12, + "buy_sma_percent": 0.98 + }, + "sell": {}, + "protection": {}, + "roi": { + "0": 0.391, + "142": 0.134, + "347": 0.067, + "818": 0 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-09 15:16:01.241716+00:00" +} \ No newline at end of file diff --git a/StrategyJD.py b/StrategyJD.py new file mode 100644 index 0000000..88e85c5 --- /dev/null +++ b/StrategyJD.py @@ -0,0 +1,282 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyJD(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + #buy_msma_10 = DecimalParameter(0.997, 1.020, decimals=3, default=0.998, space="buy") + # pourcentage sma à dépasser + buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.97, space="buy") + buy_decalage = IntParameter(1, 24, default=1, space="buy") + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + 'rsi': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + } + } + } + + # def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + # current_profit: float, **kwargs): + # dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + # last_candle = dataframe.iloc[-1].squeeze() + # + # # Above 20% profit, sell when rsi < 80 + # if current_profit > 0.2: + # if last_candle['rsi'] < 80: + # return 'rsi_below_80' + # + # # Between 2% and 10%, sell if EMA-long above EMA-short + # if 0.02 < current_profit < 0.1: + # if last_candle['ema100'] > last_candle['ema10']: + # return 'ema_long_below_80' + # + # # Sell any positions at a loss if they are held for more than one day. + # if current_profit < -0.20 and (current_time - trade.open_date_utc).days >= 3: + # return 'unclog' + + # def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + # proposed_stake: float, min_stake: float, max_stake: float, + # **kwargs) -> float: + # + # dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + # current_candle = dataframe.iloc[-1].squeeze() + # + # if dataframe['open'] < dataframe['sma100'] * 0.98: + # # if self.config['stake_amount'] == 'unlimited': + # # # Use entire available wallet during favorable conditions when in compounding mode. + # # return max_stake + # # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + # + # # Use default stake amount. + # return proposed_stake + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + + dataframe['max50'] = ta.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = ta.MAX(dataframe['close'], timeperiod=200) + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['mema10_3'] = dataframe['ema10'] / dataframe['ema10'].shift(1) + # dataframe['mema10_5'] = dataframe['ema10'].rolling(5).mean() + + # dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['msma10_3'] = dataframe['sma10'] / dataframe['sma10'].shift(1) + + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # dataframe["rolling"] = (100 * (dataframe["close"] - dataframe["bb_lowerband"]) / dataframe["bb_lowerband"]).rolling(5).mean() + # dataframe["bb_rolling"] = dataframe["rolling"] / dataframe["bb_width"] + # dataframe['bb_rolling_min'] = ta.MIN(dataframe['bb_rolling'], timeperiod=5) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + for decalage in range(self.buy_decalage.value - 2, self.buy_decalage.value): + conditions = [ + (dataframe['bb_width'].shift(decalage) >= self.buy_bollinger.value), + # (dataframe['close'].shift(decalage) < dataframe['bb_lowerband'].shift(decalage)), + (dataframe['close'].shift(decalage) < dataframe['sma10'].shift(decalage)), + (dataframe['msma10_3'] > 0.998) + ] + # GUARDS AND TRENDS + + if conditions: + dataframe.loc[ + ( + (reduce(lambda x, y: x & y, conditions)) + ) + , + ['buy', 'buy_tag']] = (1, 'buy_msma_' + str(decalage)) + break + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['close'] < dataframe['open']) & + # (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + # (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + # (dataframe['close'] < dataframe['bb_lowerband']) & + # (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + # ), 'sell'] = 1 + return dataframe diff --git a/StrategyJD_1.json b/StrategyJD_1.json new file mode 100644 index 0000000..cdaa38b --- /dev/null +++ b/StrategyJD_1.json @@ -0,0 +1,31 @@ +{ + "strategy_name": "StrategyJD_1", + "params": { + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.11, + "buy_decalage": 17 + }, + "sell": {}, + "protection": { + "n_percent": 5, + "percent_sell": -0.05 + }, + "roi": { + "0": 0.41700000000000004, + "153": 0.14200000000000002, + "212": 0.044, + "658": 0 + }, + "stoploss": { + "stoploss": -0.069 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-09 18:26:53.845529+00:00" +} \ No newline at end of file diff --git a/StrategyJD_1.py b/StrategyJD_1.py new file mode 100644 index 0000000..d461a6c --- /dev/null +++ b/StrategyJD_1.py @@ -0,0 +1,287 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyJD_1(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + #buy_msma_10 = DecimalParameter(0.997, 1.020, decimals=3, default=0.998, space="buy") + # pourcentage sma à dépasser + # buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.97, space="buy") + buy_decalage = IntParameter(1, 24, default=1, space="buy") + + n_percent = IntParameter(1, 12, default=1, space="protection") + percent_sell = DecimalParameter(-0.2, -0.01, decimals=2, default=-0.08, space="protection") + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + 'rsi': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + } + } + } + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + + if last_candle['percent' + str(self.n_percent.value)] < self.percent_sell.value: + return 'sell_lost_percent' + str(self.n_percent.value) + + # Between 2% and 10%, sell if EMA-long above EMA-short + # if 0.02 < current_profit < 0.1: + # if last_candle['ema100'] > last_candle['ema10']: + # return 'ema_long_below_80' + # + # # Sell any positions at a loss if they are held for more than one day. + # if current_profit < -0.20 and (current_time - trade.open_date_utc).days >= 3: + # return 'unclog' + + # def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + # proposed_stake: float, min_stake: float, max_stake: float, + # **kwargs) -> float: + # + # dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + # current_candle = dataframe.iloc[-1].squeeze() + # + # if dataframe['open'] < dataframe['sma100'] * 0.98: + # # if self.config['stake_amount'] == 'unlimited': + # # # Use entire available wallet during favorable conditions when in compounding mode. + # # return max_stake + # # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + # + # # Use default stake amount. + # return proposed_stake + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + + dataframe['max50'] = ta.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = ta.MAX(dataframe['close'], timeperiod=200) + + for n in range(1, 13): + dataframe["percent" + str(n)] = dataframe['close'].pct_change(n) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['mema10_3'] = dataframe['ema10'] / dataframe['ema10'].shift(1) + # dataframe['mema10_5'] = dataframe['ema10'].rolling(5).mean() + + # dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['msma10_3'] = dataframe['sma10'] / dataframe['sma10'].shift(1) + + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # dataframe["rolling"] = (100 * (dataframe["close"] - dataframe["bb_lowerband"]) / dataframe["bb_lowerband"]).rolling(5).mean() + # dataframe["bb_rolling"] = dataframe["rolling"] / dataframe["bb_width"] + # dataframe['bb_rolling_min'] = ta.MIN(dataframe['bb_rolling'], timeperiod=5) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + for decalage in range(self.buy_decalage.value - 2, self.buy_decalage.value): + conditions = [ + (dataframe['bb_width'].shift(decalage) >= self.buy_bollinger.value), + # (dataframe['close'].shift(decalage) < dataframe['bb_lowerband'].shift(decalage)), + (dataframe['close'].shift(decalage) < dataframe['sma10'].shift(decalage)), + (dataframe['msma10_3'] > 0.998) + ] + # GUARDS AND TRENDS + + if conditions: + dataframe.loc[ + ( + (reduce(lambda x, y: x & y, conditions)) + ) + , + ['buy', 'buy_tag']] = (1, 'buy_msma_' + str(decalage)) + break + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['close'] < dataframe['open']) & + # (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + # (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + # (dataframe['close'] < dataframe['bb_lowerband']) & + # (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + # ), 'sell'] = 1 + return dataframe diff --git a/StrategyJD_2.json b/StrategyJD_2.json new file mode 100644 index 0000000..1bf7043 --- /dev/null +++ b/StrategyJD_2.json @@ -0,0 +1,30 @@ +{ + "strategy_name": "StrategyJD_2", + "params": { + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.11, + "buy_decalage": 10, + "buy_rsi_max": 83, + "buy_rsi_min": 9 + }, + "sell": {}, + "protection": {}, + "roi": { + "0": 0.225, + "191": 0.126, + "423": 0.078, + "673": 0 + }, + "stoploss": { + "stoploss": -0.33 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-09 00:51:43.483560+00:00" +} \ No newline at end of file diff --git a/StrategyJD_2.py b/StrategyJD_2.py new file mode 100644 index 0000000..3e0b438 --- /dev/null +++ b/StrategyJD_2.py @@ -0,0 +1,340 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from freqtrade.strategy.strategy_helper import merge_informative_pair + + +# This class is a sample. Feel free to customize it. +class StrategyJD_2(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + # buy_bollinger2 = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + # buy_bollinger3 = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + + #buy_msma_10 = DecimalParameter(0.997, 1.020, decimals=3, default=0.998, space="buy") + # pourcentage sma à dépasser + # buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.97, space="buy") + buy_decalage = IntParameter(1, 24, default=1, space="buy") + # buy_decalage2 = IntParameter(1, 24, default=1, space="buy") + # buy_decalage3 = IntParameter(1, 24, default=1, space="buy") + + buy_rsi_min = IntParameter(0, 50, default=25, space="buy") + buy_rsi_max = IntParameter(50, 100, default=60, space="buy") + + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + 'rsi': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + } + } + } + + # def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + # current_profit: float, **kwargs): + # dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + # last_candle = dataframe.iloc[-1].squeeze() + # + # # Above 20% profit, sell when rsi < 80 + # if current_profit > 0.2: + # if last_candle['rsi'] < 80: + # return 'rsi_below_80' + # + # # Between 2% and 10%, sell if EMA-long above EMA-short + # if 0.02 < current_profit < 0.1: + # if last_candle['ema100'] > last_candle['ema10']: + # return 'ema_long_below_80' + # + # # Sell any positions at a loss if they are held for more than one day. + # if current_profit < -0.20 and (current_time - trade.open_date_utc).days >= 3: + # return 'unclog' + + # def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + # proposed_stake: float, min_stake: float, max_stake: float, + # **kwargs) -> float: + # + # dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + # current_candle = dataframe.iloc[-1].squeeze() + # + # if dataframe['open'] < dataframe['sma100'] * 0.98: + # # if self.config['stake_amount'] == 'unlimited': + # # # Use entire available wallet during favorable conditions when in compounding mode. + # # return max_stake + # # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + # + # # Use default stake amount. + # return proposed_stake + + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # informative_pairs = [(pair, '1d') for pair in pairs] + # informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs = [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + + dataframe['max50'] = ta.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = ta.MAX(dataframe['close'], timeperiod=200) + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['mema10_3'] = dataframe['ema10'] / dataframe['ema10'].shift(1) + # dataframe['mema10_5'] = dataframe['ema10'].rolling(5).mean() + + # dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['msma10_3'] = dataframe['sma10'] / dataframe['sma10'].shift(1) + + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + ################### INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = ta.RSI(informative) + informative["rsi3"] = ta.RSI(informative, 3) + # informative["mrsi3"] = informative["rsi"].rolling(3).mean() + # informative["max3"] = ta.MAX(informative['close'], timeperiod=3) + # informative["min3"] = ta.MIN(informative['close'], timeperiod=3) + # informative['pct_change_1'] = informative['close'].pct_change(1) + # informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + # informative['sma3'] = ta.SMA(informative, timeperiod=3) + # informative['sma5'] = ta.SMA(informative, timeperiod=5) + # informative['sma10'] = ta.SMA(informative, timeperiod=10) + # # informative['adx'] = ta.ADX(informative) + # + # bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + # informative['bb_lowerband'] = bollinger['lower'] + # informative['bb_middleband'] = bollinger['mid'] + # informative['bb_upperband'] = bollinger['upper'] + # informative["bb_percent"] = ( + # (informative["close"] - informative["bb_lowerband"]) / + # (informative["bb_upperband"] - informative["bb_lowerband"]) + # ) + + # informative["bb_width"] = ( + # (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + # ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + for decalage in range(self.buy_decalage.value - 2, self.buy_decalage.value): + conditions = [ + (dataframe['bb_width'].shift(decalage) >= self.buy_bollinger.value), + # (dataframe['close'].shift(decalage) < dataframe['bb_lowerband'].shift(decalage)), + (dataframe['close'].shift(decalage) < dataframe['sma10'].shift(decalage)), + (dataframe['msma10_3'] > 0.998), + (dataframe['rsi_1h'] > self.buy_rsi_min.value), + (dataframe['rsi_1h'] < self.buy_rsi_max.value), + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[ + ( + (reduce(lambda x, y: x & y, conditions)) + ) + , + ['buy', 'buy_tag']] = (1, 'buy_msma1_' + str(decalage)) + break + + # for decalage2 in range(self.buy_decalage2.value - 2, self.buy_decalage2.value): + # conditions = [ + # (dataframe['bb_width'].shift(decalage2) >= self.buy_bollinger2.value), + # # (dataframe['close'].shift(decalage) < dataframe['bb_lowerband'].shift(decalage)), + # (dataframe['close'].shift(decalage2) < dataframe['sma10'].shift(decalage2)), + # (dataframe['msma10_3'] > 0.998), + # (dataframe['rsi_1h'] < self.buy_rsi_min.value), + # ] + # # GUARDS AND TRENDS + # if conditions: + # dataframe.loc[ + # ( + # (reduce(lambda x, y: x & y, conditions)) + # ) + # , + # ['buy', 'buy_tag']] = (1, 'buy_msma2_' + str(decalage2)) + # break + # + # for decalage3 in range(self.buy_decalage3.value - 2, self.buy_decalage3.value): + # conditions = [ + # (dataframe['bb_width'].shift(decalage3) >= self.buy_bollinger3.value), + # # (dataframe['close'].shift(decalage) < dataframe['bb_lowerband'].shift(decalage)), + # (dataframe['close'].shift(decalage3) < dataframe['sma10'].shift(decalage3)), + # (dataframe['msma10_3'] > 0.998), + # (dataframe['rsi_1h'] > self.buy_rsi_max.value), + # ] + # # GUARDS AND TRENDS + # if conditions: + # dataframe.loc[ + # ( + # (reduce(lambda x, y: x & y, conditions)) + # ) + # , + # ['buy', 'buy_tag']] = (1, 'buy_msma3_' + str(decalage2)) + # break + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['close'] < dataframe['open']) & + # (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + # (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + # (dataframe['close'] < dataframe['bb_lowerband']) & + # (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + # ), 'sell'] = 1 + return dataframe diff --git a/StrategyJD_3.json b/StrategyJD_3.json new file mode 100644 index 0000000..c5e3a76 --- /dev/null +++ b/StrategyJD_3.json @@ -0,0 +1,33 @@ +{ + "strategy_name": "StrategyJD_3", + "params": { + "roi": { + "0": 0.225, + "191": 0.126, + "423": 0.078, + "673": 0 + }, + "stoploss": { + "stoploss": -0.33 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.11, + "buy_decalage": 10, + "buy_rsi_max": 83, + "buy_rsi_min": 9 + }, + "sell": {}, + "protection": { + "n_percent": 2, + "percent_sell": -0.06 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-09 19:00:21.704495+00:00" +} \ No newline at end of file diff --git a/StrategyJD_3.py b/StrategyJD_3.py new file mode 100644 index 0000000..283fa95 --- /dev/null +++ b/StrategyJD_3.py @@ -0,0 +1,336 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from freqtrade.strategy.strategy_helper import merge_informative_pair + + +# This class is a sample. Feel free to customize it. +class StrategyJD_3(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + #buy_msma_10 = DecimalParameter(0.997, 1.020, decimals=3, default=0.998, space="buy") + # pourcentage sma à dépasser + # buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.97, space="buy") + buy_decalage = IntParameter(1, 24, default=1, space="buy") + + buy_rsi_min = IntParameter(0, 50, default=25, space="buy") + buy_rsi_max = IntParameter(50, 100, default=60, space="buy") + n_percent = IntParameter(1, 12, default=1, space="protection") + percent_sell = DecimalParameter(-0.2, -0.01, decimals=2, default=-0.08, space="protection") + + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'min200': {'color': 'yellow'}, + 'min200_001': {'color': 'yellow'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + } + } + } + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + + if last_candle['percent' + str(self.n_percent.value)] < self.percent_sell.value: + return 'sell_lost_percent' + str(self.n_percent.value) + + # Between 2% and 10%, sell if EMA-long above EMA-short + # if 0.02 < current_profit < 0.1: + # if last_candle['ema100'] > last_candle['ema10']: + # return 'ema_long_below_80' + # + # # Sell any positions at a loss if they are held for more than one day. + # if current_profit < -0.20 and (current_time - trade.open_date_utc).days >= 3: + # return 'unclog' + + # def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + # proposed_stake: float, min_stake: float, max_stake: float, + # **kwargs) -> float: + # + # dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + # current_candle = dataframe.iloc[-1].squeeze() + # + # if dataframe['open'] < dataframe['sma100'] * 0.98: + # # if self.config['stake_amount'] == 'unlimited': + # # # Use entire available wallet during favorable conditions when in compounding mode. + # # return max_stake + # # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + # + # # Use default stake amount. + # return proposed_stake + + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # informative_pairs = [(pair, '1d') for pair in pairs] + # informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs = [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_001'] = dataframe['min200'] * 1.01 + + dataframe['max50'] = ta.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = ta.MAX(dataframe['close'], timeperiod=200) + + for n in range(1, 25): + dataframe["percent" + str(n)] = dataframe['close'].pct_change(n) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['mema10_3'] = dataframe['ema10'] / dataframe['ema10'].shift(1) + # dataframe['mema10_5'] = dataframe['ema10'].rolling(5).mean() + + # dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['msma10_3'] = dataframe['sma10'] / dataframe['sma10'].shift(1) + + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + ################### INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = ta.RSI(informative) + informative["rsi3"] = ta.RSI(informative, 3) + # informative["mrsi3"] = informative["rsi"].rolling(3).mean() + # informative["max3"] = ta.MAX(informative['close'], timeperiod=3) + # informative["min3"] = ta.MIN(informative['close'], timeperiod=3) + # informative['pct_change_1'] = informative['close'].pct_change(1) + # informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + # informative['sma3'] = ta.SMA(informative, timeperiod=3) + # informative['sma5'] = ta.SMA(informative, timeperiod=5) + # informative['sma10'] = ta.SMA(informative, timeperiod=10) + # # informative['adx'] = ta.ADX(informative) + # + # bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + # informative['bb_lowerband'] = bollinger['lower'] + # informative['bb_middleband'] = bollinger['mid'] + # informative['bb_upperband'] = bollinger['upper'] + # informative["bb_percent"] = ( + # (informative["close"] - informative["bb_lowerband"]) / + # (informative["bb_upperband"] - informative["bb_lowerband"]) + # ) + + # informative["bb_width"] = ( + # (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + # ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + for decalage in range(self.buy_decalage.value - 2, self.buy_decalage.value): + conditions = [ + (dataframe['bb_width'].shift(decalage) >= self.buy_bollinger.value), + # (dataframe['close'].shift(decalage) < dataframe['bb_lowerband'].shift(decalage)), + (dataframe['close'].shift(decalage) < dataframe['sma10'].shift(decalage)), + (dataframe['msma10_3'] > 0.998), + (dataframe['rsi_1h'] > self.buy_rsi_min.value), + (dataframe['rsi_1h'] < self.buy_rsi_max.value), + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[ + ( + (reduce(lambda x, y: x & y, conditions)) + ) + , + ['buy', 'buy_tag']] = (1, 'buy_msma1_' + str(decalage)) + break + + # for decalage2 in range(self.buy_decalage2.value - 2, self.buy_decalage2.value): + # conditions = [ + # (dataframe['bb_width'].shift(decalage2) >= self.buy_bollinger2.value), + # # (dataframe['close'].shift(decalage) < dataframe['bb_lowerband'].shift(decalage)), + # (dataframe['close'].shift(decalage2) < dataframe['sma10'].shift(decalage2)), + # (dataframe['msma10_3'] > 0.998), + # (dataframe['rsi_1h'] < self.buy_rsi_min.value), + # ] + # # GUARDS AND TRENDS + # if conditions: + # dataframe.loc[ + # ( + # (reduce(lambda x, y: x & y, conditions)) + # ) + # , + # ['buy', 'buy_tag']] = (1, 'buy_msma2_' + str(decalage2)) + # break + # + # for decalage3 in range(self.buy_decalage3.value - 2, self.buy_decalage3.value): + # conditions = [ + # (dataframe['bb_width'].shift(decalage3) >= self.buy_bollinger3.value), + # # (dataframe['close'].shift(decalage) < dataframe['bb_lowerband'].shift(decalage)), + # (dataframe['close'].shift(decalage3) < dataframe['sma10'].shift(decalage3)), + # (dataframe['msma10_3'] > 0.998), + # (dataframe['rsi_1h'] > self.buy_rsi_max.value), + # ] + # # GUARDS AND TRENDS + # if conditions: + # dataframe.loc[ + # ( + # (reduce(lambda x, y: x & y, conditions)) + # ) + # , + # ['buy', 'buy_tag']] = (1, 'buy_msma3_' + str(decalage2)) + # break + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['close'] < dataframe['open']) & + # (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + # (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + # (dataframe['close'] < dataframe['bb_lowerband']) & + # (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + # ), 'sell'] = 1 + return dataframe diff --git a/StrategyJD_4.json b/StrategyJD_4.json new file mode 100644 index 0000000..58e150a --- /dev/null +++ b/StrategyJD_4.json @@ -0,0 +1,31 @@ +{ + "strategy_name": "StrategyJD_4", + "params": { + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.04, + "buy_decalage": 4 + }, + "sell": {}, + "protection": { + "n_percent": 7, + "percent_sell": -0.08 + }, + "roi": { + "0": 0.627, + "151": 0.182, + "397": 0.041, + "657": 0 + }, + "stoploss": { + "stoploss": -0.033 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-11 21:11:06.341495+00:00" +} \ No newline at end of file diff --git a/StrategyJD_4.py b/StrategyJD_4.py new file mode 100644 index 0000000..33b900e --- /dev/null +++ b/StrategyJD_4.py @@ -0,0 +1,349 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from freqtrade.strategy.strategy_helper import merge_informative_pair + + +# This class is a sample. Feel free to customize it. +class StrategyJD_4(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + # buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + + # bollinger_rsi = {} + # for n in range(0, 11): + # bollinger_rsi[n] = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + + bollinger_rsi_01 = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="protection") + bollinger_rsi_02 = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="protection") + bollinger_rsi_03 = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="protection") + bollinger_rsi_04 = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="protection") + bollinger_rsi_05 = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="protection") + bollinger_rsi_06 = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="protection") + bollinger_rsi_07 = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="protection") + bollinger_rsi_08 = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="protection") + bollinger_rsi_09 = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="protection") + bollinger_rsi_10 = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="protection") + bollinger_rsi_11 = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="protection") + + #buy_msma_10 = DecimalParameter(0.997, 1.020, decimals=3, default=0.998, space="buy") + # pourcentage sma à dépasser + # buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.97, space="buy") + buy_decalage = IntParameter(1, 24, default=1, space="buy") + + buy_rsi_min = IntParameter(0, 50, default=25, space="buy") + buy_rsi_max = IntParameter(50, 100, default=60, space="buy") + n_percent = IntParameter(1, 12, default=1, space="sell") + percent_sell = DecimalParameter(-0.2, -0.01, decimals=2, default=-0.08, space="sell") + + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + 'rsi': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + } + } + } + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + + if last_candle['percent' + str(self.n_percent.value)] < self.percent_sell.value: + return 'sell_lost_percent' + str(self.n_percent.value) + + # Between 2% and 10%, sell if EMA-long above EMA-short + # if 0.02 < current_profit < 0.1: + # if last_candle['ema100'] > last_candle['ema10']: + # return 'ema_long_below_80' + # + # # Sell any positions at a loss if they are held for more than one day. + # if current_profit < -0.20 and (current_time - trade.open_date_utc).days >= 3: + # return 'unclog' + + # def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + # proposed_stake: float, min_stake: float, max_stake: float, + # **kwargs) -> float: + # + # dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + # current_candle = dataframe.iloc[-1].squeeze() + # + # if dataframe['open'] < dataframe['sma100'] * 0.98: + # # if self.config['stake_amount'] == 'unlimited': + # # # Use entire available wallet during favorable conditions when in compounding mode. + # # return max_stake + # # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + # + # # Use default stake amount. + # return proposed_stake + + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # informative_pairs = [(pair, '1d') for pair in pairs] + # informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs = [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + + dataframe['max50'] = ta.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = ta.MAX(dataframe['close'], timeperiod=200) + + for n in range(1, 25): + dataframe["percent" + str(n)] = dataframe['close'].pct_change(n) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['mema10_3'] = dataframe['ema10'] / dataframe['ema10'].shift(1) + # dataframe['mema10_5'] = dataframe['ema10'].rolling(5).mean() + + # dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['msma10_3'] = dataframe['sma10'] / dataframe['sma10'].shift(1) + + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + ################### INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = ta.RSI(informative) + informative["rsi3"] = ta.RSI(informative, 3) + informative['r_rsi'] = (informative['rsi3'].div(10).round()) + + # informative["mrsi3"] = informative["rsi"].rolling(3).mean() + # informative["max3"] = ta.MAX(informative['close'], timeperiod=3) + # informative["min3"] = ta.MIN(informative['close'], timeperiod=3) + # informative['pct_change_1'] = informative['close'].pct_change(1) + # informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + # informative['sma3'] = ta.SMA(informative, timeperiod=3) + # informative['sma5'] = ta.SMA(informative, timeperiod=5) + # informative['sma10'] = ta.SMA(informative, timeperiod=10) + # # informative['adx'] = ta.ADX(informative) + # + # bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + # informative['bb_lowerband'] = bollinger['lower'] + # informative['bb_middleband'] = bollinger['mid'] + # informative['bb_upperband'] = bollinger['upper'] + # informative["bb_percent"] = ( + # (informative["close"] - informative["bb_lowerband"]) / + # (informative["bb_upperband"] - informative["bb_lowerband"]) + # ) + + # informative["bb_width"] = ( + # (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + # ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + for r_rsi_1h in range(0, 11): + value = 0 + if r_rsi_1h == 0: + value = self.bollinger_rsi_01.value + if r_rsi_1h == 1: + value = self.bollinger_rsi_02.value + if r_rsi_1h == 2: + value = self.bollinger_rsi_03.value + if r_rsi_1h == 3: + value = self.bollinger_rsi_04.value + if r_rsi_1h == 4: + value = self.bollinger_rsi_05.value + if r_rsi_1h == 5: + value = self.bollinger_rsi_06.value + if r_rsi_1h == 6: + value = self.bollinger_rsi_07.value + if r_rsi_1h == 7: + value = self.bollinger_rsi_08.value + if r_rsi_1h == 8: + value = self.bollinger_rsi_09.value + if r_rsi_1h == 9: + value = self.bollinger_rsi_10.value + if r_rsi_1h == 10: + value = self.bollinger_rsi_11.value + # if r_rsi_1h == 11: + # value = self.bollinger_rsi_02.value + for decalage in range(self.buy_decalage.value - 2, self.buy_decalage.value): + conditions = [ + (dataframe['r_rsi_1h'] == r_rsi_1h), + (dataframe['bb_width'].shift(decalage) >= value), + # (dataframe['close'].shift(decalage) < dataframe['bb_lowerband'].shift(decalage)), + (dataframe['close'].shift(decalage) < dataframe['sma10'].shift(decalage)), + (dataframe['msma10_3'] > 0.998), + (dataframe['rsi_1h'] > self.buy_rsi_min.value), + (dataframe['rsi_1h'] < self.buy_rsi_max.value) + # (dataframe['percent1'] > 0) + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[ + ( + (reduce(lambda x, y: x & y, conditions)) + ), + ['buy', 'buy_tag']] = (1, 'buy_msma1_' + str(decalage)+ '_' + str(value)) + break + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['close'] < dataframe['open']) & + # (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + # (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + # (dataframe['close'] < dataframe['bb_lowerband']) & + # (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + # ), 'sell'] = 1 + return dataframe diff --git a/StrategyJD_5.json b/StrategyJD_5.json new file mode 100644 index 0000000..d88a307 --- /dev/null +++ b/StrategyJD_5.json @@ -0,0 +1,33 @@ +{ + "strategy_name": "StrategyJD_5", + "params": { + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_decalage": 11, + "buy_min_max_n": 0.17, + "buy_rsi_max": 74, + "buy_rsi_min": 6 + }, + "sell": {}, + "protection": { + "n_percent": 6, + "percent_sell": -0.06 + }, + "roi": { + "0": 0.209, + "200": 0.158, + "453": 0.103, + "581": 0 + }, + "stoploss": { + "stoploss": -0.243 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-12 11:07:47.704622+00:00" +} \ No newline at end of file diff --git a/StrategyJD_5.py b/StrategyJD_5.py new file mode 100644 index 0000000..8c25088 --- /dev/null +++ b/StrategyJD_5.py @@ -0,0 +1,303 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from freqtrade.strategy.strategy_helper import merge_informative_pair + + +# This class is a sample. Feel free to customize it. +class StrategyJD_5(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + # buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + #buy_msma_10 = DecimalParameter(0.997, 1.020, decimals=3, default=0.998, space="buy") + # pourcentage sma à dépasser + # buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.97, space="buy") + buy_decalage = IntParameter(1, 24, default=5, space="buy") + buy_min_max_n = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + buy_rsi_min = IntParameter(0, 50, default=25, space="buy") + buy_rsi_max = IntParameter(50, 100, default=60, space="buy") + n_percent = IntParameter(1, 12, default=1, space="protection") + percent_sell = DecimalParameter(-0.2, -0.01, decimals=2, default=-0.08, space="protection") + + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'min200': {'color': 'yellow'}, + 'min200_001': {'color': 'yellow'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + } + } + } + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + + if last_candle['percent' + str(self.n_percent.value)] < self.percent_sell.value: + return 'sell_lost_percent' + str(self.n_percent.value) + + # Between 2% and 10%, sell if EMA-long above EMA-short + # if 0.02 < current_profit < 0.1: + # if last_candle['ema100'] > last_candle['ema10']: + # return 'ema_long_below_80' + # + # # Sell any positions at a loss if they are held for more than one day. + # if current_profit < -0.20 and (current_time - trade.open_date_utc).days >= 3: + # return 'unclog' + + # def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + # proposed_stake: float, min_stake: float, max_stake: float, + # **kwargs) -> float: + # + # dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + # current_candle = dataframe.iloc[-1].squeeze() + # + # if dataframe['open'] < dataframe['sma100'] * 0.98: + # # if self.config['stake_amount'] == 'unlimited': + # # # Use entire available wallet during favorable conditions when in compounding mode. + # # return max_stake + # # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + # + # # Use default stake amount. + # return proposed_stake + + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # informative_pairs = [(pair, '1d') for pair in pairs] + # informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs = [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_001'] = dataframe['min200'] * 1.002 + dataframe['max50'] = ta.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_max50'] = (dataframe['max50'] - dataframe['min50']) / dataframe['min50'] + + for n in range(1, 25): + dataframe["percent" + str(n)] = dataframe['close'].pct_change(n) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['mema10_3'] = dataframe['ema10'] / dataframe['ema10'].shift(1) + # dataframe['mema10_5'] = dataframe['ema10'].rolling(5).mean() + + # dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['msma10_3'] = dataframe['sma10'] / dataframe['sma10'].shift(1) + + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + ################### INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = ta.RSI(informative) + informative["rsi3"] = ta.RSI(informative, 3) + # informative["mrsi3"] = informative["rsi"].rolling(3).mean() + # informative["max3"] = ta.MAX(informative['close'], timeperiod=3) + # informative["min3"] = ta.MIN(informative['close'], timeperiod=3) + # informative['pct_change_1'] = informative['close'].pct_change(1) + # informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + # informative['sma3'] = ta.SMA(informative, timeperiod=3) + # informative['sma5'] = ta.SMA(informative, timeperiod=5) + # informative['sma10'] = ta.SMA(informative, timeperiod=10) + # # informative['adx'] = ta.ADX(informative) + # + # bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + # informative['bb_lowerband'] = bollinger['lower'] + # informative['bb_middleband'] = bollinger['mid'] + # informative['bb_upperband'] = bollinger['upper'] + # informative["bb_percent"] = ( + # (informative["close"] - informative["bb_lowerband"]) / + # (informative["bb_upperband"] - informative["bb_lowerband"]) + # ) + + # informative["bb_width"] = ( + # (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + # ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + for decalage in range(self.buy_decalage.value - 2, self.buy_decalage.value): + conditions = [ + # (dataframe['bb_width'].shift(decalage) >= self.buy_bollinger.value), + (dataframe['close'].shift(decalage) < dataframe['min200_001'].shift(decalage)), + (dataframe['min_max200'] >= self.buy_min_max_n.value), + # (dataframe['close'].shift(decalage) < dataframe['bb_lowerband'].shift(decalage)), + # (dataframe['msma10_3'] > 0.998), + # (dataframe['msma10_3'] > 0.999) + (dataframe['rsi_1h'] > self.buy_rsi_min.value), + (dataframe['rsi_1h'] < self.buy_rsi_max.value), + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[ + ( + (reduce(lambda x, y: x & y, conditions)) + ) + , + ['buy', 'buy_tag']] = (1, 'buy_msma1_' + str(decalage)) + break + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['close'] < dataframe['open']) & + # (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + # (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + # (dataframe['close'] < dataframe['bb_lowerband']) & + # (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + # ), 'sell'] = 1 + return dataframe diff --git a/StrategyJD_5_2.json b/StrategyJD_5_2.json new file mode 100644 index 0000000..06be52e --- /dev/null +++ b/StrategyJD_5_2.json @@ -0,0 +1,33 @@ +{ + "strategy_name": "StrategyJD_5_2", + "params": { + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_decalage": 4, + "buy_min_max_n": 0.04, + "buy_rsi_max": 58, + "buy_rsi_min": 5 + }, + "sell": {}, + "protection": { + "n_percent": 5, + "percent_sell": -0.16 + }, + "roi": { + "0": 0.252, + "145": 0.21200000000000002, + "370": 0.096, + "831": 0 + }, + "stoploss": { + "stoploss": -0.08 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-12 14:40:04.336207+00:00" +} \ No newline at end of file diff --git a/StrategyJD_5_2.py b/StrategyJD_5_2.py new file mode 100644 index 0000000..7ec902a --- /dev/null +++ b/StrategyJD_5_2.py @@ -0,0 +1,303 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from freqtrade.strategy.strategy_helper import merge_informative_pair + + +# This class is a sample. Feel free to customize it. +class StrategyJD_5_2(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + # buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + #buy_msma_10 = DecimalParameter(0.997, 1.020, decimals=3, default=0.998, space="buy") + # pourcentage sma à dépasser + # buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.97, space="buy") + buy_decalage = IntParameter(1, 24, default=5, space="buy") + buy_min_max_n = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + buy_rsi_min = IntParameter(0, 50, default=25, space="buy") + buy_rsi_max = IntParameter(50, 100, default=60, space="buy") + n_percent = IntParameter(1, 12, default=1, space="protection") + percent_sell = DecimalParameter(-0.2, -0.01, decimals=2, default=-0.08, space="protection") + + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'min200': {'color': 'yellow'}, + 'min200_001': {'color': 'yellow'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + } + } + } + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + + if last_candle['percent' + str(self.n_percent.value)] < self.percent_sell.value: + return 'sell_lost_percent' + str(self.n_percent.value) + + # Between 2% and 10%, sell if EMA-long above EMA-short + # if 0.02 < current_profit < 0.1: + # if last_candle['ema100'] > last_candle['ema10']: + # return 'ema_long_below_80' + # + # # Sell any positions at a loss if they are held for more than one day. + # if current_profit < -0.20 and (current_time - trade.open_date_utc).days >= 3: + # return 'unclog' + + # def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + # proposed_stake: float, min_stake: float, max_stake: float, + # **kwargs) -> float: + # + # dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + # current_candle = dataframe.iloc[-1].squeeze() + # + # if dataframe['open'] < dataframe['sma100'] * 0.98: + # # if self.config['stake_amount'] == 'unlimited': + # # # Use entire available wallet during favorable conditions when in compounding mode. + # # return max_stake + # # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + # + # # Use default stake amount. + # return proposed_stake + + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # informative_pairs = [(pair, '1d') for pair in pairs] + # informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs = [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_001'] = dataframe['min200'] * 1.002 + dataframe['max50'] = ta.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_max50'] = (dataframe['max50'] - dataframe['min50']) / dataframe['min50'] + + for n in range(1, 25): + dataframe["percent" + str(n)] = dataframe['close'].pct_change(n) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['mema10_3'] = dataframe['ema10'] / dataframe['ema10'].shift(1) + # dataframe['mema10_5'] = dataframe['ema10'].rolling(5).mean() + + # dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['msma10_3'] = dataframe['sma10'] / dataframe['sma10'].shift(1) + + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + ################### INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = ta.RSI(informative) + informative["rsi3"] = ta.RSI(informative, 3) + # informative["mrsi3"] = informative["rsi"].rolling(3).mean() + # informative["max3"] = ta.MAX(informative['close'], timeperiod=3) + # informative["min3"] = ta.MIN(informative['close'], timeperiod=3) + # informative['pct_change_1'] = informative['close'].pct_change(1) + # informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + # informative['sma3'] = ta.SMA(informative, timeperiod=3) + # informative['sma5'] = ta.SMA(informative, timeperiod=5) + # informative['sma10'] = ta.SMA(informative, timeperiod=10) + # # informative['adx'] = ta.ADX(informative) + # + # bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + # informative['bb_lowerband'] = bollinger['lower'] + # informative['bb_middleband'] = bollinger['mid'] + # informative['bb_upperband'] = bollinger['upper'] + # informative["bb_percent"] = ( + # (informative["close"] - informative["bb_lowerband"]) / + # (informative["bb_upperband"] - informative["bb_lowerband"]) + # ) + + # informative["bb_width"] = ( + # (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + # ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + for decalage in range(self.buy_decalage.value - 2, self.buy_decalage.value): + conditions = [ + # (dataframe['bb_width'].shift(decalage) >= self.buy_bollinger.value), + (dataframe['close'].shift(decalage) < dataframe['min200_001'].shift(decalage)), + (dataframe['min_max200'] >= self.buy_min_max_n.value), + # (dataframe['close'].shift(decalage) < dataframe['bb_lowerband'].shift(decalage)), + # (dataframe['msma10_3'] > 0.998), + # (dataframe['msma10_3'] > 0.999) + (dataframe['rsi_1h'] > self.buy_rsi_min.value), + (dataframe['rsi_1h'] < self.buy_rsi_max.value), + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[ + ( + (reduce(lambda x, y: x & y, conditions)) + ) + , + ['buy', 'buy_tag']] = (1, 'buy_msma1_' + str(decalage)) + break + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['close'] < dataframe['open']) & + # (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + # (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + # (dataframe['close'] < dataframe['bb_lowerband']) & + # (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + # ), 'sell'] = 1 + return dataframe diff --git a/StrategyJD_5_3.json b/StrategyJD_5_3.json new file mode 100644 index 0000000..b839edf --- /dev/null +++ b/StrategyJD_5_3.json @@ -0,0 +1,44 @@ +{ + "strategy_name": "StrategyJD_5_3", + "params": { + "roi": { + "0": 0.402, + "191": 0.23, + "435": 0.078, + "711": 0 + }, + "stoploss": { + "stoploss": -0.243 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_decalage": 1, + "buy_decalage2": 10, + "buy_decalage3": 22, + "buy_decalage4": 5, + "buy_min_max_n": 0.0, + "buy_min_max_n2": 0.17, + "buy_min_max_n3": 0.17, + "buy_min_max_n4": 0.09, + "buy_rsi_max": 67, + "buy_rsi_max2": 70, + "buy_rsi_min": 10, + "buy_rsi_min2": 18, + "buy_rsi_min_1d": 5, + "buy_rsi_min_1d2": 42, + "buy_rsi_min_1d3": 52 + }, + "sell": {}, + "protection": { + "n_percent": 6, + "percent_sell": -0.06 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-13 18:46:52.767429+00:00" +} \ No newline at end of file diff --git a/StrategyJD_5_3.py b/StrategyJD_5_3.py new file mode 100644 index 0000000..3402ab0 --- /dev/null +++ b/StrategyJD_5_3.py @@ -0,0 +1,346 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from freqtrade.strategy.strategy_helper import merge_informative_pair + + +# This class is a sample. Feel free to customize it. +class StrategyJD_5_3(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + # buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + #buy_msma_10 = DecimalParameter(0.997, 1.020, decimals=3, default=0.998, space="buy") + # pourcentage sma à dépasser + # buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.97, space="buy") + buy_decalage = IntParameter(1, 24, default=5, space="buy") + buy_decalage2 = IntParameter(1, 24, default=5, space="buy") + buy_decalage3 = IntParameter(1, 24, default=5, space="buy") + buy_decalage4 = IntParameter(1, 24, default=5, space="buy") + + buy_min_max_n = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + buy_min_max_n2 = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + buy_min_max_n3 = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + buy_min_max_n4 = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + + buy_rsi_min_1d = IntParameter(0, 25, default=5, space="buy") + buy_rsi_min_1d2 = IntParameter(25, 50, default=15, space="buy") + buy_rsi_min_1d3 = IntParameter(50, 75, default=50, space="buy") + buy_rsi_min_1d4 = IntParameter(75, 100, default=75, space="buy") + + # buy_rsi_max = IntParameter(50, 100, default=60, space="buy") + + buy_rsi_min = IntParameter(0, 50, default=25, space="buy") + buy_rsi_max = IntParameter(50, 100, default=60, space="buy") + buy_rsi_min2 = IntParameter(0, 50, default=25, space="buy") + buy_rsi_max2 = IntParameter(50, 100, default=60, space="buy") + n_percent = IntParameter(1, 12, default=1, space="protection") + percent_sell = DecimalParameter(-0.2, -0.01, decimals=2, default=-0.08, space="protection") + + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'min200': {'color': 'yellow'}, + 'min200_001': {'color': 'yellow'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + } + } + } + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + + if last_candle['percent' + str(self.n_percent.value)] < self.percent_sell.value: + return 'sell_lost_percent' + str(self.n_percent.value) + + # Between 2% and 10%, sell if EMA-long above EMA-short + # if 0.02 < current_profit < 0.1: + # if last_candle['ema100'] > last_candle['ema10']: + # return 'ema_long_below_80' + # + # # Sell any positions at a loss if they are held for more than one day. + # if current_profit < -0.20 and (current_time - trade.open_date_utc).days >= 3: + # return 'unclog' + + # def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + # proposed_stake: float, min_stake: float, max_stake: float, + # **kwargs) -> float: + # + # dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + # current_candle = dataframe.iloc[-1].squeeze() + # + # if dataframe['open'] < dataframe['sma100'] * 0.98: + # # if self.config['stake_amount'] == 'unlimited': + # # # Use entire available wallet during favorable conditions when in compounding mode. + # # return max_stake + # # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + # + # # Use default stake amount. + # return proposed_stake + + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + # informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_001'] = dataframe['min200'] * 1.002 + dataframe['max50'] = ta.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_max50'] = (dataframe['max50'] - dataframe['min50']) / dataframe['min50'] + + for n in range(1, 25): + dataframe["percent" + str(n)] = dataframe['close'].pct_change(n) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['mema10_3'] = dataframe['ema10'] / dataframe['ema10'].shift(1) + # dataframe['mema10_5'] = dataframe['ema10'].rolling(5).mean() + + # dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['msma10_3'] = dataframe['sma10'] / dataframe['sma10'].shift(1) + + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + ################### INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = ta.RSI(informative) + informative["rsi3"] = ta.RSI(informative, 3) + informative['r_rsi'] = (informative['rsi3'].div(10).round()) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + ################### INFORMATIVE 1d + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = ta.RSI(informative) + informative["rsi3"] = ta.RSI(informative, 3) + informative['r_rsi'] = (informative['rsi'].div(10).round()) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # if (self.buy_min_max_n.value > self.buy_min_max_n2.value) | (self.buy_min_max_n2.value > self.buy_min_max_n3.value) | (self.buy_min_max_n3.value > self.buy_min_max_n4.value): + # return dataframe + + for decalage in range(self.buy_decalage.value - 2, self.buy_decalage.value): + conditions = [ + (dataframe['rsi_1h'] < self.buy_rsi_min_1d.value), + (dataframe['close'].shift(decalage) < dataframe['min200_001'].shift(decalage)), + (dataframe['min_max200'] >= self.buy_min_max_n.value), + (dataframe['rsi_1h'] > self.buy_rsi_min.value), + (dataframe['rsi_1h'] < self.buy_rsi_max.value), + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + break + + for decalage in range(self.buy_decalage2.value - 2, self.buy_decalage2.value): + conditions = [ + (dataframe['rsi_1h'] >= self.buy_rsi_min_1d.value), + (dataframe['rsi_1h'] < self.buy_rsi_min_1d2.value), + (dataframe['close'].shift(decalage) < dataframe['min200_001'].shift(decalage)), + (dataframe['min_max200'] >= self.buy_min_max_n2.value), + (dataframe['rsi_1h'] > self.buy_rsi_min.value), + (dataframe['rsi_1h'] < self.buy_rsi_max.value), + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + break + + for decalage in range(self.buy_decalage3.value - 2, self.buy_decalage3.value): + conditions = [ + (dataframe['rsi_1h'] >= self.buy_rsi_min_1d2.value), + (dataframe['rsi_1h'] < self.buy_rsi_min_1d3.value), + (dataframe['close'].shift(decalage) < dataframe['min200_001'].shift(decalage)), + (dataframe['min_max200'] >= self.buy_min_max_n3.value), + (dataframe['rsi_1h'] > self.buy_rsi_min.value), + (dataframe['rsi_1h'] < self.buy_rsi_max.value), + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + break + + for decalage in range(self.buy_decalage4.value - 2, self.buy_decalage4.value): + conditions = [ + (dataframe['rsi_1h'] >= self.buy_rsi_min_1d3.value), + (dataframe['rsi_1h'] < self.buy_rsi_min_1d4.value), + (dataframe['close'].shift(decalage) < dataframe['min200_001'].shift(decalage)), + (dataframe['min_max200'] >= self.buy_min_max_n4.value), + (dataframe['rsi_1h'] > self.buy_rsi_min2.value), + (dataframe['rsi_1h'] < self.buy_rsi_max2.value), + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + break + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['close'] < dataframe['open']) & + # (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + # (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + # (dataframe['close'] < dataframe['bb_lowerband']) & + # (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + # ), 'sell'] = 1 + return dataframe diff --git a/StrategyJD_5_4.json b/StrategyJD_5_4.json new file mode 100644 index 0000000..c979c96 --- /dev/null +++ b/StrategyJD_5_4.json @@ -0,0 +1,59 @@ +{ + "strategy_name": "StrategyJD_5_4", + "params": { + "roi": { + "0": 0.5 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.011, + "trailing_stop_positive_offset": 0.111, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_decalage": 5, + "buy_decalage2": 7, + "buy_decalage3": 17, + "buy_decalage4": 20, + "buy_min_max_n": 0.12, + "buy_min_max_n2": 0.14, + "buy_min_max_n3": 0.16, + "buy_min_max_n4": 0.0, + "buy_rsi_max": 51, + "buy_rsi_min": 3, + "buy_rsi_min_1d": 18, + "buy_rsi_min_1d2": 25, + "buy_rsi_min_1d3": 56, + "buy_rsi_min_1d4": 90, + "min_n": 11, + "min_p": 1.0, + "min_percent": 1.008, + "min_percent2": 1.006, + "min_percent3": 1.012, + "min_percent4": 1.018 + }, + "sell": { + "max_percent": 0.048, + "max_percent2": 0.018, + "max_percent3": 0.039, + "max_percent4": 0.003, + "max_profit": 0.0, + "max_profit2": 0.09, + "max_profit3": 0.07, + "max_profit4": 0.07, + "sell_h_RSI": 83, + "sell_h_RSI2": 72, + "sell_h_RSI2_percent": 0.017, + "sell_h_RSI3": 74 + }, + "protection": { + "n_percent": 1, + "percent_sell": -0.08 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-15 14:27:47.977295+00:00" +} \ No newline at end of file diff --git a/StrategyJD_5_4.jsonNostoploss b/StrategyJD_5_4.jsonNostoploss new file mode 100644 index 0000000..b88fabc --- /dev/null +++ b/StrategyJD_5_4.jsonNostoploss @@ -0,0 +1,41 @@ +{ + "strategy_name": "StrategyJD_5_4", + "params": { + "roi": { + "0": 0.5 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_decalage": 1, + "buy_decalage2": 9, + "buy_decalage3": 2, + "buy_decalage4": 8, + "buy_min_max_n": 0.14, + "buy_min_max_n2": 0.17, + "buy_min_max_n3": 0.07, + "buy_min_max_n4": 0.16, + "buy_rsi_max": 53, + "buy_rsi_min": 23, + "buy_rsi_min_1d": 24, + "buy_rsi_min_1d2": 50, + "buy_rsi_min_1d3": 57, + "min_n": 13, + "min_p": 1.006 + }, + "sell": {}, + "protection": { + "n_percent": 1, + "percent_sell": -0.08 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-13 21:03:59.051913+00:00" +} \ No newline at end of file diff --git a/StrategyJD_5_4.py b/StrategyJD_5_4.py new file mode 100644 index 0000000..d7c7959 --- /dev/null +++ b/StrategyJD_5_4.py @@ -0,0 +1,326 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from freqtrade.strategy.strategy_helper import merge_informative_pair + + +# This class is a sample. Feel free to customize it. +class StrategyJD_5_4(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + # buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + #buy_msma_10 = DecimalParameter(0.997, 1.020, decimals=3, default=0.998, space="buy") + # pourcentage sma à dépasser + # buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.97, space="buy") + buy_decalage = IntParameter(1, 24, default=5, space="buy") + buy_decalage2 = IntParameter(1, 24, default=5, space="buy") + buy_decalage3 = IntParameter(1, 24, default=5, space="buy") + buy_decalage4 = IntParameter(1, 24, default=5, space="buy") + + buy_min_max_n = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + buy_min_max_n2 = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + buy_min_max_n3 = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + buy_min_max_n4 = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + + buy_rsi_min_1d = IntParameter(0, 25, default=5, space="buy") + buy_rsi_min_1d2 = IntParameter(25, 50, default=15, space="buy") + buy_rsi_min_1d3 = IntParameter(50, 75, default=50, space="buy") + buy_rsi_min_1d4 = IntParameter(75, 100, default=75, space="buy") + + min_percent = DecimalParameter(1, 1.02, decimals=3, default=1.002, space='buy') + min_percent2 = DecimalParameter(1, 1.02, decimals=3, default=1.002, space='buy') + min_percent3 = DecimalParameter(1, 1.02, decimals=3, default=1.002, space='buy') + min_percent4 = DecimalParameter(1, 1.02, decimals=3, default=1.002, space='buy') + + max_percent = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + max_percent2 = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + max_percent3 = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + max_percent4 = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + + max_profit = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + max_profit2 = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + max_profit3 = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + max_profit4 = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + + # sell_b_RSI = IntParameter(70, 98, default=88, space='sell') + # sell_b_RSI2 = IntParameter(70, 98, default=88, space='sell') + # sell_b_RSI3 = IntParameter(70, 98, default=80, space='sell') + # + # sell_b_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_RSI = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_h_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + # buy_rsi_max = IntParameter(50, 100, default=60, space="buy") + + buy_rsi_min = IntParameter(0, 50, default=25, space="buy") + buy_rsi_max = IntParameter(50, 100, default=60, space="buy") + # buy_rsi_min2 = IntParameter(0, 50, default=25, space="buy") + # buy_rsi_max2 = IntParameter(50, 100, default=60, space="buy") + + min_n = IntParameter(0, 24, default=15, space="buy") + min_p = DecimalParameter(1, 1.01, decimals=3, default=1.002, space="buy") + + n_percent = IntParameter(1, 12, default=1, space="protection") + percent_sell = DecimalParameter(-0.2, -0.01, decimals=2, default=-0.08, space="protection") + n_percent2 = IntParameter(1, 12, default=1, space="protection") + percent_sell2 = DecimalParameter(-0.2, -0.01, decimals=2, default=-0.08, space="protection") + n_percent3 = IntParameter(1, 12, default=1, space="protection") + percent_sell3 = DecimalParameter(-0.2, -0.01, decimals=2, default=-0.08, space="protection") + n_percent4 = IntParameter(1, 12, default=1, space="protection") + percent_sell4 = DecimalParameter(-0.2, -0.01, decimals=2, default=-0.08, space="protection") + + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'min200': {'color': 'yellow'}, + 'min200_001': {'color': 'yellow'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + } + } + } + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + + if (last_candle['rsi_1h'] < self.buy_rsi_min_1d.value): + max_percent = self.max_percent.value + max_profit = self.max_profit.value + if last_candle['percent' + str(self.n_percent.value)] < self.percent_sell.value: + return 'sell_lost_percent' + str(self.n_percent.value) + else: + if (last_candle['rsi_1h'] < self.buy_rsi_min_1d2.value): + max_percent = self.max_percent2.value + max_profit = self.max_profit2.value + if last_candle['percent' + str(self.n_percent2.value)] < self.percent_sell2.value: + return 'sell_lost_percent' + str(self.n_percent2.value) + else: + if (last_candle['rsi_1h'] < self.buy_rsi_min_1d3.value): + max_percent = self.max_percent3.value + max_profit = self.max_profit3.value + if last_candle['percent' + str(self.n_percent3.value)] < self.percent_sell3.value: + return 'sell_lost_percent' + str(self.n_percent3.value) + else: + max_percent = self.max_percent4.value + max_profit = self.max_profit4.value + if last_candle['percent' + str(self.n_percent4.value)] < self.percent_sell4.value: + return 'sell_lost_percent' + str(self.n_percent4.value) + + if (current_profit > max_profit) & ( + (last_candle['percent1'] < -max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'h_percent_quick' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent1'] < - self.sell_h_RSI2_percent.value): + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): + return 'h_over_rsi_max' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # informative_pairs = [(pair, '1d') for pair in pairs] + # informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs = [(pair, '1h') for pair in pairs] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_001'] = dataframe['min200'] * self.min_percent.value + dataframe['min200_002'] = dataframe['min200'] * self.min_percent2.value + dataframe['min200_003'] = dataframe['min200'] * self.min_percent3.value + dataframe['min200_004'] = dataframe['min200'] * self.min_percent4.value + dataframe['max200'] = ta.MAX(dataframe['close'], timeperiod=200) + + dataframe['min_n'] = ta.MIN(dataframe['close'], timeperiod=self.min_n.value * 12) + dataframe['max_n'] = ta.MAX(dataframe['close'], timeperiod=self.min_n.value * 12) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['min_n_p'] = dataframe['min_n'] * self.min_p.value + + for n in range(1, 25): + dataframe["percent" + str(n)] = dataframe['close'].pct_change(n) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + ################### INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = ta.RSI(informative) + informative["rsi3"] = ta.RSI(informative, 3) + informative['r_rsi'] = (informative['rsi3'].div(10).round()) + for n in range(1, 5): + informative["percent" + str(n)] = informative['close'].pct_change(n) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + for decalage in range(self.buy_decalage.value - 2, self.buy_decalage.value): + conditions = [ + (dataframe['rsi_1h'] < self.buy_rsi_min_1d.value), + (dataframe['close'].shift(decalage) < dataframe['min200_001'].shift(decalage)), + (dataframe['min_max_n'] >= self.buy_min_max_n.value), + (dataframe['rsi_1h'] > self.buy_rsi_min.value), + (dataframe['rsi_1h'] < self.buy_rsi_max.value), + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + break + + for decalage in range(self.buy_decalage2.value - 2, self.buy_decalage2.value): + conditions = [ + (dataframe['rsi_1h'] >= self.buy_rsi_min_1d.value), + (dataframe['rsi_1h'] < self.buy_rsi_min_1d2.value), + (dataframe['close'].shift(decalage) < dataframe['min200_002'].shift(decalage)), + (dataframe['min_max_n'] >= self.buy_min_max_n2.value), + (dataframe['rsi_1h'] > self.buy_rsi_min.value), + (dataframe['rsi_1h'] < self.buy_rsi_max.value), + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + break + + for decalage in range(self.buy_decalage3.value - 2, self.buy_decalage3.value): + conditions = [ + (dataframe['rsi_1h'] >= self.buy_rsi_min_1d2.value), + (dataframe['rsi_1h'] < self.buy_rsi_min_1d3.value), + (dataframe['close'].shift(decalage) < dataframe['min200_003'].shift(decalage)), + (dataframe['min_max_n'] >= self.buy_min_max_n3.value), + (dataframe['rsi_1h'] > self.buy_rsi_min.value), + (dataframe['rsi_1h'] < self.buy_rsi_max.value), + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + break + + for decalage in range(self.buy_decalage4.value - 2, self.buy_decalage4.value): + conditions = [ + (dataframe['rsi_1h'] >= self.buy_rsi_min_1d3.value), + (dataframe['rsi_1h'] < self.buy_rsi_min_1d4.value), + (dataframe['close'].shift(decalage) < dataframe['min200_004'].shift(decalage)), + (dataframe['min_max_n'] >= self.buy_min_max_n4.value), + (dataframe['rsi_1h'] > self.buy_rsi_min.value), + (dataframe['rsi_1h'] < self.buy_rsi_max.value), + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_4_' + str(decalage)) + break + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + return dataframe diff --git a/StrategyJD_5_5.json b/StrategyJD_5_5.json new file mode 100644 index 0000000..086c023 --- /dev/null +++ b/StrategyJD_5_5.json @@ -0,0 +1,59 @@ +{ + "strategy_name": "StrategyJD_5_5", + "params": { + "roi": { + "0": 0.5 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_decalage": 5, + "buy_decalage2": 7, + "buy_decalage3": 17, + "buy_decalage4": 20, + "buy_min_max_n": 0.12, + "buy_min_max_n2": 0.14, + "buy_min_max_n3": 0.16, + "buy_min_max_n4": 0.0, + "buy_rsi_max": 51, + "buy_rsi_min": 3, + "buy_rsi_min_1d": 18, + "buy_rsi_min_1d2": 25, + "buy_rsi_min_1d3": 56, + "buy_rsi_min_1d4": 90, + "min_n": 11, + "min_p": 1.0, + "min_percent": 1.008, + "min_percent2": 1.006, + "min_percent3": 1.012, + "min_percent4": 1.018 + }, + "sell": { + "max_percent": 0.004, + "max_percent2": 0.006, + "max_percent3": 0.0, + "max_percent4": 0.038, + "max_profit": 0.06, + "max_profit2": 0.05, + "max_profit3": 0.0, + "max_profit4": 0.01, + "sell_h_RSI": 79, + "sell_h_RSI2": 90, + "sell_h_RSI2_percent": 0.005, + "sell_h_RSI3": 84 + }, + "protection": { + "n_percent": 1, + "percent_sell": -0.08 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-15 16:27:30.204760+00:00" +} \ No newline at end of file diff --git a/StrategyJD_5_5.py b/StrategyJD_5_5.py new file mode 100644 index 0000000..6e4585c --- /dev/null +++ b/StrategyJD_5_5.py @@ -0,0 +1,314 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from freqtrade.strategy.strategy_helper import merge_informative_pair + + +# This class is a sample. Feel free to customize it. +class StrategyJD_5_5(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + # buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + #buy_msma_10 = DecimalParameter(0.997, 1.020, decimals=3, default=0.998, space="buy") + # pourcentage sma à dépasser + # buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.97, space="buy") + buy_decalage = IntParameter(1, 24, default=5, space="buy") + buy_decalage2 = IntParameter(1, 24, default=5, space="buy") + buy_decalage3 = IntParameter(1, 24, default=5, space="buy") + buy_decalage4 = IntParameter(1, 24, default=5, space="buy") + + buy_min_max_n = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + buy_min_max_n2 = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + buy_min_max_n3 = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + buy_min_max_n4 = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + + buy_rsi_min_1d = IntParameter(0, 25, default=5, space="buy") + buy_rsi_min_1d2 = IntParameter(25, 50, default=15, space="buy") + buy_rsi_min_1d3 = IntParameter(50, 75, default=50, space="buy") + buy_rsi_min_1d4 = IntParameter(75, 100, default=75, space="buy") + + min_percent = DecimalParameter(1, 1.02, decimals=3, default=1.002, space='buy') + min_percent2 = DecimalParameter(1, 1.02, decimals=3, default=1.002, space='buy') + min_percent3 = DecimalParameter(1, 1.02, decimals=3, default=1.002, space='buy') + min_percent4 = DecimalParameter(1, 1.02, decimals=3, default=1.002, space='buy') + + max_percent = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + max_percent2 = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + max_percent3 = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + max_percent4 = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + + max_profit = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + max_profit2 = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + max_profit3 = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + max_profit4 = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + + # sell_b_RSI = IntParameter(70, 98, default=88, space='sell') + # sell_b_RSI2 = IntParameter(70, 98, default=88, space='sell') + # sell_b_RSI3 = IntParameter(70, 98, default=80, space='sell') + # + # sell_b_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_RSI = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_h_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + # buy_rsi_max = IntParameter(50, 100, default=60, space="buy") + + buy_rsi_min = IntParameter(0, 50, default=25, space="buy") + buy_rsi_max = IntParameter(50, 100, default=60, space="buy") + # buy_rsi_min2 = IntParameter(0, 50, default=25, space="buy") + # buy_rsi_max2 = IntParameter(50, 100, default=60, space="buy") + + min_n = IntParameter(0, 24, default=15, space="buy") + min_p = DecimalParameter(1, 1.01, decimals=3, default=1.002, space="buy") + n_percent = IntParameter(1, 12, default=1, space="protection") + percent_sell = DecimalParameter(-0.2, -0.01, decimals=2, default=-0.08, space="protection") + + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'min200': {'color': 'yellow'}, + 'min200_001': {'color': 'yellow'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + } + } + } + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + + if last_candle['percent' + str(self.n_percent.value)] < self.percent_sell.value: + return 'sell_lost_percent' + str(self.n_percent.value) + + if (last_candle['rsi_1h'] < self.buy_rsi_min_1d.value): + max_percent = self.max_percent.value + max_profit = self.max_profit.value + else: + if (last_candle['rsi_1h'] < self.buy_rsi_min_1d2.value): + max_percent = self.max_percent2.value + max_profit = self.max_profit2.value + else: + if (last_candle['rsi_1h'] < self.buy_rsi_min_1d3.value): + max_percent = self.max_percent3.value + max_profit = self.max_profit3.value + else: + max_percent = self.max_percent4.value + max_profit = self.max_profit4.value + + if (current_profit > max_profit) & ( + (last_candle['percent1'] < -max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'h_percent_quick' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent1'] < - self.sell_h_RSI2_percent.value): + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): + return 'h_over_rsi_max' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # informative_pairs = [(pair, '1d') for pair in pairs] + # informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs = [(pair, '1h') for pair in pairs] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_001'] = dataframe['min200'] * self.min_percent.value + dataframe['min200_002'] = dataframe['min200'] * self.min_percent2.value + dataframe['min200_003'] = dataframe['min200'] * self.min_percent3.value + dataframe['min200_004'] = dataframe['min200'] * self.min_percent4.value + dataframe['max200'] = ta.MAX(dataframe['close'], timeperiod=200) + + dataframe['min_n'] = ta.MIN(dataframe['close'], timeperiod=self.min_n.value * 12) + dataframe['max_n'] = ta.MAX(dataframe['close'], timeperiod=self.min_n.value * 12) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['min_n_p'] = dataframe['min_n'] * self.min_p.value + + for n in range(1, 25): + dataframe["percent" + str(n)] = dataframe['close'].pct_change(n) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + ################### INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = ta.RSI(informative) + informative["rsi3"] = ta.RSI(informative, 3) + informative['r_rsi'] = (informative['rsi3'].div(10).round()) + # for n in range(1, 5): + # informative["percent" + str(n)] = informative['close'].pct_change(n) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + for decalage in range(self.buy_decalage.value - 2, self.buy_decalage.value): + conditions = [ + (dataframe['rsi_1h'] < self.buy_rsi_min_1d.value), + (dataframe['close'].shift(decalage) < dataframe['min200_001'].shift(decalage)), + (dataframe['min_max_n'] >= self.buy_min_max_n.value), + (dataframe['rsi_1h'] > self.buy_rsi_min.value), + (dataframe['rsi_1h'] < self.buy_rsi_max.value), + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + break + + for decalage in range(self.buy_decalage2.value - 2, self.buy_decalage2.value): + conditions = [ + (dataframe['rsi_1h'] >= self.buy_rsi_min_1d.value), + (dataframe['rsi_1h'] < self.buy_rsi_min_1d2.value), + (dataframe['close'].shift(decalage) < dataframe['min200_002'].shift(decalage)), + (dataframe['min_max_n'] >= self.buy_min_max_n2.value), + (dataframe['rsi_1h'] > self.buy_rsi_min.value), + (dataframe['rsi_1h'] < self.buy_rsi_max.value), + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + break + + for decalage in range(self.buy_decalage3.value - 2, self.buy_decalage3.value): + conditions = [ + (dataframe['rsi_1h'] >= self.buy_rsi_min_1d2.value), + (dataframe['rsi_1h'] < self.buy_rsi_min_1d3.value), + (dataframe['close'].shift(decalage) < dataframe['min200_003'].shift(decalage)), + (dataframe['min_max_n'] >= self.buy_min_max_n3.value), + (dataframe['rsi_1h'] > self.buy_rsi_min.value), + (dataframe['rsi_1h'] < self.buy_rsi_max.value), + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + break + + for decalage in range(self.buy_decalage4.value - 2, self.buy_decalage4.value): + conditions = [ + (dataframe['rsi_1h'] >= self.buy_rsi_min_1d3.value), + (dataframe['rsi_1h'] < self.buy_rsi_min_1d4.value), + (dataframe['close'].shift(decalage) < dataframe['min200_004'].shift(decalage)), + (dataframe['min_max_n'] >= self.buy_min_max_n4.value), + (dataframe['rsi_1h'] > self.buy_rsi_min.value), + (dataframe['rsi_1h'] < self.buy_rsi_max.value), + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_4_' + str(decalage)) + break + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + return dataframe diff --git a/StrategyJD_5_6.json b/StrategyJD_5_6.json new file mode 100644 index 0000000..1439264 --- /dev/null +++ b/StrategyJD_5_6.json @@ -0,0 +1,53 @@ +{ + "strategy_name": "StrategyJD_5_6", + "params": { + "roi": { + "0": 0.5 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_decalage": 5, + "buy_decalage2": 7, + "buy_decalage3": 17, + "buy_min_max_n": 0.12, + "buy_min_max_n2": 0.14, + "buy_min_max_n3": 0.16, + "buy_rsi_max": 55, + "buy_rsi_min": 3, + "buy_rsi_min_1d": 18, + "buy_rsi_min_1d2": 25, + "buy_rsi_min_1d3": 56, + "min_n": 11, + "min_p": 1.0, + "min_percent": 1.008, + "min_percent2": 1.010, + "min_percent3": 1.012 + }, + "sell": { + "max_percent": 0.045, + "max_percent2": 0.01, + "max_percent3": 0.004, + "max_profit": 0.09, + "max_profit2": 0.09, + "max_profit3": 0.0, + "sell_RSI": 73, + "sell_RSI2": 84, + "sell_RSI2_percent": 0.003, + "sell_RSI3": 73 + }, + "protection": { + "n_percent": 1, + "percent_sell": -0.08 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-16 17:52:27.040594+00:00" +} \ No newline at end of file diff --git a/StrategyJD_5_6.py b/StrategyJD_5_6.py new file mode 100644 index 0000000..f6ca370 --- /dev/null +++ b/StrategyJD_5_6.py @@ -0,0 +1,331 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from freqtrade.strategy.strategy_helper import merge_informative_pair + + +# This class is a sample. Feel free to customize it. +class StrategyJD_5_6(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + # buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + #buy_msma_10 = DecimalParameter(0.997, 1.020, decimals=3, default=0.998, space="buy") + # pourcentage sma à dépasser + # buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.97, space="buy") + buy_decalage = IntParameter(1, 24, default=5, space="buy") + buy_decalage2 = IntParameter(1, 24, default=5, space="buy") + buy_decalage3 = IntParameter(1, 24, default=5, space="buy") + # buy_decalage4 = IntParameter(1, 24, default=5, space="buy") + + buy_min_max_n = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + buy_min_max_n2 = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + buy_min_max_n3 = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + # buy_min_max_n4 = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + + buy_rsi_min_1d = IntParameter(0, 20, default=5, space="buy") + buy_rsi_min_1d2 = IntParameter(20, 35, default=30, space="buy") + buy_rsi_min_1d3 = IntParameter(35, 50, default=45, space="buy") + # buy_rsi_min_1d4 = IntParameter(50, 70, default=60, space="buy") + + min_percent = DecimalParameter(1, 1.02, decimals=3, default=1.002, space='buy') + min_percent2 = DecimalParameter(1, 1.02, decimals=3, default=1.002, space='buy') + min_percent3 = DecimalParameter(1, 1.02, decimals=3, default=1.002, space='buy') + # min_percent4 = DecimalParameter(1, 1.02, decimals=3, default=1.002, space='buy') + + max_percent = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + max_percent2 = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + max_percent3 = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + # max_percent4 = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + + max_profit = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + max_profit2 = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + max_profit3 = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + # max_profit4 = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + + # sell_b_RSI = IntParameter(70, 98, default=88, space='sell') + # sell_b_RSI2 = IntParameter(70, 98, default=88, space='sell') + # sell_b_RSI3 = IntParameter(70, 98, default=80, space='sell') + # + # sell_b_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_RSI = IntParameter(70, 98, default=88, space='sell') + sell_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + # buy_rsi_max = IntParameter(50, 100, default=60, space="buy") + + buy_rsi_min = IntParameter(0, 50, default=25, space="buy") + buy_rsi_max = IntParameter(50, 100, default=60, space="buy") + # buy_rsi_min2 = IntParameter(0, 50, default=25, space="buy") + # buy_rsi_max2 = IntParameter(50, 100, default=60, space="buy") + # buy_rsi_min3 = IntParameter(0, 50, default=25, space="buy") + # buy_rsi_max3 = IntParameter(50, 100, default=60, space="buy") + # buy_rsi_min4 = IntParameter(0, 50, default=25, space="buy") + # buy_rsi_max4 = IntParameter(50, 100, default=60, space="buy") + + min_n = IntParameter(0, 24, default=15, space="buy") + min_p = DecimalParameter(1, 1.01, decimals=3, default=1.002, space="buy") + n_percent = IntParameter(1, 12, default=1, space="protection") + percent_sell = DecimalParameter(-0.2, -0.01, decimals=2, default=-0.08, space="protection") + + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + "main_plot": { + "bb_lowerband": { + "color": "white" + }, + "bb_upperband": { + "color": "white" + }, + "min200": { + "color": "yellow" + }, + "min200_001": { + "color": "yellow" + }, + "max200": { + "color": "yellow", + "type": "line" + } + }, + "subplots": { + "BB": { + "bb_width": { + "color": "white" + } + }, + "Percent": { + "min_max200": { + "color": "#c046bb", + "type": "line" + } + } + } + } + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + + number = 0 + if (last_candle['rsi_1h'] < self.buy_rsi_min_1d.value): + max_percent = self.max_percent.value + max_profit = self.max_profit.value + number = 1 + else: + if (last_candle['rsi_1h'] < self.buy_rsi_min_1d2.value): + max_percent = self.max_percent2.value + max_profit = self.max_profit2.value + number = 2 + else: + # if (last_candle['rsi_1h'] < self.buy_rsi_min_1d3.value): + max_percent = self.max_percent3.value + max_profit = self.max_profit3.value + number = 3 + # else: + # max_percent = self.max_percent4.value + # max_profit = self.max_profit4.value + # number = 4 + if last_candle['percent' + str(self.n_percent.value)] < self.percent_sell.value: + return 'sell_lost_percent_' + str(number) + + if (current_profit > max_profit) & ( + (last_candle['percent1'] < -max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'h_percent_quick_' + str(number) + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI.value): + return 'h_over_rsi_' + str(number) + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI2.value) & \ + (last_candle['percent1'] < - self.sell_RSI2_percent.value): + return 'h_over_rsi_2_' + str(number) + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): + return 'h_over_rsi_max_' + str(number) + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # informative_pairs = [(pair, '1d') for pair in pairs] + # informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs = [(pair, '1h') for pair in pairs] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_001'] = dataframe['min200'] * self.min_percent.value + dataframe['min200_002'] = dataframe['min200'] * self.min_percent2.value + dataframe['min200_003'] = dataframe['min200'] * self.min_percent3.value + # dataframe['min200_004'] = dataframe['min200'] * self.min_percent4.value + dataframe['max200'] = ta.MAX(dataframe['close'], timeperiod=200) + + dataframe['min_n'] = ta.MIN(dataframe['close'], timeperiod=self.min_n.value * 12) + dataframe['max_n'] = ta.MAX(dataframe['close'], timeperiod=self.min_n.value * 12) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['min_n_p'] = dataframe['min_n'] * self.min_p.value + + for n in range(1, 25): + dataframe["percent" + str(n)] = dataframe['close'].pct_change(n) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + ################### INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = ta.RSI(informative) + informative["rsi3"] = ta.RSI(informative, 3) + informative['r_rsi'] = (informative['rsi3'].div(10).round()) + # for n in range(1, 5): + # informative["percent" + str(n)] = informative['close'].pct_change(n) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + for decalage in range(self.buy_decalage.value - 2, self.buy_decalage.value): + conditions = [ + (dataframe['rsi_1h'] < self.buy_rsi_min_1d.value), + (dataframe['close'].shift(decalage) < dataframe['min200_001'].shift(decalage)), + (dataframe['min_max_n'] >= self.buy_min_max_n.value), + (dataframe['rsi_1h'] > self.buy_rsi_min.value), + (dataframe['rsi_1h'] < self.buy_rsi_max.value), + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + break + + for decalage in range(self.buy_decalage2.value - 2, self.buy_decalage2.value): + conditions = [ + (dataframe['rsi_1h'] >= self.buy_rsi_min_1d.value), + (dataframe['rsi_1h'] < self.buy_rsi_min_1d2.value), + (dataframe['close'].shift(decalage) < dataframe['min200_002'].shift(decalage)), + (dataframe['min_max_n'] >= self.buy_min_max_n2.value), + (dataframe['rsi_1h'] > self.buy_rsi_min.value), + (dataframe['rsi_1h'] < self.buy_rsi_max.value), + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + break + + for decalage in range(self.buy_decalage3.value - 2, self.buy_decalage3.value): + conditions = [ + (dataframe['rsi_1h'] >= self.buy_rsi_min_1d2.value), + (dataframe['rsi_1h'] < self.buy_rsi_min_1d3.value), + (dataframe['close'].shift(decalage) < dataframe['min200_003'].shift(decalage)), + (dataframe['min_max_n'] >= self.buy_min_max_n3.value), + (dataframe['rsi_1h'] > self.buy_rsi_min.value), + (dataframe['rsi_1h'] < self.buy_rsi_max.value), + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + break + + # for decalage in range(self.buy_decalage4.value - 2, self.buy_decalage4.value): + # conditions = [ + # (dataframe['rsi_1h'] >= self.buy_rsi_min_1d3.value), + # (dataframe['rsi_1h'] < self.buy_rsi_min_1d4.value), + # (dataframe['close'].shift(decalage) < dataframe['min200_004'].shift(decalage)), + # (dataframe['min_max_n'] >= self.buy_min_max_n4.value), + # (dataframe['rsi_1h'] > self.buy_rsi_min.value), + # (dataframe['rsi_1h'] < self.buy_rsi_max.value), + # ] + # # GUARDS AND TRENDS + # if conditions: + # dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + # ['buy', 'buy_tag']] = (1, 'buy_4_' + str(decalage)) + # break + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + return dataframe diff --git a/StrategyJD_5_7.json b/StrategyJD_5_7.json new file mode 100644 index 0000000..d20c875 --- /dev/null +++ b/StrategyJD_5_7.json @@ -0,0 +1,49 @@ +{ + "strategy_name": "StrategyJD_5_7", + "params": { + "roi": { + "0": 0.5 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_decalage": 3, + "buy_decalage2": 8, + "buy_decalage3": 19, + "buy_min_max_n": 0.12, + "buy_min_max_n2": 0.11, + "buy_min_max_n3": 0.14, + "buy_mrsi3": -0.1, + "min_n": 11, + "min_p": 1.006, + "min_percent": 1.014, + "min_percent2": 1.006, + "min_percent3": 1.006 + }, + "sell": { + "max_percent": 0.004, + "max_percent2": 0.006, + "max_percent3": 0.0, + "max_profit": 0.06, + "max_profit2": 0.05, + "max_profit3": 0.0, + "sell_h_RSI": 79, + "sell_h_RSI2": 90, + "sell_h_RSI2_percent": 0.005, + "sell_h_RSI3": 84 + }, + "protection": { + "n_percent": 1, + "percent_sell": -0.08 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-17 13:07:40.696464+00:00" +} \ No newline at end of file diff --git a/StrategyJD_5_7.py b/StrategyJD_5_7.py new file mode 100644 index 0000000..cb6960d --- /dev/null +++ b/StrategyJD_5_7.py @@ -0,0 +1,295 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from freqtrade.strategy.strategy_helper import merge_informative_pair + + +# This class is a sample. Feel free to customize it. +class StrategyJD_5_7(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + # buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + #buy_msma_10 = DecimalParameter(0.997, 1.020, decimals=3, default=0.998, space="buy") + # pourcentage sma à dépasser + # buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.97, space="buy") + buy_decalage = IntParameter(3, 10, default=5, space="buy") + buy_decalage2 = IntParameter(5, 15, default=5, space="buy") + buy_decalage3 = IntParameter(10, 20, default=5, space="buy") + + buy_min_max_n = DecimalParameter(0.06, 0.14, decimals=2, default=0.05, space='buy') + buy_min_max_n2 = DecimalParameter(0.06, 0.14, decimals=2, default=0.05, space='buy') + buy_min_max_n3 = DecimalParameter(0.06, 0.14, decimals=2, default=0.05, space='buy') + + # buy_rsi_min_1d = IntParameter(0, 25, default=5, space="buy") + # buy_rsi_min_1d2 = IntParameter(25, 50, default=15, space="buy") + # buy_rsi_min_1d3 = IntParameter(50, 75, default=50, space="buy") + + min_percent = DecimalParameter(1.005, 1.015, decimals=3, default=1.002, space='buy') + min_percent2 = DecimalParameter(1.005, 1.015, decimals=3, default=1.002, space='buy') + min_percent3 = DecimalParameter(1.005, 1.015, decimals=3, default=1.002, space='buy') + + max_percent = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + max_percent2 = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + max_percent3 = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + + max_profit = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + max_profit2 = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + max_profit3 = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + + # sell_b_RSI = IntParameter(70, 98, default=88, space='sell') + # sell_b_RSI2 = IntParameter(70, 98, default=88, space='sell') + # sell_b_RSI3 = IntParameter(70, 98, default=80, space='sell') + # + # sell_b_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_RSI = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_h_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + # buy_rsi_max = IntParameter(50, 100, default=60, space="buy") + + # buy_rsi_min = IntParameter(0, 50, default=25, space="buy") + # buy_rsi_max = IntParameter(50, 100, default=60, space="buy") + buy_mrsi3 = DecimalParameter(-0.1, 0.1, decimals=2, default=0, space="buy") + + # buy_rsi_min2 = IntParameter(0, 50, default=25, space="buy") + # buy_rsi_max2 = IntParameter(50, 100, default=60, space="buy") + + min_n = IntParameter(0, 24, default=15, space="buy") + min_p = DecimalParameter(1, 1.01, decimals=3, default=1.002, space="buy") + n_percent = IntParameter(1, 12, default=1, space="protection") + percent_sell = DecimalParameter(-0.2, -0.01, decimals=2, default=-0.08, space="protection") + + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'min200': {'color': 'yellow'}, + 'min200_001': {'color': 'yellow'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + } + } + } + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + + if last_candle['percent' + str(self.n_percent.value)] < self.percent_sell.value: + return 'sell_lost_percent' + str(self.n_percent.value) + + if (last_candle['rsi_1h'] < 18): + max_percent = self.max_percent.value + max_profit = self.max_profit.value + else: + if (last_candle['rsi_1h'] < 25): + max_percent = self.max_percent2.value + max_profit = self.max_profit2.value + else: + max_percent = self.max_percent3.value + max_profit = self.max_profit3.value + + if (current_profit > max_profit) & ( + (last_candle['percent1'] < -max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'h_percent_quick' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent1'] < - self.sell_h_RSI2_percent.value): + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): + return 'h_over_rsi_max' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # informative_pairs = [(pair, '1d') for pair in pairs] + # informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs = [(pair, '1h') for pair in pairs] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_001'] = dataframe['min200'] * self.min_percent.value + dataframe['min200_002'] = dataframe['min200'] * self.min_percent2.value + dataframe['min200_003'] = dataframe['min200'] * self.min_percent3.value + dataframe['max200'] = ta.MAX(dataframe['close'], timeperiod=200) + + dataframe['min_n'] = ta.MIN(dataframe['close'], timeperiod=self.min_n.value * 12) + dataframe['max_n'] = ta.MAX(dataframe['close'], timeperiod=self.min_n.value * 12) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['min_n_p'] = dataframe['min_n'] * self.min_p.value + + for n in range(1, 25): + dataframe["percent" + str(n)] = dataframe['close'].pct_change(n) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + ################### INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = ta.RSI(informative) + informative["rsi3"] = ta.RSI(informative, 3) + informative["mrsi3"] = informative["rsi"].pct_change(3) + informative['r_rsi'] = (informative['rsi3'].div(10).round()) + # for n in range(1, 5): + # informative["percent" + str(n)] = informative['close'].pct_change(n) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + for decalage in range(self.buy_decalage.value - 2, self.buy_decalage.value): + conditions = [ + (dataframe['rsi_1h'] < 18), + (dataframe['close'].shift(decalage) < dataframe['min200_001'].shift(decalage)), + (dataframe['min_max_n'] >= self.buy_min_max_n.value), + (dataframe['rsi_1h'] > 0), + (dataframe['rsi_1h'] < 51), + (dataframe['mrsi3_1h'] > self.buy_mrsi3.value) + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + break + + for decalage in range(self.buy_decalage2.value - 2, self.buy_decalage2.value): + conditions = [ + (dataframe['rsi_1h'] >= 18), + (dataframe['rsi_1h'] < 25), + (dataframe['close'].shift(decalage) < dataframe['min200_002'].shift(decalage)), + (dataframe['min_max_n'] >= self.buy_min_max_n2.value), + (dataframe['rsi_1h'] > 0), + (dataframe['rsi_1h'] < 51), + (dataframe['mrsi3_1h'] > self.buy_mrsi3.value) + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + break + + for decalage in range(self.buy_decalage3.value - 2, self.buy_decalage3.value): + conditions = [ + (dataframe['rsi_1h'] >= 25), + (dataframe['rsi_1h'] < 56), + (dataframe['close'].shift(decalage) < dataframe['min200_003'].shift(decalage)), + (dataframe['min_max_n'] >= self.buy_min_max_n3.value), + (dataframe['rsi_1h'] > 0), + (dataframe['rsi_1h'] < 51), + (dataframe['mrsi3_1h'] > self.buy_mrsi3.value) + + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + break + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + return dataframe diff --git a/StrategyJD_5_8.json b/StrategyJD_5_8.json new file mode 100644 index 0000000..587ff1e --- /dev/null +++ b/StrategyJD_5_8.json @@ -0,0 +1,45 @@ +{ + "strategy_name": "StrategyJD_5_8", + "params": { + "roi": { + "0": 0.5 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_min_max_n": 0.27, + "buy_min_max_n2": 0.08, + "buy_min_max_n3": 0.06, + "buy_mrsi3": -0.07, + "min_n": 3, + "min_percent": 1.01, + "min_percent2": 1.004, + "min_percent3": 1.008 + }, + "sell": { + "max_percent": 0.032, + "max_percent2": 0.019, + "max_percent3": 0.024, + "max_profit": 0.1, + "max_profit2": 0.07, + "max_profit3": 0.04, + "sell_h_RSI": 91, + "sell_h_RSI2": 96, + "sell_h_RSI2_percent": 0.019, + "sell_h_RSI3": 92 + }, + "protection": { + "n_percent": 5, + "percent_sell": -0.13 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-17 18:41:11.974567+00:00" +} \ No newline at end of file diff --git a/StrategyJD_5_8.py b/StrategyJD_5_8.py new file mode 100644 index 0000000..0cffffa --- /dev/null +++ b/StrategyJD_5_8.py @@ -0,0 +1,290 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from freqtrade.strategy.strategy_helper import merge_informative_pair + + +# This class is a sample. Feel free to customize it. +class StrategyJD_5_8(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + # buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + #buy_msma_10 = DecimalParameter(0.997, 1.020, decimals=3, default=0.998, space="buy") + # pourcentage sma à dépasser + # buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.97, space="buy") + buy_decalage = IntParameter(3, 10, default=5, space="buy") + buy_decalage2 = IntParameter(5, 15, default=5, space="buy") + buy_decalage3 = IntParameter(10, 20, default=5, space="buy") + + buy_min_max_n = DecimalParameter(0.06, 0.30, decimals=2, default=0.05, space='buy') + buy_min_max_n2 = DecimalParameter(0.06, 0.14, decimals=2, default=0.05, space='buy') + buy_min_max_n3 = DecimalParameter(0.06, 0.14, decimals=2, default=0.05, space='buy') + + # buy_rsi_min_1d = IntParameter(0, 25, default=5, space="buy") + # buy_rsi_min_1d2 = IntParameter(25, 50, default=15, space="buy") + # buy_rsi_min_1d3 = IntParameter(50, 75, default=50, space="buy") + + min_percent = DecimalParameter(1.005, 1.015, decimals=3, default=1.002, space='buy') + min_percent2 = DecimalParameter(1.005, 1.015, decimals=3, default=1.002, space='buy') + min_percent3 = DecimalParameter(1.005, 1.015, decimals=3, default=1.002, space='buy') + + buy_mrsi3 = DecimalParameter(-0.1, 0.1, decimals=2, default=0, space="buy") + + min_n = IntParameter(0, 24, default=15, space="buy") + # min_p = DecimalParameter(1, 1.01, decimals=3, default=1.002, space="buy") + + max_percent = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + max_percent2 = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + max_percent3 = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + + max_profit = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + max_profit2 = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + max_profit3 = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + + # sell_b_RSI = IntParameter(70, 98, default=88, space='sell') + # sell_b_RSI2 = IntParameter(70, 98, default=88, space='sell') + # sell_b_RSI3 = IntParameter(70, 98, default=80, space='sell') + # + # sell_b_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_RSI = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_h_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + n_percent = IntParameter(1, 12, default=1, space="protection") + percent_sell = DecimalParameter(-0.2, -0.01, decimals=2, default=-0.08, space="protection") + + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'min200': {'color': 'yellow'}, + 'min200_001': {'color': 'yellow'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + } + } + } + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + + if last_candle['percent' + str(self.n_percent.value)] < self.percent_sell.value: + return 'sell_lost_percent' + str(self.n_percent.value) + + if (last_candle['rsi_1h'] < 18): + max_percent = self.max_percent.value + max_profit = self.max_profit.value + else: + if (last_candle['rsi_1h'] < 25): + max_percent = self.max_percent2.value + max_profit = self.max_profit2.value + else: + max_percent = self.max_percent3.value + max_profit = self.max_profit3.value + + if (current_profit > max_profit) & ( + (last_candle['percent1'] < -max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'h_percent_quick' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent1'] < - self.sell_h_RSI2_percent.value): + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): + return 'h_over_rsi_max' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # informative_pairs = [(pair, '1d') for pair in pairs] + # informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs = [(pair, '1h') for pair in pairs] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['max200'] = ta.MAX(dataframe['close'], timeperiod=200) + + dataframe['min_n'] = ta.MIN(dataframe['close'], timeperiod=self.min_n.value * 24) + dataframe['max_n'] = ta.MAX(dataframe['close'], timeperiod=self.min_n.value * 24) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + # dataframe['min_n_p'] = dataframe['min_n'] * self.min_p.value + dataframe['minn_1'] = dataframe['min_n'] * self.min_percent.value + dataframe['minn_2'] = dataframe['min_n'] * self.min_percent2.value + dataframe['minn_3'] = dataframe['min_n'] * self.min_percent3.value + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + + for n in range(1, 25): + dataframe["percent" + str(n)] = dataframe['close'].pct_change(n) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # 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'] + # dataframe["bb_percent"] = ( + # (dataframe["close"] - dataframe["bb_lowerband"]) / + # (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + # ) + # dataframe["bb_width"] = ( + # (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + # ) + + ################### INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = ta.RSI(informative) + informative["rsi3"] = ta.RSI(informative, 3) + informative["mrsi3"] = informative["rsi"].pct_change(3) + informative['r_rsi'] = (informative['rsi3'].div(10).round()) + # for n in range(1, 5): + # informative["percent" + str(n)] = informative['close'].pct_change(n) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + for decalage in range(self.buy_decalage.value - 2, self.buy_decalage.value): + conditions = [ + (dataframe['rsi_1h'] < 18), + (dataframe['close'] < dataframe['minn_1']), + (dataframe['min_max_n'] >= self.buy_min_max_n.value), + (dataframe['rsi_1h'] > 0), + (dataframe['rsi_1h'] < 51), + (dataframe['mrsi3_1h'] > self.buy_mrsi3.value) + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + break + + for decalage in range(self.buy_decalage2.value - 2, self.buy_decalage2.value): + conditions = [ + (dataframe['rsi_1h'] >= 18), + (dataframe['rsi_1h'] < 25), + (dataframe['close'] < dataframe['minn_2']), + (dataframe['min_max_n'] >= self.buy_min_max_n2.value), + (dataframe['rsi_1h'] > 0), + (dataframe['rsi_1h'] < 51), + (dataframe['mrsi3_1h'] > self.buy_mrsi3.value) + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + break + + for decalage in range(self.buy_decalage3.value - 2, self.buy_decalage3.value): + conditions = [ + (dataframe['rsi_1h'] >= 25), + (dataframe['rsi_1h'] < 56), + (dataframe['close'] < dataframe['minn_3']), + (dataframe['min_max_n'] >= self.buy_min_max_n3.value), + (dataframe['rsi_1h'] > 0), + (dataframe['rsi_1h'] < 51), + (dataframe['mrsi3_1h'] > self.buy_mrsi3.value) + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + break + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + return dataframe diff --git a/StrategyJD_5_9.json b/StrategyJD_5_9.json new file mode 100644 index 0000000..e84b659 --- /dev/null +++ b/StrategyJD_5_9.json @@ -0,0 +1,43 @@ +{ + "strategy_name": "StrategyJD_5_9", + "params": { + "roi": { + "0": 0.5 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_decalage4": 1, + "min_percent4": 7, + "rapport_min_n": 14 + }, + "sell": { + "max_percent": 0.041, + "max_percent2": 0.032, + "max_percent3": 0.013, + "max_profit": 0.09, + "max_profit2": 0.1, + "max_profit3": 0.06, + "sell_h_RSI": 82, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.011, + "sell_h_RSI3": 72 + }, + "protection": { + "hours_sell": 16, + "n_percent": 7, + "percent_sell": -0.07, + "percent_sell_sma5_1d": -0.07, + "percent_sell_stop": -0.7 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-21 09:43:15.542215+00:00" +} \ No newline at end of file diff --git a/StrategyJD_5_9.py b/StrategyJD_5_9.py new file mode 100644 index 0000000..2573060 --- /dev/null +++ b/StrategyJD_5_9.py @@ -0,0 +1,301 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from freqtrade.strategy.strategy_helper import merge_informative_pair + + +# This class is a sample. Feel free to customize it. +class StrategyJD_5_9(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + buy_decalage4 = IntParameter(1, 10, default=5, space="buy") + + min_percent4 = IntParameter(1, 20, default=10, space='buy') + # buy_mrsi3 = DecimalParameter(-0.1, 0.1, decimals=2, default=0, space="buy") + + rapport_min_n = IntParameter(1, 20, default=10, space='buy') + + min_n = 16 + + max_percent = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + max_percent2 = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + max_percent3 = DecimalParameter(0, 0.05, decimals=3, default=0.005, space='sell') + + max_profit = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + max_profit2 = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + max_profit3 = DecimalParameter(0, 0.1, decimals=2, default=0.01, space='sell') + + sell_h_RSI = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_h_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + n_percent = IntParameter(1, 12, default=1, space="protection") + percent_sell = DecimalParameter(-0.1, -0.01, decimals=2, default=-0.08, space="protection") + percent_sell_stop = DecimalParameter(-0.8, -0.1, decimals=1, default=-0.8, space="protection") + percent_sell_sma5_1d = DecimalParameter(-0.1, 0, decimals=2, default=0, space="protection") + hours_sell = IntParameter(5, 48, default=24, space="protection") + + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + lim_0 = 10 + lim_1 = 18 + lim_2 = 25 + lim_3 = 51 + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + "main_plot": { + "bb_lowerband": { + "color": "white" + }, + "bb_upperband": { + "color": "white" + }, + "min200": { + "color": "yellow" + }, + "max200": { + "color": "yellow" + }, + "min_n": { + "color": "#600e82" + }, + "max_n": { + "color": "#600e82" + }, + "min5_1d": { + "color": "#6aa123", + }, + "max5_1d": { + "color": "red" + }, + "max3_1d": { + "color": "blue" + }, + "close_1d": { + "color": "green" + }, + "close_1M": { + "color": "cyan" + }, + "min_n_1d": { + "color": "pink" + }, + "max_n_1d": { + "color": "pink" + }, + "sma5_1d": { + "color": "black" + } + }, + "subplots": { + "Rsi": { + "rsi_1h": { + "color": "blue" + }, + "rsi_1d": { + "color": "red" + }, + "rsi": { + "color": "green" + }, + }, + # "Percent": { + # "min_max200": { + # "color": "#c046bb" + # } + # } + } + } + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_previous_last_candle = dataframe.iloc[-2].squeeze() + + days = (current_time - trade.open_date_utc).days + minutes = (current_time - trade.open_date_utc).seconds / 60 + hours = minutes / 60 + + if (last_candle['sma5_diff_1d'] < self.percent_sell_sma5_1d.value) & (current_profit < -0.05): + return 'sell_sma5_1d' + + if (current_profit < self.percent_sell_stop.value) & (hours >= self.hours_sell.value) & (last_candle['percent_1h'] <= -0.05): + return 'sell_stop' + + # if (last_candle['percent1'] < -0.005) & (last_candle['max200'] == previous_previous_last_candle['max200']) & (minutes > 30): + # return 'sell_percent1' + # + # if (last_candle['percent3'] < -0.005) & (last_candle['percent1'] < 0) \ + # & (previous_last_candle['percent1'] < 0) & (previous_previous_last_candle['percent1'] < 0) & (minutes > 30): + # return 'sell_percent3' + + #if last_candle['percent' + str(self.n_percent.value)] < self.percent_sell.value: + # return 'sell_lost_percent_' + str(self.n_percent.value) + + if (last_candle['rsi_1h'] < self.lim_1): + max_percent = self.max_percent.value + max_profit = self.max_profit.value + else: + if (last_candle['rsi_1h'] < self.lim_2): + max_percent = self.max_percent2.value + max_profit = self.max_profit2.value + else: + max_percent = self.max_percent3.value + max_profit = self.max_profit3.value + + if (current_profit > max_profit) & ( + #(last_candle['percent1'] < -max_percent) | + (last_candle['percent3'] < -max_percent) | (last_candle['percent5'] < -max_percent)): + #& (last_candle['close'] > last_candle['max3_1d']): + return 'h_percent_quick' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent1'] < - self.sell_h_RSI2_percent.value): + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle['max3_1d']): + return 'h_over_rsi_max' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '1M') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # print(self.min_percent.value, self.min_percent2.value, self.min_percent3.value, + # self.buy_decalage.value, self.buy_decalage2.value, self.buy_decalage3.value, + # ) + dataframe['min200'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['max200'] = ta.MAX(dataframe['close'], timeperiod=200) + period = int(self.min_n * 24) + dataframe['min_n'] = ta.MIN(dataframe['close'], timeperiod=period) + dataframe['max_n'] = ta.MAX(dataframe['close'], timeperiod=period) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + for n in range(1, 25): + dataframe["percent" + str(n)] = dataframe['close'].pct_change(n) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + ################### INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = ta.RSI(informative) + informative["rsi3"] = ta.RSI(informative, 3) + # informative["mrsi3"] = informative["rsi"].pct_change(3) + informative['r_rsi'] = (informative['rsi3'].div(10).round()) + informative['percent'] = informative['close'].pct_change(1) + # for n in range(1, 5): + # informative["percent" + str(n)] = informative['close'].pct_change(n) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + ################### INFORMATIVE 1d + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = ta.RSI(informative, period=5) + informative['min5'] = ta.MIN(informative['close'], timeperiod=5) + informative['max3'] = ta.MAX(informative['close'], timeperiod=3) + informative['max5'] = ta.MAX(informative['close'], timeperiod=5) + informative['sma5'] = ta.SMA(informative['close'], timeperiod=5) + informative['min_n'] = ta.MIN(informative['close'], timeperiod=14) + informative['max_n'] = ta.MAX(informative['close'], timeperiod=14) + informative['min_max_n'] = (informative['max_n'] - informative['min_n']) / informative['min_n'] + informative['percent'] = informative['close'].pct_change(1) + informative['sma5_diff'] = informative['sma5'] - informative['sma5'].shift(1) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ################### INFORMATIVE 1M + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1M") + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1M", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [ + (dataframe['close'] <= dataframe['min_n_1d']), # * (1 + (dataframe['min_max_n_1d'] / self.min_percent3.value))), + (dataframe['close'] <= dataframe['min_n'] * (1 + (dataframe['min_max_n'] / self.min_percent4.value))), + (dataframe['min_n'].shift(self.buy_decalage4.value) == dataframe['min_n']), + (dataframe['min_n_1d'] / dataframe['min_n'] > 1 + self.rapport_min_n.value / 100) + ] + # GUARDS AND TRENDS + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + ['buy', 'buy_tag']] = (1, 'buy_4') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + return dataframe diff --git a/StrategyPierrick.py b/StrategyPierrick.py new file mode 100644 index 0000000..a2b1cc9 --- /dev/null +++ b/StrategyPierrick.py @@ -0,0 +1,139 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + +# This class is a sample. Feel free to customize it. +class StrategyPierrick(IStrategy): + + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + + # ROI table: + minimal_roi = { + #"0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 #0.015 + trailing_only_offset_is_reached = True + + #max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "Aaron": { + 'aroonup': {'color': 'blue'}, + 'aroondown': {'color': 'red'} + } + } + } + + def informative_pairs(self): + + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # MACD + #macd = ta.MACD(dataframe) + #dataframe['macd'] = macd['macd'] + #dataframe['macdsignal'] = macd['macdsignal'] + #dataframe['macdhist'] = macd['macdhist'] + + # RSI + #dataframe['rsi'] = ta.RSI(dataframe) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + ( + (dataframe['close'] < dataframe['bb_lowerband']) + & (dataframe['bb_width'] >= 0.065) + #& (dataframe['rsi'] < 45) + & (dataframe['volume'] > 0) + ) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + + ), + + 'sell'] = 1 + return dataframe + diff --git a/StrategyPierrick2.jsonOLD b/StrategyPierrick2.jsonOLD new file mode 100644 index 0000000..cf5277d --- /dev/null +++ b/StrategyPierrick2.jsonOLD @@ -0,0 +1,28 @@ +{ + "strategy_name": "StrategyPierrick2", + "params": { + "buy": { + "buy_bollinger": 0.04, + "buy_sma_percent": 0.93 + }, + "sell": {}, + "protection": {}, + "roi": { + "0": 0.237, + "19": 0.065, + "65": 0.026, + "185": 0 + }, + "stoploss": { + "stoploss": -0.327 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.116, + "trailing_stop_positive_offset": 0.15000000000000002, + "trailing_only_offset_is_reached": true + } + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-18 21:32:34.141258+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick2.py b/StrategyPierrick2.py new file mode 100644 index 0000000..4883184 --- /dev/null +++ b/StrategyPierrick2.py @@ -0,0 +1,272 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from functools import reduce + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter +from pandas import DataFrame + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + +# This class is a sample. Feel free to customize it. +class StrategyPierrick2(IStrategy): + + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + + # ROI table: + minimal_roi = { + "0": 1, + # "600": 0.12, + # "1200": 0.08, + # "2400": 0.06, + # "3600": 0.04, + # "7289": 0 + } + + # Stoploss: + stoploss = -1 + # Buy hypers + timeframe = '4h' + + # Trailing stoploss + trailing_stop = False + trailing_stop_positive = 0.15 + trailing_stop_positive_offset = 0.20 + trailing_only_offset_is_reached = True + + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "Aaron": { + 'aroonup': {'color': 'blue'}, + 'aroondown': {'color': 'red'} + } + } + } + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + #print("last_candle", last_candle) + #print("previous_last_candle", previous_last_candle) + + count = 0 + for coin, balance in self.wallets.get_all_balances().items(): + count = count + 1 + # print(coin, " ", balance) + # print("count=", count) + + # (last_candle['percent5'] < -0.005) \ + # if (0 < current_profit < 0.005) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 2): + # # & (previous_last_candle['sma10'] > last_candle['sma10']): + # print("too_small_gain", pair, trade, " profit=", current_profit, " rate=", current_rate, " percent5=", + # last_candle['percent5']) + # return 'too_small_gain' + + # if (current_profit < -0.05) \ + # & ((current_time - trade.open_date_utc).days >= 3): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'stop_loss_profit' + + # if (current_profit > 0.02) \ + # & (last_candle['percent'] < 0.01) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'lost_half_profit' + + # ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \ + if (current_profit > 0) \ + & (previous_5_candle['sma10'] > last_candle['sma10'] * 1.005) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < - (current_profit / 4))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_bb_band_sma10_desc' + + # if (current_profit > 0) \ + # & (last_candle['percent'] < -0.02): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'stop_percent_loss' + + #if (current_profit > 0) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \ + # & (previous_last_candle['sma20'] > last_candle['sma20']) \ + # & (last_candle['percent'] < 0): + # print("over_bb_band_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'over_bb_band_sma20_desc' + + if (current_profit > 0) \ + & (last_candle['rsi'] > 88): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + # description trade + # Trade(id=0, pair=CAKE/USDT, amount=4.19815281, open_rate=11.91000000, open_since=2021-12-22 17:55:00) + # print(last_candle) + # if 0.05 < current_profit < 1: + # if ( + # (previous_last_candle['sma10'] > last_candle['sma10'] * 1.005) & + # (current_time - trade.open_date_utc).seconds >= 3600 * 3 + # # ) | ( + # # (current_time - trade.open_date_utc).seconds >= 3600 * 6 + # ): + # # self.lock_pair(pair, until=current_time + timedelta(hours=3)) + # + # print("profit_3h_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'profit_3h_sma10_desc' + # + # if (0 < current_profit < 0.1) \ + # & (previous_last_candle['sma20'] > last_candle['sma20']) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 5): + # print("profit_5h_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'profit_5h_sma20_desc' + + # if (count == self.config['max_open_trades']) & (current_profit < -0.04) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 6): + # self.lock_pair(pair, until=current_time + timedelta(hours=10)) + # print("stop_short_loss", pair, trade, " profit=", current_profit, " rate=", current_rate, + # "count=", count, "max=", self.config['max_open_trades']) + # return 'stop_short_loss' + + + def informative_pairs(self): + + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # dataframe['min'] = ta.MIN(dataframe) + # dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + # Bollinger Bands - Weighted (EMA based instead of SMA) + weighted_bollinger = qtpylib.weighted_bollinger_bands( + qtpylib.typical_price(dataframe), window=20, stds=2 + ) + dataframe["wbb_upperband"] = weighted_bollinger["upper"] + dataframe["wbb_lowerband"] = weighted_bollinger["lower"] + dataframe["wbb_middleband"] = weighted_bollinger["mid"] + dataframe["wbb_percent"] = ( + (dataframe["close"] - dataframe["wbb_lowerband"]) / + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) + ) + dataframe["wbb_width"] = ( + (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"] + ) + + # # EMA - Exponential Moving Average + # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + ( + (dataframe['close'] < dataframe['bb_lowerband']) + & (dataframe['bb_width'] >= 0.065) + #& (dataframe['rsi'] < 45) + & (dataframe['volume'] * dataframe['close'] / 1000 >= 100) + ) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + + ), + + 'sell'] = 1 + return dataframe + diff --git a/StrategyPierrick22.py b/StrategyPierrick22.py new file mode 100644 index 0000000..7d99d71 --- /dev/null +++ b/StrategyPierrick22.py @@ -0,0 +1,156 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + +# This class is a sample. Feel free to customize it. +class StrategyPierrick22(IStrategy): + + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + + # ROI table: + minimal_roi = { + #"0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 #0.015 + trailing_only_offset_is_reached = True + + #max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "Aaron": { + 'aroonup': {'color': 'blue'}, + 'aroondown': {'color': 'red'} + } + } + } + + def informative_pairs(self): + + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # MACD + #macd = ta.MACD(dataframe) + #dataframe['macd'] = macd['macd'] + #dataframe['macdsignal'] = macd['macdsignal'] + #dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + # Aroon, Aroon Oscillator + aroon = ta.AROON(dataframe) + dataframe['aroonup'] = aroon['aroonup'] + dataframe['aroondown'] = aroon['aroondown'] + dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + #dataframe['rsi'] = ta.RSI(dataframe) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + + dataframe.loc[ + ( + ( + (dataframe['close'] < dataframe['bb_lowerband']) + & ( + (dataframe['bb_width'] >= 0.095) | + ((dataframe['bb_width'] >= 0.08) & (dataframe['close'].shift(20) / dataframe['close'] >= 1.03)) + ) + & (dataframe['volume'] > 0) + ) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + + ), + + 'sell'] = 1 + return dataframe + diff --git a/StrategyPierrick23.py b/StrategyPierrick23.py new file mode 100644 index 0000000..571e8fe --- /dev/null +++ b/StrategyPierrick23.py @@ -0,0 +1,159 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + +# This class is a sample. Feel free to customize it. +class StrategyPierrick23(IStrategy): + + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + + # ROI table: + minimal_roi = { + #"0": 0.015 + "0": 0.10, + "60": 0.05, + "120": 0.03, + "180": 0.015, + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 #0.015 + trailing_only_offset_is_reached = True + + #max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "Aaron": { + 'aroonup': {'color': 'blue'}, + 'aroondown': {'color': 'red'} + } + } + } + + def informative_pairs(self): + + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # MACD + #macd = ta.MACD(dataframe) + #dataframe['macd'] = macd['macd'] + #dataframe['macdsignal'] = macd['macdsignal'] + #dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + # Aroon, Aroon Oscillator + aroon = ta.AROON(dataframe) + dataframe['aroonup'] = aroon['aroonup'] + dataframe['aroondown'] = aroon['aroondown'] + dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + #dataframe['rsi'] = ta.RSI(dataframe) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + + dataframe.loc[ + ( + ( + (dataframe['close'] < dataframe['bb_lowerband']) + & ( + (dataframe['bb_width'] >= 0.095) | + ((dataframe['bb_width'] >= 0.08) & (dataframe['close'].shift(20) / dataframe['close'] >= 1.03)) + ) + & (dataframe['volume'] > 0) + ) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + + ), + + 'sell'] = 1 + return dataframe + diff --git a/StrategyPierrick3.py b/StrategyPierrick3.py new file mode 100644 index 0000000..26d5612 --- /dev/null +++ b/StrategyPierrick3.py @@ -0,0 +1,180 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame +import math + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick3(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "Aaron": { + 'aroonup': {'color': 'blue'}, + 'aroondown': {'color': 'red'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # Aroon, Aroon Oscillator + aroon = ta.AROON(dataframe) + dataframe['aroonup'] = aroon['aroonup'] + dataframe['aroondown'] = aroon['aroondown'] + dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + # dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + + condition = np.where(value >= 1.02, True, False) + dataframe.loc[ + ( + ( + (dataframe['close'] < dataframe['bb_lowerband']) + & ( + (dataframe['bb_width'] >= 0.095) | + ( + (dataframe['bb_width'] >= 0.04) & condition + ) + ) + # & (dataframe['close'] <= dataframe['sma500']) + & (dataframe['volume'] > 0) + ) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + + ), + + 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick31.py b/StrategyPierrick31.py new file mode 100644 index 0000000..ef2b345 --- /dev/null +++ b/StrategyPierrick31.py @@ -0,0 +1,196 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame +import math + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + +# This class is a sample. Feel free to customize it. +class StrategyPierrick31(IStrategy): + + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + + # ROI table: + minimal_roi = { + #"0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 #0.015 + trailing_only_offset_is_reached = True + + #max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "Aaron": { + 'aroonup': {'color': 'blue'}, + 'aroondown': {'color': 'red'} + } + } + } + + def informative_pairs(self): + + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # MACD + #macd = ta.MACD(dataframe) + #dataframe['macd'] = macd['macd'] + #dataframe['macdsignal'] = macd['macdsignal'] + #dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + # Aroon, Aroon Oscillator + aroon = ta.AROON(dataframe) + dataframe['aroonup'] = aroon['aroonup'] + dataframe['aroondown'] = aroon['aroondown'] + dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + #dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # Baisse depuis 20 cycles + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + condition = np.where(value >= 1.02, True, False) + + # Baisse dans les 20% inférieur + horizon = int(12 * 60 / 5) + pmin = dataframe['min'].shift(horizon) + pmax = dataframe['max'].shift(horizon) + pmoy = (dataframe['max'] - dataframe['min']) / (pmax - pmin) + # print("pmoy=", pmoy) + for k, v in pmoy.iteritems(): + # print(k, v) + value = v + condition2 = np.where(value <= 0.8, True, False) + + dataframe.loc[ + ( + ( + (dataframe['close'] < dataframe['bb_lowerband']) + & ( + (dataframe['bb_width'] >= 0.095) | + ( + (dataframe['bb_width'] >= 0.04) & condition & condition2 + ) + ) + & (dataframe['volume'] > 0) + ) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + + ), + + 'sell'] = 1 + return dataframe + diff --git a/StrategyPierrick32.py b/StrategyPierrick32.py new file mode 100644 index 0000000..c29e382 --- /dev/null +++ b/StrategyPierrick32.py @@ -0,0 +1,177 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame +import math + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + +# This class is a sample. Feel free to customize it. +class StrategyPierrick32(IStrategy): + + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + + # ROI table: + minimal_roi = { + #"0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 #0.015 + trailing_only_offset_is_reached = True + + #max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "Aaron": { + 'aroonup': {'color': 'blue'}, + 'aroondown': {'color': 'red'} + } + } + } + + def informative_pairs(self): + + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # MACD + #macd = ta.MACD(dataframe) + #dataframe['macd'] = macd['macd'] + #dataframe['macdsignal'] = macd['macdsignal'] + #dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + # Aroon, Aroon Oscillator + aroon = ta.AROON(dataframe) + dataframe['aroonup'] = aroon['aroonup'] + dataframe['aroondown'] = aroon['aroondown'] + dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + #dataframe['rsi'] = ta.RSI(dataframe) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # Baisse depuis 20 cycles + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + condition = np.where(value >= 1.02, True, False) + + # Baisse dans les 20% inférieur + horizon = int(4 * 60 / 5) + pmin = dataframe['min'].shift(horizon) + pmax = dataframe['max'].shift(horizon) + pmoy = (dataframe['max'] - dataframe['min']) / (pmax - pmin) + # print("pmoy=", pmoy) + for k, v in pmoy.iteritems(): + # print(k, v) + value = v + condition2 = np.where(value <= 0.8, True, False) + + dataframe.loc[ + ( + ( + (dataframe['close'] < dataframe['bb_lowerband']) + & ( + (dataframe['bb_width'] >= 0.095) | + ( + (dataframe['bb_width'] >= 0.04) & condition & condition2 + ) + ) + & (dataframe['volume'] > 0) + ) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + + ), + + 'sell'] = 1 + return dataframe + diff --git a/StrategyPierrick4.py b/StrategyPierrick4.py new file mode 100644 index 0000000..ecea40b --- /dev/null +++ b/StrategyPierrick4.py @@ -0,0 +1,166 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame +import math + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + +# This class is a sample. Feel free to customize it. +class StrategyPierrick4(IStrategy): + + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + + # ROI table: + minimal_roi = { + #"0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 #0.015 + trailing_only_offset_is_reached = True + + #max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "Aaron": { + 'aroonup': {'color': 'blue'}, + 'aroondown': {'color': 'red'} + } + } + } + + def informative_pairs(self): + + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # MACD + #macd = ta.MACD(dataframe) + #dataframe['macd'] = macd['macd'] + #dataframe['macdsignal'] = macd['macdsignal'] + #dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # Aroon, Aroon Oscillator + aroon = ta.AROON(dataframe) + dataframe['aroonup'] = aroon['aroonup'] + dataframe['aroondown'] = aroon['aroondown'] + dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + #dataframe['rsi'] = ta.RSI(dataframe) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + + condition = np.where(value >= 1.04, True, False) + dataframe.loc[ + ( + ( + (dataframe['close'] < dataframe['bb_lowerband']) + & ( + (dataframe['bb_width'] >= 0.095) | + ( + (dataframe['bb_width'] >= 0.04) & condition + ) + ) + & (dataframe['volume'] > 0) + ) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + + ), + + 'sell'] = 1 + return dataframe + diff --git a/StrategyPierrick41.py b/StrategyPierrick41.py new file mode 100644 index 0000000..18eb352 --- /dev/null +++ b/StrategyPierrick41.py @@ -0,0 +1,207 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame +import math + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick41(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma500_97': {'color': 'gray'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + # 'volatility_dcp': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "Aaron": { + 'aroonup': {'color': 'blue'}, + 'aroondown': {'color': 'red'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # Aroon, Aroon Oscillator + aroon = ta.AROON(dataframe) + dataframe['aroonup'] = aroon['aroonup'] + dataframe['aroondown'] = aroon['aroondown'] + dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + # dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + + condition = np.where(value >= 1.04, True, False) + dataframe.loc[ + ( + (dataframe['close'] < dataframe['bb_lowerband']) & + (dataframe['volume'] > 0) & + ( + (dataframe['bb_width'] >= 0.095) | + ((dataframe['bb_width'] >= 0.04) & condition) + & (dataframe['close'] <= dataframe['sma200_95']) + ) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + + ), + + 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick411.json b/StrategyPierrick411.json new file mode 100644 index 0000000..0f14c34 --- /dev/null +++ b/StrategyPierrick411.json @@ -0,0 +1,34 @@ +{ + "strategy_name": "StrategyPierrick411", + "params": { + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.09, + "buy_bollinger_2": 0.08, + "buy_bollinger_2_enabled": false, + "buy_bollinger_enabled": true, + "buy_sma_percent": 0.99, + "buy_sma_percent_enabled": false, + "buy_volume": 18.9, + "buy_volume_enabled": true + }, + "sell": {}, + "protection": {}, + "roi": { + "0": 0.096, + "30": 0.049, + "60": 0.032, + "120": 0 + } + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-07 21:14:40.877512+00:00" +} diff --git a/StrategyPierrick411.jsonMaxDrawDownHyperOptLoss b/StrategyPierrick411.jsonMaxDrawDownHyperOptLoss new file mode 100644 index 0000000..1c90cb0 --- /dev/null +++ b/StrategyPierrick411.jsonMaxDrawDownHyperOptLoss @@ -0,0 +1,33 @@ +{ + "strategy_name": "StrategyPierrick411", + "params": { + "roi": { + "0": 0.5 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.09, + "buy_bollinger_2": 0.08, + "buy_bollinger_2_enabled": true, + "buy_bollinger_3": 0.07, + "buy_bollinger_3_enabled": true, + "buy_bollinger_enabled": true, + "buy_sma_percent": 0.98, + "buy_sma_percent_enabled": true, + "buy_volume": 13.5, + "buy_volume_enabled": false + }, + "sell": {}, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-05 18:56:50.185120+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick411.jsonOLD2 b/StrategyPierrick411.jsonOLD2 new file mode 100644 index 0000000..1487f67 --- /dev/null +++ b/StrategyPierrick411.jsonOLD2 @@ -0,0 +1,33 @@ +{ + "strategy_name": "StrategyPierrick411", + "params": { + "roi": { + "0": 0.5 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.09, + "buy_bollinger_2": 0.0, + "buy_bollinger_2_enabled": false, + "buy_bollinger_3": 0.08, + "buy_bollinger_3_enabled": true, + "buy_bollinger_enabled": true, + "buy_sma_percent": 0.97, + "buy_sma_percent_enabled": false, + "buy_volume": 42.0, + "buy_volume_enabled": false + }, + "sell": {}, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-04 21:37:54.391956+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick411.jsonOld b/StrategyPierrick411.jsonOld new file mode 100644 index 0000000..bf080e1 --- /dev/null +++ b/StrategyPierrick411.jsonOld @@ -0,0 +1,27 @@ +{ + "strategy_name": "StrategyPierrick411", + "params": { + "roi": { + "0": 0.5 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.08, + "buy_bollinger_2": 0.04, + "buy_bollinger_2_enabled": true, + "buy_bollinger_enabled": true + }, + "sell": {}, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-04 16:23:57.580987+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick411.jsonOnlyProfitHyperOptLoss b/StrategyPierrick411.jsonOnlyProfitHyperOptLoss new file mode 100644 index 0000000..d8517e5 --- /dev/null +++ b/StrategyPierrick411.jsonOnlyProfitHyperOptLoss @@ -0,0 +1,33 @@ +{ + "strategy_name": "StrategyPierrick411", + "params": { + "roi": { + "0": 0.5 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.04, + "buy_bollinger_2": 0.0, + "buy_bollinger_2_enabled": true, + "buy_bollinger_3": 0.04, + "buy_bollinger_3_enabled": true, + "buy_bollinger_enabled": true, + "buy_sma_percent": 1.05, + "buy_sma_percent_enabled": true, + "buy_volume": 37.9, + "buy_volume_enabled": false + }, + "sell": {}, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-05 17:11:37.461801+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick411.jsonSharpeHyperOptLoss b/StrategyPierrick411.jsonSharpeHyperOptLoss new file mode 100644 index 0000000..58fa0d1 --- /dev/null +++ b/StrategyPierrick411.jsonSharpeHyperOptLoss @@ -0,0 +1,33 @@ +{ + "strategy_name": "StrategyPierrick411", + "params": { + "roi": { + "0": 0.5 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.11, + "buy_bollinger_2": 0.0, + "buy_bollinger_2_enabled": false, + "buy_bollinger_3": 0.07, + "buy_bollinger_3_enabled": true, + "buy_bollinger_enabled": true, + "buy_sma_percent": 0.98, + "buy_sma_percent_enabled": false, + "buy_volume": 40.3, + "buy_volume_enabled": false + }, + "sell": {}, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-05 17:31:26.224609+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick411.jsonSharpeHyperOptLossDaily b/StrategyPierrick411.jsonSharpeHyperOptLossDaily new file mode 100644 index 0000000..8676d45 --- /dev/null +++ b/StrategyPierrick411.jsonSharpeHyperOptLossDaily @@ -0,0 +1,33 @@ +{ + "strategy_name": "StrategyPierrick411", + "params": { + "roi": { + "0": 0.5 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.06, + "buy_bollinger_2": 0.02, + "buy_bollinger_2_enabled": false, + "buy_bollinger_3": 0.06, + "buy_bollinger_3_enabled": true, + "buy_bollinger_enabled": true, + "buy_sma_percent": 1.02, + "buy_sma_percent_enabled": false, + "buy_volume": 14.7, + "buy_volume_enabled": true + }, + "sell": {}, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-05 17:53:59.162189+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick411.jsonShortTradeDurHyperOptLoss b/StrategyPierrick411.jsonShortTradeDurHyperOptLoss new file mode 100644 index 0000000..170c744 --- /dev/null +++ b/StrategyPierrick411.jsonShortTradeDurHyperOptLoss @@ -0,0 +1,33 @@ +{ + "strategy_name": "StrategyPierrick411", + "params": { + "roi": { + "0": 0.5 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.12, + "buy_bollinger_2": 0.07, + "buy_bollinger_2_enabled": false, + "buy_bollinger_3": 0.02, + "buy_bollinger_3_enabled": false, + "buy_bollinger_enabled": true, + "buy_sma_percent": 0.98, + "buy_sma_percent_enabled": true, + "buy_volume": 10.5, + "buy_volume_enabled": false + }, + "sell": {}, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-05 16:38:40.768689+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick411.jsonSortinoHyperOptLoss b/StrategyPierrick411.jsonSortinoHyperOptLoss new file mode 100644 index 0000000..95ffdbf --- /dev/null +++ b/StrategyPierrick411.jsonSortinoHyperOptLoss @@ -0,0 +1,33 @@ +{ + "strategy_name": "StrategyPierrick411", + "params": { + "roi": { + "0": 0.5 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.07, + "buy_bollinger_2": 0.02, + "buy_bollinger_2_enabled": true, + "buy_bollinger_3": 0.07, + "buy_bollinger_3_enabled": false, + "buy_bollinger_enabled": true, + "buy_sma_percent": 0.95, + "buy_sma_percent_enabled": false, + "buy_volume": 7.1, + "buy_volume_enabled": false + }, + "sell": {}, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-05 18:12:23.363449+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick411.jsonSortinoHyperOptLossDaily b/StrategyPierrick411.jsonSortinoHyperOptLossDaily new file mode 100644 index 0000000..57faef8 --- /dev/null +++ b/StrategyPierrick411.jsonSortinoHyperOptLossDaily @@ -0,0 +1,33 @@ +{ + "strategy_name": "StrategyPierrick411", + "params": { + "roi": { + "0": 0.5 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.09, + "buy_bollinger_2": 0.04, + "buy_bollinger_2_enabled": true, + "buy_bollinger_3": 0.06, + "buy_bollinger_3_enabled": false, + "buy_bollinger_enabled": true, + "buy_sma_percent": 0.96, + "buy_sma_percent_enabled": true, + "buy_volume": 11.9, + "buy_volume_enabled": true + }, + "sell": {}, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-05 18:27:26.631033+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick411.py b/StrategyPierrick411.py new file mode 100644 index 0000000..38fbc0d --- /dev/null +++ b/StrategyPierrick411.py @@ -0,0 +1,228 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick411(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + # pourcentage sma à dépasser + buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.97, space="buy") + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + 'rsi': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['adx'] = ta.ADX(dataframe) + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + dataframe["rolling"] = (100 * (dataframe["close"] - dataframe["bb_lowerband"]) / dataframe["bb_lowerband"]).rolling(5).mean() + dataframe["bb_rolling"] = dataframe["rolling"] / dataframe["bb_width"] + dataframe['bb_rolling_min'] = ta.MIN(dataframe['bb_rolling'], timeperiod=5) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [ + dataframe['bb_width'] >= self.buy_bollinger.value, + (dataframe['close'] < dataframe['bb_lowerband']), + (dataframe['close'] < dataframe['sma100'] * self.buy_sma_percent.value) + ] + # GUARDS AND TRENDS + + if conditions: + dataframe.loc[ + ( + (reduce(lambda x, y: x & y, conditions)) + ) + , + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick4111.json b/StrategyPierrick4111.json new file mode 100644 index 0000000..b60d84a --- /dev/null +++ b/StrategyPierrick4111.json @@ -0,0 +1,42 @@ +{ + "strategy_name": "StrategyPierrick4111", + "params": { + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_baisse_bollinger": 0.07, + "buy_baisse_bollinger_2": 0.05, + "buy_baisse_bollinger_2_enabled": true, + "buy_baisse_bollinger_enabled": true, + "buy_baisse_sma_percent": 0.99, + "buy_baisse_sma_percent_enabled": true, + "buy_baisse_volume": 44.7, + "buy_baisse_volume_enabled": true, + "buy_hausse_bollinger": 0.05, + "buy_hausse_bollinger_2": 0.0, + "buy_hausse_bollinger_2_enabled": false, + "buy_hausse_bollinger_enabled": false, + "buy_hausse_sma_percent": 1.05, + "buy_hausse_sma_percent_enabled": true, + "buy_hausse_volume": 12.7, + "buy_hausse_volume_enabled": true + }, + "sell": {}, + "protection": {}, + "roi": { + "0": 0.092, + "29": 0.059, + "63": 0.036, + "182": 0 + } + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-07 20:42:50.719031+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick4111.jsonOLD2 b/StrategyPierrick4111.jsonOLD2 new file mode 100644 index 0000000..a523722 --- /dev/null +++ b/StrategyPierrick4111.jsonOLD2 @@ -0,0 +1,39 @@ +{ + "strategy_name": "StrategyPierrick4111", + "params": { + "roi": { + "0": 0.5 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_baisse_bollinger": 0.08, + "buy_baisse_bollinger_2": 0.03, + "buy_baisse_bollinger_2_enabled": true, + "buy_baisse_bollinger_enabled": true, + "buy_baisse_sma_percent": 1.0, + "buy_baisse_sma_percent_enabled": true, + "buy_baisse_volume": 32.0, + "buy_baisse_volume_enabled": false, + "buy_hausse_bollinger": 0.12, + "buy_hausse_bollinger_2": 0.01, + "buy_hausse_bollinger_2_enabled": true, + "buy_hausse_bollinger_enabled": true, + "buy_hausse_sma_percent": 1.01, + "buy_hausse_sma_percent_enabled": true, + "buy_hausse_volume": 12.7, + "buy_hausse_volume_enabled": true + }, + "sell": {}, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-06 21:26:35.306929+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick4111.py b/StrategyPierrick4111.py new file mode 100644 index 0000000..1056094 --- /dev/null +++ b/StrategyPierrick4111.py @@ -0,0 +1,327 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick4111(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_hausse_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.095, space="buy") + buy_hausse_bollinger_enabled = BooleanParameter(default=True, space="buy") + # Valeur de la deuxième h_condition bollinger avec h_condition sma200 + buy_hausse_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + buy_hausse_bollinger_2_enabled = BooleanParameter(default=True, space="buy") + # pourcentage sma à dépasser + buy_hausse_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.098, space="buy") + buy_hausse_sma_percent_enabled = BooleanParameter(default=True, space="buy") + # volume à atteindre + buy_hausse_volume = DecimalParameter(0, 50, decimals=1, default=0, space="buy") + buy_hausse_volume_enabled = BooleanParameter(default=True, space="buy") + + # valeur de bbwidth pour démarrer + buy_baisse_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.095, space="buy") + buy_baisse_bollinger_enabled = BooleanParameter(default=True, space="buy") + # Valeur de la deuxième h_condition bollinger avec h_condition sma200 + buy_baisse_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + buy_baisse_bollinger_2_enabled = BooleanParameter(default=True, space="buy") + # pourcentage sma à dépasser + buy_baisse_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.098, space="buy") + buy_baisse_sma_percent_enabled = BooleanParameter(default=True, space="buy") + # volume à atteindre + buy_baisse_volume = DecimalParameter(0, 50, decimals=1, default=0, space="buy") + buy_baisse_volume_enabled = BooleanParameter(default=True, space="buy") + + # buy_hausse_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_hausse_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_hausse_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_hausse_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = 0.03 + trailing_stop = True + trailing_stop_positive = 0.005 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_hausse_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + # 'volatility_dcp': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "Aaron": { + 'aroonup': {'color': 'blue'}, + 'aroondown': {'color': 'red'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # Aroon, Aroon Oscillator + aroon = ta.AROON(dataframe) + dataframe['aroonup'] = aroon['aroonup'] + dataframe['aroondown'] = aroon['aroondown'] + dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + # dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + + h_condition1 = np.where(value >= 1.04, True, False) + + h_conditions = [] + # GUARDS AND TRENDS + if self.buy_hausse_bollinger_enabled.value: + h_conditions.append(dataframe['bb_width'] >= self.buy_hausse_bollinger.value) + + h_conditions2 = [] + if self.buy_hausse_bollinger_2_enabled.value: + h_conditions2.append(dataframe['bb_width'] >= self.buy_hausse_bollinger_2.value) + + h_conditions_volume = [] + h_condition_volume = True + if self.buy_hausse_volume_enabled.value: + h_conditions_volume.append(dataframe['volume'] >= self.buy_hausse_volume.value * 1000) + if h_conditions_volume: + h_condition_volume = np.where(h_conditions_volume, True, False) + + h_condition2 = False + if h_conditions2: + h_condition2 = reduce(lambda x, y: x & y, h_conditions2) & h_condition1 + + h_condition_sma = False + h_conditions_sma = [] + if self.buy_hausse_sma_percent_enabled.value: + #h_conditions_sma.append(dataframe['close'] <= dataframe['sma200'] * self.buy_hausse_sma_percent.value) + h_conditions_sma.append((dataframe['sma100'].shift(36) - dataframe['sma100']) / dataframe['sma100'] > self.buy_hausse_sma_percent.value) + if h_conditions_sma: + h_condition_sma = reduce(lambda x, y: x & y, h_conditions_sma) + + b_condition1 = np.where(value >= 1.04, True, False) + + b_conditions = [] + # GUARDS AND TRENDS + if self.buy_baisse_bollinger_enabled.value: + b_conditions.append(dataframe['bb_width'] >= self.buy_baisse_bollinger.value) + + b_conditions2 = [] + if self.buy_baisse_bollinger_2_enabled.value: + b_conditions2.append(dataframe['bb_width'] >= self.buy_baisse_bollinger_2.value) + + b_conditions_volume = [] + b_condition_volume = True + if self.buy_baisse_volume_enabled.value: + b_conditions_volume.append(dataframe['volume'] >= self.buy_baisse_volume.value * 1000) + if b_conditions_volume: + b_condition_volume = np.where(b_conditions_volume, True, False) + + b_condition2 = False + if b_conditions2: + b_condition2 = reduce(lambda x, y: x & y, b_conditions2) & b_condition1 + + b_condition_sma = False + b_conditions_sma = [] + if self.buy_baisse_sma_percent_enabled.value: + #b_conditions_sma.append(dataframe['close'] <= dataframe['sma200'] * self.buy_baisse_sma_percent.value) + b_conditions_sma.append((dataframe['sma100'].shift(36) - dataframe['sma100']) / dataframe['sma100'] > self.buy_baisse_sma_percent.value) + if b_conditions_sma: + b_condition_sma = reduce(lambda x, y: x & y, b_conditions_sma) + + p = dataframe['sma500'].shift(60) - dataframe['sma500'] + for k, v in p.iteritems(): + # print(k, v) + value = v + hausse = np.where(value < 0, True, False) + + if hausse: + if h_conditions: + dataframe.loc[ + ( + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) < 0.025) & + (dataframe['close'] < dataframe['bb_lowerband']) & + h_condition_volume & + ( + ( + reduce(lambda x, y: x & y, h_conditions) | + (h_condition2 & h_condition_sma) + ) + ) + ), + 'buy'] = 1 + else: + if b_conditions: + dataframe.loc[ + ( + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) < 0.025) & + (dataframe['close'] < dataframe['bb_lowerband']) & + b_condition_volume & + ( + ( + reduce(lambda x, y: x & y, b_conditions) | + (b_condition2 & b_condition_sma) + ) + ) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(1) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.015) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), + + 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick4112.json b/StrategyPierrick4112.json new file mode 100644 index 0000000..22c9839 --- /dev/null +++ b/StrategyPierrick4112.json @@ -0,0 +1,34 @@ +{ + "strategy_name": "StrategyPierrick4112", + "params": { + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.09, + "buy_bollinger_2": 0.08, + "buy_bollinger_2_enabled": false, + "buy_bollinger_enabled": true, + "buy_sma_percent": 0.98, + "buy_sma_percent_enabled": false, + "buy_volume": 18.9, + "buy_volume_enabled": false + }, + "sell": {}, + "protection": {}, + "roi": { + "0": 0.096, + "30": 0.049, + "60": 0.032, + "120": 0 + } + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-07 21:14:40.877512+00:00" +} diff --git a/StrategyPierrick4112.py b/StrategyPierrick4112.py new file mode 100644 index 0000000..5220803 --- /dev/null +++ b/StrategyPierrick4112.py @@ -0,0 +1,288 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick4112(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.095, space="buy") + buy_bollinger_enabled = BooleanParameter(default=True, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + buy_bollinger_2_enabled = BooleanParameter(default=True, space="buy") + # pourcentage sma à dépasser + buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.098, space="buy") + buy_sma_percent_enabled = BooleanParameter(default=True, space="buy") + # volume à atteindre + buy_volume = IntParameter(0, 50, default=0, space="buy") + buy_volume_enabled = BooleanParameter(default=True, space="buy") + + buy_candel_percent = DecimalParameter(1.02, 1.10, decimals=2, default=1.04, space="buy") + + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + 'rsi': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "Pct": { + 'percent': {'color': 'white'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['adx'] = ta.ADX(dataframe) + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + + condition1 = np.where(value >= 1.04, True, False) + + conditions = [] + # GUARDS AND TRENDS + if self.buy_bollinger_enabled.value: + conditions.append(dataframe['bb_width'] >= self.buy_bollinger.value) + + conditions2 = [] + if self.buy_bollinger_2_enabled.value: + conditions2.append(dataframe['bb_width'] >= self.buy_bollinger_2.value) + + conditions_volume = [] + condition_volume = True + if self.buy_volume_enabled.value: + conditions_volume.append(dataframe['volume'] >= self.buy_volume.value * 1000) + if conditions_volume: + condition_volume = np.where(conditions_volume, True, False) + + condition2 = False + if conditions2: + condition2 = reduce(lambda x, y: x & y, conditions2) & condition1 + + condition_sma = False + conditions_sma = [] + if self.buy_sma_percent_enabled.value: + # conditions_sma.append(dataframe['close'] <= dataframe['sma200'] * self.buy_sma_percent.value) + conditions_sma.append((dataframe['sma100'].shift(36) - dataframe['sma100']) / dataframe[ + 'sma100'] > self.buy_sma_percent.value) + if conditions_sma: + condition_sma = reduce(lambda x, y: x & y, conditions_sma) + + if conditions: + dataframe.loc[ + ( + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) < 0.025) & + (dataframe['close'] < dataframe['bb_lowerband']) & + condition_volume & + (reduce(lambda x, y: x & y, conditions)) #| (condition2 & condition_sma)) + ) | ( + # condition2 & + (dataframe['close'] > dataframe['bb_upperband']) & + (dataframe['close'] > dataframe['open'] * 1.04) & + # (dataframe['bb_width'] < 0.2) + (dataframe['sma100'].shift(4) < dataframe['sma100'] * 1.01) & + (dataframe['sma100'].shift(4) > dataframe['sma100'] * 0.99) + # ) | ( + # ( + # (dataframe['percent'] + # + dataframe['percent'].shift(1) + # + dataframe['percent'].shift(2) + # + dataframe['percent'].shift(3) + # + dataframe['percent'].shift(4) > 1.04) & + # (dataframe['close'] > dataframe['bb_upperband']) + # ) & ( + # dataframe['close'] > dataframe['open'].shift(2) * 1.04 + # ) + ) + , + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), + + 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick4113.json b/StrategyPierrick4113.json new file mode 100644 index 0000000..49e6da8 --- /dev/null +++ b/StrategyPierrick4113.json @@ -0,0 +1,34 @@ +{ + "strategy_name": "StrategyPierrick4113", + "params": { + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.09, + "buy_bollinger_2": 0.04, + "buy_bollinger_2_enabled": true, + "buy_bollinger_enabled": true, + "buy_sma_percent": 0.98, + "buy_sma_percent_enabled": false, + "buy_volume": 18.9, + "buy_volume_enabled": true + }, + "sell": {}, + "protection": {}, + "roi": { + "0": 0.096, + "30": 0.049, + "60": 0.032, + "120": 0 + } + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-07 21:14:40.877512+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick4113.py b/StrategyPierrick4113.py new file mode 100644 index 0000000..e4b52a3 --- /dev/null +++ b/StrategyPierrick4113.py @@ -0,0 +1,281 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter +from pandas import DataFrame +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick4113(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.095, space="buy") + buy_bollinger_enabled = BooleanParameter(default=True, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + buy_bollinger_2_enabled = BooleanParameter(default=True, space="buy") + # pourcentage sma à dépasser + buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.098, space="buy") + buy_sma_percent_enabled = BooleanParameter(default=True, space="buy") + # volume à atteindre + buy_volume = DecimalParameter(0, 50, decimals=1, default=0, space="buy") + buy_volume_enabled = BooleanParameter(default=True, space="buy") + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + 'rsi': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['adx'] = ta.ADX(dataframe) + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + + condition1 = np.where(value >= 1.04, True, False) + + conditions = [] + # GUARDS AND TRENDS + if self.buy_bollinger_enabled.value: + conditions.append(dataframe['bb_width'] >= self.buy_bollinger.value) + + conditions2 = [] + if self.buy_bollinger_2_enabled.value: + conditions2.append(dataframe['bb_width'] >= self.buy_bollinger_2.value) + + conditions_volume = [] + condition_volume = True + if self.buy_volume_enabled.value: + conditions_volume.append(dataframe['volume'] >= self.buy_volume.value * 1000) + if conditions_volume: + condition_volume = np.where(conditions_volume, True, False) + + condition2 = False + if conditions2: + condition2 = reduce(lambda x, y: x & y, conditions2) & condition1 + + condition_sma = False + conditions_sma = [] + if self.buy_sma_percent_enabled.value: + # conditions_sma.append(dataframe['close'] <= dataframe['sma200'] * self.buy_sma_percent.value) + conditions_sma.append((dataframe['sma100'].shift(36) - dataframe['sma100']) / dataframe[ + 'sma100'] > self.buy_sma_percent.value) + if conditions_sma: + condition_sma = reduce(lambda x, y: x & y, conditions_sma) + + if conditions: + dataframe.loc[ + ( + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) < 0.025) & + (dataframe['close'] < dataframe['bb_lowerband']) & + condition_volume & + (reduce(lambda x, y: x & y, conditions) | (condition2 & condition_sma)) + ) | ( + (dataframe['close'] > dataframe['bb_upperband']) & + ( + (dataframe['close'] > dataframe['open'] * 1.04) | + (((dataframe['close'] - dataframe['bb_upperband']) / dataframe['bb_upperband']) >= 0.025) + ) & + # (dataframe['bb_width'] < 0.2) + (dataframe['sma100'].shift(4) < dataframe['sma100'] * 1.01) & + (dataframe['sma100'].shift(4) > dataframe['sma100'] * 0.99) + # (dataframe['close'] < dataframe['open'] * 1.06) + # ) | ( + # ( + # (dataframe['close'] > dataframe['bb_upperband']) & + # (dataframe['close'].shift(1) > dataframe['bb_upperband'].shift(1)) & + # (dataframe['close'].shift(2) > dataframe['bb_upperband'].shift(2)) + # # (dataframe['bb_width'] < 0.12) + # ) & ( + # dataframe['close'] > dataframe['open'].shift(2) * 1.04 + # ) + ) + , + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), + + 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick4114.json b/StrategyPierrick4114.json new file mode 100644 index 0000000..1e50e07 --- /dev/null +++ b/StrategyPierrick4114.json @@ -0,0 +1,34 @@ +{ + "strategy_name": "StrategyPierrick4114", + "params": { + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.09, + "buy_bollinger_2": 0.07, + "buy_bollinger_2_enabled": false, + "buy_bollinger_enabled": true, + "buy_sma_percent": 1.05, + "buy_sma_percent_enabled": false, + "buy_volume": 14.0, + "buy_volume_enabled": false + }, + "sell": {}, + "protection": {}, + "roi": { + "0": 0.096, + "30": 0.049, + "60": 0.032, + "120": 0 + } + "stoploss": { + "stoploss": -0.31 + } + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-11 13:07:34.278116+00:00" +} diff --git a/StrategyPierrick4114.py b/StrategyPierrick4114.py new file mode 100644 index 0000000..7467372 --- /dev/null +++ b/StrategyPierrick4114.py @@ -0,0 +1,264 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick4114(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.095, space="buy") + buy_bollinger_enabled = BooleanParameter(default=True, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.065, space="buy") + buy_bollinger_2_enabled = BooleanParameter(default=True, space="buy") + # pourcentage sma à dépasser + buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.098, space="buy") + buy_sma_percent_enabled = BooleanParameter(default=True, space="buy") + # volume à atteindre + buy_volume = DecimalParameter(0, 50, decimals=1, default=0, space="buy") + buy_volume_enabled = BooleanParameter(default=True, space="buy") + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + 'rsi': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['adx'] = ta.ADX(dataframe) + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + + condition1 = np.where(value >= 1.04, True, False) + + conditions = [] + # GUARDS AND TRENDS + if self.buy_bollinger_enabled.value: + conditions.append(dataframe['bb_width'] >= self.buy_bollinger.value) + + conditions2 = [] + if self.buy_bollinger_2_enabled.value: + conditions2.append(dataframe['bb_width'] / dataframe['bb_width'].shift(5) >= 1.5) #self.buy_bollinger_2.value) + # conditions2.append(dataframe['close'] <= dataframe['sma100'] * 0.99) + + conditions_volume = [] + condition_volume = True + if self.buy_volume_enabled.value: + conditions_volume.append(dataframe['volume'] >= self.buy_volume.value * 1000) + if conditions_volume: + condition_volume = np.where(conditions_volume, True, False) + + condition2 = False + if conditions2: + condition2 = reduce(lambda x, y: x & y, conditions2) & condition1 + + condition_sma = False + conditions_sma = [] + if self.buy_sma_percent_enabled.value: + # conditions_sma.append(dataframe['close'] <= dataframe['sma200'] * self.buy_sma_percent.value) + conditions_sma.append((dataframe['sma100'].shift(36) - dataframe['sma100']) / dataframe['sma100'] > self.buy_sma_percent.value) + if conditions_sma: + condition_sma = reduce(lambda x, y: x & y, conditions_sma) + + if conditions: + dataframe.loc[ + ( + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) < 0.025) & + (dataframe['close'] < dataframe['bb_lowerband']) & + condition_volume & + (reduce(lambda x, y: x & y, conditions) | (condition2 & condition_sma)) + # ) | ( + ) + , + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), + + 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick4115.json b/StrategyPierrick4115.json new file mode 100644 index 0000000..5e59e5a --- /dev/null +++ b/StrategyPierrick4115.json @@ -0,0 +1,34 @@ +{ + "strategy_name": "StrategyPierrick4115", + "params": { + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.09, + "buy_bollinger_2": 0.05, + "buy_bollinger_2_enabled": true, + "buy_bollinger_enabled": true, + "buy_sma_percent": 0.98, + "buy_sma_percent_enabled": false, + "buy_volume": 18.9, + "buy_volume_enabled": false + }, + "sell": {}, + "protection": {}, + "roi": { + "0": 0.096, + "30": 0.049, + "60": 0.032, + "120": 0 + } + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-11 20:27:50.805367+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick4115.jsonOLD b/StrategyPierrick4115.jsonOLD new file mode 100644 index 0000000..daf984f --- /dev/null +++ b/StrategyPierrick4115.jsonOLD @@ -0,0 +1,34 @@ +{ + "strategy_name": "StrategyPierrick4115", + "params": { + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.09, + "buy_bollinger_2": 0.05, + "buy_bollinger_2_enabled": true, + "buy_bollinger_enabled": true, + "buy_sma_percent": 0.98, + "buy_sma_percent_enabled": false, + "buy_volume": 18.9, + "buy_volume_enabled": false + }, + "sell": {}, + "protection": {}, + "roi": { + "0": 0.096, + "30": 0.049, + "60": 0.032, + "120": 0 + } + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-07 21:14:40.877512+00:00" +} diff --git a/StrategyPierrick4115.py b/StrategyPierrick4115.py new file mode 100644 index 0000000..a0e34ee --- /dev/null +++ b/StrategyPierrick4115.py @@ -0,0 +1,282 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick4115(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.095, space="buy") + buy_bollinger_enabled = BooleanParameter(default=True, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + buy_bollinger_2_enabled = BooleanParameter(default=True, space="buy") + # pourcentage sma à dépasser + buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.098, space="buy") + buy_sma_percent_enabled = BooleanParameter(default=True, space="buy") + # volume à atteindre + buy_volume = IntParameter(0, 50, default=0, space="buy") + buy_volume_enabled = BooleanParameter(default=True, space="buy") + + buy_candel_percent = DecimalParameter(1.02, 1.10, decimals=2, default=1.04, space="buy") + buy_candel_sma_percent = DecimalParameter(0.97, 1.04, decimals=2, default=0.99, space="buy") + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + 'rsi': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "Pct": { + 'percent': {'color': 'white'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['adx'] = ta.ADX(dataframe) + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + + # condition1 = np.where(value >= 1.04, True, False) + + conditions_bb_1 = [] + # GUARDS AND TRENDS + if self.buy_bollinger_enabled.value: + conditions_bb_1.append(dataframe['bb_width'] >= self.buy_bollinger.value) + + condition1 = False + if conditions_bb_1: + condition1 = reduce(lambda x, y: x & y, conditions_bb_1) + + conditions2_bb_2 = [] + if self.buy_bollinger_2_enabled.value: + conditions2_bb_2.append(dataframe['bb_width'] >= self.buy_bollinger_2.value) + # conditions2_bb_2.append(dataframe['bb_width'] >= 0.03) + + # conditions2_bb_2.append(dataframe['bb_width'] <= 0.1) + condition2 = False + if conditions2_bb_2: + condition2 = reduce(lambda x, y: x & y, conditions2_bb_2) + + conditions_volume = [] + condition_volume = True + if self.buy_volume_enabled.value: + conditions_volume.append(dataframe['volume'] >= self.buy_volume.value * 1000) + if conditions_volume: + condition_volume = np.where(conditions_volume, True, False) + + condition_sma = False + conditions_sma = [] + if self.buy_sma_percent_enabled.value: + # conditions_sma.append(dataframe['close'] <= dataframe['sma200'] * self.buy_sma_percent.value) + conditions_sma.append((dataframe['sma100'].shift(36) - dataframe['sma100']) / dataframe[ + 'sma100'] > self.buy_sma_percent.value) + if conditions_sma: + condition_sma = reduce(lambda x, y: x & y, conditions_sma) + + dataframe.loc[ + ( + (dataframe['close'] < dataframe['bb_lowerband']) & + condition_volume & + condition1 + ) | ( + (dataframe['close'] > dataframe['bb_upperband']) & + (dataframe['close'] > dataframe['open'] * 1.04) & + (dataframe['close'] < dataframe['open'].shift(4) * 1.06) & + (dataframe['close'].shift(4) < dataframe['sma100'].shift(4) * 1.04) + ) + , + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe[ + 'bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ) | ( + (dataframe['close'] * 1.04 < dataframe['open']) + ), + + 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick4116.json b/StrategyPierrick4116.json new file mode 100644 index 0000000..55f79ab --- /dev/null +++ b/StrategyPierrick4116.json @@ -0,0 +1,33 @@ +{ + "strategy_name": "StrategyPierrick4116", + "params": { + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.06, + "buy_bollinger_2": 0.06, + "buy_candel_bb1": 2, + "buy_candel_percent": 1.05, + "buy_volume": 33 + }, + "sell": { + "sell_candel_percent": 1.1 + }, + "protection": {}, + "roi": { + "0": 0.256, + "29": 0.076, + "52": 0.031, + "76": 0 + }, + "stoploss": { + "stoploss": -0.057 + } + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-14 23:21:44.054656+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick4116.jsonOLD b/StrategyPierrick4116.jsonOLD new file mode 100644 index 0000000..ce4da9f --- /dev/null +++ b/StrategyPierrick4116.jsonOLD @@ -0,0 +1,36 @@ +{ + "strategy_name": "StrategyPierrick4116", + "params": { + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.03, + "buy_bollinger_2": 0.02, + "buy_bollinger_2_enabled": true, + "buy_bollinger_enabled": true, + "buy_candel_percent": 1.02, + "buy_candel_sma_percent": 1.03, + "buy_sma_percent": 1.02, + "buy_sma_percent_enabled": true, + "buy_volume": 24, + "buy_volume_enabled": true + }, + "sell": {}, + "protection": {}, + "roi": { + "0": 0.122, + "34": 0.092, + "62": 0.032, + "85": 0 + }, + "stoploss": { + "stoploss": -0.306 + } + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-14 19:35:12.845988+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick4116.py b/StrategyPierrick4116.py new file mode 100644 index 0000000..c6806c6 --- /dev/null +++ b/StrategyPierrick4116.py @@ -0,0 +1,255 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick4116(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.095, space="buy") + buy_candel_bb1 = IntParameter(0, 10, default=5, space="buy") + + # Valeur de la deuxième condition bollinger avec condition sma200 + buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + # volume à atteindre + buy_volume = IntParameter(0, 50, default=0, space="buy") + + buy_candel_percent = DecimalParameter(1.00, 1.10, decimals=2, default=1.04, space="buy") + sell_candel_percent = DecimalParameter(1.0, 1.10, decimals=2, default=1.04, space="sell") + + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma10': {'color': 'yellow'}, + 'sma20': {'color': 'cyan'} + # 'rsi': {'color': '#c58893'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "Pct": { + 'percent': {'color': 'white'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['adx'] = ta.ADX(dataframe) + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + + # condition1 = np.where(value >= 1.04, True, False) + + conditions_bb_1 = [] + # GUARDS AND TRENDS + conditions_bb_1.append(dataframe['bb_width'] >= self.buy_bollinger.value) + conditions_bb_1.append(dataframe['volume'] >= self.buy_volume.value * 1000) + conditions_bb_1.append(dataframe['close'] < dataframe['bb_lowerband']) + conditions_bb_1.append( + (dataframe['open'] - dataframe['bb_lowerband']) / (dataframe['bb_lowerband'] - dataframe['close']) > self.buy_candel_bb1.value + ) + conditions_bb_1.append(dataframe['percent'] > -0.03) + condition1 = reduce(lambda x, y: x & y, conditions_bb_1) + + conditions2_bb_2 = [] + conditions2_bb_2.append(dataframe['bb_width'] <= self.buy_bollinger_2.value) + conditions2_bb_2.append(dataframe['close'] > dataframe['open'] * self.buy_candel_percent.value) + conditions2_bb_2.append(dataframe['close'] < dataframe['bb_upperband']) + condition2 = reduce(lambda x, y: x & y, conditions2_bb_2) + + dataframe.loc[ + ( + condition1 + ) | ( + False & + (dataframe['bb_width'] > 0.065) & + (dataframe['close'] > dataframe['bb_upperband']) & + (dataframe['close'].shift(1) < dataframe['bb_upperband'].shift(1)) & + (dataframe['close'].shift(1) > dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) > dataframe['open'].shift(2)) & + (dataframe['close'].shift(3) > dataframe['open'].shift(3)) & + (dataframe['close'] < dataframe['open'].shift(3) * 1.03) & + (dataframe['sma10'].shift(1) < dataframe['sma10'] * 1.01) & + (dataframe['sma10'].shift(1) > dataframe['sma10'] * 0.99) + ) | ( + condition2 & + # (dataframe['close'] > dataframe['open'] * 1.04) & + # (dataframe['close'] <= dataframe['open'] * 1.05) & + (dataframe['close'] > dataframe['sma10']) & + (dataframe['open'] < dataframe['sma10']) & + (dataframe['sma100'].shift(4) < dataframe['sma100'] * 1.01) & + (dataframe['sma100'].shift(4) > dataframe['sma100'] * 0.99) + # (dataframe['sma100'].shift(1) <= dataframe['sma100']) + ) | ( + False & + (dataframe['close'] > dataframe['bb_upperband']) & + (dataframe['close'] > dataframe['open'] * 1.02) & + (dataframe['close'] > dataframe['sma100']) & + (dataframe['open'] < dataframe['sma100']) & + (dataframe['close'].shift(1) > dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) > dataframe['open'].shift(2)) + ) + , + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions_sell = [] + # GUARDS AND TRENDS + conditions_sell.append(dataframe['close'] < dataframe['open']) + conditions_sell.append(dataframe['close'] < dataframe['bb_lowerband']) + conditions_sell.append(dataframe['close'].shift(1) < dataframe['open'].shift(1)) + conditions_sell.append(dataframe['close'].shift(2) < dataframe['open'].shift(2)) + conditions_sell.append(dataframe['close'] * self.sell_candel_percent.value < dataframe['open'].shift(2)) + condition1 = reduce(lambda x, y: x & y, conditions_sell) + + dataframe.loc[condition1, 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick4117.json b/StrategyPierrick4117.json new file mode 100644 index 0000000..075a25b --- /dev/null +++ b/StrategyPierrick4117.json @@ -0,0 +1,34 @@ +{ + "strategy_name": "StrategyPierrick4117", + "params": { + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.09, + "buy_bollinger_2": 0.05, + "buy_bollinger_2_enabled": true, + "buy_bollinger_enabled": true, + "buy_sma_percent": 0.98, + "buy_sma_percent_enabled": false, + "buy_volume": 18.9, + "buy_volume_enabled": false + }, + "sell": {}, + "protection": {}, + "roi": { + "0": 0.02, + "30": 0.015, + "60": 0.01, + "120": 0 + } + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-11 20:27:50.805367+00:00" +} diff --git a/StrategyPierrick4117.py b/StrategyPierrick4117.py new file mode 100644 index 0000000..136af21 --- /dev/null +++ b/StrategyPierrick4117.py @@ -0,0 +1,319 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +from functools import reduce +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from numpy.lib import math + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick4117(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.095, space="buy") + buy_bollinger_enabled = BooleanParameter(default=True, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + buy_bollinger_2_enabled = BooleanParameter(default=True, space="buy") + # pourcentage sma à dépasser + buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.098, space="buy") + buy_sma_percent_enabled = BooleanParameter(default=True, space="buy") + # volume à atteindre + buy_volume = IntParameter(0, 50, default=0, space="buy") + buy_volume_enabled = BooleanParameter(default=True, space="buy") + + buy_candel_percent = DecimalParameter(1.02, 1.10, decimals=2, default=1.04, space="buy") + buy_candel_sma_percent = DecimalParameter(0.97, 1.04, decimals=2, default=0.99, space="buy") + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma10': {'color': 'yellow'}, + 'sma20': {'color': 'cyan'} + # 'rsi': {'color': '#c58893'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "Pct": { + 'percent': {'color': 'white'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['adx'] = ta.ADX(dataframe) + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + #dataframe['min'] = ta.MIN(dataframe) + #dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + + # condition1 = np.where(value >= 1.04, True, False) + + conditions_bb_1 = [ + dataframe['bb_width'] >= self.buy_bollinger.value, + dataframe['bb_width'] < 0.12, + dataframe['volume'] >= self.buy_volume.value * 1000, + dataframe['close'] < dataframe['bb_lowerband']] + condition1 = reduce(lambda x, y: x & y, conditions_bb_1) + + dataframe.loc[ + ( + False & condition1 + ) | ( + False & + (dataframe['bb_width'] > 0.065) & + (dataframe['close'] > dataframe['bb_upperband']) & + (dataframe['close'].shift(1) < dataframe['bb_upperband'].shift(1)) & + (dataframe['close'].shift(1) > dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) > dataframe['open'].shift(2)) & + (dataframe['close'].shift(3) > dataframe['open'].shift(3)) & + (dataframe['close'] < dataframe['open'].shift(3) * 1.03) & + (dataframe['sma10'].shift(1) < dataframe['sma10'] * 1.01) & + (dataframe['sma10'].shift(1) > dataframe['sma10'] * 0.99) + ) | ( + # (dataframe['bb_width'] >= 0.065) & + # (dataframe['bb_width'] <= 0.15) & + (dataframe['sma10'] < dataframe['sma100']) & + (dataframe['sma10'] / dataframe['sma50'] > 1.008) & + (dataframe['sma10'] / dataframe['sma50'] < 1.012) & + (dataframe['close'] > dataframe['bb_upperband']) + # (dataframe['close'] < dataframe['open'] * 1.025) & + # (dataframe['close'].shift(1) > dataframe['open'].shift(1)) & + # (dataframe['close'].shift(2) > dataframe['open'].shift(2)) & + # (dataframe['close'].shift(1) <= dataframe['bb_upperband'].shift(1)) & + # (dataframe['close'].shift(2) <= dataframe['bb_upperband'].shift(2)) & + # (dataframe['sma100'].shift(4) < dataframe['sma100'] * 1.01) & + # (dataframe['sma100'].shift(4) > dataframe['sma100'] * 0.99) & + # (dataframe['sma100'].shift(1) <= dataframe['sma100']) + ) | ( + False & + (dataframe['close'] > dataframe['bb_upperband']) & + (dataframe['close'] > dataframe['open'] * 1.02) & + (dataframe['close'] > dataframe['sma100']) & + (dataframe['open'] < dataframe['sma100']) & + (dataframe['close'].shift(1) > dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) > dataframe['open'].shift(2)) + # ) | ( + # (dataframe['close'] > dataframe['bb_upperband']) & + # ((dataframe['percent'] + # + dataframe['percent'].shift(1) + # + dataframe['percent'].shift(2) + # + dataframe['percent'].shift(3)) > 0.015) & + # (dataframe['bb_width'] >= 0.03) & + # (dataframe['sma100'].shift(4) < dataframe['sma100'] * 1.01) & + # (dataframe['sma100'].shift(4) > dataframe['sma100'] * 0.99) + # ) | ( + # ( + # (dataframe['bb_width'] < 0.2) & + # # (dataframe['percent'] > 0.02) & + # ((dataframe['percent'] + # + dataframe['percent'].shift(1) + # + dataframe['percent'].shift(2) + # + dataframe['percent'].shift(3)) > 0.015) & + # (dataframe['close'] > dataframe['bb_upperband']) + # # dataframe['bb_width'] >= 0.04 + # ) + ) + , + 'buy'] = 1 + # else: + # if conditions2_bb_2: + # dataframe.loc[ + # ( + # (dataframe['close'] > dataframe['bb_upperband']) & + # reduce(lambda x, y: x & y, + # (dataframe['close'] > dataframe['open'] * self.buy_candel_percent.value)) & + # # (dataframe['bb_width'] < 0.2) + # reduce(lambda x, y: x & y, + # (dataframe['sma100'].shift(4) < dataframe[ + # 'sma100'] * self.buy_candel_sma_percent.value + 0.02)) & + # reduce(lambda x, y: x & y, + # (dataframe['sma100'].shift(4) > dataframe[ + # 'sma100'] * self.buy_candel_sma_percent.value)) + # ) + # , 'buy'] = 1 + # else: + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) + & ( + (dataframe['close'] < dataframe['bb_lowerband']) # & + # (dataframe['close'] < dataframe['sma10']) + ) + # (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe[ + # 'bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ) | ( + (dataframe['close'] * 1.04 < dataframe['open']) + ), + + 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick4118.json b/StrategyPierrick4118.json new file mode 100644 index 0000000..1693d91 --- /dev/null +++ b/StrategyPierrick4118.json @@ -0,0 +1,31 @@ +{ + "strategy_name": "StrategyPierrick4118", + "params": { + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.119, + "buy_candel_percent": 0.001, + "buy_volume": 17 + }, + "sell": { + "sell_candel_percent": 1.1 + }, + "protection": {}, + "roi": { + "0": 0.146, + "31": 0.042, + "78": 0.021, + "186": 0 + }, + "stoploss": { + "stoploss": -0.337 + } + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-16 19:58:16.082610+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick4118.jsonOnlyProfitHyperOptLoss b/StrategyPierrick4118.jsonOnlyProfitHyperOptLoss new file mode 100644 index 0000000..1693d91 --- /dev/null +++ b/StrategyPierrick4118.jsonOnlyProfitHyperOptLoss @@ -0,0 +1,31 @@ +{ + "strategy_name": "StrategyPierrick4118", + "params": { + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.119, + "buy_candel_percent": 0.001, + "buy_volume": 17 + }, + "sell": { + "sell_candel_percent": 1.1 + }, + "protection": {}, + "roi": { + "0": 0.146, + "31": 0.042, + "78": 0.021, + "186": 0 + }, + "stoploss": { + "stoploss": -0.337 + } + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-16 19:58:16.082610+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick4118.jsonStrategyPierrick4118.json b/StrategyPierrick4118.jsonStrategyPierrick4118.json new file mode 100644 index 0000000..5d7957a --- /dev/null +++ b/StrategyPierrick4118.jsonStrategyPierrick4118.json @@ -0,0 +1,31 @@ +{ + "strategy_name": "StrategyPierrick4118", + "params": { + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.125, + "buy_candel_percent": 0.001, + "buy_volume": 38 + }, + "sell": { + "sell_candel_percent": 1.05 + }, + "protection": {}, + "roi": { + "0": 0.22000000000000003, + "35": 0.083, + "91": 0.017, + "133": 0 + }, + "stoploss": { + "stoploss": -0.321 + } + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-16 19:41:29.563783+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick4118.py b/StrategyPierrick4118.py new file mode 100644 index 0000000..d951711 --- /dev/null +++ b/StrategyPierrick4118.py @@ -0,0 +1,250 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick4118(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.005, 0.125, decimals=3, default=0.03, space="buy") + + # volume à atteindre + buy_volume = IntParameter(0, 50, default=0, space="buy") + + buy_candel_percent = DecimalParameter(0.001, 0.01, decimals=3, default=0.006, space="buy") + sell_candel_percent = DecimalParameter(1.0, 1.10, decimals=2, default=1.04, space="sell") + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'green'}, + 'max': {'color': 'yellow'}, + 'sma20': {'color': 'cyan'} + # 'rsi': {'color': '#c58893'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "Pct": { + 'percent': {'color': 'white'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['adx'] = ta.ADX(dataframe) + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # dataframe['min'] = ta.MIN(dataframe) + # dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions_bb_1 = [ + dataframe['bb_width'] >= self.buy_bollinger.value, + dataframe['volume'] >= self.buy_volume.value * 1000, + dataframe['percent'].shift(3) >= self.buy_candel_percent.value + ] + condition1 = reduce(lambda x, y: x & y, conditions_bb_1) + # dataframe.loc[ + # ( + # condition1 & + # (dataframe['min'] == dataframe['min'].shift(1)) & + # (dataframe['min'].shift(1) == dataframe['min'].shift(2)) & + # # courant est positif + # (dataframe['open'].shift(2) < dataframe['close'].shift(2)) & + # # previous cut BB + # (dataframe['close'].shift(3) < dataframe['bb_lowerband'].shift(3)) & + # # min + # (dataframe['min'].shift(2) * 1.001 >= dataframe['open'].shift(2)) + # ), 'buy'] = 1 + dataframe.loc[ + ( + condition1 & + (dataframe['min'] == dataframe['min'].shift(1)) & + (dataframe['min'].shift(1) == dataframe['min'].shift(2)) & + (dataframe['min'].shift(2) == dataframe['min'].shift(3)) & + # courant est positif + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + # previous cut BB + (dataframe['close'].shift(4) < dataframe['bb_lowerband'].shift(4)) & + # min + (dataframe['min'].shift(3) * 1.001 >= dataframe['open'].shift(3)) + ), 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions_sell = [] + # GUARDS AND TRENDS + conditions_sell.append(dataframe['close'] < dataframe['open']) + conditions_sell.append(dataframe['close'] < dataframe['bb_lowerband']) + conditions_sell.append(dataframe['close'].shift(1) < dataframe['open'].shift(1)) + conditions_sell.append(dataframe['close'].shift(2) < dataframe['open'].shift(2)) + #conditions_sell.append(dataframe['close'] * self.sell_candel_percent.value < dataframe['open'].shift(2)) + conditions_sell.append((((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02)) + condition1 = reduce(lambda x, y: x & y, conditions_sell) + + # (qtpylib.crossed_above(dataframe['close'], dataframe['ema800'])) & + + dataframe.loc[condition1, 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick4119.json b/StrategyPierrick4119.json new file mode 100644 index 0000000..5c2305e --- /dev/null +++ b/StrategyPierrick4119.json @@ -0,0 +1,34 @@ +{ + "strategy_name": "StrategyPierrick4119", + "params": { + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.09, + "buy_bollinger_2": 0.08, + "buy_bollinger_2_enabled": false, + "buy_bollinger_enabled": true, + "buy_sma_percent": 0.98, + "buy_sma_percent_enabled": false, + "buy_volume": 18.9, + "buy_volume_enabled": true + }, + "sell": {}, + "protection": {}, + "roi": { + "0": 0.096, + "30": 0.049, + "60": 0.032, + "120": 0 + } + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-07 21:14:40.877512+00:00" +} diff --git a/StrategyPierrick4119.py b/StrategyPierrick4119.py new file mode 100644 index 0000000..9f254b4 --- /dev/null +++ b/StrategyPierrick4119.py @@ -0,0 +1,281 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick4119(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.095, space="buy") + buy_bollinger_enabled = BooleanParameter(default=True, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + buy_bollinger_2_enabled = BooleanParameter(default=True, space="buy") + # pourcentage sma à dépasser + buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.098, space="buy") + buy_sma_percent_enabled = BooleanParameter(default=True, space="buy") + # volume à atteindre + buy_volume = DecimalParameter(0, 50, decimals=1, default=0, space="buy") + buy_volume_enabled = BooleanParameter(default=True, space="buy") + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'green'}, + 'max': {'color': 'yellow'}, + 'sma20': {'color': 'cyan'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['adx'] = ta.ADX(dataframe) + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + + condition1 = np.where(value >= 1.04, True, False) + + conditions = [] + # GUARDS AND TRENDS + if self.buy_bollinger_enabled.value: + conditions.append(dataframe['bb_width'] >= self.buy_bollinger.value) + + conditions2 = [] + if self.buy_bollinger_2_enabled.value: + conditions2.append(dataframe['bb_width'] >= self.buy_bollinger_2.value) + + conditions_volume = [] + condition_volume = True + if self.buy_volume_enabled.value: + conditions_volume.append(dataframe['volume'] >= self.buy_volume.value * 1000) + if conditions_volume: + condition_volume = np.where(conditions_volume, True, False) + + condition2 = False + if conditions2: + condition2 = reduce(lambda x, y: x & y, conditions2) & condition1 + + condition_sma = False + conditions_sma = [] + if self.buy_sma_percent_enabled.value: + # conditions_sma.append(dataframe['close'] <= dataframe['sma200'] * self.buy_sma_percent.value) + conditions_sma.append((dataframe['sma100'].shift(36) - dataframe['sma100']) / dataframe['sma100'] > self.buy_sma_percent.value) + if conditions_sma: + condition_sma = reduce(lambda x, y: x & y, conditions_sma) + + condition1 = reduce(lambda x, y: x & y, conditions) + + conditions_bb_1 = [ + dataframe['bb_width'] >= 0.119, # self.buy_bollinger.value, + dataframe['volume'] >= 17000, #self.buy_volume.value * 1000, + dataframe['percent'].shift(3) >= 0.001 #self.buy_candel_percent.value + ] + condition_bb = reduce(lambda x, y: x & y, conditions_bb_1) + + + dataframe.loc[ + ( + (dataframe['close'] < dataframe['bb_lowerband']) & + condition_volume & + (condition1 | (condition2 & condition_sma)) + ) | ( + condition_bb & + (dataframe['min'] == dataframe['min'].shift(1)) & + (dataframe['min'].shift(1) == dataframe['min'].shift(2)) & + (dataframe['min'].shift(2) == dataframe['min'].shift(3)) & + # courant est positif + (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + # previous cut BB + (dataframe['close'].shift(4) < dataframe['bb_lowerband'].shift(4)) & + # min + (dataframe['min'].shift(3) * 1.001 >= dataframe['open'].shift(3)) + ), 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick411_02.json b/StrategyPierrick411_02.json new file mode 100644 index 0000000..bfc7627 --- /dev/null +++ b/StrategyPierrick411_02.json @@ -0,0 +1,28 @@ +{ + "strategy_name": "StrategyPierrick411_02", + "params": { + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.01, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.08, + "buy_sma_percent": 1.05 + }, + "sell": {}, + "protection": {}, + "roi": { + "0": 0.11, + "13": 0.055, + "51": 0.034, + "141": 0 + }, + "stoploss": { + "stoploss": -0.329 + } + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-21 18:22:30.332701+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick411_02.py b/StrategyPierrick411_02.py new file mode 100644 index 0000000..a7116d4 --- /dev/null +++ b/StrategyPierrick411_02.py @@ -0,0 +1,246 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter +from pandas import DataFrame +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick411_02(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + # pourcentage sma à dépasser + buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.97, space="buy") + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma10': {'color': 'yellow'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + 'rsi': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + } + } + } + + # def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + # current_profit: float, **kwargs): + # dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + # last_candle = dataframe.iloc[-1].squeeze() + # previous_last_candle = dataframe.iloc[-2].squeeze() + # # print(last_candle) + # if current_profit > 0.05: + # if previous_last_candle['sma50'] > last_candle['sma50']: + # return 'ema_long_below_80' + # else: + # if current_profit > 0.005: + # if previous_last_candle['sma20'] > last_candle['sma20'] and ( + # current_time - trade.open_date_utc).seconds >= 3200: + # return 'ema_long_below_80' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['adx'] = ta.ADX(dataframe) + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + dataframe["rolling"] = ( + 100 * (dataframe["close"] - dataframe["bb_lowerband"]) / dataframe["bb_lowerband"]).rolling( + 5).mean() + dataframe["bb_rolling"] = dataframe["rolling"] / dataframe["bb_width"] + dataframe['bb_rolling_min'] = ta.MIN(dataframe['bb_rolling'], timeperiod=5) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [ + dataframe['bb_width'] >= self.buy_bollinger.value, + (dataframe['close'] < dataframe['bb_lowerband']), + (dataframe['close'] < dataframe['sma100'] * self.buy_sma_percent.value) + ] + # GUARDS AND TRENDS + + if conditions: + dataframe.loc[ + ( + (reduce(lambda x, y: x & y, conditions)) + ) + , + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe[ + 'bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick411_03.py b/StrategyPierrick411_03.py new file mode 100644 index 0000000..9cc3cac --- /dev/null +++ b/StrategyPierrick411_03.py @@ -0,0 +1,228 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick411(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + # pourcentage sma à dépasser + buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.97, space="buy") + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + 'rsi': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['adx'] = ta.ADX(dataframe) + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + dataframe["rolling"] = (100 * (dataframe["close"] - dataframe["bb_lowerband"]) / dataframe["bb_lowerband"]).rolling(5).mean() + dataframe["bb_rolling"] = dataframe["rolling"] / dataframe["bb_width"] + dataframe['bb_rolling_min'] = ta.MIN(dataframe['bb_rolling'], timeperiod=5) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [ + dataframe['bb_width'] >= self.buy_bollinger.value, + (dataframe['close'] < dataframe['bb_lowerband']), + (dataframe['close'] < dataframe['sma100'] * self.buy_sma_percent.value) + ] + # GUARDS AND TRENDS + + if conditions: + dataframe.loc[ + ( + (reduce(lambda x, y: x & y, conditions)) + ) + , + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick412.json b/StrategyPierrick412.json new file mode 100644 index 0000000..5bd8a62 --- /dev/null +++ b/StrategyPierrick412.json @@ -0,0 +1,38 @@ +{ + "strategy_name": "StrategyPierrick412", + "params": { + "roi": { + "0": 0.096, + "30": 0.049, + "60": 0.032, + "120": 0 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.12, + "buy_bollinger_2": 0.04, + "buy_bollinger_2_enabled": true, + "buy_bollinger_enabled": true, + "buy_candel": 0.07, + "buy_candel_enabled": true, + "buy_cross": 1.0, + "buy_cross_enabled": true, + "buy_sma_percent": 1.05, + "buy_sma_percent_enabled": false, + "buy_volume": 11.6, + "buy_volume_enabled": true + }, + "sell": {}, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-06 19:04:44.234113+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick412.jsonOLD b/StrategyPierrick412.jsonOLD new file mode 100644 index 0000000..6d7a1cf --- /dev/null +++ b/StrategyPierrick412.jsonOLD @@ -0,0 +1,37 @@ +{ + "strategy_name": "StrategyPierrick412", + "params": { + "roi": { + "0": 0.5 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.04, + "buy_bollinger_2": 0.06, + "buy_bollinger_2_enabled": false, + "buy_bollinger_3": 0.04, + "buy_bollinger_3_enabled": true, + "buy_bollinger_enabled": true, + "buy_candel": 0.06, + "buy_candel_enabled": true, + "buy_cross": 1.1, + "buy_cross_enabled": true, + "buy_sma_percent": 0.96, + "buy_sma_percent_enabled": true, + "buy_volume": 7.3, + "buy_volume_enabled": true + }, + "sell": {}, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-05 14:52:48.625435+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick412.py b/StrategyPierrick412.py new file mode 100644 index 0000000..2064a77 --- /dev/null +++ b/StrategyPierrick412.py @@ -0,0 +1,282 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick412(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.095, space="buy") + buy_bollinger_enabled = BooleanParameter(default=True, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + buy_bollinger_2_enabled = BooleanParameter(default=True, space="buy") + # pourcentage sma à dépasser + buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.098, space="buy") + buy_sma_percent_enabled = BooleanParameter(default=True, space="buy") + # volume à atteindre + buy_volume = DecimalParameter(0, 50, decimals=1, default=0, space="buy") + buy_volume_enabled = BooleanParameter(default=True, space="buy") + # Valeur maxi de la chandelle qui coupe la bande du bas + buy_candel = DecimalParameter(0, 0.10, decimals=2, default=0, space="buy") + buy_candel_enabled = BooleanParameter(default=True, space="buy") + + buy_cross = DecimalParameter(0.8, 1.2, decimals=1, default=1, space="buy") + buy_cross_enabled = BooleanParameter(default=True, space="buy") + + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + # 'volatility_dcp': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "Aaron": { + 'aroonup': {'color': 'blue'}, + 'aroondown': {'color': 'red'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + # dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + + condition1 = np.where(value >= 1.04, True, False) + + conditions = [] + # GUARDS AND TRENDS + if self.buy_bollinger_enabled.value: + conditions.append(dataframe['bb_width'] >= self.buy_bollinger.value) + + conditions2 = [] + if self.buy_bollinger_2_enabled.value: + conditions2.append(dataframe['bb_width'] >= self.buy_bollinger_2.value) + + conditions_volume = [] + condition_volume = True + if self.buy_volume_enabled.value: + conditions_volume.append(dataframe['volume'] >= self.buy_volume.value * 1000) + if conditions_volume: + condition_volume = np.where(conditions_volume, True, False) + + condition2 = False + if conditions2: + condition2 = reduce(lambda x, y: x & y, conditions2) & condition1 + + condition_sma = False + conditions_sma = [] + if self.buy_sma_percent_enabled.value: + #conditions_sma.append(dataframe['close'] <= dataframe['sma200'] * self.buy_sma_percent.value) + conditions_sma.append((dataframe['sma100'].shift(36) - dataframe['sma100']) / dataframe['sma100'] > self.buy_sma_percent.value) + if conditions_sma: + condition_sma = reduce(lambda x, y: x & y, conditions_sma) + + condition_candel = True + conditions_candel = [] + if self.buy_candel_enabled.value: + conditions_candel.append((dataframe['open'] - dataframe['close']) / dataframe['close'] <= self.buy_candel.value) + if conditions_candel: + condition_candel = reduce(lambda x, y: x & y, conditions_candel) + + condition_cross = True + conditions_cross = [] + if self.buy_cross_enabled.value: + conditions_cross.append(dataframe['close'] < dataframe['bb_lowerband'] * self.buy_cross.value) + if conditions_cross: + condition_cross = reduce(lambda x, y: x & y, conditions_cross) + + if conditions: + dataframe.loc[ + ( + condition_cross & + condition_candel & + condition_volume & + ( + ( + reduce(lambda x, y: x & y, conditions) | + (condition2 & condition_sma) + ) + ) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(1) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.015) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), + + 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick4120.json b/StrategyPierrick4120.json new file mode 100644 index 0000000..2eb5854 --- /dev/null +++ b/StrategyPierrick4120.json @@ -0,0 +1,34 @@ +{ + "strategy_name": "StrategyPierrick4120", + "params": { + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.09, + "buy_bollinger_2": 0.08, + "buy_bollinger_2_enabled": false, + "buy_bollinger_enabled": true, + "buy_sma_percent": 0.98, + "buy_sma_percent_enabled": false, + "buy_volume": 18.9, + "buy_volume_enabled": true + }, + "sell": {}, + "protection": {}, + "roi": { + "0": 0.096, + "30": 0.049, + "60": 0.032, + "120": 0 + } + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-07 21:14:40.877512+00:00" +} diff --git a/StrategyPierrick4120.py b/StrategyPierrick4120.py new file mode 100644 index 0000000..7fef8c1 --- /dev/null +++ b/StrategyPierrick4120.py @@ -0,0 +1,281 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick4120(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.095, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + buy_min = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + buy_percent = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # volume à atteindre + buy_volume = DecimalParameter(0, 50, decimals=1, default=18, space="buy") + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -0.1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'green'}, + 'max': {'color': 'yellow'}, + 'sma20': {'color': 'cyan'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "rolling": { + 'rolling': {'color': 'white'}, + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['adx'] = ta.ADX(dataframe) + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + dataframe["rolling"] = (100 * (dataframe["close"].shift(3) - dataframe["bb_lowerband"].shift(3)) / dataframe["bb_lowerband"].shift(3)).rolling(20).mean() + dataframe["bb_rolling"] = dataframe["rolling"] / dataframe["bb_width"] + dataframe['bb_rolling_min'] = ta.MIN(dataframe['bb_rolling'], timeperiod=20) + + # print(dataframe["rolling"].tolist()) + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # count = 0 + # for i in range(20): + # print("i=", i, " count=", count) + # c = [(dataframe['close'].shift(i) <= dataframe['bb_lowerband'].shift(i))] + # value = reduce(lambda x, y: x & y, c) + + # for k, v in value.iteritems(): + # print("i=", i, " count=", count, " v=", v) + # if v: + # count = count + 1 + # for i in range(20): + # print("i=", i) + # a = 1 if bool(dataframe['close'].shift(i) <= dataframe['bb_lowerband'].shift(i)) else 0 + # print(a) + + conditions_bb_1 = [ + (dataframe['close'].shift(2) < dataframe['bb_lowerband'].shift(2)), + (dataframe['bb_width'].shift(2) >= 0.09), # & (dataframe['rolling'] < 2)) + (dataframe['volume'].shift(2) >= self.buy_volume.value * 1000), + (dataframe['open'] < dataframe['close']) + # ( + # (dataframe['bb_width'] >= 0.119) | + # (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + # ((dataframe['bb_width'].shift(3) >= 0.0) & (dataframe['bb_rolling'].shift(3) < 1)) + + # ((dataframe['bb_width'] >= 0.08) & (dataframe['rolling'] < 2)) + # ((dataframe['bb_width'] >= 0.03) & (count >= 6)) | + # ( + # (dataframe['bb_width'] >= self.buy_bollinger_2.value) & + # (dataframe['close'] / dataframe['min'] <= self.buy_min.value) + # ) + # ), # self.buy_bollinger.value, + # dataframe['volume'] >= self.buy_volume.value * 1000, + # dataframe['percent'].shift(3) >= self.buy_percent.value + ] + condition_bb = reduce(lambda x, y: x & y, conditions_bb_1) + conditions_bb_2 = [ + (dataframe['close'].shift(3) < dataframe['bb_lowerband'].shift(3)), + (dataframe['bb_width'].shift(3) >= 0.09), # & (dataframe['rolling'] < 2)) + (dataframe['volume'].shift(3) >= self.buy_volume.value * 1000), + (dataframe['open'] < dataframe['close']) + ] + condition_bb2 = reduce(lambda x, y: x & y, conditions_bb_2) + + dataframe.loc[ + ( + (condition_bb | condition_bb2) & + (dataframe['min'] == dataframe['min'].shift(1)) & + (dataframe['min'].shift(1) == dataframe['min'].shift(2)) & + (dataframe['min'].shift(2) == dataframe['min'].shift(3)) + # # courant est positif + # (dataframe['open'].shift(3) < dataframe['close'].shift(3)) & + # # previous cut BB + # (dataframe['close'].shift(4) < dataframe['bb_lowerband'].shift(4)) + # min + # (dataframe['min'].shift(3) * 1.001 >= dataframe['open'].shift(3)) + ), 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick4121.jsonOLD b/StrategyPierrick4121.jsonOLD new file mode 100644 index 0000000..130c178 --- /dev/null +++ b/StrategyPierrick4121.jsonOLD @@ -0,0 +1,34 @@ +{ + "strategy_name": "StrategyPierrick4121", + "params": { + "stoploss": { + "stoploss": -1 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_bollinger": 0.12, + "buy_bollinger_2": 0.08, + "buy_bollinger_2_enabled": false, + "buy_bollinger_enabled": true, + "buy_sma_percent": 0.98, + "buy_sma_percent_enabled": false, + "buy_volume": 18.9, + "buy_volume_enabled": true + }, + "sell": {}, + "protection": {}, + "roi": { + "0": 0.096, + "30": 0.049, + "60": 0.032, + "120": 0 + } + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-18 12:33:59.714495+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick4121.py b/StrategyPierrick4121.py new file mode 100644 index 0000000..906b7b8 --- /dev/null +++ b/StrategyPierrick4121.py @@ -0,0 +1,286 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter +from pandas import DataFrame +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick4121(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.05, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + # buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + # buy_min = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # buy_percent = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # volume à atteindre + buy_volume = DecimalParameter(0, 50, decimals=1, default=18, space="buy") + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = False + trailing_stop_positive = 0.02 + trailing_stop_positive_offset = 0.0275 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + # 'sma500': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + } + } + } + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + # print(last_candle) + # Above 20% profit, sell when rsi < 80 + if (last_candle['bb_upperband'] > last_candle['max']) & (previous_last_candle['sma20'] > last_candle['sma20']): + return 'over_bb_band' + + # # Between 2% and 10%, sell if EMA-long above EMA-short + if 0.05 < current_profit < 0.1: + if previous_last_candle['sma20'] > last_candle['sma20'] and (current_time - trade.open_date_utc).seconds >= 3600 * 3: + return 'profit_3h' + + # Sell any positions at a loss if they are held for more than one day. + if 0.1 > current_profit > 0.0 and previous_last_candle['sma20'] > last_candle['sma20'] \ + and (current_time - trade.open_date_utc).seconds >= 3600 * 5: + return 'profit_5h' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['adx'] = ta.ADX(dataframe) + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + dataframe["rolling"] = ( + 100 * (dataframe["close"] - dataframe["bb_lowerband"]) / dataframe["bb_lowerband"]).rolling( + 3).mean() + dataframe["bb_rolling"] = dataframe["rolling"] / dataframe["bb_width"] + dataframe['bb_rolling_min'] = ta.MIN(dataframe['bb_rolling'], timeperiod=10) + dataframe['bb_buy'] = (dataframe['min'] + (dataframe['max'] - dataframe['min']) / 3) + # print(dataframe["rolling"].tolist()) + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # condition_bb_rolling = [ + # (dataframe['bb_width'].shift(3) >= self.buy_bollinger.value), + # (dataframe['volume'].shift(3) * dataframe['close'] / 1000 >= 100), # self.buy_volume.value * 1000), + # (dataframe['open'] < dataframe['close']), + # (dataframe['open'] * 1.02 < dataframe['close']), + # # (dataframe['close'] <= dataframe['sma100'] * 1.01), + # (dataframe['close'].shift(1) <= dataframe['bb_lowerband'].shift(1)) | + # (dataframe['close'].shift(2) <= dataframe['bb_lowerband'].shift(2)) | + # (dataframe['close'].shift(3) <= dataframe['bb_lowerband'].shift(3)), + # ] + # condition_bb_rolling2 = reduce(lambda x, y: x & y, condition_bb_rolling) + # + # condition_bb_rolling_inf = [ + # (dataframe['bb_width'] >= 0.04), + # (dataframe['volume'] * dataframe['close'] / 1000 >= 100), #>= self.buy_volume.value * 1000), + # (dataframe['open'] < dataframe['close']), + # (dataframe['open'] * 1.02 < dataframe['close']), + # # (dataframe['close'] <= dataframe['min'] * 1.01), + # (dataframe['close'] * 1.04) <= dataframe['sma100'], + # (dataframe['close'].shift(1) <= dataframe['bb_lowerband'].shift(1)) + # ] + # condition_bb_rolling_inf2 = reduce(lambda x, y: x & y, condition_bb_rolling_inf) + + condition_bb_rolling = [ + # dataframe['bb_width'] >= self.buy_bollinger.value, + # (dataframe['open'] < dataframe['close']), + (dataframe['bb_rolling_min'].shift(5) <= -6), + (dataframe['bb_rolling_min'].shift(5) == dataframe['bb_rolling'].shift(5)), + # (dataframe['sma100'].shift(1) <= dataframe['sma100']), + (dataframe['close'].shift(5) < dataframe['min'].shift(5) + ( + dataframe['max'].shift(5) - dataframe['min'].shift(5)) / 3), + (dataframe['min'].shift(5) == dataframe['min']) + ] + condition_bb_rolling2 = reduce(lambda x, y: x & y, condition_bb_rolling) + + dataframe.loc[ + ( + condition_bb_rolling2 # | condition_bb_rolling_inf2 + + ), 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['close'] < dataframe['open']) & + # (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + # (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + # (dataframe['close'] < dataframe['bb_lowerband']) & + # (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + # ), 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick41211.py b/StrategyPierrick41211.py new file mode 100644 index 0000000..1a553a7 --- /dev/null +++ b/StrategyPierrick41211.py @@ -0,0 +1,281 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter +from pandas import DataFrame +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick41211(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.065, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + # buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + # buy_min = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # buy_percent = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # volume à atteindre + buy_volume = DecimalParameter(0, 50, decimals=1, default=18, space="buy") + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = False + trailing_stop_positive = 0.02 + trailing_stop_positive_offset = 0.0275 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma500': {'color': 'pink'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + } + } + } + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + # print(last_candle) + if (current_profit > 0) \ + & (last_candle['bb_upperband'] > last_candle['max']) \ + & (previous_last_candle['sma20'] > last_candle['sma20']): + return 'over_bb_band_sma20_desc' + + if (current_profit > 0) \ + & (last_candle['close'] < last_candle['open']) \ + & (previous_last_candle['bb_upperband'] < previous_last_candle['close']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + # & (previous_last_candle['sma100'] > last_candle['sma100']) \ + return 'close_over_bb_band_sma100_desc' + + if 0.05 < current_profit < 0.1: + if ( + (previous_last_candle['sma10'] > last_candle['sma10']) & + (current_time - trade.open_date_utc).seconds >= 3600 * 3 + ) | ( + (current_time - trade.open_date_utc).seconds >= 3600 * 6 + ): + return 'profit_3h_sma10_desc' + + if (0.1 > current_profit > 0.0) \ + & (previous_last_candle['sma20'] > last_candle['sma20']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 5): + return 'profit_5h_sma20_desc' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['adx'] = ta.ADX(dataframe) + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + dataframe["rolling"] = ( + 100 * (dataframe["close"] - dataframe["bb_lowerband"]) / dataframe["bb_lowerband"]).rolling( + 3).mean() + dataframe["bb_rolling"] = dataframe["rolling"] / dataframe["bb_width"] + dataframe['bb_rolling_min'] = ta.MIN(dataframe['bb_rolling'], timeperiod=10) + dataframe['bb_buy'] = (dataframe['min'] + (dataframe['max'] - dataframe['min']) / 3) + # print(dataframe["rolling"].tolist()) + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + step = 3 + bb_rolling_max = -6 + condition_bb_rolling = [ + # dataframe['bb_width'] >= self.buy_bollinger.value, + (dataframe['close'] < dataframe['sma10']), + (dataframe['bb_rolling_min'].shift(step) <= bb_rolling_max), + (dataframe['bb_rolling_min'].shift(step) == dataframe['bb_rolling'].shift(step)), + # (dataframe['sma100'].shift(1) <= dataframe['sma100']), + (dataframe['close'].shift(step) < dataframe['min'].shift(step) + ( + dataframe['max'].shift(step) - dataframe['min'].shift(step)) / 3), + (dataframe['min'].shift(step) == dataframe['min']) + ] + condition_bb_rolling2 = reduce(lambda x, y: x & y, condition_bb_rolling) + + # conditions = [ + # dataframe['bb_width'] >= 0.09, #self.buy_bollinger.value, + # (dataframe['close'] < dataframe['bb_lowerband']), + # (dataframe['close'] < dataframe['sma100'] * 0.99) #self.buy_sma_percent.value) + # ] + + dataframe.loc[ + ( + condition_bb_rolling2 #| (reduce(lambda x, y: x & y, conditions)) + ), 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['close'] < dataframe['open']) & + # (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + # (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + # (dataframe['close'] < dataframe['bb_lowerband']) & + # (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + # ), 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick41212.py b/StrategyPierrick41212.py new file mode 100644 index 0000000..7cf164e --- /dev/null +++ b/StrategyPierrick41212.py @@ -0,0 +1,338 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick41212(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.00, 0.18, decimals=2, default=0.065, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + # buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + # buy_min = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # buy_percent = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # volume à atteindre + buy_volume = DecimalParameter(0, 50, decimals=1, default=18, space="buy") + + buy_step = IntParameter(1, 8, default=3, space="buy") + buy_rolling = IntParameter(-20, 0, default=-6, space="buy") + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = False + trailing_stop_positive = 0.02 + trailing_stop_positive_offset = 0.0275 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma500': {'color': 'pink'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + # "ADX": { + # 'adx': {'color': 'white'}, + # 'minus_dm': {'color': 'blue'}, + # 'plus_dm': {'color': 'red'} + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + } + } + } + + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + + # # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + # if current_candle['bb_width'] > 0.05: + # print("use more stake", pair, " ", proposed_stake * 2) + # return min(max_stake, proposed_stake * 2) + # + # if current_candle['bb_width'] > 0.035: + # print("use more stake", pair, " ", proposed_stake * 1.5) + # return min(max_stake, proposed_stake * 1.5) + + # if current_candle['bb_width'] < 0.020: + # print("use less stake", pair, " ", proposed_stake / 2) + # return min(max_stake, proposed_stake / 2) + # if self.config['stake_amount'] == 'unlimited': + # # Use entire available wallet during favorable conditions when in compounding mode. + # return max_stake + # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + + + # Use default stake amount. + return proposed_stake + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + if (current_profit > 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \ + & (previous_5_candle['sma20'] > last_candle['sma20']): + # & (last_candle['bb_upperband'] > last_candle['max']) \ + print("over_bb_band_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_bb_band_sma20_desc' + + if (current_profit > 0) \ + & (last_candle['rsi'] > 75): + print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + # description trade + # Trade(id=0, pair=CAKE/USDT, amount=4.19815281, open_rate=11.91000000, open_since=2021-12-22 17:55:00) + # print(last_candle) + # if (current_profit > 0) \ + # & (last_candle['close'] < last_candle['open']) \ + # & (previous_last_candle['bb_upperband'] < previous_last_candle['close']) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600) \ + # & ((previous_last_candle['sma10'] - last_candle['sma10']) / last_candle['sma10'] < 0.1): + # # & (previous_last_candle['sma100'] > last_candle['sma100']) \ + # print("close_over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'close_over_bb_band_sma10_desc' + if 0.05 < current_profit < 0.1: + if ( + (previous_last_candle['sma10'] > last_candle['sma10']) & + (current_time - trade.open_date_utc).seconds >= 3600 * 3 + ) | ( + (current_time - trade.open_date_utc).seconds >= 3600 * 6 + ): + print("profit_3h_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_3h_sma10_desc' + + if (0 < current_profit < 0.1) \ + & (previous_last_candle['sma20'] > last_candle['sma20']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 5): + print("profit_5h_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_5h_sma20_desc' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + dataframe["rolling"] = ( + 100 * (dataframe["close"] - dataframe["bb_lowerband"]) / dataframe["bb_lowerband"]).rolling( + 3).mean() + dataframe["bb_rolling"] = dataframe["rolling"] / dataframe["bb_width"] + dataframe['bb_rolling_min'] = ta.MIN(dataframe['bb_rolling'], timeperiod=10) + dataframe['bb_buy'] = (dataframe['min'] + (dataframe['max'] - dataframe['min']) / 3) + # print(dataframe["rolling"].tolist()) + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + step = self.buy_step.value + bb_rolling_max = self.buy_rolling.value + condition_bb_rolling = [ + # dataframe['bb_width'] >= self.buy_bollinger.value, + # (100 * (dataframe['sma100'].shift(12) - dataframe['sma100']) / dataframe['sma100']) < 0.1, + (dataframe['close'] < dataframe['sma10']), + (dataframe['bb_rolling_min'].shift(step) <= bb_rolling_max), + (dataframe['bb_rolling_min'].shift(step) == dataframe['bb_rolling'].shift(step)), + # (dataframe['sma100'].shift(1) <= dataframe['sma100']), + #(100 * ((dataframe['sma20'].shift(1) - dataframe['sma20']) / dataframe['sma20']) < 0.1), + (dataframe['close'].shift(step) < dataframe['min'].shift(step) + ( + dataframe['max'].shift(step) - dataframe['min'].shift(step)) / 3), + (dataframe['min'].shift(step) == dataframe['min']) + ] + condition_bb_rolling2 = reduce(lambda x, y: x & y, condition_bb_rolling) + + conditions = [ + dataframe['bb_width'] >= 0.065, #self.buy_bollinger.value, + (dataframe['close'] < dataframe['bb_lowerband']) + ] + + dataframe.loc[ + ( + ( + condition_bb_rolling2 + # | (reduce(lambda x, y: x & y, conditions)) + ) & ( + dataframe['volume'] > 0 + ) + ), 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick41213.py b/StrategyPierrick41213.py new file mode 100644 index 0000000..d942ad4 --- /dev/null +++ b/StrategyPierrick41213.py @@ -0,0 +1,347 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick41213(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.00, 0.18, decimals=2, default=0.065, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + # buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + # buy_min = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # buy_percent = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # volume à atteindre + buy_volume = DecimalParameter(0, 50, decimals=1, default=18, space="buy") + + buy_step = IntParameter(1, 8, default=3, space="buy") + buy_rolling = IntParameter(-20, 0, default=-6, space="buy") + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = False + trailing_stop_positive = 0.02 + trailing_stop_positive_offset = 0.0275 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma500': {'color': 'pink'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + # "ADX": { + # 'adx': {'color': 'white'}, + # 'minus_dm': {'color': 'blue'}, + # 'plus_dm': {'color': 'red'} + # }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + } + } + } + + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + + # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + # if current_candle['bb_width'] > 0.05: + # print("use more stake", pair, " ", proposed_stake * 2) + # return min(max_stake, proposed_stake * 2) + # + # if current_candle['bb_width'] > 0.035: + # print("use more stake", pair, " ", proposed_stake * 1.5) + # return min(max_stake, proposed_stake * 1.5) + + # if current_candle['bb_width'] < 0.020: + # print("use less stake", pair, " ", proposed_stake / 2) + # return min(max_stake, proposed_stake / 2) + # if self.config['stake_amount'] == 'unlimited': + # # Use entire available wallet during favorable conditions when in compounding mode. + # return max_stake + # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + + + # Use default stake amount. + return proposed_stake + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # description trade + # Trade(id=0, pair=CAKE/USDT, amount=4.19815281, open_rate=11.91000000, open_since=2021-12-22 17:55:00) + # print(last_candle) + if (current_profit > 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \ + & (previous_5_candle['sma20'] > last_candle['sma20']): + # & (last_candle['bb_upperband'] > last_candle['max']) \ + print("over_bb_band_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_bb_band_sma20_desc' + + if (current_profit > 0) \ + & (last_candle['rsi'] > 80): + print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + # if (current_profit > 0) \ + # & (last_candle['close'] < last_candle['open']) \ + # & (previous_last_candle['bb_upperband'] < previous_last_candle['close']) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600) \ + # & ((previous_last_candle['sma10'] - last_candle['sma10']) / last_candle['sma10'] < 0.1): + # # & (previous_last_candle['sma100'] > last_candle['sma100']) \ + # print("close_over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'close_over_bb_band_sma10_desc' + + if 0.05 < current_profit < 0.1: + if ( + (previous_last_candle['sma10'] > last_candle['sma10']) & + (current_time - trade.open_date_utc).seconds >= 3600 * 3 + ) | ( + (current_time - trade.open_date_utc).seconds >= 3600 * 6 + ): + print("profit_3h_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_3h_sma10_desc' + + if (0 < current_profit < 0.1) \ + & (previous_last_candle['sma20'] > last_candle['sma20']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 5): + print("profit_5h_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_5h_sma20_desc' + + # if (current_profit < -0.025) \ + # & (previous_last_candle['sma20'] > last_candle['sma20']) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 3): + # print("stop_loss_3h", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'stop_loss_3h' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + dataframe["rolling"] = ( + 100 * (dataframe["close"] - dataframe["bb_lowerband"]) / dataframe["bb_lowerband"]).rolling( + 3).mean() + dataframe["bb_rolling"] = dataframe["rolling"] / dataframe["bb_width"] + dataframe['bb_rolling_min'] = ta.MIN(dataframe['bb_rolling'], timeperiod=10) + dataframe['bb_buy'] = (dataframe['min'] + (dataframe['max'] - dataframe['min']) / 3) + # print(dataframe["rolling"].tolist()) + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + step = self.buy_step.value + bb_rolling_max = self.buy_rolling.value + condition_bb_rolling = [ + # dataframe['bb_width'] >= self.buy_bollinger.value, + # (100 * (dataframe['sma100'].shift(12) - dataframe['sma100']) / dataframe['sma100']) < 0.1, + (dataframe['close'] < dataframe['sma10']), + (dataframe['bb_rolling_min'].shift(step) <= bb_rolling_max), + (dataframe['bb_rolling_min'].shift(step) == dataframe['bb_rolling'].shift(step)), + # (100 * ((dataframe['sma100'].shift(12) - dataframe['sma100']) / dataframe['sma100']) < 0.25), + (dataframe['close'].shift(step) < dataframe['min'].shift(step) + ( + dataframe['max'].shift(step) - dataframe['min'].shift(step)) / 3), + (dataframe['min'].shift(step) == dataframe['min']), + (dataframe['rsi'] <= 35) + + ] + condition_bb_rolling2 = reduce(lambda x, y: x & y, condition_bb_rolling) + + conditions = [ + dataframe['bb_width'] >= 0.065, #self.buy_bollinger.value, + (dataframe['close'] < dataframe['bb_lowerband']), + (dataframe['rsi'] <= 35) + ] + + dataframe.loc[ + ( + ( + # condition_bb_rolling2 + (reduce(lambda x, y: x & y, conditions)) + ) & ( + dataframe['volume'] > 0 + ) + ), 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['close'] < dataframe['open']) & + # (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + # (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + # (dataframe['close'] < dataframe['bb_lowerband']) & + # (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + # ), 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick41214.py b/StrategyPierrick41214.py new file mode 100644 index 0000000..a353c40 --- /dev/null +++ b/StrategyPierrick41214.py @@ -0,0 +1,359 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick41214(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.00, 0.18, decimals=2, default=0.065, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + # buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + # buy_min = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # buy_percent = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # volume à atteindre + buy_volume = DecimalParameter(0, 50, decimals=1, default=18, space="buy") + + buy_step = IntParameter(1, 8, default=3, space="buy") + buy_rolling = IntParameter(-20, 0, default=-6, space="buy") + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = False + trailing_stop_positive = 0.02 + trailing_stop_positive_offset = 0.0275 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma500': {'color': 'pink'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + # "ADX": { + # 'adx': {'color': 'white'}, + # 'minus_dm': {'color': 'blue'}, + # 'plus_dm': {'color': 'red'} + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + }, + "percent": { + "percent": {'color': 'green'}, + "percent5": {'color': 'red'} + } + } + } + + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + + # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + # if current_candle['bb_width'] > 0.05: + # print("use more stake", pair, " ", proposed_stake * 2) + # return min(max_stake, proposed_stake * 2) + # + # if current_candle['bb_width'] > 0.035: + # print("use more stake", pair, " ", proposed_stake * 1.5) + # return min(max_stake, proposed_stake * 1.5) + + # if current_candle['bb_width'] < 0.020: + # print("use less stake", pair, " ", proposed_stake / 2) + # return min(max_stake, proposed_stake / 2) + # if self.config['stake_amount'] == 'unlimited': + # # Use entire available wallet during favorable conditions when in compounding mode. + # return max_stake + # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + + # Use default stake amount. + return proposed_stake + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # (last_candle['percent5'] < -0.005) \ + # if (0 < current_profit < 0.005) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 2): + # # & (previous_last_candle['sma10'] > last_candle['sma10']): + # print("too_small_gain", pair, trade, " profit=", current_profit, " rate=", current_rate, " percent5=", + # last_candle['percent5']) + # return 'too_small_gain' + + if (current_profit > 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \ + & (previous_5_candle['sma20'] > last_candle['sma20']) \ + & (last_candle['percent5'] < 0): + print("over_bb_band_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_bb_band_sma20_desc' + + if (current_profit > 0) \ + & (last_candle['rsi'] > 75): + print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + # description trade + # Trade(id=0, pair=CAKE/USDT, amount=4.19815281, open_rate=11.91000000, open_since=2021-12-22 17:55:00) + # print(last_candle) + # if (current_profit > 0) \ + # & (last_candle['close'] < last_candle['open']) \ + # & (previous_last_candle['bb_upperband'] < previous_last_candle['close']) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600) \ + # & ((previous_last_candle['sma10'] - last_candle['sma10']) / last_candle['sma10'] < 0.1): + # # & (previous_last_candle['sma100'] > last_candle['sma100']) \ + # print("close_over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'close_over_bb_band_sma10_desc' + if 0.05 < current_profit < 0.1: + if ( + (previous_last_candle['sma10'] > last_candle['sma10']) & + (current_time - trade.open_date_utc).seconds >= 3600 * 3 + ) | ( + (current_time - trade.open_date_utc).seconds >= 3600 * 6 + ): + print("profit_3h_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_3h_sma10_desc' + + if (0 < current_profit < 0.1) \ + & (previous_last_candle['sma20'] > last_candle['sma20']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 5): + print("profit_5h_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_5h_sma20_desc' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + dataframe["rolling"] = ( + 100 * (dataframe["close"] - dataframe["bb_lowerband"]) / dataframe["bb_lowerband"]).rolling( + 3).mean() + dataframe["bb_rolling"] = dataframe["rolling"] / dataframe["bb_width"] + dataframe['bb_rolling_min'] = ta.MIN(dataframe['bb_rolling'], timeperiod=10) + dataframe['bb_buy'] = (dataframe['min'] + (dataframe['max'] - dataframe['min']) / 3) + # print(dataframe["rolling"].tolist()) + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + step = self.buy_step.value + if reduce(lambda x, y: x & y, dataframe['bb_width'] < 0.015): + step = 1 + else: + if reduce(lambda x, y: x & y, dataframe['bb_width'] < 0.03): + step = 2 + bb_rolling_max = self.buy_rolling.value + condition_bb_rolling = [ + # dataframe['bb_width'] >= self.buy_bollinger.value, + # (100 * (dataframe['sma100'].shift(12) - dataframe['sma100']) / dataframe['sma100']) < 0.1, + (dataframe['close'] < dataframe['sma10']), + (dataframe['bb_rolling_min'].shift(step) <= bb_rolling_max), + (dataframe['bb_rolling_min'].shift(step) == dataframe['bb_rolling'].shift(step)), + # (dataframe['sma100'].shift(1) <= dataframe['sma100']), + # (100 * ((dataframe['sma20'].shift(1) - dataframe['sma20']) / dataframe['sma20']) < 0.1), + (dataframe['close'].shift(step) < dataframe['min'].shift(step) + ( + dataframe['max'].shift(step) - dataframe['min'].shift(step)) / 3), + (dataframe['min'].shift(step) == dataframe['min']), + # (dataframe['rsi'].shift(step) <= 32) + + ] + condition_bb_rolling2 = reduce(lambda x, y: x & y, condition_bb_rolling) + + conditions = [ + dataframe['bb_width'] >= 0.065, # self.buy_bollinger.value, + (dataframe['close'] < dataframe['bb_lowerband']) + ] + + dataframe.loc[ + ( + ( + condition_bb_rolling2 + # | (reduce(lambda x, y: x & y, conditions)) + ) & ( + dataframe['volume'] > 0 + ) + ), 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe[ + 'bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick41215.py b/StrategyPierrick41215.py new file mode 100644 index 0000000..7ab9fcf --- /dev/null +++ b/StrategyPierrick41215.py @@ -0,0 +1,311 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick41215(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.00, 0.18, decimals=2, default=0.065, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + # buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + # buy_min = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # buy_percent = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # volume à atteindre + buy_volume = DecimalParameter(0, 50, decimals=1, default=18, space="buy") + + buy_step = IntParameter(1, 8, default=3, space="buy") + buy_rolling = IntParameter(-20, 0, default=-6, space="buy") + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = False + trailing_stop_positive = 0.02 + trailing_stop_positive_offset = 0.0275 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + # "ADX": { + # 'adx': {'color': 'white'}, + # 'minus_dm': {'color': 'blue'}, + # 'plus_dm': {'color': 'red'} + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + }, + "percent": { + "percent": {'color': 'green'}, + "percent5": {'color': 'red'} + } + } + } + + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + + # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + # if current_candle['bb_width'] > 0.05: + # print("use more stake", pair, " ", proposed_stake * 2) + # return min(max_stake, proposed_stake * 2) + # + # if current_candle['bb_width'] > 0.035: + # print("use more stake", pair, " ", proposed_stake * 1.5) + # return min(max_stake, proposed_stake * 1.5) + + # if current_candle['bb_width'] < 0.020: + # print("use less stake", pair, " ", proposed_stake / 2) + # return min(max_stake, proposed_stake / 2) + # if self.config['stake_amount'] == 'unlimited': + # # Use entire available wallet during favorable conditions when in compounding mode. + # return max_stake + # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + + # Use default stake amount. + return proposed_stake + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # (last_candle['percent5'] < -0.005) \ + # if (0 < current_profit < 0.005) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 2): + # # & (previous_last_candle['sma10'] > last_candle['sma10']): + # print("too_small_gain", pair, trade, " profit=", current_profit, " rate=", current_rate, " percent5=", + # last_candle['percent5']) + # return 'too_small_gain' + + if (current_profit > 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \ + & (previous_5_candle['sma20'] > last_candle['sma20']) \ + & (last_candle['percent'] < 0) \ + & (last_candle['percent5'] < 0): + print("over_bb_band_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_bb_band_sma20_desc' + + if (current_profit > 0) \ + & (last_candle['rsi'] > 75): + print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + # description trade + # Trade(id=0, pair=CAKE/USDT, amount=4.19815281, open_rate=11.91000000, open_since=2021-12-22 17:55:00) + # print(last_candle) + if 0.05 < current_profit < 0.1: + if ( + (previous_last_candle['sma10'] > last_candle['sma10']) & + (current_time - trade.open_date_utc).seconds >= 3600 * 3 + ) | ( + (current_time - trade.open_date_utc).seconds >= 3600 * 6 + ): + print("profit_3h_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_3h_sma10_desc' + + if (0 < current_profit < 0.1) \ + & (previous_last_candle['sma20'] > last_candle['sma20']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 5): + print("profit_5h_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_5h_sma20_desc' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + dataframe["rolling"] = ( + 100 * (dataframe["close"] - dataframe["bb_lowerband"]) / dataframe["bb_lowerband"]).rolling( + 3).mean() + dataframe["bb_rolling"] = dataframe["rolling"] / dataframe["bb_width"] + dataframe['bb_rolling_min'] = ta.MIN(dataframe['bb_rolling'], timeperiod=10) + dataframe['bb_buy'] = (dataframe['min'] + (dataframe['max'] - dataframe['min']) / 3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + step = self.buy_step.value + if reduce(lambda x, y: x & y, dataframe['bb_width'] < 0.015): + step = 5 + else: + if reduce(lambda x, y: x & y, dataframe['bb_width'] < 0.03): + step = 4 + bb_rolling_max = self.buy_rolling.value + condition_bb_rolling = [ + (dataframe['close'] < dataframe['sma10']), + (dataframe['bb_rolling_min'].shift(step) <= bb_rolling_max), + (dataframe['bb_rolling_min'].shift(step) == dataframe['bb_rolling'].shift(step)), + (dataframe['close'].shift(step) < dataframe['min'].shift(step) + ( + dataframe['max'].shift(step) - dataframe['min'].shift(step)) / 3), + (dataframe['min'].shift(step) == dataframe['min']), + (dataframe['volume'] > 0) + ] + condition_bb_rolling2 = reduce(lambda x, y: x & y, condition_bb_rolling) + + dataframe.loc[condition_bb_rolling2, 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe[ + 'bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick41216.py b/StrategyPierrick41216.py new file mode 100644 index 0000000..b813aee --- /dev/null +++ b/StrategyPierrick41216.py @@ -0,0 +1,323 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick41216(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.00, 0.18, decimals=2, default=0.065, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + # buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + # buy_min = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # buy_percent = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # volume à atteindre + buy_volume = DecimalParameter(0, 50, decimals=1, default=18, space="buy") + + buy_step = IntParameter(1, 8, default=3, space="buy") + buy_rolling = IntParameter(-20, 0, default=-6, space="buy") + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = False + trailing_stop_positive = 0.02 + trailing_stop_positive_offset = 0.0275 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + # "ADX": { + # 'adx': {'color': 'white'}, + # 'minus_dm': {'color': 'blue'}, + # 'plus_dm': {'color': 'red'} + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + }, + "percent": { + "percent": {'color': 'green'}, + "percent5": {'color': 'red'} + } + } + } + + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + + # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + if current_candle['bb_width'] > 0.065: + print("use more stake", pair, " ", proposed_stake * 2) + return min(max_stake, proposed_stake * 2) + + if current_candle['bb_width'] > 0.045: + print("use more stake", pair, " ", proposed_stake * 1.5) + return min(max_stake, proposed_stake * 1.5) + + # if current_candle['bb_width'] < 0.020: + # print("use less stake", pair, " ", proposed_stake / 2) + # return min(max_stake, proposed_stake / 2) + # if self.config['stake_amount'] == 'unlimited': + # # Use entire available wallet during favorable conditions when in compounding mode. + # return max_stake + # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + + # Use default stake amount. + return proposed_stake + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # (last_candle['percent5'] < -0.005) \ + # if (0 < current_profit < 0.005) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 2): + # # & (previous_last_candle['sma10'] > last_candle['sma10']): + # print("too_small_gain", pair, trade, " profit=", current_profit, " rate=", current_rate, " percent5=", + # last_candle['percent5']) + # return 'too_small_gain' + + # if (current_profit < -0.05) \ + # & ((current_time - trade.open_date_utc).days >= 3): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'stop_loss_profit' + + # if (current_profit > 0.02) \ + # & (last_candle['percent'] < 0.01) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'lost_half_profit' + + if (current_profit > 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \ + & (previous_5_candle['sma20'] > last_candle['sma20']) \ + & (last_candle['percent'] < 0) \ + & (last_candle['percent5'] < 0): + print("over_bb_band_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_bb_band_sma20_desc' + + if (current_profit > 0) \ + & (last_candle['rsi'] > 75): + print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + # description trade + # Trade(id=0, pair=CAKE/USDT, amount=4.19815281, open_rate=11.91000000, open_since=2021-12-22 17:55:00) + # print(last_candle) + if 0.05 < current_profit < 0.1: + if ( + (previous_last_candle['sma10'] > last_candle['sma10']) & + (current_time - trade.open_date_utc).seconds >= 3600 * 3 + ) | ( + (current_time - trade.open_date_utc).seconds >= 3600 * 6 + ): + print("profit_3h_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_3h_sma10_desc' + + if (0 < current_profit < 0.1) \ + & (previous_last_candle['sma20'] > last_candle['sma20']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 5): + print("profit_5h_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_5h_sma20_desc' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + dataframe["rolling"] = ( + 100 * (dataframe["close"] - dataframe["bb_lowerband"]) / dataframe["bb_lowerband"]).rolling( + 3).mean() + dataframe["bb_rolling"] = dataframe["rolling"] / dataframe["bb_width"] + dataframe['bb_rolling_min'] = ta.MIN(dataframe['bb_rolling'], timeperiod=10) + dataframe['bb_buy'] = (dataframe['min'] + (dataframe['max'] - dataframe['min']) / 3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + step = self.buy_step.value + # if reduce(lambda x, y: x & y, dataframe['bb_width'] < 0.015): + # step = 5 + # else: + # if reduce(lambda x, y: x & y, dataframe['bb_width'] < 0.03): + # step = 4 + bb_rolling_max = self.buy_rolling.value + condition_bb_rolling = [ + (dataframe['bb_width'] >= 0.035), + (dataframe['close'] < dataframe['sma10']), + (dataframe['bb_rolling_min'].shift(step) <= bb_rolling_max), + (dataframe['bb_rolling_min'].shift(step) >= dataframe['bb_rolling'].shift(step)), + (dataframe['close'].shift(step) < dataframe['min'].shift(step) + ( + dataframe['max'].shift(step) - dataframe['min'].shift(step)) / 3), + (dataframe['min'].shift(step) == dataframe['min']), + (dataframe['volume'] > 0) + ] + condition_bb_rolling2 = reduce(lambda x, y: x & y, condition_bb_rolling) + + dataframe.loc[condition_bb_rolling2, 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe[ + 'bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick41217.py b/StrategyPierrick41217.py new file mode 100644 index 0000000..3a2431e --- /dev/null +++ b/StrategyPierrick41217.py @@ -0,0 +1,350 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick41217(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.00, 0.18, decimals=2, default=0.065, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + # buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + # buy_min = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # buy_percent = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # volume à atteindre + buy_volume = DecimalParameter(0, 50, decimals=1, default=18, space="buy") + + buy_step = IntParameter(1, 8, default=3, space="buy") + buy_rolling = IntParameter(-20, 0, default=-6, space="buy") + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 1 + } + + # Stoploss: + stoploss = -1 + trailing_stop = False + trailing_stop_positive = 0.02 + trailing_stop_positive_offset = 0.0275 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + 'bb_min': {'color': 'red'}, + }, + # "ADX": { + # 'adx': {'color': 'white'}, + # 'minus_dm': {'color': 'blue'}, + # 'plus_dm': {'color': 'red'} + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + }, + "percent": { + "percent": {'color': 'green'}, + "percent5": {'color': 'red'} + } + } + } + + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + + # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + if current_candle['bb_width'] > 0.065: + print("use more stake", pair, " ", proposed_stake * 2) + return min(max_stake, proposed_stake * 2) + + if current_candle['bb_width'] > 0.045: + print("use more stake", pair, " ", proposed_stake * 1.5) + return min(max_stake, proposed_stake * 1.5) + + # if current_candle['bb_width'] < 0.020: + # print("use less stake", pair, " ", proposed_stake / 2) + # return min(max_stake, proposed_stake / 2) + # if self.config['stake_amount'] == 'unlimited': + # # Use entire available wallet during favorable conditions when in compounding mode. + # return max_stake + # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + + # Use default stake amount. + return proposed_stake + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # (last_candle['percent5'] < -0.005) \ + # if (0 < current_profit < 0.005) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 2): + # # & (previous_last_candle['sma10'] > last_candle['sma10']): + # print("too_small_gain", pair, trade, " profit=", current_profit, " rate=", current_rate, " percent5=", + # last_candle['percent5']) + # return 'too_small_gain' + + # if (current_profit < -0.05) \ + # & ((current_time - trade.open_date_utc).days >= 3): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'stop_loss_profit' + + # if (current_profit > 0.02) \ + # & (last_candle['percent'] < 0.01) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'lost_half_profit' + + if (current_profit > 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \ + & (previous_5_candle['sma20'] > last_candle['sma20']) \ + & (last_candle['percent'] < 0) \ + & (last_candle['percent5'] < 0): + print("over_bb_band_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_bb_band_sma20_desc' + + if (current_profit > 0) \ + & (last_candle['rsi'] > 75): + print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + # description trade + # Trade(id=0, pair=CAKE/USDT, amount=4.19815281, open_rate=11.91000000, open_since=2021-12-22 17:55:00) + # print(last_candle) + if 0.05 < current_profit < 1: + if ( + (previous_last_candle['sma10'] > last_candle['sma10'] * 1.005) & + (current_time - trade.open_date_utc).seconds >= 3600 * 3 + # ) | ( + # (current_time - trade.open_date_utc).seconds >= 3600 * 6 + ): + # self.lock_pair(pair, until=current_time + timedelta(hours=3)) + + print("profit_3h_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_3h_sma10_desc' + + if (0 < current_profit < 0.1) \ + & (previous_last_candle['sma20'] > last_candle['sma20']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 5): + print("profit_5h_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_5h_sma20_desc' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # dataframe['min'] = ta.MIN(dataframe) + # dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe["rolling"] = ( + 100 * (dataframe["close"] - dataframe["bb_lowerband"]) / dataframe["bb_lowerband"]).rolling( + 3).mean() + dataframe["bb_rolling"] = dataframe["rolling"] / dataframe["bb_width"] + dataframe['bb_rolling_min'] = ta.MIN(dataframe['bb_rolling'], timeperiod=10) + dataframe['bb_buy'] = (dataframe['min'] + (dataframe['max'] - dataframe['min']) / 3) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + step = self.buy_step.value + # if reduce(lambda x, y: x & y, dataframe['bb_width'] < 0.015): + # step = 5 + # else: + # if reduce(lambda x, y: x & y, dataframe['bb_width'] < 0.03): + # step = 4 + bb_rolling_max = self.buy_rolling.value + condition_bb_rolling_1 = [ + (dataframe['bb_width'] >= 0.035), + # (dataframe['close'] > dataframe['open']), + (dataframe['close'] < dataframe['sma10']), + (dataframe['bb_rolling_min'].shift(step) <= bb_rolling_max), + (dataframe['bb_rolling_min'].shift(step) >= dataframe['bb_rolling'].shift(step)), + (dataframe['close'].shift(step) < dataframe['min'].shift(step) + ( + dataframe['max'].shift(step) - dataframe['min'].shift(step)) / 3), + (dataframe['min'].shift(step) == dataframe['min']), + (dataframe['volume'] * dataframe['close'] / 1000 > 100), + # (dataframe['rsi'] <= 30) + ] + condition_bb_rolling1 = reduce(lambda x, y: x & y, condition_bb_rolling_1) + step = 2 + condition_bb_rolling_2 = [ + (dataframe['bb_width'] >= 0.04), + (dataframe['close'] < dataframe['sma10']), + ((dataframe['close'] > dataframe['open']) | (dataframe['percent'] > -0.01)), + # (dataframe['bb_rolling_min'].shift(step) <= bb_rolling_max), + # (dataframe['bb_rolling_min'].shift(step) >= dataframe['bb_rolling'].shift(step)), + (dataframe['close'].shift(step) < dataframe['min'].shift(step) + ( + dataframe['max'].shift(step) - dataframe['min'].shift(step)) / 3), + (dataframe['min'].shift(step) == dataframe['min']), + (dataframe['volume'] * dataframe['close'] / 1000 > 100), + (dataframe['rsi'] <= 30) + ] + condition_bb_rolling2 = reduce(lambda x, y: x & y, condition_bb_rolling_2) + + dataframe.loc[ + condition_bb_rolling1 | + condition_bb_rolling2, 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe[ + 'bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick41218.py b/StrategyPierrick41218.py new file mode 100644 index 0000000..89d7d6d --- /dev/null +++ b/StrategyPierrick41218.py @@ -0,0 +1,349 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime, timezone, timedelta + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick41218(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.00, 0.18, decimals=2, default=0.065, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + # buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + # buy_min = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # buy_percent = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # volume à atteindre + buy_volume = DecimalParameter(0, 50, decimals=1, default=18, space="buy") + + buy_step = IntParameter(1, 8, default=3, space="buy") + buy_rolling = IntParameter(-20, 0, default=-6, space="buy") + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = False + trailing_stop_positive = 0.02 + trailing_stop_positive_offset = 0.0275 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + # "ADX": { + # 'adx': {'color': 'white'}, + # 'minus_dm': {'color': 'blue'}, + # 'plus_dm': {'color': 'red'} + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + }, + "percent": { + "percent": {'color': 'green'}, + "percent5": {'color': 'red'} + } + } + } + + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + + # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + if current_candle['bb_width'] > 0.065: + print("use more stake", pair, " ", proposed_stake * 2) + return min(max_stake, proposed_stake * 2) + + if current_candle['bb_width'] > 0.045: + print("use more stake", pair, " ", proposed_stake * 1.5) + return min(max_stake, proposed_stake * 1.5) + + # if current_candle['bb_width'] < 0.020: + # print("use less stake", pair, " ", proposed_stake / 2) + # return min(max_stake, proposed_stake / 2) + # if self.config['stake_amount'] == 'unlimited': + # # Use entire available wallet during favorable conditions when in compounding mode. + # return max_stake + # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + + # Use default stake amount. + return proposed_stake + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # (last_candle['percent5'] < -0.005) \ + # if (0 < current_profit < 0.005) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 2): + # # & (previous_last_candle['sma10'] > last_candle['sma10']): + # print("too_small_gain", pair, trade, " profit=", current_profit, " rate=", current_rate, " percent5=", + # last_candle['percent5']) + # return 'too_small_gain' + + # if (current_profit < -0.05) \ + # & ((current_time - trade.open_date_utc).days >= 3): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'stop_loss_profit' + + # if (current_profit > 0.02) \ + # & (last_candle['percent'] < 0.01) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'lost_half_profit' + + if (current_profit > 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \ + & (previous_5_candle['sma20'] > last_candle['sma20']) \ + & (last_candle['percent'] < 0) \ + & (last_candle['percent5'] < 0): + print("over_bb_band_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_bb_band_sma20_desc' + + if (current_profit > 0) \ + & (last_candle['rsi'] > 75): + self.lock_pair(pair, until=datetime.now(timezone.utc) + timedelta(hours=6)) + print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + # description trade + # Trade(id=0, pair=CAKE/USDT, amount=4.19815281, open_rate=11.91000000, open_since=2021-12-22 17:55:00) + # print(last_candle) + if 0.05 < current_profit < 1: + if ( + (previous_last_candle['sma10'] > last_candle['sma10']) & + ((current_time - trade.open_date_utc).seconds >= 3600 * 3) & + (last_candle['percent'] < 0) & (last_candle['percent5'] < 0) & + (last_candle['close'] < last_candle['bb_width']) + ): + # self.lock_pair(pair, until=datetime.now(timezone.utc) + timedelta(hours=2)) + print("profit_3h_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate, ' percent5=', last_candle['percent5']) + return 'profit_3h_sma10_desc' + + if (0 < current_profit < 0.1) \ + & (previous_last_candle['sma20'] > last_candle['sma20']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 5)\ + & (last_candle['percent'] < 0) & (last_candle['percent5'] < 0) \ + & (last_candle['close'] < last_candle['bb_width']): + # self.lock_pair(pair, until=datetime.now(timezone.utc) + timedelta(hours=3)) + print("profit_5h_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate, + "percent5=", abs(last_candle['percent5'])) + return 'profit_5h_sma20_desc' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # dataframe['min'] = ta.MIN(dataframe) + # dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + dataframe["rolling"] = ( + 100 * (dataframe["close"] - dataframe["bb_lowerband"]) / dataframe["bb_lowerband"]).rolling( + 3).mean() + dataframe["bb_rolling"] = dataframe["rolling"] / dataframe["bb_width"] + dataframe['bb_rolling_min'] = ta.MIN(dataframe['bb_rolling'], timeperiod=10) + dataframe['bb_buy'] = (dataframe['min'] + (dataframe['max'] - dataframe['min']) / 3) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + step = self.buy_step.value + # if reduce(lambda x, y: x & y, dataframe['bb_width'] < 0.015): + # step = 5 + # else: + # if reduce(lambda x, y: x & y, dataframe['bb_width'] < 0.03): + # step = 4 + bb_rolling_max = self.buy_rolling.value + condition_bb_rolling_1 = [ + (dataframe['bb_width'] >= 0.035), + # (dataframe['close'] > dataframe['open']), + (dataframe['close'] < dataframe['sma10']), + (dataframe['bb_rolling_min'].shift(step) <= bb_rolling_max), + (dataframe['bb_rolling_min'].shift(step) >= dataframe['bb_rolling'].shift(step)), + (dataframe['close'].shift(step) < dataframe['min'].shift(step) + ( + dataframe['max'].shift(step) - dataframe['min'].shift(step)) / 3), + (dataframe['min'].shift(step) == dataframe['min']), + (dataframe['volume'] * dataframe['close'] / 1000 > 100), + # (dataframe['rsi'] <= 30) + ] + condition_bb_rolling1 = reduce(lambda x, y: x & y, condition_bb_rolling_1) + step = 2 + condition_bb_rolling_2 = [ + (dataframe['bb_width'] >= 0.04), + # (dataframe['percent5'] >= 0.01), + (dataframe['close'] < dataframe['sma10']), + ((dataframe['close'] > dataframe['open']) | (dataframe['percent'] > -0.01)), + # (dataframe['bb_rolling_min'].shift(step) <= bb_rolling_max), + # (dataframe['bb_rolling_min'].shift(step) >= dataframe['bb_rolling'].shift(step)), + (dataframe['close'].shift(step) < dataframe['min'].shift(step) + ( + dataframe['max'].shift(step) - dataframe['min'].shift(step)) / 3), + (dataframe['min'].shift(step) == dataframe['min']), + (dataframe['volume'] * dataframe['close'] / 1000 > 100), + (dataframe['rsi'] <= 30) + ] + condition_bb_rolling2 = reduce(lambda x, y: x & y, condition_bb_rolling_2) + + dataframe.loc[ + condition_bb_rolling1 | + condition_bb_rolling2, 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe[ + 'bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick41219.py b/StrategyPierrick41219.py new file mode 100644 index 0000000..9993de0 --- /dev/null +++ b/StrategyPierrick41219.py @@ -0,0 +1,378 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime, timezone, timedelta + +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +from freqtrade.persistence import Trade +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick41219(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.00, 0.18, decimals=2, default=0.065, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + # buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + # buy_min = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # buy_percent = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # volume à atteindre + buy_volume = DecimalParameter(0, 50, decimals=1, default=18, space="buy") + + buy_step = IntParameter(1, 8, default=3, space="buy") + buy_rolling = IntParameter(-20, 0, default=-6, space="buy") + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = False + trailing_stop_positive = 0.02 + trailing_stop_positive_offset = 0.0275 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + # "ADX": { + # 'adx': {'color': 'white'}, + # 'minus_dm': {'color': 'blue'}, + # 'plus_dm': {'color': 'red'} + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + }, + "percent": { + "percent": {'color': 'green'}, + "percent5": {'color': 'red'} + } + } + } + + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + + # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + if current_candle['bb_width'] > 0.065: + print("use more stake", pair, " ", proposed_stake * 2) + return min(max_stake, proposed_stake * 2) + + if current_candle['bb_width'] > 0.045: + print("use more stake", pair, " ", proposed_stake * 1.5) + return min(max_stake, proposed_stake * 1.5) + + # if current_candle['bb_width'] < 0.020: + # print("use less stake", pair, " ", proposed_stake / 2) + # return min(max_stake, proposed_stake / 2) + # if self.config['stake_amount'] == 'unlimited': + # # Use entire available wallet during favorable conditions when in compounding mode. + # return max_stake + # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + + # Use default stake amount. + return proposed_stake + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # (last_candle['percent5'] < -0.005) \ + # if (0 < current_profit < 0.005) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 2): + # # & (previous_last_candle['sma10'] > last_candle['sma10']): + # print("too_small_gain", pair, trade, " profit=", current_profit, " rate=", current_rate, " percent5=", + # last_candle['percent5']) + # return 'too_small_gain' + + # if (current_profit < -0.05) \ + # & ((current_time - trade.open_date_utc).days >= 3): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'stop_loss_profit' + + # if (current_profit > 0.025) \ + # & (last_candle['percent'] < 0) \ + # & ((current_time - trade.open_date_utc).seconds <= 1800): + # print("stop_quick_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'stop_quick_profit' + + if (current_profit > 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \ + & (previous_5_candle['sma20'] > last_candle['sma20']) \ + & (last_candle['percent'] < 0) \ + & (last_candle['percent5'] < 0): + print("over_bb_band_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_bb_band_sma20_desc' + + if (current_profit > 0) \ + & (last_candle['rsi'] > 79): + self.lock_pair(pair, until=datetime.now(timezone.utc) + timedelta(hours=6)) + print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + # description trade + # Trade(id=0, pair=CAKE/USDT, amount=4.19815281, open_rate=11.91000000, open_since=2021-12-22 17:55:00) + # print(last_candle) + if 0.05 < current_profit < 0.1: + if ( + (previous_last_candle['sma10'] > last_candle['sma10']) & + ((current_time - trade.open_date_utc).seconds >= 3600 * 3) & + (last_candle['percent'] < 0) & + (abs(last_candle['percent5']) > current_profit / 5) + ): + # self.lock_pair(pair, until=datetime.now(timezone.utc) + timedelta(hours=2)) + print("profit_3h_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate, ' percent5=', last_candle['percent5']) + return 'profit_3h_sma10_desc' + + if (0 < current_profit < 0.1) \ + & (previous_last_candle['sma20'] > last_candle['sma20']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 5)\ + & (last_candle['percent'] < 0) \ + & (abs(last_candle['percent5']) > current_profit / 5): + # self.lock_pair(pair, until=datetime.now(timezone.utc) + timedelta(hours=3)) + print("profit_5h_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_5h_sma20_desc' + + # if (-0.01 < current_profit < 0) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 10): + # # self.lock_pair(pair, until=datetime.now(timezone.utc) + timedelta(hours=3)) + # print("stop_wait_10h", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'stop_wait_10h' + # + # if (-0.02 < current_profit < -0.01) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 5): + # # self.lock_pair(pair, until=datetime.now(timezone.utc) + timedelta(hours=3)) + # print("stop_wait_5h", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'stop_wait_5h' + # + # if (current_profit < -0.02) : + # self.lock_pair(pair, until=datetime.now(timezone.utc) + timedelta(hours=3)) + # print("stop_big_loss", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'stop_big_loss' + + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # dataframe['min'] = ta.MIN(dataframe) + # dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + dataframe["rolling"] = ( + 100 * (dataframe["close"] - dataframe["bb_lowerband"]) / dataframe["bb_lowerband"]).rolling( + 3).mean() + dataframe["bb_rolling"] = dataframe["rolling"] / dataframe["bb_width"] + dataframe['bb_rolling_min'] = ta.MIN(dataframe['bb_rolling'], timeperiod=10) + dataframe['bb_buy'] = (dataframe['min'] + (dataframe['max'] - dataframe['min']) / 3) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # if self.config['runmode'].value in ('live', 'dry_run'): + # trades = Trade.get_trades([Trade.pair == metadata['pair'], + # Trade.open_date > datetime.utcnow() - timedelta(days=1), + # Trade.is_open.is_(False), + # ]).order_by(Trade.close_date).all() + # # Summarize profit for this pair. + # print("Trades=", trades) + # curdayprofit = sum(trade.close_profit for trade in trades) + + step = self.buy_step.value + # if reduce(lambda x, y: x & y, dataframe['bb_width'] > 0.065): + # step = 1 + # else: + # if reduce(lambda x, y: x & y, dataframe['bb_width'] > 0.045): + # step = 2 + + bb_rolling_max = self.buy_rolling.value + condition_bb_rolling_1 = [ + (dataframe['bb_width'] >= 0.025), + # (dataframe['close'] > dataframe['open']), + (dataframe['close'] < dataframe['sma10']), + (dataframe['bb_rolling_min'].shift(step) <= bb_rolling_max), + (dataframe['bb_rolling_min'].shift(step) >= dataframe['bb_rolling'].shift(step)), + (dataframe['close'].shift(step) < dataframe['min'].shift(step) + ( + dataframe['max'].shift(step) - dataframe['min'].shift(step)) / 3), + (dataframe['min'].shift(step) == dataframe['min']), + (dataframe['volume'] * dataframe['close'] / 1000 > 100), + # (dataframe['rsi'] <= 30) + ] + condition_bb_rolling1 = reduce(lambda x, y: x & y, condition_bb_rolling_1) + step = 2 + condition_bb_rolling_2 = [ + (dataframe['bb_width'] >= 0.04), + # (dataframe['percent5'] >= 0.01), + (dataframe['close'] < dataframe['sma10']), + ((dataframe['close'] > dataframe['open']) | (dataframe['percent'] > -0.01)), + # (dataframe['bb_rolling_min'].shift(step) <= bb_rolling_max), + # (dataframe['bb_rolling_min'].shift(step) >= dataframe['bb_rolling'].shift(step)), + (dataframe['close'].shift(step) < dataframe['min'].shift(step) + ( + dataframe['max'].shift(step) - dataframe['min'].shift(step)) / 3), + (dataframe['min'].shift(step) == dataframe['min']), + (dataframe['volume'] * dataframe['close'] / 1000 > 100), + (dataframe['rsi'] <= 30) + ] + condition_bb_rolling2 = reduce(lambda x, y: x & y, condition_bb_rolling_2) + + dataframe.loc[ + condition_bb_rolling1 | + condition_bb_rolling2, 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe[ + 'bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + # ) | ( + # (dataframe['close'] * 1.04 < dataframe['open']) + ), 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick4122.json b/StrategyPierrick4122.json new file mode 100644 index 0000000..f2b59f5 --- /dev/null +++ b/StrategyPierrick4122.json @@ -0,0 +1,31 @@ +{ + "strategy_name": "StrategyPierrick4122", + "params": { + "buy": { + "buy_bollinger": 0.04, + "buy_bollinger_max": 0.03, + "buy_bollinger_min": 0.02, + "buy_sma100": 0.96, + "buy_volume": 14.2 + }, + "sell": {}, + "protection": {}, + "roi": { + "0": 0.11399999999999999, + "40": 0.08299999999999999, + "54": 0.04, + "64": 0 + }, + "stoploss": { + "stoploss": -0.191 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.062, + "trailing_stop_positive_offset": 0.10400000000000001, + "trailing_only_offset_is_reached": false + } + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-19 18:19:33.181406+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick4122.py b/StrategyPierrick4122.py new file mode 100644 index 0000000..2e80860 --- /dev/null +++ b/StrategyPierrick4122.py @@ -0,0 +1,289 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick4122(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.09, space="buy") + + buy_bollinger_min = DecimalParameter(0.0, 0.06, decimals=2, default=0.02, space="buy") + buy_bollinger_max = DecimalParameter(0.02, 0.09, decimals=2, default=0.04, space="buy") + + buy_sma100 = DecimalParameter(0.94, 1.2, decimals=2, default=1.0, space="buy") + + + # Valeur de la deuxième condition bollinger avec condition sma200 + # buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + # buy_min = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # buy_percent = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # volume à atteindre + buy_volume = DecimalParameter(0, 50, decimals=1, default=18, space="buy") + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -0.1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + # 'sma500': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['adx'] = ta.ADX(dataframe) + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + dataframe["rolling"] = (100 * (dataframe["close"] - dataframe["bb_lowerband"]) / dataframe["bb_lowerband"]).rolling(5).mean() + dataframe["bb_rolling"] = dataframe["rolling"] / dataframe["bb_width"] + dataframe['bb_rolling_min'] = ta.MIN(dataframe['bb_rolling'], timeperiod=5) + + # print(dataframe["rolling"].tolist()) + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + condition_bb_rolling_1 = [ + (dataframe['bb_width'].shift(3) >= self.buy_bollinger.value), + (dataframe['volume'].shift(3) * dataframe['close'] / 1000 >= 100), # self.buy_volume.value * 1000), + (dataframe['open'] < dataframe['close']), + (dataframe['open'] * 1.02 < dataframe['close']), + # (dataframe['close'] <= dataframe['sma100'] * 1.01), + (dataframe['close'].shift(1) <= dataframe['bb_lowerband'].shift(1)) | + (dataframe['close'].shift(2) <= dataframe['bb_lowerband'].shift(2)) | + (dataframe['close'].shift(3) <= dataframe['bb_lowerband'].shift(3)), + ] + condition_bb_rolling1 = reduce(lambda x, y: x & y, condition_bb_rolling_1) + condition_bb_rolling_2 = [ + # En dessous de la moyenne 100 + (dataframe['open'] < dataframe['sma100'] * self.buy_sma100.value), + # Sma 10 monte + (dataframe['sma10'] > dataframe['sma10'].shift(2)), + # Chandelle courante et précédente positives + (dataframe['open'] < dataframe['close']), + (dataframe['open'].shift(1) < dataframe['close'].shift(1)), + # 3/6 chandelles précédentes sur bbwidth pincée + (dataframe['bb_width'].shift(2) > self.buy_bollinger_min.value), + (dataframe['bb_width'].shift(4) > self.buy_bollinger_min.value), + (dataframe['bb_width'].shift(6) > self.buy_bollinger_min.value), + (dataframe['bb_width'].shift(2) < self.buy_bollinger_max.value), + (dataframe['bb_width'].shift(4) < self.buy_bollinger_max.value), + (dataframe['bb_width'].shift(6) < self.buy_bollinger_max.value), + # (dataframe['open'] * 1.02 < dataframe['close']), + # (dataframe['open'].shift(1) * 1.02 < dataframe['close'].shift(1)), + # hausse courante et précédente < 2% + (dataframe['percent'] < 0.02), + (dataframe['percent'].shift(1) < 0.015), + # Chandelle courante et précédente frôle upperband + (dataframe['close'] <= dataframe['bb_upperband']), + (dataframe['close'] * 1.002 >= dataframe['bb_upperband']), + (dataframe['close'].shift(1) <= dataframe['bb_upperband'].shift(1)), + (dataframe['close'].shift(1) * 1.002 >= dataframe['bb_upperband'].shift(1)), + + ] + condition_bb_rolling2 = reduce(lambda x, y: x & y, condition_bb_rolling_2) + + condition_bb_rolling_inf = [ + False, + (dataframe['bb_width'] >= 0.04), + (dataframe['volume'] * dataframe['close'] / 1000 >= 100), #>= self.buy_volume.value * 1000), + (dataframe['open'] < dataframe['close']), + (dataframe['open'] * 1.02 < dataframe['close']), + # (dataframe['close'] <= dataframe['min'] * 1.01), + (dataframe['close'] * 1.04) <= dataframe['sma100'], + (dataframe['close'].shift(1) <= dataframe['bb_lowerband'].shift(1)) + ] + condition_bb_rolling_inf2 = reduce(lambda x, y: x & y, condition_bb_rolling_inf) + + dataframe.loc[ + ( + # condition_bb_rolling1 + condition_bb_rolling2 + # condition_bb_rolling_inf2 + ), 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick41220.py b/StrategyPierrick41220.py new file mode 100644 index 0000000..32b9788 --- /dev/null +++ b/StrategyPierrick41220.py @@ -0,0 +1,410 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime, timezone, timedelta + +import numpy +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick41220(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.00, 0.18, decimals=2, default=0.065, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + # buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + # buy_min = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # buy_percent = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # volume à atteindre + buy_volume = DecimalParameter(0, 50, decimals=1, default=18, space="buy") + + buy_step = IntParameter(1, 8, default=3, space="buy") + buy_rolling = IntParameter(-20, 0, default=-6, space="buy") + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = False + trailing_stop_positive = 0.02 + trailing_stop_positive_offset = 0.0275 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'min20': {'color': 'pink'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + 'bb_min': {'color': 'red'}, + }, + # "ADX": { + # 'adx': {'color': 'white'}, + # 'minus_dm': {'color': 'blue'}, + # 'plus_dm': {'color': 'red'} + # }, + "Rsi": { + 'rsi': {'color': 'pink'}, + 'rsi3': {'color': 'blue'}, + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + }, + "percent": { + "percent": {'color': 'green'}, + "percent5": {'color': 'red'}, + "percent20": {'color': 'blue'}, + "pente": {'color': 'yellow'} + } + } + } + + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + + # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + if current_candle['bb_width'] > 0.065: + print("use more stake", pair, " ", proposed_stake * 2) + return min(max_stake, proposed_stake * 2) + + if current_candle['bb_width'] > 0.045: + print("use more stake", pair, " ", proposed_stake * 1.5) + return min(max_stake, proposed_stake * 1.5) + + # if current_candle['bb_width'] < 0.020: + # print("use less stake", pair, " ", proposed_stake / 2) + # return min(max_stake, proposed_stake / 2) + # if self.config['stake_amount'] == 'unlimited': + # # Use entire available wallet during favorable conditions when in compounding mode. + # return max_stake + # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + + # Use default stake amount. + return proposed_stake + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # (last_candle['percent5'] < -0.005) \ + # if (0 < current_profit < 0.005) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 2): + # # & (previous_last_candle['sma10'] > last_candle['sma10']): + # print("too_small_gain", pair, trade, " profit=", current_profit, " rate=", current_rate, " percent5=", + # last_candle['percent5']) + # return 'too_small_gain' + + # if (current_profit < -0.05) \ + # & ((current_time - trade.open_date_utc).days >= 3): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'stop_loss_profit' + + # if (current_profit > 0.02) \ + # & (last_candle['percent'] < 0.01) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'lost_half_profit' + + if (current_profit > 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \ + & (previous_5_candle['sma20'] > last_candle['sma20']) \ + & (last_candle['percent'] < 0) \ + & (last_candle['percent5'] < 0): + # self.lock_pair(pair, until=current_time + timedelta(hours=3)) + print("over_bb_band_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_bb_band_sma20_desc' + + if (current_profit > 0) \ + & (last_candle['rsi'] > 75): + print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + # description trade + # Trade(id=0, pair=CAKE/USDT, amount=4.19815281, open_rate=11.91000000, open_since=2021-12-22 17:55:00) + # print(last_candle) + if 0.05 < current_profit < 1: + if ( + (previous_last_candle['sma10'] > last_candle['sma10'] * 1.005) & + (current_time - trade.open_date_utc).seconds >= 3600 * 3 + # ) | ( + # (current_time - trade.open_date_utc).seconds >= 3600 * 6 + ): + # self.lock_pair(pair, until=current_time + timedelta(hours=3)) + + print("profit_3h_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_3h_sma10_desc' + + if (0 < current_profit < 0.1) \ + & (previous_last_candle['sma20'] > last_candle['sma20']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 5): + print("profit_5h_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_5h_sma20_desc' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # dataframe['min'] = ta.MIN(dataframe) + # dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + dataframe["rsi3"] = (dataframe['rsi']).rolling(3).mean() + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + + dataframe['pente'] = ((dataframe['sma20'] - dataframe['sma20'].shift(1)) / dataframe['sma20'].shift(1)) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe["rolling"] = ( + 100 * (dataframe["close"] - dataframe["bb_lowerband"]) / dataframe["bb_lowerband"]).rolling( + 3).mean() + dataframe["bb_rolling"] = dataframe["rolling"] / dataframe["bb_width"] + dataframe['bb_rolling_min'] = ta.MIN(dataframe['bb_rolling'], timeperiod=10) + dataframe['bb_buy'] = (dataframe['min'] + (dataframe['max'] - dataframe['min']) / 3) + + # # Stoch + # stoch = ta.STOCH(dataframe) + # dataframe['slowk'] = stoch['slowk'] + # + # # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy) + # rsi = 0.1 * (dataframe['rsi'] - 50) + # dataframe['fisher_rsi'] = (numpy.exp(2 * rsi) - 1) / (numpy.exp(2 * rsi) + 1) + # + # # SAR Parabol + # dataframe['sar'] = ta.SAR(dataframe) + # + # # Hammer: values [0, 100] + # dataframe['CDLHAMMER'] = ta.CDLHAMMER(dataframe) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + step = self.buy_step.value + # if reduce(lambda x, y: x & y, dataframe['bb_width'] < 0.015): + # step = 5 + # else: + # if reduce(lambda x, y: x & y, dataframe['bb_width'] < 0.03): + # step = 4 + bb_rolling_max = self.buy_rolling.value + condition_bb_rolling_1 = [ + (dataframe['bb_width'] >= 0.035), + # (dataframe['close'] > dataframe['open']), + (dataframe['close'] < dataframe['sma10']), + # (dataframe['bb_rolling_min'].shift(step) <= bb_rolling_max), + # (dataframe['bb_rolling_min'].shift(step) >= dataframe['bb_rolling'].shift(step)), + (dataframe['close'].shift(step) < dataframe['min'].shift(step) + ( + dataframe['max'].shift(step) - dataframe['min'].shift(step)) / 3), + (dataframe['min'].shift(step) == dataframe['min']), + (dataframe['volume'] * dataframe['close'] / 1000 > 100), + # (dataframe['rsi'] <= 30) + ] + condition_bb_rolling1 = reduce(lambda x, y: x & y, condition_bb_rolling_1) + step = 2 + condition_bb_rolling_2 = [ + (dataframe['bb_width'] >= 0.04), + (dataframe['close'] < dataframe['sma10']), + ((dataframe['close'] > dataframe['open']) | (dataframe['percent'] > -0.01)), + # (dataframe['bb_rolling_min'].shift(step) <= bb_rolling_max), + # (dataframe['bb_rolling_min'].shift(step) >= dataframe['bb_rolling'].shift(step)), + (dataframe['close'].shift(step) < dataframe['min'].shift(step) + ( + dataframe['max'].shift(step) - dataframe['min'].shift(step)) / 3), + (dataframe['min'].shift(step) == dataframe['min']), + (dataframe['volume'] * dataframe['close'] / 1000 > 100), + (dataframe['rsi'] <= 30) + ] + condition_bb_rolling2 = reduce(lambda x, y: x & y, condition_bb_rolling_2) + + dataframe.loc[ + condition_bb_rolling1 | + condition_bb_rolling2, 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + condition_sell_1 = [ + (dataframe['close'] < dataframe['open']), + (dataframe['close'].shift(1) < dataframe['open'].shift(1)), + (dataframe['close'].shift(2) < dataframe['open'].shift(2)), + (dataframe['close'] < dataframe['bb_lowerband']), + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe[ + 'bb_lowerband']) >= 0.02), + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + (dataframe['rsi'] <= 40) + ] + condition_sell1 = reduce(lambda x, y: x & y, condition_sell_1) + # condition = np.where(condition_sell1, 'True', 'False') + # if bool(condition_sell_1): + # # print('condition_sell1=', condition_sell1) + # self.lock_pair(metadata['pair'], until=datetime.now(timezone.utc) + timedelta(hours=3)) + + dataframe.loc[ + ( + condition_sell1 + # ) | ( + # (dataframe['close'] < dataframe['bb_lowerband']) & + # StrategyHelperLocal.red_candles(dataframe) & + # (dataframe['percent5'] < -0.04) + ), 'sell'] = 1 + + return dataframe + + +class StrategyHelperLocal: + @staticmethod + def red_candles(dataframe, shift=0): + """ + evaluates if we are having 8 red candles in a row + :param self: + :param dataframe: + :param shift: shift the pattern by n + :return: + """ + return ( + (dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) & + (dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) & + (dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) & + (dataframe['open'].shift(3 + shift) > dataframe['close'].shift(3 + shift)) & + (dataframe['open'].shift(4 + shift) > dataframe['close'].shift(4 + shift)) & + (dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) & + (dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) & + (dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) + + ) diff --git a/StrategyPierrick41221.py b/StrategyPierrick41221.py new file mode 100644 index 0000000..0ef5de8 --- /dev/null +++ b/StrategyPierrick41221.py @@ -0,0 +1,453 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick41221(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.00, 0.18, decimals=2, default=0.075, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + # buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + # buy_min = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # buy_percent = DecimalParameter(1, 1.1, decimals=2, default=1.01, space="buy") + # volume à atteindre + buy_volume = DecimalParameter(0, 50, decimals=1, default=18, space="buy") + + buy_step = IntParameter(1, 8, default=3, space="buy") + buy_rolling = IntParameter(-20, 0, default=-6, space="buy") + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + "0": 100 + # "0": 0.015, + # "5": 0.01, + # "10": 0.005, + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.02 + trailing_stop_positive_offset = 0.0275 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '1m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'green'}, + 'bb_min': {'color': 'red'}, + }, + "Volume": { + 'volume5': {'color': 'yellow'}, + }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + }, + "percent": { + "percent": {'color': 'green'}, + "percent5": {'color': 'red'} + } + } + } + trades = list() + + # buy_base = DecimalParameter(0, 0.2, decimals=2, default=0.10, space='buy') + # buy_diff = DecimalParameter(0, 0.2, decimals=2, default=0.20, space='buy') + # + # buy_rsi = IntParameter(20, 80, default=42, space='buy') + # buy_rsi_max = IntParameter(60, 90, default=60, space='buy') + # + # buy_rsi_1h = IntParameter(20, 80, default=72, space='buy') + # buy_rsi_max_1h = IntParameter(60, 80, default=72, space='buy') + # + # buy_min_horizon = IntParameter(50, 200, default=72, space='buy') + # + # buy_0_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + # buy_2_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + # buy_3_percent20 = DecimalParameter(-0.1, 0.1, decimals=2, default=-0.02, space='buy') + # + # buy_0_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + # buy_2_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + # buy_3_distance = DecimalParameter(-0.1, 0.1, decimals=2, default=0.02, space='buy') + # + # buy_decalage_deb_0 = IntParameter(0, 3, default=5, space='buy') + # buy_decalage_deb_2 = IntParameter(0, 3, default=5, space='buy') + # buy_decalage_deb_3 = IntParameter(0, 3, default=5, space='buy') + # + # buy_real_num0 = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + # buy_real_num1 = DecimalParameter(0, 1, decimals=2, default=0.67, space='buy') + # buy_real_num2 = DecimalParameter(0, 2, decimals=2, default=0.67, space='buy') + # + # buy_decalage0 = IntParameter(buy_decalage_deb_0.value + 1, 8, default=5, space='buy') + # buy_decalage2 = IntParameter(buy_decalage_deb_2.value + 1, 8, default=5, space='buy') + # buy_decalage3 = IntParameter(buy_decalage_deb_3.value + 1, 8, default=5, space='buy') + # + # buy_1_bb_lower_5 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + # buy_2_bb_lower_5 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + # buy_3_bb_lower_5 = DecimalParameter(0, 0.6, decimals=2, default=0.7, space='buy') + # + # buy_b_real = DecimalParameter(0.001, 0.999, decimals=4, default=0.11908, space='buy') + # buy_b_cat = CategoricalParameter([">R", "=R", "R", "=R", " 0.015) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + & (days < self.sell_b_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > last_candle['bb_width'] / 1.3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # dataframe['min'] = ta.MIN(dataframe) + # dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_5'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_max200_5'] = (dataframe['min200'] * (1 + dataframe['min_max200'] / 5)) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['volume5'] = dataframe["volume"].rolling(5).mean() + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # conditions = [ + # dataframe['bb_width'] >= self.buy_bollinger.value, + # (dataframe['close'] < dataframe['bb_lowerband']), + # (dataframe['close'] < dataframe['sma100'] * 0.99) #self.buy_sma_percent.value) + # ] + conditions_vol = [] + conditions_vol.append(dataframe['percent5'] > 0) #self.buy_bollinger.value, + conditions_vol.append(dataframe['percent'].shift(1) > 0) + conditions_vol.append(dataframe['percent'] > 2 * dataframe['percent'].shift(1)) + conditions_vol.append(dataframe['close'] > dataframe['open']) + # conditions_vol.append(dataframe['bb_width'] > dataframe['bb_width'].shift(3) * 2) + + for decalage in range(5, 10): + conditions_vol.append(dataframe['bb_width'].shift(decalage) <= dataframe['bb_width'].shift(decalage - 1)) + # conditions_vol.append(-0.002 <= dataframe['percent'].shift(decalage)) + # conditions_vol.append(dataframe['percent'].shift(decalage) <= 0.002) + + for decalage in range(2, 5): + conditions_vol.append(dataframe['volume5'].shift(decalage) <= dataframe['volume5'].shift(decalage - 1)) + conditions_vol.append(dataframe['percent'].shift(decalage) <= dataframe['percent'].shift(decalage - 1)) + + # GUARDS AND TRENDS + + dataframe.loc[ + ( + # (reduce(lambda x, y: x & y, conditions)) + (reduce(lambda x, y: x & y, conditions_vol)) + ) + , + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['close'] < dataframe['open']) & + # (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + # (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + # (dataframe['close'] < dataframe['bb_lowerband']) & + # (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) & + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + # ), 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick41222.py b/StrategyPierrick41222.py new file mode 100644 index 0000000..ae47a8a --- /dev/null +++ b/StrategyPierrick41222.py @@ -0,0 +1,312 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick41222(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + # pourcentage sma à dépasser + buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.97, space="buy") + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = False + trailing_stop_positive = 0.02 + trailing_stop_positive_offset = 0.0275 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'green'}, + 'bb_min': {'color': 'red'}, + }, + "Volume": { + 'volume5': {'color': 'yellow'}, + }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + }, + "percent": { + "percent": {'color': 'green'}, + "percent5": {'color': 'red'} + } + } + } + + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + + # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + if current_candle['bb_width'] > 0.012: + print("use more stake", pair, " ", proposed_stake * 2) + return min(max_stake, proposed_stake * 2) + + if current_candle['bb_width'] > 0.09: + print("use more stake", pair, " ", proposed_stake * 1.5) + return min(max_stake, proposed_stake * 1.5) + + # if current_candle['bb_width'] < 0.020: + # print("use less stake", pair, " ", proposed_stake / 2) + # return min(max_stake, proposed_stake / 2) + # if self.config['stake_amount'] == 'unlimited': + # # Use entire available wallet during favorable conditions when in compounding mode. + # return max_stake + # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + + # Use default stake amount. + return proposed_stake + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # (last_candle['percent5'] < -0.005) \ + # if (0 < current_profit < 0.005) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 2): + # # & (previous_last_candle['sma10'] > last_candle['sma10']): + # print("too_small_gain", pair, trade, " profit=", current_profit, " rate=", current_rate, " percent5=", + # last_candle['percent5']) + # return 'too_small_gain' + + # if (current_profit < -0.05) \ + # & ((current_time - trade.open_date_utc).days >= 3): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'stop_loss_profit' + + # if (current_profit > 0.02) \ + # & (last_candle['percent'] < 0.01) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'lost_half_profit' + + if (current_profit > 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \ + & (previous_5_candle['sma20'] > last_candle['sma20']) \ + & (last_candle['percent'] < 0) \ + & (last_candle['percent5'] < 0): + print("over_bb_band_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_bb_band_sma20_desc' + + if (current_profit > 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & (last_candle['rsi'] > 75): + print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + # description trade + # Trade(id=0, pair=CAKE/USDT, amount=4.19815281, open_rate=11.91000000, open_since=2021-12-22 17:55:00) + # print(last_candle) + if 0.05 < current_profit < 1: + if ( + (previous_last_candle['sma10'] > last_candle['sma10'] * 1.005) & + (current_time - trade.open_date_utc).seconds >= 3600 * 3 + # ) | ( + # (current_time - trade.open_date_utc).seconds >= 3600 * 6 + ): + self.lock_pair(pair, until=current_time + timedelta(hours=3)) + + print("profit_3h_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_3h_sma10_desc' + + if (0 < current_profit < 0.1) \ + & (previous_last_candle['sma20'] > last_candle['sma20']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 5): + print("profit_5h_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_5h_sma20_desc' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # dataframe['min'] = ta.MIN(dataframe) + # dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + dataframe['volume5'] = dataframe["volume"].rolling(5).mean() + + dataframe["rolling"] = ( + 100 * (dataframe["close"] - dataframe["bb_lowerband"]) / dataframe["bb_lowerband"]).rolling( + 3).mean() + dataframe["bb_rolling"] = dataframe["rolling"] / dataframe["bb_width"] + dataframe['bb_rolling_min'] = ta.MIN(dataframe['bb_rolling'], timeperiod=10) + dataframe['bb_buy'] = (dataframe['min'] + (dataframe['max'] - dataframe['min']) / 3) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [ + dataframe['bb_width'] >= self.buy_bollinger.value, + (dataframe['close'] < dataframe['bb_lowerband']), + (dataframe['close'] < dataframe['sma100'] * self.buy_sma_percent.value) + ] + # GUARDS AND TRENDS + + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick41223.py b/StrategyPierrick41223.py new file mode 100644 index 0000000..eb3b2e3 --- /dev/null +++ b/StrategyPierrick41223.py @@ -0,0 +1,355 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +from datetime import datetime + +import numpy +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick41223(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.07, space="buy") + # pourcentage sma à dépasser + buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.99, space="buy") + buy_rolling = IntParameter(-20, 0, default=-6, space="buy") + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = False + trailing_stop_positive = 0.02 + trailing_stop_positive_offset = 0.0275 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'red'}, + 'bb_upperband': {'color': 'green'}, + 'sma100': {'color': 'blue'}, + 'sma10': {'color': 'yellow'}, + 'min': {'color': 'white'}, + 'max': {'color': 'white'}, + 'sma20': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'green'}, + 'bb_min': {'color': 'red'}, + }, + "Volume": { + 'volume5': {'color': 'yellow'}, + }, + "Rsi": { + 'rsi': {'color': 'pink'}, + }, + "rolling": { + 'bb_rolling': {'color': '#87e470'}, + "bb_rolling_min": {'color': '#ac3e2a'} + }, + "percent": { + "percent": {'color': 'green'}, + "percent5": {'color': 'red'} + } + } + } + + @property + def protections(self): + return [ + { + "method": "CooldownPeriod", + "stop_duration_candles": 5 + }, + { + "method": "MaxDrawdown", + "lookback_period_candles": 48, + "trade_limit": 20, + "stop_duration_candles": 4, + "max_allowed_drawdown": 0.2 + }, + { + "method": "StoplossGuard", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": 2, + "only_per_pair": False + }, + { + "method": "LowProfitPairs", + "lookback_period_candles": 6, + "trade_limit": 2, + "stop_duration_candles": 60, + "required_profit": 0.02 + }, + { + "method": "LowProfitPairs", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": 2, + "required_profit": 0.01 + } + ] + + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + + # print("proposed_stake=", proposed_stake, " max_stake=", max_stake) + if current_candle['bb_width'] > 0.012: + print("use more stake", pair, " ", proposed_stake * 2) + return min(max_stake, proposed_stake * 2) + + if current_candle['bb_width'] > 0.09: + print("use more stake", pair, " ", proposed_stake * 1.5) + return min(max_stake, proposed_stake * 1.5) + + # if current_candle['bb_width'] < 0.020: + # print("use less stake", pair, " ", proposed_stake / 2) + # return min(max_stake, proposed_stake / 2) + # if self.config['stake_amount'] == 'unlimited': + # # Use entire available wallet during favorable conditions when in compounding mode. + # return max_stake + # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + + # Use default stake amount. + return proposed_stake + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + # (last_candle['percent5'] < -0.005) \ + # if (0 < current_profit < 0.005) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600 * 2): + # # & (previous_last_candle['sma10'] > last_candle['sma10']): + # print("too_small_gain", pair, trade, " profit=", current_profit, " rate=", current_rate, " percent5=", + # last_candle['percent5']) + # return 'too_small_gain' + + # if (current_profit < -0.05) \ + # & ((current_time - trade.open_date_utc).days >= 3): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'stop_loss_profit' + + # if (current_profit > 0.02) \ + # & (last_candle['percent'] < 0.01) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600): + # print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'lost_half_profit' + + if (current_profit > 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \ + & (previous_5_candle['sma20'] > last_candle['sma20']) \ + & (last_candle['percent'] < 0) \ + & (last_candle['percent5'] < 0): + print("over_bb_band_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_bb_band_sma20_desc' + + if (current_profit > 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & (last_candle['rsi'] > 75): + print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + # description trade + # Trade(id=0, pair=CAKE/USDT, amount=4.19815281, open_rate=11.91000000, open_since=2021-12-22 17:55:00) + # print(last_candle) + if 0.05 < current_profit < 1: + if ( + (previous_last_candle['sma10'] > last_candle['sma10'] * 1.005) & + (current_time - trade.open_date_utc).seconds >= 3600 * 3 + # ) | ( + # (current_time - trade.open_date_utc).seconds >= 3600 * 6 + ): + # self.lock_pair(pair, until=current_time + timedelta(hours=3)) + + print("profit_3h_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_3h_sma10_desc' + + if (0 < current_profit < 0.1) \ + & (previous_last_candle['sma20'] > last_candle['sma20']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600 * 5): + print("profit_5h_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'profit_5h_sma20_desc' + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # # Plus Directional Indicator / Movement + # dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + # dataframe['plus_di'] = ta.PLUS_DI(dataframe) + # + # # Minus Directional Indicator / Movement + # dataframe['adx'] = ta.ADX(dataframe) + # dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + # dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + # dataframe['min'] = ta.MIN(dataframe) + # dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + # dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + # dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + # dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + # dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200) + dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20) + + dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200) + dataframe['max_min'] = dataframe['max'] / dataframe['min'] + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + dataframe['volume5'] = dataframe["volume"].rolling(5).mean() + + dataframe["rolling"] = ( + 100 * (dataframe["close"] - dataframe["bb_lowerband"]) / dataframe["bb_lowerband"]).rolling( + 3).mean() + dataframe["bb_rolling"] = dataframe["rolling"] / dataframe["bb_width"] + dataframe['bb_rolling_min'] = ta.MIN(dataframe['bb_rolling'], timeperiod=10) + dataframe['bb_buy'] = (dataframe['min'] + (dataframe['max'] - dataframe['min']) / 3) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [ + # dataframe['bb_width'] >= self.buy_bollinger.value, + (dataframe['close'] < dataframe['bb_lowerband']), + (dataframe['close'] <= dataframe['min']), + (dataframe['close'] < dataframe['sma100'] * self.buy_sma_percent.value), + (dataframe['bb_rolling'] <= self.buy_rolling.value), + # (dataframe['bb_rolling_min'] >= dataframe['bb_rolling']), + (dataframe['volume'] * dataframe['close'] / 1000 > 100), + (dataframe['rsi'] < 27) + ] + # GUARDS AND TRENDS + + if conditions: + dataframe.loc[(reduce(lambda x, y: x & y, conditions)), 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick413.json b/StrategyPierrick413.json new file mode 100644 index 0000000..061f110 --- /dev/null +++ b/StrategyPierrick413.json @@ -0,0 +1,37 @@ +{ + "strategy_name": "StrategyPierrick413", + "params": { + "roi": { + "0": 0.096, + "30": 0.049, + "60": 0.032, + "120": 0 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": true, + "trailing_stop_positive": 0.001, + "trailing_stop_positive_offset": 0.0175, + "trailing_only_offset_is_reached": true + }, + "buy": { + "buy_adx": 8, + "buy_bollinger": 0.08, + "buy_bollinger_2": 0.08, + "buy_bollinger_2_enabled": false, + "buy_bollinger_enabled": true, + "buy_plusdi": 0.7251500178901511, + "buy_rsi": 37, + "buy_sma_percent": 0.98, + "buy_sma_percent_enabled": false, + "buy_volume": 21.4, + "buy_volume_enabled": true + }, + "sell": {}, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2021-12-08 19:24:09.461327+00:00" +} \ No newline at end of file diff --git a/StrategyPierrick413.py b/StrategyPierrick413.py new file mode 100644 index 0000000..0fd9250 --- /dev/null +++ b/StrategyPierrick413.py @@ -0,0 +1,267 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter, RealParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick413(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + buy_bollinger = DecimalParameter(0.025, 0.125, decimals=2, default=0.095, space="buy") + buy_bollinger_enabled = BooleanParameter(default=True, space="buy") + # Valeur de la deuxième condition bollinger avec condition sma200 + buy_bollinger_2 = DecimalParameter(0.0, 0.08, decimals=2, default=0.04, space="buy") + buy_bollinger_2_enabled = BooleanParameter(default=True, space="buy") + # pourcentage sma à dépasser + buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.098, space="buy") + buy_sma_percent_enabled = BooleanParameter(default=True, space="buy") + # volume à atteindre + buy_volume = DecimalParameter(0, 50, decimals=1, default=0, space="buy") + buy_volume_enabled = BooleanParameter(default=True, space="buy") + + buy_rsi = IntParameter([0, 50], default=30, space='buy') + buy_adx = IntParameter([0, 100], default=50, space='buy') + buy_plusdi = RealParameter(low=0, high=1, default=0.5, space='buy') + + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + 'rsi': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['adx'] = ta.ADX(dataframe) + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + + condition1 = np.where(value >= 1.04, True, False) + + conditions = [] + # GUARDS AND TRENDS + if self.buy_bollinger_enabled.value: + conditions.append(dataframe['bb_width'] >= self.buy_bollinger.value) + + conditions2 = [] + if self.buy_bollinger_2_enabled.value: + conditions2.append(dataframe['bb_width'] >= self.buy_bollinger_2.value) + + conditions_volume = [] + condition_volume = True + if self.buy_volume_enabled.value: + conditions_volume.append(dataframe['volume'] >= self.buy_volume.value * 1000) + if conditions_volume: + condition_volume = np.where(conditions_volume, True, False) + + condition2 = False + if conditions2: + condition2 = reduce(lambda x, y: x & y, conditions2) & condition1 + + condition_sma = False + conditions_sma = [] + if self.buy_sma_percent_enabled.value: + # conditions_sma.append(dataframe['close'] <= dataframe['sma200'] * self.buy_sma_percent.value) + conditions_sma.append((dataframe['sma100'].shift(36) - dataframe['sma100']) / dataframe['sma100'] > self.buy_sma_percent.value) + if conditions_sma: + condition_sma = reduce(lambda x, y: x & y, conditions_sma) + + if conditions: + dataframe.loc[ + ( + ( + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) < 0.025) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (reduce(lambda x, y: x & y, conditions) | (condition2 & condition_sma)) & + (dataframe['rsi'] < self.buy_rsi.value) & + (dataframe['adx'] > self.buy_adx.value) & + (dataframe['plus_di'] > self.buy_plusdi.value) + ) + ) & + condition_volume, + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(1) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.015) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), + + 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick415.py b/StrategyPierrick415.py new file mode 100644 index 0000000..4a1f898 --- /dev/null +++ b/StrategyPierrick415.py @@ -0,0 +1,231 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick415(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + # buy_bollinger = DecimalParameter(0.05, 0.14, decimals=2, default=0.095, space="buy") + # pourcentage sma à dépasser + # buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.98, space="buy") + # volume à atteindre + # buy_volume = DecimalParameter(0, 50, decimals=1, default=0, space="buy") + + # buy_close = DecimalParameter(0.95, 1.05, decimals=2, default=0, space="buy") + # buy_sma = IntParameter(1, 40, default=10, space="buy") + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + 'rsi': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['adx'] = ta.ADX(dataframe) + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + + # conditions = [] + # conditions.append(dataframe['bb_width'] >= self.buy_bollinger.value) + # + # if conditions: + dataframe.loc[ + ( + # (dataframe['open'] <= dataframe['bb_upperband']) & + (dataframe['close'] > dataframe['bb_upperband']) & + (dataframe['close'] > dataframe['open'] * 1.04) + # (dataframe['sma100'] < dataframe['sma500']) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), + + 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick4151.py b/StrategyPierrick4151.py new file mode 100644 index 0000000..cdf2fae --- /dev/null +++ b/StrategyPierrick4151.py @@ -0,0 +1,231 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick4151(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + # buy_bollinger = DecimalParameter(0.05, 0.14, decimals=2, default=0.095, space="buy") + # pourcentage sma à dépasser + # buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.98, space="buy") + # volume à atteindre + # buy_volume = DecimalParameter(0, 50, decimals=1, default=0, space="buy") + + # buy_close = DecimalParameter(0.95, 1.05, decimals=2, default=0, space="buy") + # buy_sma = IntParameter(1, 40, default=10, space="buy") + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + 'rsi': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['adx'] = ta.ADX(dataframe) + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + + # conditions = [] + # conditions.append(dataframe['bb_width'] >= self.buy_bollinger.value) + # + # if conditions: + dataframe.loc[ + ( + # (dataframe['open'] <= dataframe['bb_upperband']) & + (dataframe['close'] > dataframe['bb_upperband']) & + (dataframe['close'] > dataframe['open'] * 1.04) + # (dataframe['open'] <= dataframe['sma100']) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), + + 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick416.py b/StrategyPierrick416.py new file mode 100644 index 0000000..dcd1ee3 --- /dev/null +++ b/StrategyPierrick416.py @@ -0,0 +1,236 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from freqtrade.strategy.parameters import DecimalParameter, BooleanParameter, IntParameter +from pandas import DataFrame +import math +from functools import reduce + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick416(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # valeur de bbwidth pour démarrer + # buy_bollinger = DecimalParameter(0.05, 0.14, decimals=2, default=0.095, space="buy") + # pourcentage sma à dépasser + # buy_sma_percent = DecimalParameter(0.95, 1.05, decimals=2, default=0.98, space="buy") + # volume à atteindre + # buy_volume = DecimalParameter(0, 50, decimals=1, default=0, space="buy") + + # buy_close = DecimalParameter(0.95, 1.05, decimals=2, default=0, space="buy") + # buy_sma = IntParameter(1, 40, default=10, space="buy") + + # buy_rsi = IntParameter(20, 40, default=30, space="buy") + # buy_adx_enabled = BooleanParameter(default=True, space="buy") + # buy_rsi_enabled = CategoricalParameter([True, False], default=False, space="buy") + # buy_trigger = CategoricalParameter(["bb_lower", "macd_cross_signal"], default="bb_lower", space="buy") + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + 'rsi': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "ADX": { + 'adx': {'color': 'white'}, + 'minus_dm': {'color': 'blue'}, + 'plus_dm': {'color': 'red'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['adx'] = ta.ADX(dataframe) + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # # Aroon, Aroon Oscillator + # aroon = ta.AROON(dataframe) + # dataframe['aroonup'] = aroon['aroonup'] + # dataframe['aroondown'] = aroon['aroondown'] + # dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + # dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + # dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + # dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + + # conditions = [] + # conditions.append(dataframe['bb_width'] >= self.buy_bollinger.value) + # + # if conditions: + dataframe.loc[ + ( + (dataframe['close'] > dataframe['bb_upperband']) & + (dataframe['close'] > dataframe['open'] * 1.04) + # (dataframe['bb_width'] >= dataframe['bb_width'].shift(4)) + ) | ( + (dataframe['close'] > dataframe['bb_upperband']) & + (dataframe['close'].shift(1) > dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) > dataframe['open'].shift(2)) + # (dataframe['bb_width'] >= dataframe['bb_width'].shift(4)) + # (dataframe['close'] > dataframe['open'].shift(2) * 1.04) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] < dataframe['open']) & + (dataframe['close'].shift(1) < dataframe['open'].shift(1)) & + (dataframe['close'].shift(2) < dataframe['open'].shift(2)) & + (dataframe['close'] < dataframe['bb_lowerband']) & + (((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe[ + 'bb_lowerband']) >= 0.02) + # (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025) + ), + + 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick42.py b/StrategyPierrick42.py new file mode 100644 index 0000000..fdd950c --- /dev/null +++ b/StrategyPierrick42.py @@ -0,0 +1,189 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame +import math + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick42(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma500_97': {'color': 'gray'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "Aaron": { + 'aroonup': {'color': 'blue'}, + 'aroondown': {'color': 'red'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # Aroon, Aroon Oscillator + aroon = ta.AROON(dataframe) + dataframe['aroonup'] = aroon['aroonup'] + dataframe['aroondown'] = aroon['aroondown'] + dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + # dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + + condition = np.where(value >= 1.04, True, False) + dataframe.loc[ + ( + ( + (dataframe['close'] < dataframe['bb_lowerband']) + & ( + (dataframe['bb_width'] >= 0.065) | + ( + (dataframe['bb_width'] >= 0.04) & condition + ) + ) + & (dataframe['close'] <= dataframe['sma200_95']) + & (dataframe['volume'] > 0) + ) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + + ), + + 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick43.py b/StrategyPierrick43.py new file mode 100644 index 0000000..09813e5 --- /dev/null +++ b/StrategyPierrick43.py @@ -0,0 +1,189 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame +import math + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick43(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.005 + trailing_stop_positive_offset = 0.02 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma500_97': {'color': 'gray'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'} + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "Aaron": { + 'aroonup': {'color': 'blue'}, + 'aroondown': {'color': 'red'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # Aroon, Aroon Oscillator + aroon = ta.AROON(dataframe) + dataframe['aroonup'] = aroon['aroonup'] + dataframe['aroondown'] = aroon['aroondown'] + dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + # dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + + condition = np.where(value >= 1.04, True, False) + dataframe.loc[ + ( + ( + (dataframe['close'] < dataframe['bb_lowerband']) + & ( + (dataframe['bb_width'] >= 0.095) | + ( + (dataframe['bb_width'] >= 0.04) & condition + ) + ) + & (dataframe['close'] <= dataframe['sma200_95']) + & (dataframe['volume'] > 0) + ) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + + ), + + 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick44.py b/StrategyPierrick44.py new file mode 100644 index 0000000..ee86eb5 --- /dev/null +++ b/StrategyPierrick44.py @@ -0,0 +1,229 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame +import math + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick44(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma500_97': {'color': 'gray'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + # 'volatility_dcp': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "Aaron": { + 'aroonup': {'color': 'blue'}, + 'aroondown': {'color': 'red'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # Aroon, Aroon Oscillator + aroon = ta.AROON(dataframe) + dataframe['aroonup'] = aroon['aroonup'] + dataframe['aroondown'] = aroon['aroondown'] + dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + # dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + + condition = np.where(value >= 1.04, True, False) + p = 100 * ((dataframe['sma500'] - dataframe['sma500'].shift(60)) / dataframe['sma500']) + # for k, v in p.iteritems(): + # # print(k, v) + # value = v + # hausse = np.where(value >= 0.1, True, False) + # print("hausse=", hausse, "value=", value) + dataframe.loc[ + ( + ( + # hausse & + ( + (dataframe['close'] < dataframe['bb_lowerband']) | + (dataframe['close'].shift(1) < dataframe['bb_lowerband'].shift(1)) | + (dataframe['close'].shift(2) < dataframe['bb_lowerband'].shift(2)) | + (dataframe['close'].shift(3) < dataframe['bb_lowerband'].shift(3)) | + (dataframe['close'].shift(4) < dataframe['bb_lowerband'].shift(4)) | + (dataframe['close'].shift(5) < dataframe['bb_lowerband'].shift(5)) | + (dataframe['close'].shift(6) < dataframe['bb_lowerband'].shift(6)) | + (dataframe['close'].shift(7) < dataframe['bb_lowerband'].shift(7)) + ) + & (dataframe['bb_width'] >= 0.065) + & (dataframe['bb_upperband'] < dataframe['bb_upperband'].shift(1)) + # & ( + # ((dataframe['bb_upperband'] - dataframe['bb_lowerband']) / dataframe['bb_lowerband'] >= 0.4) + # # ( + # # (dataframe['bb_width'] >= 0.04) & condition + # # ) + # ) + # & (dataframe['close'] <= dataframe['sma200_98']) + & (dataframe['volume'] > 0) + ) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + + ), + + 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick5.py b/StrategyPierrick5.py new file mode 100644 index 0000000..dacadc5 --- /dev/null +++ b/StrategyPierrick5.py @@ -0,0 +1,222 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame +import math + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick5(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma500_97': {'color': 'gray'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + # 'volatility_dcp': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "Aaron": { + 'aroonup': {'color': 'blue'}, + 'aroondown': {'color': 'red'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # Aroon, Aroon Oscillator + aroon = ta.AROON(dataframe) + dataframe['aroonup'] = aroon['aroonup'] + dataframe['aroondown'] = aroon['aroondown'] + dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + # dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + condition = np.where(value >= 1.04, True, False) + + p = dataframe['sma100'].shift(100) - dataframe['sma100'] + for k, v in p.iteritems(): + # print(k, v) + value = v + hausse = np.where(value < 0, True, False) + dataframe.loc[ + ( + ( + hausse & + (dataframe['close'] < dataframe['bb_lowerband']) & + (dataframe['bb_width'] >= 0.065) & + (dataframe['volume'] > 0) + ) | ( + (dataframe['close'] < dataframe['bb_lowerband']) + & ( + (dataframe['bb_width'] >= 0.095) | + ( + (dataframe['bb_width'] >= 0.04) & condition + ) + ) + & (not hausse) + & (dataframe['close'] <= dataframe['sma200_95']) + & (dataframe['volume'] > 0) + ) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + + ), + + 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick51.py b/StrategyPierrick51.py new file mode 100644 index 0000000..1bd2943 --- /dev/null +++ b/StrategyPierrick51.py @@ -0,0 +1,222 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame +import math + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick51(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma500_97': {'color': 'gray'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + # 'volatility_dcp': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "Aaron": { + 'aroonup': {'color': 'blue'}, + 'aroondown': {'color': 'red'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # Aroon, Aroon Oscillator + aroon = ta.AROON(dataframe) + dataframe['aroonup'] = aroon['aroonup'] + dataframe['aroondown'] = aroon['aroondown'] + dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + # dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + condition = np.where(value >= 1.04, True, False) + + p = dataframe['sma100'].shift(60) - dataframe['sma100'] + for k, v in p.iteritems(): + # print(k, v) + value = v + hausse = np.where(value < 0, True, False) + dataframe.loc[ + ( + ( + hausse & + (dataframe['close'] < dataframe['bb_lowerband']) & + (dataframe['bb_width'] >= 0.065) & + (dataframe['volume'] > 0) + ) | ( + (dataframe['close'] < dataframe['bb_lowerband']) + & ( + (dataframe['bb_width'] >= 0.095) | + ( + (dataframe['bb_width'] >= 0.04) & condition + ) + ) + & (not hausse) + & (dataframe['close'] <= dataframe['sma200_95']) + & (dataframe['volume'] > 0) + ) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + + ), + + 'sell'] = 1 + return dataframe diff --git a/StrategyPierrick52.py b/StrategyPierrick52.py new file mode 100644 index 0000000..929e468 --- /dev/null +++ b/StrategyPierrick52.py @@ -0,0 +1,222 @@ +# pr#agma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame +import math + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +# This class is a sample. Feel free to customize it. +class StrategyPierrick52(IStrategy): + # Strategy interface version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + INTERFACE_VERSION = 2 + + # ROI table: + minimal_roi = { + # "0": 0.015 + "0": 0.5 + } + + # Stoploss: + stoploss = -1 + trailing_stop = True + trailing_stop_positive = 0.001 + trailing_stop_positive_offset = 0.0175 # 0.015 + trailing_only_offset_is_reached = True + + # max_open_trades = 3 + + # Optimal ticker interval for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'bb_lowerband': {'color': 'white'}, + 'bb_upperband': {'color': 'white'}, + 'sma100': {'color': 'green'}, + 'sma500': {'color': 'blue'}, + 'sma500_97': {'color': 'gray'}, + 'sma200_98': {'color': 'yellow'}, + 'sma200_95': {'color': 'cyan'}, + # 'volatility_dcp': {'color': '#c58893'} + + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "BB": { + 'bb_width': {'color': 'white'}, + }, + "Aaron": { + 'aroonup': {'color': 'blue'}, + 'aroondown': {'color': 'red'} + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # MACD + # macd = ta.MACD(dataframe) + # dataframe['macd'] = macd['macd'] + # dataframe['macdsignal'] = macd['macdsignal'] + # dataframe['macdhist'] = macd['macdhist'] + + # # Plus Directional Indicator / Movement + dataframe['plus_dm'] = ta.PLUS_DM(dataframe) + dataframe['plus_di'] = ta.PLUS_DI(dataframe) + + # Minus Directional Indicator / Movement + dataframe['minus_dm'] = ta.MINUS_DM(dataframe) + dataframe['minus_di'] = ta.MINUS_DI(dataframe) + + dataframe['min'] = ta.MIN(dataframe) + dataframe['max'] = ta.MAX(dataframe) + + # Aroon, Aroon Oscillator + aroon = ta.AROON(dataframe) + dataframe['aroonup'] = aroon['aroonup'] + dataframe['aroondown'] = aroon['aroondown'] + dataframe['aroonosc'] = ta.AROONOSC(dataframe) + + # RSI + # dataframe['rsi'] = ta.RSI(dataframe) + + # # 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['ema21'] = ta.EMA(dataframe, timeperiod=21) + # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50) + dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100) + + # # SMA - Simple Moving Average + # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3) + # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10) + # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21) + # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100) + dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200) + dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95 + dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98 + dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500) + dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9 + dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95 + dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2 + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + # dataframe['volatility_kcw'] = ta.volatility.keltner_channel_wband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=20, + # window_atr=10, + # fillna=False, + # original_version=True + # ) + # + # dataframe['volatility_dcp'] = ta.volatility.donchian_channel_pband( + # dataframe['high'], + # dataframe['low'], + # dataframe['close'], + # window=10, + # offset=0, + # fillna=False + # ) + + # dataframe['bb_lower_reg'] = dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + value = 0 + p = dataframe['close'].shift(20) / dataframe['close'] + for k, v in p.iteritems(): + # print(k, v) + value = v + condition = np.where(value >= 1.04, True, False) + + p = dataframe['sma100'].shift(30) - dataframe['sma100'] + for k, v in p.iteritems(): + # print(k, v) + value = v + hausse = np.where(value < 0, True, False) + dataframe.loc[ + ( + ( + hausse & + (dataframe['close'] < dataframe['bb_lowerband']) & + (dataframe['bb_width'] >= 0.065) & + (dataframe['volume'] > 0) + ) | ( + (dataframe['close'] < dataframe['bb_lowerband']) + & ( + (dataframe['bb_width'] >= 0.095) | + ( + (dataframe['bb_width'] >= 0.04) & condition + ) + ) + & (not hausse) + & (dataframe['close'] <= dataframe['sma200_95']) + & (dataframe['volume'] > 0) + ) + ), + 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + + ), + + 'sell'] = 1 + return dataframe diff --git a/Supertrend.py b/Supertrend.py new file mode 100644 index 0000000..cfc0679 --- /dev/null +++ b/Supertrend.py @@ -0,0 +1,177 @@ +""" +Supertrend strategy: +* Description: Generate a 3 supertrend indicators for 'buy' strategies & 3 supertrend indicators for 'sell' strategies + Buys if the 3 'buy' indicators are 'up' + Sells if the 3 'sell' indicators are 'down' +* Author: @juankysoriano (Juan Carlos Soriano) +* github: https://github.com/juankysoriano/ + +*** NOTE: This Supertrend strategy is just one of many possible strategies using `Supertrend` as indicator. It should on any case used at your own risk. + It comes with at least a couple of caveats: + 1. The implementation for the `supertrend` indicator is based on the following discussion: https://github.com/freqtrade/freqtrade-strategies/issues/30 . Concretelly https://github.com/freqtrade/freqtrade-strategies/issues/30#issuecomment-853042401 + 2. The implementation for `supertrend` on this strategy is not validated; meaning this that is not proven to match the results by the paper where it was originally introduced or any other trusted academic resources +""" + +import logging +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +from freqtrade.strategy.parameters import IntParameter +from pandas import DataFrame +import talib.abstract as ta +import numpy as np + +class Supertrend(IStrategy): + # Buy params, Sell params, ROI, Stoploss and Trailing Stop are values generated by 'freqtrade hyperopt --strategy Supertrend --hyperopt-loss ShortTradeDurHyperOptLoss --timerange=20210101- --timeframe=1h --spaces all' + # It's encourage you find the values that better suites your needs and risk management strategies + + # Buy hyperspace params: + buy_params = { + "buy_m1": 4, + "buy_m2": 7, + "buy_m3": 1, + "buy_p1": 8, + "buy_p2": 9, + "buy_p3": 8, + } + + # Sell hyperspace params: + sell_params = { + "sell_m1": 1, + "sell_m2": 3, + "sell_m3": 6, + "sell_p1": 16, + "sell_p2": 18, + "sell_p3": 18, + } + + # ROI table: + minimal_roi = { + "0": 0.087, + "372": 0.058, + "861": 0.029, + "2221": 0 + } + + # Stoploss: + stoploss = -0.265 + + # Trailing stop: + trailing_stop = True + trailing_stop_positive = 0.05 + trailing_stop_positive_offset = 0.144 + trailing_only_offset_is_reached = False + + timeframe = '1h' + + startup_candle_count = 18 + + buy_m1 = IntParameter(1, 7, default=4) + buy_m2 = IntParameter(1, 7, default=4) + buy_m3 = IntParameter(1, 7, default=4) + buy_p1 = IntParameter(7, 21, default=14) + buy_p2 = IntParameter(7, 21, default=14) + buy_p3 = IntParameter(7, 21, default=14) + + sell_m1 = IntParameter(1, 7, default=4) + sell_m2 = IntParameter(1, 7, default=4) + sell_m3 = IntParameter(1, 7, default=4) + sell_p1 = IntParameter(7, 21, default=14) + sell_p2 = IntParameter(7, 21, default=14) + sell_p3 = IntParameter(7, 21, default=14) + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + for multiplier in self.buy_m1.range: + for period in self.buy_p1.range: + dataframe[f'supertrend_1_buy_{multiplier}_{period}'] = self.supertrend(dataframe, multiplier, period)['STX'] + + for multiplier in self.buy_m2.range: + for period in self.buy_p2.range: + dataframe[f'supertrend_2_buy_{multiplier}_{period}'] = self.supertrend(dataframe, multiplier, period)['STX'] + + for multiplier in self.buy_m3.range: + for period in self.buy_p3.range: + dataframe[f'supertrend_3_buy_{multiplier}_{period}'] = self.supertrend(dataframe, multiplier, period)['STX'] + + for multiplier in self.sell_m1.range: + for period in self.sell_p1.range: + dataframe[f'supertrend_1_sell_{multiplier}_{period}'] = self.supertrend(dataframe, multiplier, period)['STX'] + + for multiplier in self.sell_m2.range: + for period in self.sell_p2.range: + dataframe[f'supertrend_2_sell_{multiplier}_{period}'] = self.supertrend(dataframe, multiplier, period)['STX'] + + for multiplier in self.sell_m3.range: + for period in self.sell_p3.range: + dataframe[f'supertrend_3_sell_{multiplier}_{period}'] = self.supertrend(dataframe, multiplier, period)['STX'] + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe[f'supertrend_1_buy_{self.buy_m1.value}_{self.buy_p1.value}'] == 'up') & + (dataframe[f'supertrend_2_buy_{self.buy_m2.value}_{self.buy_p2.value}'] == 'up') & + (dataframe[f'supertrend_3_buy_{self.buy_m3.value}_{self.buy_p3.value}'] == 'up') & # The three indicators are 'up' for the current candle + (dataframe['volume'] > 0) # There is at least some trading volume + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe[f'supertrend_1_sell_{self.sell_m1.value}_{self.sell_p1.value}'] == 'down') & + (dataframe[f'supertrend_2_sell_{self.sell_m2.value}_{self.sell_p2.value}'] == 'down') & + (dataframe[f'supertrend_3_sell_{self.sell_m3.value}_{self.sell_p3.value}'] == 'down') & # The three indicators are 'down' for the current candle + (dataframe['volume'] > 0) # There is at least some trading volume + ), + 'sell'] = 1 + + return dataframe + + + + """ + Supertrend Indicator; adapted for freqtrade + from: https://github.com/freqtrade/freqtrade-strategies/issues/30 + """ + def supertrend(self, dataframe: DataFrame, multiplier, period): + df = dataframe.copy() + + df['TR'] = ta.TRANGE(df) + df['ATR'] = ta.SMA(df['TR'], period) + + st = 'ST_' + str(period) + '_' + str(multiplier) + stx = 'STX_' + str(period) + '_' + str(multiplier) + + # Compute basic upper and lower bands + df['basic_ub'] = (df['high'] + df['low']) / 2 + multiplier * df['ATR'] + df['basic_lb'] = (df['high'] + df['low']) / 2 - multiplier * df['ATR'] + + # Compute final upper and lower bands + df['final_ub'] = 0.00 + df['final_lb'] = 0.00 + for i in range(period, len(df)): + df['final_ub'].iat[i] = df['basic_ub'].iat[i] if df['basic_ub'].iat[i] < df['final_ub'].iat[i - 1] or df['close'].iat[i - 1] > df['final_ub'].iat[i - 1] else df['final_ub'].iat[i - 1] + df['final_lb'].iat[i] = df['basic_lb'].iat[i] if df['basic_lb'].iat[i] > df['final_lb'].iat[i - 1] or df['close'].iat[i - 1] < df['final_lb'].iat[i - 1] else df['final_lb'].iat[i - 1] + + # Set the Supertrend value + df[st] = 0.00 + for i in range(period, len(df)): + df[st].iat[i] = df['final_ub'].iat[i] if df[st].iat[i - 1] == df['final_ub'].iat[i - 1] and df['close'].iat[i] <= df['final_ub'].iat[i] else \ + df['final_lb'].iat[i] if df[st].iat[i - 1] == df['final_ub'].iat[i - 1] and df['close'].iat[i] > df['final_ub'].iat[i] else \ + df['final_lb'].iat[i] if df[st].iat[i - 1] == df['final_lb'].iat[i - 1] and df['close'].iat[i] >= df['final_lb'].iat[i] else \ + df['final_ub'].iat[i] if df[st].iat[i - 1] == df['final_lb'].iat[i - 1] and df['close'].iat[i] < df['final_lb'].iat[i] else 0.00 + # Mark the trend direction up/down + df[stx] = np.where((df[st] > 0.00), np.where((df['close'] < df[st]), 'down', 'up'), np.NaN) + + # Remove basic and final bands from the columns + df.drop(['basic_ub', 'basic_lb', 'final_ub', 'final_lb'], inplace=True, axis=1) + + df.fillna(0, inplace=True) + + return DataFrame(index=df.index, data={ + 'ST' : df[st], + 'STX' : df[stx] + }) diff --git a/Swing-High-To-Sky.py b/Swing-High-To-Sky.py new file mode 100644 index 0000000..c2d1444 --- /dev/null +++ b/Swing-High-To-Sky.py @@ -0,0 +1,110 @@ +""" +author = "Kevin Ossenbrück" +copyright = "Free For Use" +credits = ["Bloom Trading, Mohsen Hassan"] +license = "MIT" +version = "1.0" +maintainer = "Kevin Ossenbrück" +email = "kevin.ossenbrueck@pm.de" +status = "Live" +""" + +from freqtrade.strategy import IStrategy +from freqtrade.strategy import IntParameter +from functools import reduce +from pandas import DataFrame + +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +import numpy + + + +# CCI timerperiods and values +cciBuyTP = 72 +cciBuyVal = -175 +cciSellTP = 66 +cciSellVal = -106 + +# RSI timeperiods and values +rsiBuyTP = 36 +rsiBuyVal = 90 +rsiSellTP = 45 +rsiSellVal = 88 + + +class SwingHighToSky(IStrategy): + INTERFACE_VERSION = 2 + + timeframe = '15m' + + stoploss = -0.34338 + + minimal_roi = {"0": 0.27058, "33": 0.0853, "64": 0.04093, "244": 0} + + buy_cci = IntParameter(low=-200, high=200, default=100, space='buy', optimize=True) + buy_cciTime = IntParameter(low=10, high=80, default=20, space='buy', optimize=True) + buy_rsi = IntParameter(low=10, high=90, default=30, space='buy', optimize=True) + buy_rsiTime = IntParameter(low=10, high=80, default=26, space='buy', optimize=True) + + sell_cci = IntParameter(low=-200, high=200, default=100, space='sell', optimize=True) + sell_cciTime = IntParameter(low=10, high=80, default=20, space='sell', optimize=True) + sell_rsi = IntParameter(low=10, high=90, default=30, space='sell', optimize=True) + sell_rsiTime = IntParameter(low=10, high=80, default=26, space='sell', optimize=True) + + # Buy hyperspace params: + buy_params = { + "buy_cci": -175, + "buy_cciTime": 72, + "buy_rsi": 90, + "buy_rsiTime": 36, + } + + # Sell hyperspace params: + sell_params = { + "sell_cci": -106, + "sell_cciTime": 66, + "sell_rsi": 88, + "sell_rsiTime": 45, + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + for val in self.buy_cciTime.range: + dataframe[f'cci-{val}'] = ta.CCI(dataframe, timeperiod=val) + + for val in self.sell_cciTime.range: + dataframe[f'cci-sell-{val}'] = ta.CCI(dataframe, timeperiod=val) + + for val in self.buy_rsiTime.range: + dataframe[f'rsi-{val}'] = ta.RSI(dataframe, timeperiod=val) + + for val in self.sell_rsiTime.range: + dataframe[f'rsi-sell-{val}'] = ta.RSI(dataframe, timeperiod=val) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + (dataframe[f'cci-{self.buy_cciTime.value}'] < self.buy_cci.value) & + (dataframe[f'rsi-{self.buy_rsiTime.value}'] < self.buy_rsi.value) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + (dataframe[f'cci-sell-{self.sell_cciTime.value}'] > self.sell_cci.value) & + (dataframe[f'rsi-sell-{self.sell_rsiTime.value}'] > self.sell_rsi.value) + ), + 'sell'] = 1 + + return dataframe diff --git a/TheForce.py b/TheForce.py new file mode 100644 index 0000000..e02c59d --- /dev/null +++ b/TheForce.py @@ -0,0 +1,188 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement + +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame + +from freqtrade.strategy import IStrategy + +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class TheForce(IStrategy): + + INTERFACE_VERSION = 2 + + minimal_roi = { + "30": 0.005, + "15": 0.01, + "0": 0.012 + } + + stoploss = -0.015 + + # Trailing stoploss + trailing_stop = False + # trailing_only_offset_is_reached = False + # trailing_stop_positive = 0.01 + # trailing_stop_positive_offset = 0.0 # Disabled / not configured + + # Optimal timeframe for the strategy. + timeframe = '15m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'tema': {}, + 'sar': {'color': 'white'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "MACD": { + 'macd': {'color': 'blue'}, + 'macdsignal': {'color': 'orange'}, + }, + "RSI": { + 'rsi': {'color': 'red'}, + } + } + } + + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) : + """ + 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: Dataframe with data from the exchange + :param metadata: Additional information, like the currently traded pair + :return: a Dataframe with all mandatory indicators for the strategies + """ + + # Momentum Indicators + # ------------------------------------ + + # Stochastic Fast + stoch_fast = ta.STOCHF(dataframe,5,3,3) + dataframe['fastd'] = stoch_fast['fastd'] + dataframe['fastk'] = stoch_fast['fastk'] + + # # Stochastic RSI + stoch_rsi = ta.STOCHRSI(dataframe) + dataframe['fastd_rsi'] = stoch_rsi['fastd'] + dataframe['fastk_rsi'] = stoch_rsi['fastk'] + + # MACD + macd = ta.MACD(dataframe,12,26,1) + dataframe['macd'] = macd['macd'] + dataframe['macdsignal'] = macd['macdsignal'] + dataframe['macdhist'] = macd['macdhist'] + + # # EMA - Exponential Moving Average + + dataframe['ema5c'] = ta.EMA(dataframe['close'], timeperiod=5) + dataframe['ema5o'] = ta.EMA(dataframe['open'], timeperiod=5) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) : + """ + 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['fastk'] >= 20) & (dataframe['fastk'] <= 80) + & + (dataframe['fastd'] >= 20) & (dataframe['fastd'] <= 80) + ) + & + ( + (dataframe['macd'] > dataframe['macd'].shift(1)) + & + (dataframe['macdsignal'] > dataframe['macdsignal'].shift(1)) + ) + & + ( + (dataframe['close'] > dataframe['close'].shift(1)) + ) + & + ( + (dataframe['ema5c'] >= dataframe['ema5o']) + ) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) : + """ + 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['fastk'] <= 80) + & + (dataframe['fastd'] <= 80) + ) + & + ( + (dataframe['macd'] < dataframe['macd'].shift(1)) + & + (dataframe['macdsignal'] < dataframe['macdsignal'].shift(1)) + ) + & + ( + (dataframe['ema5c'] < dataframe['ema5o']) + ) + + ), + 'sell'] = 1 + return dataframe diff --git a/TheForce_1.py b/TheForce_1.py new file mode 100644 index 0000000..928f07b --- /dev/null +++ b/TheForce_1.py @@ -0,0 +1,188 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement + +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame + +from freqtrade.strategy import IStrategy + +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class TheForce_1(IStrategy): + + INTERFACE_VERSION = 2 + + minimal_roi = { + "30": 0.005, + "15": 0.01, + "0": 0.012 + } + + stoploss = -0.015 + + # Trailing stoploss + trailing_stop = False + # trailing_only_offset_is_reached = False + # trailing_stop_positive = 0.01 + # trailing_stop_positive_offset = 0.0 # Disabled / not configured + + # Optimal timeframe for the strategy. + timeframe = '5m' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = False + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = False + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'tema': {}, + 'sar': {'color': 'white'}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "MACD": { + 'macd': {'color': 'blue'}, + 'macdsignal': {'color': 'orange'}, + }, + "RSI": { + 'rsi': {'color': 'red'}, + } + } + } + + def informative_pairs(self): + """ + Define additional, informative pair/interval combinations to be cached from the exchange. + These pair/interval combinations are non-tradeable, unless they are part + of the whitelist as well. + For more information, please consult the documentation + :return: List of tuples in the format (pair, interval) + Sample: return [("ETH/USDT", "5m"), + ("BTC/USDT", "15m"), + ] + """ + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) : + """ + 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: Dataframe with data from the exchange + :param metadata: Additional information, like the currently traded pair + :return: a Dataframe with all mandatory indicators for the strategies + """ + + # Momentum Indicators + # ------------------------------------ + + # Stochastic Fast + stoch_fast = ta.STOCHF(dataframe,5,3,3) + dataframe['fastd'] = stoch_fast['fastd'] + dataframe['fastk'] = stoch_fast['fastk'] + + # # Stochastic RSI + stoch_rsi = ta.STOCHRSI(dataframe) + dataframe['fastd_rsi'] = stoch_rsi['fastd'] + dataframe['fastk_rsi'] = stoch_rsi['fastk'] + + # MACD + macd = ta.MACD(dataframe,12,26,1) + dataframe['macd'] = macd['macd'] + dataframe['macdsignal'] = macd['macdsignal'] + dataframe['macdhist'] = macd['macdhist'] + + # # EMA - Exponential Moving Average + + dataframe['ema5c'] = ta.EMA(dataframe['close'], timeperiod=5) + dataframe['ema5o'] = ta.EMA(dataframe['open'], timeperiod=5) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) : + """ + 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['fastk'] >= 20) & (dataframe['fastk'] <= 80) + & + (dataframe['fastd'] >= 20) & (dataframe['fastd'] <= 80) + ) + & + ( + (dataframe['macd'] > dataframe['macd'].shift(1)) + & + (dataframe['macdsignal'] > dataframe['macdsignal'].shift(1)) + ) + & + ( + (dataframe['close'] > dataframe['close'].shift(1)) + ) + & + ( + (dataframe['ema5c'] >= dataframe['ema5o']) + ) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) : + """ + 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['fastk'] <= 80) + & + (dataframe['fastd'] <= 80) + ) + & + ( + (dataframe['macd'] < dataframe['macd'].shift(1)) + & + (dataframe['macdsignal'] < dataframe['macdsignal'].shift(1)) + ) + & + ( + (dataframe['ema5c'] < dataframe['ema5o']) + ) + + ), + 'sell'] = 1 + return dataframe diff --git a/Zeus.json b/Zeus.json new file mode 100644 index 0000000..c3c5223 --- /dev/null +++ b/Zeus.json @@ -0,0 +1,34 @@ +{ + "strategy_name": "Zeus", + "params": { + "stoploss": { + "stoploss": -1.0 + }, + "buy": { + "buy_bb_lowerband": 1.02, + "buy_bb_width": 0.09, + "buy_cat": "R", "=R", "R", "=R", " DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_real.value + OPR = self.buy_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == " self.buy_pct_1.value) + & (dataframe['pct_change_3_1d'] > self.buy_pct_3.value) + & (dataframe['pct_change_5_1d'] > self.buy_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_bb_width.value) + , + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # conditions = [] + # IND = 'trend_kst_diff' + # REAL = self.sell_real.value + # OPR = self.sell_cat.value + # DFIND = dataframe[IND] + # # print(DFIND.mean()) + # + # if OPR == ">R": + # conditions.append(DFIND > REAL) + # elif OPR == "=R": + # conditions.append(np.isclose(DFIND, REAL)) + # elif OPR == "", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_10(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", " 0.015) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + & (days < self.sell_b_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > last_candle['bb_width'] / 1.3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + if (current_profit > 0.025) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'h_percent_quick' + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > last_candle['bb_width'] / 0.8) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent'] < - self.sell_h_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '4h') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + # dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + # dataframe['high'], + # dataframe['low'], + # window1=9, + # window2=26, + # visual=False, + # fillna=False + # ) + # KST = ta.trend.KSTIndicator( + # close=dataframe['close'], + # roc1=10, + # roc2=15, + # roc3=20, + # roc4=30, + # window1=10, + # window2=10, + # window3=10, + # window4=15, + # nsig=9, + # fillna=False + # ) + # + # dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_5'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_max200_5'] = (dataframe['min200'] * (1 + dataframe['min_max200'] / 5)) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma10xpct+'] = dataframe['sma10'] * 1.0075 + dataframe['sma10xpct-'] = dataframe['sma10'] * 0.9925 + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # + # dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + # dataframe['min1.1'] = 1.01 * dataframe['min'] + + # dataframe['sar'] = talib.SAR(dataframe) + # # Normalization + # tib = dataframe['trend_ichimoku_base'] + # dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + # tkd = dataframe['trend_kst_diff'] + # dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ################### INFORMATIVE BTC 1H + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + # informative["rsi"] = talib.RSI(informative) + # informative["rsi_5"] = talib.RSI(informative, timeperiod=5) + # informative['pct_change_1'] = informative['close'].pct_change(1) + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'].shift(1) <= dataframe['bb_lowerband']) + & (dataframe['open'] <= dataframe['close']) + ), ['buy', 'buy_tag']] = (1, 'buy_lowerband') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['close'] >= dataframe['bb_upperband']) + # ), + # 'sell'] = 1 + return dataframe diff --git a/Zeus_2.json b/Zeus_2.json new file mode 100644 index 0000000..9fec29d --- /dev/null +++ b/Zeus_2.json @@ -0,0 +1,54 @@ +{ + "strategy_name": "Zeus_2", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_bb_lowerband": 1.02, + "buy_bb_width": 0.15, + "buy_cat": "R", "=R", "R", "=R", " expected_profit) & (last_candle['pct_change_1_1d'] < 0): + # return "exp_profit_down" + + if (current_profit >= - self.sell_too_old_percent.value) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value)\ + & ((current_time - trade.open_date_utc).days < self.sell_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.01" + if (current_profit >= - self.sell_too_old_percent.value * 2) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value * 2)\ + & ((current_time - trade.open_date_utc).days < self.sell_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.02" + if (current_profit >= - self.sell_too_old_percent.value * 3) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.03" + + if self.profit_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "quick_lost" + + if self.profit_no_change.value and (current_profit > self.sell_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + if (current_profit > self.sell_percent.value) & (last_candle['percent3'] < - self.sell_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_candels.value): + return "quick_gain_param" + + if self.profit_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma5' + + if self.profit_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + if self.profit_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI2.value) & \ + (last_candle['percent'] < - self.sell_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_max' + + if self.profit_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # Assign tf to each pair so they can be downloaded and cached for strategy. + # informative_pairs = [(pair, "5m") for pair in pairs] + informative_pairs = [(pair, '1d') for pair in pairs] + # informative_pairs += [(pair, '1w') for pair in pairs] + + # Optionally Add additional "static" pairs + # informative_pairs = [("BTC/USDT", "1w"), ("BTC/USDT", "1d"), ("BTC/USDT", "5m")] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5); + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_real.value + OPR = self.buy_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == " self.buy_pct_1.value) + & (dataframe['pct_change_3_1d'] > self.buy_pct_3.value) + & (dataframe['pct_change_5_1d'] > 0) #self.buy_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + , + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # conditions = [] + # IND = 'trend_kst_diff' + # REAL = self.sell_real.value + # OPR = self.sell_cat.value + # DFIND = dataframe[IND] + # # print(DFIND.mean()) + # + # if OPR == ">R": + # conditions.append(DFIND > REAL) + # elif OPR == "=R": + # conditions.append(np.isclose(DFIND, REAL)) + # elif OPR == "R", + "buy_pct": 0.008, + "buy_pct_1": -0.18, + "buy_pct_3": -0.09, + "buy_pct_5": -0.14, + "buy_real": 0.0919 + }, + "sell": { + "profit_no_change": false, + "profit_old_sma10": false, + "profit_over_rsi": false, + "profit_quick_gain": true, + "profit_quick_gain_3": false, + "profit_quick_lost": false, + "profit_short_loss": true, + "profit_sma10": false, + "profit_sma20": false, + "profit_sma5": false, + "profit_very_old_sma10": true, + "sell_RSI": 79, + "sell_RSI2": 82, + "sell_RSI2_percent": 0.006, + "sell_RSI3": 70, + "sell_candels": 43, + "sell_percent": 0.02, + "sell_percent3": 0.013, + "sell_profit_no_change": 0.015, + "sell_profit_percent10": 0.0, + "sell_too_old_day": 2, + "sell_too_old_percent": 0.013 + }, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2022-04-02 15:57:05.428547+00:00" +} \ No newline at end of file diff --git a/Zeus_2_1.py b/Zeus_2_1.py new file mode 100644 index 0000000..fe86110 --- /dev/null +++ b/Zeus_2_1.py @@ -0,0 +1,356 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class Zeus_2_1(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_params = { + "buy_cat": "R", "=R", "R", "=R", " expected_profit) & (last_candle['pct_change_1_1d'] < 0): + # return "exp_profit_down" + + if (current_profit >= - self.sell_too_old_percent.value) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value)\ + & ((current_time - trade.open_date_utc).days < self.sell_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.01" + if (current_profit >= - self.sell_too_old_percent.value * 2) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value * 2)\ + & ((current_time - trade.open_date_utc).days < self.sell_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.02" + if (current_profit >= - self.sell_too_old_percent.value * 3) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.03" + + if self.profit_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "quick_lost" + + if self.profit_no_change.value and (current_profit > self.sell_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + if (current_profit > self.sell_percent.value) & (last_candle['percent3'] < - self.sell_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_candels.value): + return "quick_gain_param" + + if self.profit_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma5' + + if self.profit_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + if self.profit_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI2.value) & \ + (last_candle['percent'] < - self.sell_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_max' + + if self.profit_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # Assign tf to each pair so they can be downloaded and cached for strategy. + # informative_pairs = [(pair, "5m") for pair in pairs] + informative_pairs = [(pair, '1d') for pair in pairs] + # informative_pairs += [(pair, '1w') for pair in pairs] + + # Optionally Add additional "static" pairs + # informative_pairs = [("BTC/USDT", "1w"), ("BTC/USDT", "1d"), ("BTC/USDT", "5m")] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5); + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe['sar'] = talib.SAR(dataframe) + + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_real.value + OPR = self.buy_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == " self.buy_pct_1.value) + & (dataframe['pct_change_3_1d'] > self.buy_pct_3.value) + & (dataframe['pct_change_5_1d'] > 0) #self.buy_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + , + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # conditions = [] + # IND = 'trend_kst_diff' + # REAL = self.sell_real.value + # OPR = self.sell_cat.value + # DFIND = dataframe[IND] + # # print(DFIND.mean()) + # + # if OPR == ">R": + # conditions.append(DFIND > REAL) + # elif OPR == "=R": + # conditions.append(np.isclose(DFIND, REAL)) + # elif OPR == "R", "=R", "R", "=R", " expected_profit) & (last_candle['pct_change_1_1d'] < 0): + # return "exp_profit_down" + + if (current_profit >= - self.sell_too_old_percent.value) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value)\ + & ((current_time - trade.open_date_utc).days < self.sell_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.01" + if (current_profit >= - self.sell_too_old_percent.value * 2) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value * 2)\ + & ((current_time - trade.open_date_utc).days < self.sell_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.02" + if (current_profit >= - self.sell_too_old_percent.value * 3) & ((current_time - trade.open_date_utc).days >= self.sell_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.03" + + if self.profit_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "quick_lost" + + if self.profit_no_change.value and (current_profit > self.sell_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + if (current_profit > self.sell_percent.value) & (last_candle['percent3'] < - self.sell_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_candels.value): + return "quick_gain_param" + + if self.profit_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma5' + + if self.profit_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + if self.profit_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI2.value) & \ + (last_candle['percent'] < - self.sell_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_max' + + if self.profit_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + # Assign tf to each pair so they can be downloaded and cached for strategy. + # informative_pairs = [(pair, "5m") for pair in pairs] + informative_pairs = [(pair, '1d') for pair in pairs] + # informative_pairs += [(pair, '1w') for pair in pairs] + + # Optionally Add additional "static" pairs + # informative_pairs = [("BTC/USDT", "1w"), ("BTC/USDT", "1d"), ("BTC/USDT", "5m")] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5); + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe['sar'] = talib.SAR(dataframe) + + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_real.value + OPR = self.buy_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == " self.buy_pct_1.value) + & (dataframe['pct_change_3_1d'] > self.buy_pct_3.value) + & (dataframe['pct_change_5_1d'] > 0) #self.buy_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + , + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # conditions = [] + # IND = 'trend_kst_diff' + # REAL = self.sell_real.value + # OPR = self.sell_cat.value + # DFIND = dataframe[IND] + # # print(DFIND.mean()) + # + # if OPR == ">R": + # conditions.append(DFIND > REAL) + # elif OPR == "=R": + # conditions.append(np.isclose(DFIND, REAL)) + # elif OPR == "R", "=R", "R", "=R", " expected_profit) & (last_candle['pct_change_1_1d'] < 0): + # return "exp_profit_down" + + if (current_profit >= - self.sell_b_too_old_percent.value) & ((current_time - trade.open_date_utc).days >= self.sell_b_too_old_day.value)\ + & ((current_time - trade.open_date_utc).days < self.sell_b_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & ((current_time - trade.open_date_utc).days >= self.sell_b_too_old_day.value * 2)\ + & ((current_time - trade.open_date_utc).days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & ((current_time - trade.open_date_utc).days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_b_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5); + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_b_real.value + OPR = self.buy_b_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == " self.buy_b_pct_1.value) + & (dataframe['pct_change_3_1d'] > self.buy_b_pct_3.value) + & (dataframe['pct_change_5_1d'] > 0) #self.buy_b_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_b_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_b_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + , + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # conditions = [] + # IND = 'trend_kst_diff' + # REAL = self.sell_b_real.value + # OPR = self.sell_b_cat.value + # DFIND = dataframe[IND] + # # print(DFIND.mean()) + # + # if OPR == ">R": + # conditions.append(DFIND > REAL) + # elif OPR == "=R": + # conditions.append(np.isclose(DFIND, REAL)) + # elif OPR == "R", + "buy_h_pct": 0.016, + "buy_h_real": 0.026 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.001, + "sell_b_profit_percent10": 0.0016, + "sell_b_too_old_day": 3, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2, + "sell_h_too_old_percent": 0.01 + }, + "protection": {}, + "stoploss": { + "stoploss": -1 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-04-03 17:09:52.289232+00:00" +} \ No newline at end of file diff --git a/Zeus_4.jsonFirst b/Zeus_4.jsonFirst new file mode 100644 index 0000000..36eadf3 --- /dev/null +++ b/Zeus_4.jsonFirst @@ -0,0 +1,85 @@ +{ + "strategy_name": "Zeus_4", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_h_bb_lowerband": 1.04, + "buy_h_bb_width": 0.09, + "buy_h_cat": ">R", + "buy_h_pct": 0.008, + "buy_h_pct_1": -0.18, + "buy_h_pct_3": -0.09, + "buy_h_pct_5": -0.14, + "buy_h_real": 0.0919, + + "buy_b_bb_lowerband": 1.02, + "buy_b_bb_width": 0.15, + "buy_b_cat": "R", "=R", "R", "=R", "R", "=R", "= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + & (days < self.sell_b_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_b_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + else: + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma10' + + if self.profit_h_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent'] < - self.sell_h_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5); + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['sar'] = talib.SAR(informative) + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_h_real.value + OPR = self.buy_h_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == " 0) + & (dataframe['pct_change_3_1d'] > 0) + & (dataframe['pct_change_5_1d'] > 0) #self.buy_h_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_h_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_h_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + + & (dataframe['volume10'] * dataframe['close'] / 1000 >= 10) + , + ['buy', 'buy_tag']] = (1, 'buy_h') + + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_b_real.value + OPR = self.buy_b_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == " 0) + & (dataframe['pct_change_3_1d'] < 0) # self.buy_b_pct_3.value) + & (dataframe['pct_change_5_1d'] < 0) #self.buy_b_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_b_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_b_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + + & (dataframe['volume10'] * dataframe['close'] / 1000 >= 10) + , + ['buy', 'buy_tag']] = (1, 'buy_b') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # conditions = [] + # IND = 'trend_kst_diff' + # REAL = self.sell_h_real.value + # OPR = self.sell_h_cat.value + # DFIND = dataframe[IND] + # # print(DFIND.mean()) + # + # if OPR == ">R": + # conditions.append(DFIND > REAL) + # elif OPR == "=R": + # conditions.append(np.isclose(DFIND, REAL)) + # elif OPR == "R", + "buy_h_pct": 0.016, + "buy_h_real": 0.026, + "buy_0_distance": -0.03, + "buy_0_percent20": -0.03, + "buy_1_bb_lower_5": 0.19, + "buy_1_pente_sma10": 0.48, + "buy_1_pente_sma20": 0.33, + "buy_2_bb_lower_5": 0.59, + "buy_2_distance": 0.08, + "buy_2_pente_sma10": 0.46, + "buy_2_pente_sma20": 0.08, + "buy_2_percent20": 0.06, + "buy_3_bb_lower_5": 0.05, + "buy_3_distance": 0.01, + "buy_3_pente_sma10": 0.12, + "buy_3_pente_sma20": 0.27, + "buy_3_percent20": -0.03, + "buy_4_pente_sma10": 0.35, + "buy_4_pente_sma20": 0.22, + "buy_decalage0": 7, + "buy_decalage2": 6, + "buy_decalage3": 7, + "buy_decalage_deb_0": 1, + "buy_decalage_deb_2": 1, + "buy_decalage_deb_3": 1, + "buy_min_horizon": 192, + "buy_real_num0": 0.61, + "buy_real_num1": 0.34, + "buy_real_num2": 0.35 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2, + "sell_h_too_old_percent": 0.01 + }, + "protection": {}, + "stoploss": { + "stoploss": -1 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-04-03 14:39:34.819040+00:00" +} diff --git a/Zeus_5.py b/Zeus_5.py new file mode 100644 index 0000000..de03d98 --- /dev/null +++ b/Zeus_5.py @@ -0,0 +1,1121 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), + real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + + +class Zeus_5(IStrategy): + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", "R", "=R", "R", "=R", " bool: + allow_to_buy = True + informative, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + info_previous_last_candle = informative.iloc[-2].squeeze() + info_previous_5_candle = informative.iloc[-5].squeeze() + + btc, _ = self.dp.get_analyzed_dataframe(pair="BTC/USDT", timeframe=self.timeframe) + btc_last_candle = btc.iloc[-1].squeeze() + btc_previous_last_candle = btc.iloc[-2].squeeze() + btc_previous_5_candle = btc.iloc[-5].squeeze() + + # if self.stop_buy_for_all is True: + # if (btc_last_candle['percent20'] > 0) & (btc_last_candle['min200'] == btc_previous_5_candle['min200']): # self.btc_allow_to_buy.value: + # self.stop_buy_for_all = False + # logger.info("1 - BUYING IS ENABLED %s date %s", pair, info_last_candle['date']) + # else: + # logger.info("1 - BUYING IS BLOCKED BY BTC FALL %s date %s", pair, info_last_candle['date']) + # return False + + # if self.stop_buying.get(pair, None) is None: + # print("enable buying tag", pair) + # self.stop_buying[pair] = False + # + # if self.stop_buying[pair] is True: + # if (info_last_candle['min200'] == info_previous_5_candle['min200']): + # # if (info_last_candle['rsi5'] > 20) & (info_last_candle['rsi'] > 30): + # # print("1 - Enable buying ", pair, info_last_candle['date'], info_last_candle['rsi5']) + # logger.info("1 - Enable buying %s date %s", pair, info_last_candle['date']) + # self.stop_buying[pair] = False + # + # if self.stop_buying[pair]: + # allow_to_buy = False + # logger.info("3 - cancel buying %s date %s", pair, info_last_candle['date']) + # else: + # logger.info("3 - accept buying %s date %s", pair, info_last_candle['date']) + + return allow_to_buy + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + expected_profit = 0.01 + minutes = (current_time - trade.open_date_utc).seconds / 60 + days = (current_time - trade.open_date_utc).days + candels_past = int(minutes / 5) + + positive = 0 + negative = 0 + if (candels_past > 12) & (candels_past <= 24): + # print(trade.pair, trade.open_rate, candels_past, minutes, (current_time - trade.open_date_utc).seconds) + sum_percent = 0 + for candel in range(0, candels_past): + df = dataframe.iloc[candel - candels_past].squeeze() + rate = (df['close'] - trade.open_rate) / trade.open_rate + if df['percent'] < 0: + negative = negative + 1 + else: + positive = positive + 1 + sum_percent = sum_percent + df['percent'] + # print(candels_past - candel, df['date'], rate, df['percent'], sum_percent) + # print(trade.pair, "positive=", positive, "negative=", negative, "pourcent=", + # positive / (positive + negative), + # "sum_percent=", sum_percent) + ###### + btc, _ = self.dp.get_analyzed_dataframe(pair="BTC/USDT", timeframe=self.timeframe) + btc_last_candle = btc.iloc[-1].squeeze() + btc_previous_last_candle = btc.iloc[-2].squeeze() + + # if ( + # (btc_last_candle['percent'] < -0.02) | (btc_last_candle['percent5'] < -0.04)) & (current_profit > -0.03): + # self.stop_buy_for_all = True + # return "btc_fall" + + # bb_width_lim = last_candle['bb_width'] / 4 + # bb_width_up = last_candle['bb_upperband'] * (1 - last_candle['bb_width'] / 5) + + if (self.market_overview_pct5 < 0) | (last_candle['pct_change_1_4h'] < 0): + max_percent = 0.004 # last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0.004 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > 0.01) & \ + (last_candle['percent10'] < -0.005) & ((current_time - trade.open_date_utc).seconds >= 3600): + return 'b_percent10' + if (current_profit > max_profit) & \ + ((last_candle['percent'] < - max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value) \ + & (days < self.sell_b_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2) \ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= max_profit) & ( + last_candle['percent3'] < -max_percent): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > max_percent) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + max_percent = 0.005 # last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0.01 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > max_profit) & ( + (last_candle['percent'] < -max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'h_percent_quick' + + # if (last_candle['bb_width'] < 0.02) & (current_profit > 0) & (last_candle['close'] > bb_width_up) & \ + # ((last_candle['percent'] < - bb_width_lim) | (last_candle['percent3'] < - bb_width_lim) | (last_candle['percent5'] < - bb_width_lim)): + # return 'h_bb_width_max' + + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value) \ + & (days < self.sell_h_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2) \ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= max_profit) & ( + last_candle['percent3'] < -max_percent): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > max_percent) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_h_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle[ + 'max200']): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '4h') for pair in pairs] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_1'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_max50'] = (dataframe['max50'] - dataframe['min50']) / dataframe['min50'] + + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma10xpct+'] = dataframe['sma10'] * 1.015 + dataframe['sma10xpct-'] = dataframe['sma10'] * 0.985 + + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / 4)) + + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + dataframe['min_max_close'] = ( + (dataframe['max200'] - dataframe['close']) / (dataframe['close'] - dataframe['min200'])) + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib - tib.min()) / (tib.max() - tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd - tkd.min()) / (tkd.max() - tkd.min()) + # test = dataframe['trend_ichimoku_base'].tail(200) + # dataframe['trend_ichimoku_base_2'] = (test - test.min()) / (test.max() - test.min()) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + + # test = dataframe.copy().tail(200) + # test[buy_crossed_indicator0] = gene_calculator(test, buy_crossed_indicator0) + # test[buy_indicator0] = gene_calculator(test, buy_indicator0) + # dataframe["cond2"] = test[buy_indicator0].div(test[buy_crossed_indicator0]) + + dataframe['atr'] = talib.ATR(dataframe, timeperiod=14) + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + ok = False + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # ok = (self.market_overview['up'] / (self.market_overview['down'] + self.market_overview['up']) > 0.35) + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_h_real.value + OPR = self.buy_h_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_h_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['pct_change_3_1d'] > 0) + & (dataframe['pct_change_5_1d'] > 0) # self.buy_h_pct_5.value) + # & (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_h_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_h_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + & (dataframe['trend_ichimoku_base'] <= 0.1) + , + ['buy', 'buy_tag']] = (1, 'buy_h') + + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_b_real.value + OPR = self.buy_b_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_b_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['pct_change_3_1d'] < 0) # self.buy_b_pct_3.value) + & (dataframe['pct_change_5_1d'] < 0) # self.buy_b_pct_5.value) + # & (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_b_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_b_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + , + ['buy', 'buy_tag']] = (1, 'buy_b') + + for decalage in range(self.buy_decalage_deb_0.value, self.buy_decalage0.value): + # if self.buy_0.value: + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value, + self.buy_decalage0.value + ) + conditions.append(condition1) + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_0_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_1_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_1_bb_lower_5.value) + # & (dataframe['percent_1d'] >= self.buy_1_percent_1d_num.value) + # & (dataframe['percent_4h'] > 0) + # & (dataframe['percent3_4h'] <= self.buy_1_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_2.value, self.buy_decalage2.value): + # if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) # self.buy_real_num0.value / 2) + & (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + & (dataframe['bb_lower_5'] <= self.buy_2_bb_lower_5.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + # d = dataframe.tail(1).iloc[0] + # print(metadata['pair'], d['cond1'], d['bb_width'], d['close'], d['sma10'], d['sma20']) + for decalage in range(self.buy_decalage_deb_3.value, self.buy_decalage3.value): + # if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) + & (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + #  & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + # & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_3_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_3_bb_lower_5.value) + # & (dataframe['percent_4h'] > 0) + # & (dataframe['percent3_4h'] <= self.buy_3_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= 0.12) + & (dataframe['bb_width'] > 0.018) + & (dataframe['rsi'] < 79) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_lower_width_5']) + & (dataframe['close'] < dataframe['min50'] * 1.005) + & (dataframe['percent'].shift(1) > -0.003) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + # d = dataframe.tail(1) + # print(metadata['pair'], d['percent50'].iloc[0], d['buy'].iloc[0], d['buy_tag'].iloc[0]) + + dataframe.loc[ + ( + # (dataframe['min_max50'] >= 0.03) + # & (dataframe['bb_width'] >= 0.02) + (dataframe['cond1'].shift(2) <= 0.75) + & (dataframe['bb_width'] > 0.018) + & (dataframe['rsi'] < 72) + & (dataframe['close'] < dataframe['min50'] * 1.006) + & (dataframe['min_max_close'] > 2) + # & (dataframe['volume'] * dataframe['close'] / 1000 >= 10) + # & (dataframe['percent'] > -0.003) + # & (dataframe['percent'].shift(1) > -0.003) + # & (dataframe['percent5'] > -0.003) + # & (dataframe['min200'].shift(2) <= dataframe['min200']) + # & (dataframe['pct_change_1_1d'] > 0) + # & (dataframe['pct_change_3_1d'] > 0) + # & (dataframe['pct_change_5_1d'] > 0) + ), ['buy', 'buy_tag']] = (1, 'buy_min_max') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + if (len(dataframe) < 1): + return None + last_candle = dataframe.iloc[-1].squeeze() + filled_buys = trade.select_filled_orders('buy') + count_of_buys = len(filled_buys) + condition = (last_candle['cond1'] <= 0.75) & (last_candle['bb_width'] > 0.018) & ( + last_candle['rsi'] < 72) & (last_candle['close'] < last_candle['min50'] * 1.006) & (last_candle['min_max_close'] > 2) + # print(reduce(lambda x, y: x & y, condition)) + if (0 < count_of_buys <= self.max_dca_orders) & (current_profit < -0.15) & (condition): + try: + print(last_candle['cond1'],last_candle['bb_width'],last_candle['rsi'],last_candle['close'],last_candle['percent5'], + last_candle['trend_ichimoku_base']) + # This returns first order stake size + stake_amount = self.config['stake_amount'] * (count_of_buys + 1) # filled_buys[0].cost + # This then calculates current safety order size + # stake_amount = stake_amount * 1.5 #pow(2, count_of_buys) + print("-----------" + trade.pair + " " + str(current_profit) + "---------------------") + print("count_of_buys = " + str(count_of_buys)) + print("stake_amount = " + str(stake_amount)) + return stake_amount + except Exception as exception: + print("exception") + return None + return None diff --git a/Zeus_5_1.json b/Zeus_5_1.json new file mode 100644 index 0000000..e7a9551 --- /dev/null +++ b/Zeus_5_1.json @@ -0,0 +1,114 @@ +{ + "strategy_name": "Zeus_5_1", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_base": 0.08, + "buy_rsi": 70, + "buy_b_bb_lowerband": 1.03, + "buy_b_bb_width": 0.02, + "buy_b_cat": "R", + "buy_h_pct": 0.016, + "buy_h_real": 0.026, + "buy_0_distance": -0.03, + "buy_0_percent20": -0.03, + "buy_1_bb_lower_5": 0.19, + "buy_1_pente_sma10": 0.48, + "buy_1_pente_sma20": 0.33, + "buy_2_bb_lower_5": 0.59, + "buy_2_distance": 0.08, + "buy_2_pente_sma10": 0.46, + "buy_2_pente_sma20": 0.08, + "buy_2_percent20": 0.06, + "buy_3_bb_lower_5": 0.05, + "buy_3_distance": 0.01, + "buy_3_pente_sma10": 0.12, + "buy_3_pente_sma20": 0.27, + "buy_3_percent20": -0.03, + "buy_4_pente_sma10": 0.35, + "buy_4_pente_sma20": 0.22, + "buy_decalage0": 7, + "buy_decalage2": 6, + "buy_decalage3": 7, + "buy_decalage_deb_0": 1, + "buy_decalage_deb_2": 1, + "buy_decalage_deb_3": 1, + "buy_min_horizon": 192, + "buy_real_num0": 0.61, + "buy_real_num1": 0.34, + "buy_real_num2": 0.35 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2, + "sell_h_too_old_percent": 0.01, + "sell_rsi5_1h": 41 + }, + "protection": { + "btc_allow_to_buy": 0.0, + "btc_fall_1": -0.03, + "btc_fall_3": -0.06, + "btc_fall_5": -0.09, + "btc_sell_all_profit": -0.42 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-04-03 14:39:34.819040+00:00" +} diff --git a/Zeus_5_1.py b/Zeus_5_1.py new file mode 100644 index 0000000..443bc82 --- /dev/null +++ b/Zeus_5_1.py @@ -0,0 +1,1072 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_5_1(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", "R", "=R", "R", "=R", " None: + inf_tf = '5m' + pairs = self.dp.current_whitelist() + print("Calcul des pairs informatives") + for pairname in pairs: + self.stop_buying[pairname] = True + print("Fin Calcul des pairs informatives") + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + allow_to_buy = True + informative, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + info_previous_last_candle = informative.iloc[-2].squeeze() + + if self.stop_buy_for_all is True: + if info_last_candle['pct_change_1_1d'] > self.btc_allow_to_buy.value: + self.stop_buy_for_all = False + logger.info("1 -BUYING IS ENABLED %s date %s", pair, info_last_candle['date']) + else: + logger.info("1 -BUYING IS BLOCKED BY BTC FALL %s date %s", pair, info_last_candle['date']) + return False + + if self.stop_buying.get(pair, None) is None: + print("enable buying tag", pair) + self.stop_buying[pair] = False + # if (info_last_candle['rsi_1h'] < self.protection_RSI_enable.value) & (self.stop_buying[pair] is True): + # print("Enable buying", pair) + # self.stop_buying[pair] = False + + # if self.stop_buying[pair] is True: + # if (info_last_candle['rsi_5_1h'] > 20) & (info_previous_last_candle['rsi_5_1h'] <= info_last_candle['rsi_5_1h']) \ + # & (info_last_candle['max_rsi_1h'] < 50): + # print("2 - Enable buying", pair, info_last_candle['date'], info_last_candle['rsi_5_1h'], info_last_candle['max_rsi_1h']) + # self.stop_buying[pair] = False + + if self.stop_buying[pair] is True: + if (info_last_candle['rsi5'] > 20) & (info_last_candle['rsi'] > 30): + # print("1 - Enable buying ", pair, info_last_candle['date'], info_last_candle['rsi5']) + logger.info("1 - Enable buying %s date %s", pair, info_last_candle['date']) + + self.stop_buying[pair] = False + + if self.stop_buying[pair]: + allow_to_buy = False + logger.info("3 - cancel buying %s date %s", pair, info_last_candle['date']) + else: + logger.info("3 - accept buying %s date %s", pair, info_last_candle['date']) + # , (info_last_candle['trend_ichimoku_base'], + # (info_last_candle['close'] < info_last_candle['sma10']), + # (info_previous_last_candle['sma10'], info_last_candle['sma10']))) + + return allow_to_buy + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + if (self.stop_buy_for_all is True) & (current_profit < self.btc_sell_all_profit.value): + return "btc_fall" + + expected_profit = 0.01 + days = (current_time - trade.open_date_utc).days + ###### + + btc, _ = self.dp.get_analyzed_dataframe(pair="BTC/USDT", timeframe=self.timeframe) + btc_last_candle = btc.iloc[-1].squeeze() + btc_previous_last_candle = btc.iloc[-2].squeeze() + + informative, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + info_previous_last_candle = informative.iloc[-2].squeeze() + + if self.stop_buying.get(pair, None) is None: + print("enable buying tag", pair) + self.stop_buying[pair] = False + + if self.stop_buying[pair] is False: + if (last_candle['rsi5'] < 16): + logger.info("0 - Disable buying %s date=%s rsi=%s", pair, last_candle['date'], last_candle['rsi5']) + self.stop_buying[pair] = True + if (current_profit > 0): + return "stop_buying" + + if self.stop_buying[pair] is True: + if (last_candle['rsi5'] > 20) & (last_candle['percent10'] > 0): + logger.info("1 - Enable buying %s date=%s rsi=%s", pair, last_candle['date'], last_candle['rsi5']) + # print("1 - Enable buying ", pair, last_candle['date'], last_candle['rsi5']) + self.stop_buying[pair] = False + + + if (days < 10) & ((btc_last_candle['percent5'] < self.btc_fall_5.value) | (btc_last_candle['percent3'] < self.btc_fall_3.value) | ( + btc_last_candle['percent'] < self.btc_fall_1.value)): + self.stop_buy_for_all = True + return "btc_fall" + + max_percent = last_candle['bb_width'] / 4 #0.005 + max_profit = last_candle['bb_width'] * 3 / 4 # 0.015 + if (self.market_overview_pct5 < 0) | (last_candle['pct_change_1_4h'] < 0): + if (current_profit > max_profit) & ((last_candle['percent'] < -max_percent) | (last_candle['percent3'] < -max_percent) | (last_candle['percent5'] < -max_percent)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value) \ + & (days < self.sell_b_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2) \ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= max_profit) & (last_candle['percent3'] < -max_percent): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > last_candle['bb_width'] / 1.3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + if (current_profit > 0.025) & ((last_candle['percent'] < -max_percent) | (last_candle['percent3'] < -max_percent) | (last_candle['percent5'] < -max_percent)): + return 'h_percent_quick' + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= max_profit) & (last_candle['percent3'] < -max_percent): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > last_candle['bb_width'] / 0.8) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent'] < - self.sell_h_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_1'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / 4)) + + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + # dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + # dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + # dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + # dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + # dataframe['atr'] = talib.ATR(dataframe, timeperiod=14) + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ################### INFORMATIVE BTC 1H + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative["rsi_5"] = talib.RSI(informative, timeperiod=5) + informative['pct_change_1'] = informative['close'].pct_change(1) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + ok = True + #if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # ok = (self.market_overview['up'] / (self.market_overview['down'] + self.market_overview['up']) > 0.35) + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_h_real.value + OPR = self.buy_h_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_h_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['pct_change_3_1d'] > 0) + & (dataframe['pct_change_5_1d'] > 0) #self.buy_h_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_h_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_h_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + & (dataframe['trend_ichimoku_base'] <= 0.1) + , + ['buy', 'buy_tag']] = (1, 'buy_h') + + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_b_real.value + OPR = self.buy_b_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_b_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['pct_change_3_1d'] < 0) # self.buy_b_pct_3.value) + & (dataframe['pct_change_5_1d'] < 0) #self.buy_b_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_b_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_b_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + , + ['buy', 'buy_tag']] = (1, 'buy_b') + + for decalage in range(self.buy_decalage_deb_0.value, self.buy_decalage0.value): + #if self.buy_0.value: + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value, + self.buy_decalage0.value + ) + conditions.append(condition1) + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + #& (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_0_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift(decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_1_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_1_bb_lower_5.value) + #& (dataframe['percent_1d'] >= self.buy_1_percent_1d_num.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_1_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_2.value, self.buy_decalage2.value): + #if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_real_num0.value / 2) + #& (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + & (dataframe['bb_lower_5'] <= self.buy_2_bb_lower_5.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + # d = dataframe.tail(1).iloc[0] + # print(metadata['pair'], d['cond1'], d['bb_width'], d['close'], d['sma10'], d['sma20']) + for decalage in range(self.buy_decalage_deb_3.value, self.buy_decalage3.value): + #if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) + #& (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_3_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_3_bb_lower_5.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_3_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + # dataframe.loc[ + # ( + # (dataframe['cond1'] <= 1) + # & (dataframe['close'] < dataframe['sma10']) + # & (dataframe['open'] < dataframe['sma20']) + # & (dataframe['open'] < dataframe['sma10']) + # & (dataframe['close'] <= dataframe['min'] * 1.005) + # & (dataframe['moy200_12'] == dataframe['min200']) + # & (dataframe['volume10'].shift(1) * dataframe['close'].shift(1) / 1000 >= 10) + # & (dataframe['percent20'] >= -0.005) + # + # ), ['buy', 'buy_tag']] = (1, 'buy_near_m50') + + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= 0.12) + & (dataframe['rsi'] < 79) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_lower_width_5']) + & (dataframe['close'] < dataframe['min50'] * 1.005) + # & (dataframe['close'] < dataframe['min50'] * 1.01) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + # d = dataframe.tail(1) + # print(metadata['pair'], d['percent50'].iloc[0], d['buy'].iloc[0], d['buy_tag'].iloc[0]) + + dataframe.loc[ + ( + # (dataframe['min_max50'] >= 0.03) + # & (dataframe['bb_width'] >= 0.02) + (dataframe['cond1'].shift(2) <= 1.2) + & (dataframe['rsi'] < 72) + & (dataframe['close'] < dataframe['min50'] * 1.005) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + & (dataframe['pct_change_1_1d'] > 0) + # & (dataframe['pct_change_3_1d'] > 0) + # & (dataframe['pct_change_5_1d'] > 0) + ), ['buy', 'buy_tag']] = (1, 'buy_min_max') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe diff --git a/Zeus_5_2.ALGOjson b/Zeus_5_2.ALGOjson new file mode 100644 index 0000000..768b7b1 --- /dev/null +++ b/Zeus_5_2.ALGOjson @@ -0,0 +1,114 @@ +{ + "strategy_name": "Zeus_5_2", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_0_distance": 0.09, + "buy_0_percent20": -0.03, + "buy_1_bb_lower_5": 0.51, + "buy_2_bb_lower_5": 0.28, + "buy_2_distance": 0.1, + "buy_2_percent20": -0.1, + "buy_3_bb_lower_5": 0.19, + "buy_3_distance": 0.01, + "buy_3_percent20": -0.1, + "buy_b_bb_lowerband": 1.03, + "buy_b_bb_width": 0.15, + "buy_b_cat": ">R", + "buy_b_pct": 0.002, + "buy_b_pct_1": 0.2, + "buy_b_pct_3": -0.19, + "buy_b_pct_5": 0.1, + "buy_b_real": 0.243, + "buy_b_sma": "sma5_1d", + "buy_b_sma_close": "sma5_1d", + "buy_base": 0.2, + "buy_decalage0": 7, + "buy_decalage2": 7, + "buy_decalage3": 6, + "buy_decalage_deb_0": 0, + "buy_decalage_deb_2": 2, + "buy_decalage_deb_3": 3, + "buy_h_bb_lowerband": 1.04, + "buy_h_bb_width": 0.15, + "buy_h_cat": "=R", + "buy_h_pct": 0.015, + "buy_h_pct_1": 0.16, + "buy_h_pct_3": 0.19, + "buy_h_pct_5": 0.17, + "buy_h_real": 0.9762, + "buy_h_sma": "sma10_1d", + "buy_h_sma_close": "sma3_1d", + "buy_min_horizon": 70, + "buy_real_num0": 0.51, + "buy_real_num1": 0.55, + "buy_real_num2": 1.35, + "buy_rsi": 37, + "decalage_b": 3, + "decalage_h": 3 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2, + "sell_h_too_old_percent": 0.01 + }, + "protection": { + "protection_lost_candles": 5, + "protection_lost_percent": 0.06 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-05-28 19:28:12.187097+00:00" +} \ No newline at end of file diff --git a/Zeus_5_2.BTCjson b/Zeus_5_2.BTCjson new file mode 100644 index 0000000..77a2d2a --- /dev/null +++ b/Zeus_5_2.BTCjson @@ -0,0 +1,114 @@ +{ + "strategy_name": "Zeus_5_2", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_0_distance": 0.0, + "buy_0_percent20": 0.05, + "buy_1_bb_lower_5": 0.08, + "buy_2_bb_lower_5": 0.24, + "buy_2_distance": -0.09, + "buy_2_percent20": 0.05, + "buy_3_bb_lower_5": 0.01, + "buy_3_distance": 0.07, + "buy_3_percent20": -0.02, + "buy_b_bb_lowerband": 1.02, + "buy_b_bb_width": 0.12, + "buy_b_cat": ">R", + "buy_b_pct": 0.015, + "buy_b_pct_1": 0.03, + "buy_b_pct_3": 0.14, + "buy_b_pct_5": 0.19, + "buy_b_real": 0.5889, + "buy_b_sma": "sma5_1d", + "buy_b_sma_close": "sma10_1d", + "buy_base": 0.13, + "buy_decalage0": 6, + "buy_decalage2": 7, + "buy_decalage3": 8, + "buy_decalage_deb_0": 3, + "buy_decalage_deb_2": 2, + "buy_decalage_deb_3": 1, + "buy_h_bb_lowerband": 1.0, + "buy_h_bb_width": 0.05, + "buy_h_cat": ">R", + "buy_h_pct": 0.007, + "buy_h_pct_1": 0.12, + "buy_h_pct_3": 0.1, + "buy_h_pct_5": -0.07, + "buy_h_real": 0.2931, + "buy_h_sma": "sma10_1d", + "buy_h_sma_close": "sma3_1d", + "buy_min_horizon": 61, + "buy_real_num0": 0.15, + "buy_real_num1": 0.81, + "buy_real_num2": 0.04, + "buy_rsi": 46, + "decalage_b": 1, + "decalage_h": 2 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2, + "sell_h_too_old_percent": 0.01 + }, + "protection": { + "protection_lost_candles": 5, + "protection_lost_percent": 0.06 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-05-28 18:36:28.951431+00:00" +} \ No newline at end of file diff --git a/Zeus_5_2.json b/Zeus_5_2.json new file mode 100644 index 0000000..6fad82b --- /dev/null +++ b/Zeus_5_2.json @@ -0,0 +1,114 @@ +{ + "strategy_name": "Zeus_5_2", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_base": 0.08, + "buy_rsi": 70, + "buy_b_bb_lowerband": 1.03, + "buy_b_bb_width": 0.02, + "buy_b_cat": "R", + "buy_h_pct": 0.016, + "buy_h_real": 0.026, + "buy_0_distance": -0.03, + "buy_0_percent20": -0.03, + "buy_1_bb_lower_5": 0.19, + "buy_1_pente_sma10": 0.48, + "buy_1_pente_sma20": 0.33, + "buy_2_bb_lower_5": 0.59, + "buy_2_distance": 0.08, + "buy_2_pente_sma10": 0.46, + "buy_2_pente_sma20": 0.08, + "buy_2_percent20": 0.06, + "buy_3_bb_lower_5": 0.05, + "buy_3_distance": 0.01, + "buy_3_pente_sma10": 0.12, + "buy_3_pente_sma20": 0.27, + "buy_3_percent20": -0.03, + "buy_4_pente_sma10": 0.35, + "buy_4_pente_sma20": 0.22, + "buy_decalage0": 7, + "buy_decalage2": 6, + "buy_decalage3": 7, + "buy_decalage_deb_0": 1, + "buy_decalage_deb_2": 1, + "buy_decalage_deb_3": 1, + "buy_min_horizon": 192, + "buy_real_num0": 0.61, + "buy_real_num1": 0.34, + "buy_real_num2": 0.35 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2, + "sell_h_too_old_percent": 0.01, + "sell_rsi5_1h": 41 + }, + "protection": { + "btc_allow_to_buy": 0.0, + "btc_fall_1": -0.03, + "btc_fall_3": -0.06, + "btc_fall_5": -0.09, + "btc_sell_all_profit": -0.42 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-05-28 20:13:07.504171+00:00" +} \ No newline at end of file diff --git a/Zeus_5_2.py b/Zeus_5_2.py new file mode 100644 index 0000000..1908c26 --- /dev/null +++ b/Zeus_5_2.py @@ -0,0 +1,1104 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_5_2(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", "R", "=R", "R", "=R", " None: + inf_tf = '5m' + pairs = self.dp.current_whitelist() + print("Calcul des pairs informatives") + for pairname in pairs: + self.stop_buying[pairname] = True + print("Fin Calcul des pairs informatives") + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + allow_to_buy = True + informative, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + info_previous_last_candle = informative.iloc[-2].squeeze() + + if self.stop_buy_for_all is True: + if info_last_candle['pct_change_1_1d'] > self.btc_allow_to_buy.value: + self.stop_buy_for_all = False + logger.info("1 -BUYING IS ENABLED %s date %s", pair, info_last_candle['date']) + else: + logger.info("1 -BUYING IS BLOCKED BY BTC FALL %s date %s", pair, info_last_candle['date']) + return False + + if self.stop_buying.get(pair, None) is None: + print("enable buying tag", pair) + self.stop_buying[pair] = False + # if (info_last_candle['rsi_1h'] < self.protection_RSI_enable.value) & (self.stop_buying[pair] is True): + # print("Enable buying", pair) + # self.stop_buying[pair] = False + + # if self.stop_buying[pair] is True: + # if (info_last_candle['rsi_5_1h'] > 20) & (info_previous_last_candle['rsi_5_1h'] <= info_last_candle['rsi_5_1h']) \ + # & (info_last_candle['max_rsi_1h'] < 50): + # print("2 - Enable buying", pair, info_last_candle['date'], info_last_candle['rsi_5_1h'], info_last_candle['max_rsi_1h']) + # self.stop_buying[pair] = False + + if self.stop_buying[pair] is True: + if (info_last_candle['rsi5'] > 20) & (info_last_candle['rsi'] > 30): + # print("1 - Enable buying ", pair, info_last_candle['date'], info_last_candle['rsi5']) + logger.info("1 - Enable buying %s date %s", pair, info_last_candle['date']) + + self.stop_buying[pair] = False + + if self.stop_buying[pair]: + allow_to_buy = False + logger.info("3 - cancel buying %s date %s", pair, info_last_candle['date']) + else: + logger.info("3 - accept buying %s date %s", pair, info_last_candle['date']) + # , (info_last_candle['trend_ichimoku_base'], + # (info_last_candle['close'] < info_last_candle['sma10']), + # (info_previous_last_candle['sma10'], info_last_candle['sma10']))) + + return allow_to_buy + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + if (self.stop_buy_for_all is True) & (current_profit < self.btc_sell_all_profit.value): + return "btc_fall" + + expected_profit = 0.01 + days = (current_time - trade.open_date_utc).days + ###### + + btc, _ = self.dp.get_analyzed_dataframe(pair="BTC/USDT", timeframe=self.timeframe) + btc_last_candle = btc.iloc[-1].squeeze() + btc_previous_last_candle = btc.iloc[-2].squeeze() + + informative, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + info_previous_last_candle = informative.iloc[-2].squeeze() + + if self.stop_buying.get(pair, None) is None: + print("enable buying tag", pair) + self.stop_buying[pair] = False + + if self.stop_buying[pair] is False: + if (last_candle['rsi5'] < 16): + logger.info("0 - Disable buying %s date=%s rsi=%s", pair, last_candle['date'], last_candle['rsi5']) + self.stop_buying[pair] = True + if (current_profit > 0): + return "stop_buying" + + if self.stop_buying[pair] is True: + if (last_candle['rsi5'] > 20) & (last_candle['percent10'] > 0): + logger.info("1 - Enable buying %s date=%s rsi=%s", pair, last_candle['date'], last_candle['rsi5']) + # print("1 - Enable buying ", pair, last_candle['date'], last_candle['rsi5']) + self.stop_buying[pair] = False + + + if (days < 10) & ((btc_last_candle['percent5'] < self.btc_fall_5.value) | (btc_last_candle['percent3'] < self.btc_fall_3.value) | ( + btc_last_candle['percent'] < self.btc_fall_1.value)): + self.stop_buy_for_all = True + return "btc_fall" + + if (self.market_overview_pct5 < 0) | (last_candle['pct_change_1_4h'] < 0): + max_percent = last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > max_profit) & \ + ((last_candle['percent'] < - max_percent) | (last_candle['percent3'] < -max_percent) | (last_candle['percent5'] < -max_percent)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value) \ + & (days < self.sell_b_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2) \ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= max_profit) & (last_candle['percent3'] < -max_percent): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > max_percent) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + max_percent = last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0.0 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > max_profit) & ((last_candle['percent'] < -max_percent) | (last_candle['percent3'] < -max_percent) | (last_candle['percent5'] < -max_percent)): + return 'h_percent_quick' + + # if (last_candle['bb_width'] < 0.02) & (current_profit > 0) & (last_candle['close'] > bb_width_up) & \ + # ((last_candle['percent'] < - bb_width_lim) | (last_candle['percent3'] < - bb_width_lim) | (last_candle['percent5'] < - bb_width_lim)): + # return 'h_bb_width_max' + + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= max_profit) & (last_candle['percent3'] < -max_percent): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > max_percent) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent'] < - self.sell_h_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_1'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / 4)) + + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + dataframe['min_max_close'] = ((dataframe['max200'] - dataframe['close']) / (dataframe['close'] - dataframe['min200'])) + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + # dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + # dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + # dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + # dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + # dataframe['atr'] = talib.ATR(dataframe, timeperiod=14) + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ################### INFORMATIVE BTC 1H + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative["rsi_5"] = talib.RSI(informative, timeperiod=5) + informative['pct_change_1'] = informative['close'].pct_change(1) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + ok = True + #if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # ok = (self.market_overview['up'] / (self.market_overview['down'] + self.market_overview['up']) > 0.35) + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_h_real.value + OPR = self.buy_h_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_h_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + # & (dataframe['pct_change_3_1d'] > 0) + # & (dataframe['pct_change_5_1d'] > 0) #self.buy_h_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_h_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_h_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + & (dataframe['trend_ichimoku_base'] <= 0.1) + , + ['buy', 'buy_tag']] = (1, 'buy_h') + + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_b_real.value + OPR = self.buy_b_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_b_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + # & (dataframe['pct_change_3_1d'] < 0) # self.buy_b_pct_3.value) + # & (dataframe['pct_change_5_1d'] < 0) #self.buy_b_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_b_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_b_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + , + ['buy', 'buy_tag']] = (1, 'buy_b') + + for decalage in range(self.buy_decalage_deb_0.value, self.buy_decalage0.value): + #if self.buy_0.value: + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value, + self.buy_decalage0.value + ) + conditions.append(condition1) + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + #& (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_0_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift(decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_1_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_1_bb_lower_5.value) + #& (dataframe['percent_1d'] >= self.buy_1_percent_1d_num.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_1_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_2.value, self.buy_decalage2.value): + #if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_real_num0.value / 2) + #& (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + & (dataframe['bb_lower_5'] <= self.buy_2_bb_lower_5.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + # d = dataframe.tail(1).iloc[0] + # print(metadata['pair'], d['cond1'], d['bb_width'], d['close'], d['sma10'], d['sma20']) + for decalage in range(self.buy_decalage_deb_3.value, self.buy_decalage3.value): + #if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) + #& (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_3_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_3_bb_lower_5.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_3_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + # dataframe.loc[ + # ( + # (dataframe['cond1'] <= 1) + # & (dataframe['close'] < dataframe['sma10']) + # & (dataframe['open'] < dataframe['sma20']) + # & (dataframe['open'] < dataframe['sma10']) + # & (dataframe['close'] <= dataframe['min'] * 1.005) + # & (dataframe['moy200_12'] == dataframe['min200']) + # & (dataframe['volume10'].shift(1) * dataframe['close'].shift(1) / 1000 >= 10) + # & (dataframe['percent20'] >= -0.005) + # + # ), ['buy', 'buy_tag']] = (1, 'buy_near_m50') + + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= 0.12) + & (dataframe['rsi'] < 79) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_lower_width_5']) + & (dataframe['close'] < dataframe['min50'] * 1.005) + & (dataframe['volume'] * dataframe['close'] / 1000 >= 10) + & (dataframe['percent'] > -0.003) + & (dataframe['percent'].shift(1) > -0.003) + # & (dataframe['percent5'] > -0.025) + + # & (dataframe['close'] / dataframe['close'].shift(288) < 1.05) + # & (dataframe['percent'] > -0.005) + # & (dataframe['min_max50'] >= 0.02) + # & (dataframe['min200'].shift(2) <= dataframe['min200']) + # & (dataframe['max200'] / dataframe['min50'] >= 1.02) + # & (dataframe['bb_lowerband'].shift(1) <= dataframe['bb_lowerband']) + # & (dataframe['close'] < dataframe['min50'] * 1.01) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + # d = dataframe.tail(1) + # print(metadata['pair'], d['percent50'].iloc[0], d['buy'].iloc[0], d['buy_tag'].iloc[0]) + + dataframe.loc[ + ( + # (dataframe['min_max50'] >= 0.03) + # & (dataframe['bb_width'] >= 0.02) + (dataframe['cond1'].shift(2) <= 1.2) + & (dataframe['rsi'] < 72) + & (dataframe['close'] < dataframe['min50'] * 1.006) + & (dataframe['min_max_close'] > 1) + & (dataframe['volume'] * dataframe['close'] / 1000 >= 10) + & (dataframe['percent'] > -0.003) + & (dataframe['percent'].shift(1) > -0.003) + # & (dataframe['percent5'] > -0.025) + # & (dataframe['min200'].shift(2) <= dataframe['min200']) + & (dataframe['pct_change_1_1d'] > 0) + # & (dataframe['close'] / dataframe['close'].shift(288) < 1.05) + # & (dataframe['pct_change_3_1d'] > 0) + # & (dataframe['pct_change_5_1d'] > 0) + ), ['buy', 'buy_tag']] = (1, 'buy_min_max') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe diff --git a/Zeus_5_3.json b/Zeus_5_3.json new file mode 100644 index 0000000..9c4df7e --- /dev/null +++ b/Zeus_5_3.json @@ -0,0 +1,105 @@ +{ + "strategy_name": "Zeus_5_3", + "params": { + "roi": { + "0": 10 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_b_bb_lowerband": 1.03, + "buy_b_bb_width": 0.02, + "buy_b_cat": "R", + "buy_h_pct": 0.016, + "buy_h_real": 0.026, + "buy_0_distance": -0.03, + "buy_0_percent20": -0.03, + "buy_1_bb_lower_5": 0.19, + "buy_1_pente_sma10": 0.48, + "buy_1_pente_sma20": 0.33, + "buy_2_bb_lower_5": 0.59, + "buy_2_distance": 0.08, + "buy_2_pente_sma10": 0.46, + "buy_2_pente_sma20": 0.08, + "buy_2_percent20": 0.06, + "buy_3_bb_lower_5": 0.05, + "buy_3_distance": 0.01, + "buy_3_pente_sma10": 0.12, + "buy_3_pente_sma20": 0.27, + "buy_3_percent20": -0.03, + "buy_4_pente_sma10": 0.35, + "buy_4_pente_sma20": 0.22, + "buy_decalage0": 7, + "buy_decalage2": 6, + "buy_decalage3": 7, + "buy_decalage_deb_0": 1, + "buy_decalage_deb_2": 1, + "buy_decalage_deb_3": 1, + "buy_min_horizon": 192, + "buy_real_num0": 0.61, + "buy_real_num1": 0.34, + "buy_real_num2": 0.35 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2, + "sell_h_too_old_percent": 0.01 + }, + "protection": {}, + "stoploss": { + "stoploss": -1 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-04-03 14:39:34.819040+00:00" +} diff --git a/Zeus_5_3.py b/Zeus_5_3.py new file mode 100644 index 0000000..5ca8139 --- /dev/null +++ b/Zeus_5_3.py @@ -0,0 +1,1144 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), + real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + + +class Zeus_5_3(IStrategy): + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", "R", "=R", "R", "=R", " bool: + allow_to_buy = True + informative, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + info_previous_last_candle = informative.iloc[-2].squeeze() + info_previous_5_candle = informative.iloc[-5].squeeze() + + btc, _ = self.dp.get_analyzed_dataframe(pair="BTC/USDT", timeframe=self.timeframe) + btc_last_candle = btc.iloc[-1].squeeze() + btc_previous_last_candle = btc.iloc[-2].squeeze() + btc_previous_5_candle = btc.iloc[-5].squeeze() + + # if self.stop_buy_for_all is True: + # if (btc_last_candle['percent20'] > 0) & (btc_last_candle['min200'] == btc_previous_5_candle['min200']): # self.btc_allow_to_buy.value: + # self.stop_buy_for_all = False + # logger.info("1 - BUYING IS ENABLED %s date %s", pair, info_last_candle['date']) + # else: + # logger.info("1 - BUYING IS BLOCKED BY BTC FALL %s date %s", pair, info_last_candle['date']) + # return False + + # if self.stop_buying.get(pair, None) is None: + # print("enable buying tag", pair) + # self.stop_buying[pair] = False + # + # if self.stop_buying[pair] is True: + # if (info_last_candle['min200'] == info_previous_5_candle['min200']): + # # if (info_last_candle['rsi5'] > 20) & (info_last_candle['rsi'] > 30): + # # print("1 - Enable buying ", pair, info_last_candle['date'], info_last_candle['rsi5']) + # logger.info("1 - Enable buying %s date %s", pair, info_last_candle['date']) + # self.stop_buying[pair] = False + # + # if self.stop_buying[pair]: + # allow_to_buy = False + # logger.info("3 - cancel buying %s date %s", pair, info_last_candle['date']) + # else: + # logger.info("3 - accept buying %s date %s", pair, info_last_candle['date']) + + return allow_to_buy + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + expected_profit = 0.01 + minutes = (current_time - trade.open_date_utc).seconds / 60 + days = (current_time - trade.open_date_utc).days + candels_past = int(minutes / 5) + + positive = 0 + negative = 0 + if (candels_past > 12) & (candels_past <= 24): + # print(trade.pair, trade.open_rate, candels_past, minutes, (current_time - trade.open_date_utc).seconds) + sum_percent = 0 + for candel in range(0, candels_past): + df = dataframe.iloc[candel - candels_past].squeeze() + rate = (df['close'] - trade.open_rate) / trade.open_rate + if df['percent'] < 0: + negative = negative + 1 + else: + positive = positive + 1 + sum_percent = sum_percent + df['percent'] + # print(candels_past - candel, df['date'], rate, df['percent'], sum_percent) + # print(trade.pair, "positive=", positive, "negative=", negative, "pourcent=", + # positive / (positive + negative), + # "sum_percent=", sum_percent) + ###### + btc, _ = self.dp.get_analyzed_dataframe(pair="BTC/USDT", timeframe=self.timeframe) + btc_last_candle = btc.iloc[-1].squeeze() + btc_previous_last_candle = btc.iloc[-2].squeeze() + + # if ( + # (btc_last_candle['percent'] < -0.02) | (btc_last_candle['percent5'] < -0.04)) & (current_profit > -0.03): + # self.stop_buy_for_all = True + # return "btc_fall" + + # bb_width_lim = last_candle['bb_width'] / 4 + # bb_width_up = last_candle['bb_upperband'] * (1 - last_candle['bb_width'] / 5) + + if (self.market_overview_pct5 < 0) | (last_candle['pct_change_1_4h'] < 0): + max_percent = 0.004 # last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0.004 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > 0.01) & \ + (last_candle['percent10'] < -0.005) & ((current_time - trade.open_date_utc).seconds >= 3600): + return 'b_percent10' + if (current_profit > max_profit) & \ + ((last_candle['percent'] < - max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value) \ + & (days < self.sell_b_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2) \ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= max_profit) & ( + last_candle['percent3'] < -max_percent): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > max_percent) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + max_percent = 0.005 # last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0.01 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > max_profit) & ( + (last_candle['percent'] < -max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'h_percent_quick' + + # if (last_candle['bb_width'] < 0.02) & (current_profit > 0) & (last_candle['close'] > bb_width_up) & \ + # ((last_candle['percent'] < - bb_width_lim) | (last_candle['percent3'] < - bb_width_lim) | (last_candle['percent5'] < - bb_width_lim)): + # return 'h_bb_width_max' + + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value) \ + & (days < self.sell_h_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2) \ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= max_profit) & ( + last_candle['percent3'] < -max_percent): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > max_percent) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_h_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle[ + 'max200']): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '4h') for pair in pairs] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + # if not self.config['runmode'].value == 'hyperopt': + # dataframe = self.calculateIndicators(dataframe, metadata) + return dataframe + + def calculateIndicators(self, dataframe, metadata): + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_1'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_max50'] = (dataframe['max50'] - dataframe['min50']) / dataframe['min50'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma10xpct+'] = dataframe['sma10'] * 1.015 + dataframe['sma10xpct-'] = dataframe['sma10'] * 0.985 + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / 4)) + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + dataframe['min_max_close'] = ( + (dataframe['max200'] - dataframe['close']) / (dataframe['close'] - dataframe['min200'])) + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib - tib.min()) / (tib.max() - tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd - tkd.min()) / (tkd.max() - tkd.min()) + # test = dataframe['trend_ichimoku_base'].tail(200) + # dataframe['trend_ichimoku_base_2'] = (test - test.min()) / (test.max() - test.min()) + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + # test = dataframe.copy().tail(200) + # test[buy_crossed_indicator0] = gene_calculator(test, buy_crossed_indicator0) + # test[buy_indicator0] = gene_calculator(test, buy_indicator0) + # dataframe["cond2"] = test[buy_indicator0].div(test[buy_crossed_indicator0]) + dataframe['atr'] = talib.ATR(dataframe, timeperiod=14) + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['sar'] = talib.SAR(informative) + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + # pprint_df(dataframe['date'], dataframe['cond1'], dataframe['trend_ichimoku_base']) + # pprint_df(dataframe) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # if self.config['runmode'].value == 'hyperopt': + + dataframe = self.calculateIndicators(dataframe, metadata) + + ok = False + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # ok = (self.market_overview['up'] / (self.market_overview['down'] + self.market_overview['up']) > 0.35) + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_h_real.value + OPR = self.buy_h_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_h_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['pct_change_3_1d'] > 0) + & (dataframe['pct_change_5_1d'] > 0) # self.buy_h_pct_5.value) + # & (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_h_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_h_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + & (dataframe['trend_ichimoku_base'] <= 0.1) + , + ['buy', 'buy_tag']] = (1, 'buy_h') + + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_b_real.value + OPR = self.buy_b_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_b_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['pct_change_3_1d'] < 0) # self.buy_b_pct_3.value) + & (dataframe['pct_change_5_1d'] < 0) # self.buy_b_pct_5.value) + # & (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_b_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_b_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + , + ['buy', 'buy_tag']] = (1, 'buy_b') + + for decalage in range(self.buy_decalage_deb_0.value, self.buy_decalage0.value): + # if self.buy_0.value: + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value, + self.buy_decalage0.value + ) + conditions.append(condition1) + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_0_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_1_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_1_bb_lower_5.value) + # & (dataframe['percent_1d'] >= self.buy_1_percent_1d_num.value) + # & (dataframe['percent_4h'] > 0) + # & (dataframe['percent3_4h'] <= self.buy_1_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_2.value, self.buy_decalage2.value): + # if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) # self.buy_real_num0.value / 2) + & (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + & (dataframe['bb_lower_5'] <= self.buy_2_bb_lower_5.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + # d = dataframe.tail(1).iloc[0] + # print(metadata['pair'], d['cond1'], d['bb_width'], d['close'], d['sma10'], d['sma20']) + for decalage in range(self.buy_decalage_deb_3.value, self.buy_decalage3.value): + # if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) + & (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + #  & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + # & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_3_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_3_bb_lower_5.value) + # & (dataframe['percent_4h'] > 0) + # & (dataframe['percent3_4h'] <= self.buy_3_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + # dataframe.loc[ + # ( + # (dataframe['cond1'] <= 1) + # & (dataframe['close'] < dataframe['sma10']) + # & (dataframe['open'] < dataframe['sma20']) + # & (dataframe['open'] < dataframe['sma10']) + # & (dataframe['close'] <= dataframe['min'] * 1.005) + # & (dataframe['moy200_12'] == dataframe['min200']) + # & (dataframe['volume10'].shift(1) * dataframe['close'].shift(1) / 1000 >= 10) + # & (dataframe['percent20'] >= -0.005) + # + # ), ['buy', 'buy_tag']] = (1, 'buy_near_m50') + + coef = 4 + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= 0.12) + & (dataframe['bb_width'] > 0.018) + & (dataframe['rsi'] < 79) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_lower_width_5']) + & (dataframe['close'] < dataframe['min50'] * 1.005) + # & (dataframe['volume'] * dataframe['close'] / 1000 >= 10) + # & (dataframe['percent'] > - 0.003) + & (dataframe['percent'].shift(1) > -0.003) + # & (dataframe['percent5'] > -0.025) + # & (dataframe['pct_change_1_1d'] > 0) + # & (dataframe['pct_change_3_1d'] > 0) + # & (dataframe['pct_change_5_1d'] > 0) + # & (dataframe['close'] / dataframe['close'].shift(288) < 1.05) + # & (dataframe['percent'] > -0.005) + # & (dataframe['min_max50'] >= 0.02) + # & (dataframe['min200'].shift(2) <= dataframe['min200']) + # & (dataframe['max200'] / dataframe['min50'] >= 1.02) + # & (dataframe['bb_lowerband'].shift(1) <= dataframe['bb_lowerband']) + # & (dataframe['close'] < dataframe['min50'] * 1.01) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + # d = dataframe.tail(1) + # print(metadata['pair'], d['percent50'].iloc[0], d['buy'].iloc[0], d['buy_tag'].iloc[0]) + + dataframe.loc[ + ( + # (dataframe['min_max50'] >= 0.03) + # & (dataframe['bb_width'] >= 0.02) + (dataframe['cond1'].shift(2) <= 0.75) + & (dataframe['bb_width'] > 0.018) + & (dataframe['rsi'] < 72) + & (dataframe['close'] < dataframe['min50'] * 1.006) + & (dataframe['min_max_close'] > 2) + # & (dataframe['volume'] * dataframe['close'] / 1000 >= 10) + # & (dataframe['percent'] > -0.003) + # & (dataframe['percent'].shift(1) > -0.003) + # & (dataframe['percent5'] > -0.003) + # & (dataframe['min200'].shift(2) <= dataframe['min200']) + # & (dataframe['pct_change_1_1d'] > 0) + # & (dataframe['pct_change_3_1d'] > 0) + # & (dataframe['pct_change_5_1d'] > 0) + ), ['buy', 'buy_tag']] = (1, 'buy_min_max') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + if (len(dataframe) < 1): + return None + last_candle = dataframe.iloc[-1].squeeze() + filled_buys = trade.select_filled_orders('buy') + count_of_buys = len(filled_buys) + condition = (last_candle['cond1'] <= 0.75) & (last_candle['bb_width'] > 0.018) & ( + last_candle['rsi'] < 72) & (last_candle['close'] < last_candle['min50'] * 1.006) & (last_candle['min_max_close'] > 2) + # print(reduce(lambda x, y: x & y, condition)) + if (0 < count_of_buys <= self.max_dca_orders) & (current_profit < -0.1) & (condition): + try: + print(last_candle['cond1'],last_candle['bb_width'],last_candle['rsi'],last_candle['close'],last_candle['percent5'], + last_candle['trend_ichimoku_base']) + # This returns first order stake size + stake_amount = self.config['stake_amount'] # filled_buys[0].cost + # This then calculates current safety order size + stake_amount = stake_amount * 1.5 #pow(2, count_of_buys) + print("-----------" + trade.pair + " " + str(current_profit) + "---------------------") + print("count_of_buys = " + str(count_of_buys)) + print("stake_amount = " + str(stake_amount)) + return stake_amount + except Exception as exception: + print("exception") + return None + return None diff --git a/Zeus_6.json b/Zeus_6.json new file mode 100644 index 0000000..3467653 --- /dev/null +++ b/Zeus_6.json @@ -0,0 +1,114 @@ +{ + "strategy_name": "Zeus_6", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_base": 0.08, + "buy_rsi": 70, + "buy_b_bb_lowerband": 1.03, + "buy_b_bb_width": 0.02, + "buy_b_cat": "R", + "buy_h_pct": 0.016, + "buy_h_real": 0.026, + "buy_0_distance": -0.03, + "buy_0_percent20": -0.03, + "buy_1_bb_lower_5": 0.19, + "buy_1_pente_sma10": 0.48, + "buy_1_pente_sma20": 0.33, + "buy_2_bb_lower_5": 0.59, + "buy_2_distance": 0.08, + "buy_2_pente_sma10": 0.46, + "buy_2_pente_sma20": 0.08, + "buy_2_percent20": 0.06, + "buy_3_bb_lower_5": 0.05, + "buy_3_distance": 0.01, + "buy_3_pente_sma10": 0.12, + "buy_3_pente_sma20": 0.27, + "buy_3_percent20": -0.03, + "buy_4_pente_sma10": 0.35, + "buy_4_pente_sma20": 0.22, + "buy_decalage0": 7, + "buy_decalage2": 6, + "buy_decalage3": 7, + "buy_decalage_deb_0": 1, + "buy_decalage_deb_2": 1, + "buy_decalage_deb_3": 1, + "buy_min_horizon": 192, + "buy_real_num0": 0.61, + "buy_real_num1": 0.34, + "buy_real_num2": 0.35 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2, + "sell_h_too_old_percent": 0.01, + "sell_rsi5_1h": 41 + }, + "protection": { + "btc_allow_to_buy": 0.0, + "btc_fall_1": -0.01, + "btc_fall_3": -0.02, + "btc_fall_5": -0.03, + "btc_sell_all_profit": -0.42 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-04-03 14:39:34.819040+00:00" +} diff --git a/Zeus_6.json2 b/Zeus_6.json2 new file mode 100644 index 0000000..c78edc1 --- /dev/null +++ b/Zeus_6.json2 @@ -0,0 +1,111 @@ +{ + "strategy_name": "Zeus_6", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_0_distance": 0.01, + "buy_0_percent20": -0.1, + "buy_1_bb_lower_5": 0.32, + "buy_2_bb_lower_5": 0.28, + "buy_2_distance": -0.05, + "buy_2_percent20": -0.09, + "buy_3_bb_lower_5": 0.0, + "buy_3_distance": 0.1, + "buy_3_percent20": 0.09, + "buy_b_bb_lowerband": 1.0, + "buy_b_bb_width": 0.07, + "buy_b_cat": "=R", + "buy_b_pct": 0.012, + "buy_b_pct_1": -0.19, + "buy_b_pct_3": 0.19, + "buy_b_pct_5": 0.05, + "buy_b_real": 0.3281, + "buy_b_sma": "sma10_1d", + "buy_b_sma_close": "sma5_1d", + "buy_base": 0.09, + "buy_decalage0": 8, + "buy_decalage2": 8, + "buy_decalage3": 8, + "buy_decalage_deb_0": 3, + "buy_decalage_deb_2": 1, + "buy_decalage_deb_3": 0, + "buy_diff": 0.03, + "buy_h_bb_lowerband": 1.05, + "buy_h_bb_width": 0.09, + "buy_h_cat": "", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_6(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", "R", "=R", "R", "=R", " None: + inf_tf = '5m' + pairs = self.dp.current_whitelist() + print("Calcul des pairs informatives") + for pairname in pairs: + self.stop_buying[pairname] = True + print("Fin Calcul des pairs informatives") + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + allow_to_buy = True + informative, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + info_previous_last_candle = informative.iloc[-2].squeeze() + info_previous_5_candle = informative.iloc[-5].squeeze() + + btc, _ = self.dp.get_analyzed_dataframe(pair="BTC/USDT", timeframe=self.timeframe) + btc_last_candle = btc.iloc[-1].squeeze() + btc_previous_last_candle = btc.iloc[-2].squeeze() + btc_previous_5_candle = btc.iloc[-5].squeeze() + + if self.stop_buy_for_all is True: + if (btc_last_candle['percent20'] > 0) & (btc_last_candle['min200'] == btc_previous_5_candle['min200']): # self.btc_allow_to_buy.value: + self.stop_buy_for_all = False + logger.info("1 - BUYING IS ENABLED %s date %s", pair, info_last_candle['date']) + else: + logger.info("1 - BUYING IS BLOCKED BY BTC FALL %s date %s", pair, info_last_candle['date']) + return False + + if self.stop_buying.get(pair, None) is None: + print("enable buying tag", pair) + self.stop_buying[pair] = False + # if (info_last_candle['rsi_1h'] < self.protection_RSI_enable.value) & (self.stop_buying[pair] is True): + # print("Enable buying", pair) + # self.stop_buying[pair] = False + + # if self.stop_buying[pair] is True: + # if (info_last_candle['rsi_5_1h'] > 20) & (info_previous_last_candle['rsi_5_1h'] <= info_last_candle['rsi_5_1h']) \ + # & (info_last_candle['max_rsi_1h'] < 50): + # print("2 - Enable buying", pair, info_last_candle['date'], info_last_candle['rsi_5_1h'], info_last_candle['max_rsi_1h']) + # self.stop_buying[pair] = False + + if self.stop_buying[pair] is True: + if (info_last_candle['min200'] == info_previous_5_candle['min200']): + # if (info_last_candle['rsi5'] > 20) & (info_last_candle['rsi'] > 30): + # print("1 - Enable buying ", pair, info_last_candle['date'], info_last_candle['rsi5']) + logger.info("1 - Enable buying %s date %s", pair, info_last_candle['date']) + self.stop_buying[pair] = False + + if self.stop_buying[pair]: + allow_to_buy = False + logger.info("3 - cancel buying %s date %s", pair, info_last_candle['date']) + else: + logger.info("3 - accept buying %s date %s", pair, info_last_candle['date']) + # , (info_last_candle['trend_ichimoku_base'], + # (info_last_candle['close'] < info_last_candle['sma10']), + # (info_previous_last_candle['sma10'], info_last_candle['sma10']))) + + return allow_to_buy + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + if (self.stop_buy_for_all is True) & (current_profit < self.btc_sell_all_profit.value): + return "btc_fall" + + expected_profit = 0.01 + days = (current_time - trade.open_date_utc).days + ###### + + btc, _ = self.dp.get_analyzed_dataframe(pair="BTC/USDT", timeframe=self.timeframe) + btc_last_candle = btc.iloc[-1].squeeze() + btc_previous_last_candle = btc.iloc[-2].squeeze() + + informative, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + info_previous_last_candle = informative.iloc[-2].squeeze() + + if self.stop_buying.get(pair, None) is None: + print("enable buying tag", pair) + self.stop_buying[pair] = False + + if self.stop_buying[pair] is False: + if (last_candle['rsi5'] < 16): + logger.info("0 - Disable buying %s date=%s rsi=%s", pair, last_candle['date'], last_candle['rsi5']) + self.stop_buying[pair] = True + if (current_profit > 0): + return "stop_buying" + + if self.stop_buying[pair] is True: + if (last_candle['rsi5'] > 20) & (last_candle['percent10'] > 0): + logger.info("1 - Enable buying %s date=%s rsi=%s", pair, last_candle['date'], last_candle['rsi5']) + # print("1 - Enable buying ", pair, last_candle['date'], last_candle['rsi5']) + self.stop_buying[pair] = False + + if (days < 10) & ((btc_last_candle['percent5'] < self.btc_fall_5.value) | (btc_last_candle['percent3'] < self.btc_fall_3.value) | ( + btc_last_candle['percent'] < self.btc_fall_1.value)): + self.stop_buy_for_all = True + logger.info("0 - Disable ALL %s date=%s pct=%s pct3=%s pct5=%s", pair, last_candle['date'], btc_last_candle['percent'], + btc_last_candle['percent3'], btc_last_candle['percent5']) + if (current_profit > 0): + return "btc_fall" + + max_percent = last_candle['bb_width'] / 4 #0.005 + max_profit = last_candle['bb_width'] * 3 / 4 # 0.015 + if (self.market_overview_pct5 < 0) | (last_candle['pct_change_1_4h'] < 0): + if (current_profit > max_profit) & ((last_candle['percent'] < -max_percent) | (last_candle['percent3'] < -max_percent) | (last_candle['percent5'] < -max_percent)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value) \ + & (days < self.sell_b_too_old_day.value * 2) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2) \ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= max_profit) & (last_candle['percent3'] < -max_percent): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > last_candle['bb_width'] / 1.3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + if (current_profit > 0.025) & ((last_candle['percent'] < -max_percent) | (last_candle['percent3'] < -max_percent) | (last_candle['percent5'] < -max_percent)): + return 'h_percent_quick' + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= max_profit) & (last_candle['percent3'] < -max_percent): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > last_candle['bb_width'] / 0.8) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent'] < - self.sell_h_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_1'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / 4)) + + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + # dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + # dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + # dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + # dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + # dataframe['atr'] = talib.ATR(dataframe, timeperiod=14) + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ################### INFORMATIVE BTC 1H + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative["rsi_5"] = talib.RSI(informative, timeperiod=5) + informative['pct_change_1'] = informative['close'].pct_change(1) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + ok = True + #if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # ok = (self.market_overview['up'] / (self.market_overview['down'] + self.market_overview['up']) > 0.35) + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_h_real.value + OPR = self.buy_h_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_h_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['pct_change_3_1d'] > 0) + & (dataframe['pct_change_5_1d'] > 0) #self.buy_h_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_h_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_h_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + & (dataframe['trend_ichimoku_base'] <= 0.1) + , + ['buy', 'buy_tag']] = (1, 'buy_h') + + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_b_real.value + OPR = self.buy_b_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_b_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['pct_change_3_1d'] < 0) # self.buy_b_pct_3.value) + & (dataframe['pct_change_5_1d'] < 0) #self.buy_b_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_b_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_b_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + , + ['buy', 'buy_tag']] = (1, 'buy_b') + + for decalage in range(self.buy_decalage_deb_0.value, self.buy_decalage0.value): + #if self.buy_0.value: + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value, + self.buy_decalage0.value + ) + conditions.append(condition1) + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + #& (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_0_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift(decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_1_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_1_bb_lower_5.value) + #& (dataframe['percent_1d'] >= self.buy_1_percent_1d_num.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_1_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_2.value, self.buy_decalage2.value): + #if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_real_num0.value / 2) + #& (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + & (dataframe['bb_lower_5'] <= self.buy_2_bb_lower_5.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + # d = dataframe.tail(1).iloc[0] + # print(metadata['pair'], d['cond1'], d['bb_width'], d['close'], d['sma10'], d['sma20']) + for decalage in range(self.buy_decalage_deb_3.value, self.buy_decalage3.value): + #if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) + #& (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_3_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_3_bb_lower_5.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_3_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + # dataframe.loc[ + # ( + # (dataframe['cond1'] <= 1) + # & (dataframe['close'] < dataframe['sma10']) + # & (dataframe['open'] < dataframe['sma20']) + # & (dataframe['open'] < dataframe['sma10']) + # & (dataframe['close'] <= dataframe['min'] * 1.005) + # & (dataframe['moy200_12'] == dataframe['min200']) + # & (dataframe['volume10'].shift(1) * dataframe['close'].shift(1) / 1000 >= 10) + # & (dataframe['percent20'] >= -0.005) + # + # ), ['buy', 'buy_tag']] = (1, 'buy_near_m50') + + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= 0.12) + & (dataframe['rsi'] < 79) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_lower_width_5']) + & (dataframe['close'] < dataframe['min50'] * 1.005) + # & (dataframe['close'] < dataframe['min50'] * 1.01) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + # d = dataframe.tail(1) + # print(metadata['pair'], d['percent50'].iloc[0], d['buy'].iloc[0], d['buy_tag'].iloc[0]) + + dataframe.loc[ + ( + # (dataframe['min_max50'] >= 0.03) + # & (dataframe['bb_width'] >= 0.02) + (dataframe['cond1'].shift(2) <= 1.2) + & (dataframe['rsi'] < 72) + & (dataframe['close'] < dataframe['min50'] * 1.005) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + & (dataframe['pct_change_1_1d'] > 0) + # & (dataframe['pct_change_3_1d'] > 0) + # & (dataframe['pct_change_5_1d'] > 0) + ), ['buy', 'buy_tag']] = (1, 'buy_min_max') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe diff --git a/Zeus_6.py2 b/Zeus_6.py2 new file mode 100644 index 0000000..edc78f2 --- /dev/null +++ b/Zeus_6.py2 @@ -0,0 +1,1089 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.hyper import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_6(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", "R", "=R", "R", "=R", " float: + # + # dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + # current_candle = dataframe.iloc[-1].squeeze() + # + # if current_candle['close'] < current_candle['sma10_1d']: + # print("use more stake", pair, " ", proposed_stake * 2) + # return min(max_stake, proposed_stake * 2) + # else: + # if current_candle['close'] < current_candle['sma5_1d']: + # print("use more stake", pair, " ", proposed_stake * 1.5) + # return min(max_stake, proposed_stake * 1.5) + # + # # Use default stake amount. + # return proposed_stake + + # def bot_loop_start(self, **kwargs) -> None: + # inf_tf = '5m' + # pairs = self.dp.current_whitelist() + # market_overview = {'up': 0, 'down': 0} + # sum_pct5 = 0 + # sum_pct1 = 0 + # self.max_open_trades = self.config['max_open_trades'] + # self.max_amount = self.config['stake_amount'] + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # for pairname in pairs: + # informative = self.dp.get_pair_dataframe(pair=pairname, timeframe=inf_tf) + # informative['pct'] = informative['close'].pct_change(1) + # informative['pct5'] = informative['close'].pct_change(5) + # # print(informative['pct5']) + # pct5 = (informative.tail(1).iloc[0]['pct5']) * 100 + # pct1 = (informative.tail(1).iloc[0]['pct']) * 100 + # if pairname == "COCOS/USDT": + # print(pairname, round(pct1, 5), round(pct5, 5)) #, informative.tail(1).iloc[0]) + # + # if pairname == "BTC/USDT": + # current = informative.tail(1).iloc[0]['close'] + # # 50000 => 2 30000 => 20 + # if (current > 50000): + # self.max_open_trades = 2 + # self.max_amount = self.config['stake_amount'] / 2 + # else: + # if (current > 32000): + # self.max_open_trades = 2 + int((50000 - current) / 1000) + # self.max_amount = self.config['stake_amount'] / 2 \ + # + self.config['stake_amount'] * self.max_open_trades / self.config['max_open_trades'] + # else: + # self.max_open_trades = self.config['max_open_trades'] + # self.max_amount = self.config['stake_amount'] + # + # logger.info("pair '%s' prix %s max open trade %s max amount %s.", + # pairname, current, self.max_open_trades, self.max_amount) + # + # if pct5 > 0: + # market_overview['up'] += 1 + # elif pct5 < 0: + # market_overview['down'] += 1 + # sum_pct5 += pct5 + # sum_pct1 += pct1 + # + # self.market_overview_pct1 = sum_pct1 / len(pairs) + # self.market_overview_pct5 = sum_pct5 / len(pairs) + # + # logger.info("market_overview_pct1 %s %s %s", round(self.market_overview_pct1,5), round(self.market_overview_pct5, 5), market_overview) + # self.market_overview = market_overview + + # def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + # current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + # + # # {'symbol': 'FTM/USDT', 'timestamp': 1646494199570, 'datetime': '2022-03-05T15:29:59.570Z', 'high': 1.7489, + # # 'low': 1.6084, 'bid': 1.6505, 'bidVolume': 2135.0, 'ask': 1.6508, 'askVolume': 2815.0, 'vwap': 1.66852198, + # # 'open': 1.7313, 'close': 1.6505, 'last': 1.6505, 'previousClose': '1.73170000', 'change': -0.0808, + # # 'percentage': -4.667, 'average': 1.6909, 'baseVolume': 124656725.0, 'quoteVolume': 207992485.7799, + # # 'info': + # # {'symbol': 'FTMUSDT', 'priceChange': '-0.08080000', 'priceChangePercent': '-4.667', + # # 'weightedAvgPrice': '1.66852198', 'prevClosePrice': '1.73170000', 'lastPrice': '1.65050000', + # # 'lastQty': '143.00000000', 'bidPrice': '1.65050000', 'bidQty': '2135.00000000', + # # 'askPrice': '1.65080000', 'askQty': '2815.00000000', 'openPrice': '1.73130000', + # # 'highPrice': '1.74890000', 'lowPrice': '1.60840000', 'volume': '124656725.00000000', + # # 'quoteVolume': '207992485.77990000', 'openTime': '1646407799570', 'closeTime': '1646494199570', + # # 'firstId': '137149614', 'lastId': '137450289', 'count': '300676'}} - 0.9817468621938484 + # + # allow_to_buy = True + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # if len(Trade.get_open_trades()) >= self.max_open_trades: + # logger.info("too much open trades for BTC current value") + # allow_to_buy = False + # + # return allow_to_buy + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + expected_profit = 0.01 + #print(last_candle['buy_tag']) + + days = (current_time - trade.open_date_utc).days + ###### + + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # if (current_profit > - 0.04) & (self.market_overview_pct1 < - 1) & (self.market_overview['up'] / (self.market_overview['down'] + self.market_overview['up']) < 0.05): + # return 'send_all' + + # if (current_profit > 0) & (last_candle['percent'] < 0) \ + # (self.market_overview['up'] / (self.market_overview['down'] + self.market_overview['up']) < 0.25): + # return 'all_down' + + if (self.market_overview_pct5 < 0) | (last_candle['pct_change_1_4h'] < 0): + if (current_profit > 0.015) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + & (days < self.sell_b_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > last_candle['bb_width'] / 1.3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + if (current_profit > 0.025) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'h_percent_quick' + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > last_candle['bb_width'] / 0.8) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent'] < - self.sell_h_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_5'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max50'] = (dataframe['max50'] - dataframe['min50']) / dataframe['min50'] + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_max200_5'] = (dataframe['min200'] * (1 + dataframe['min_max200'] / 5)) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + dataframe['atr'] = talib.ATR(dataframe, timeperiod=14) + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ################### INFORMATIVE BTC 1H + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative["rsi_5"] = talib.RSI(informative, timeperiod=5) + informative['pct_change_1'] = informative['close'].pct_change(1) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + ok = True + #if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # ok = (self.market_overview['up'] / (self.market_overview['down'] + self.market_overview['up']) > 0.35) + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_h_real.value + OPR = self.buy_h_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_h_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['pct_change_3_1d'] > 0) + & (dataframe['pct_change_5_1d'] > 0) #self.buy_h_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_h_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_h_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + , + ['buy', 'buy_tag']] = (1, 'buy_h') + + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_b_real.value + OPR = self.buy_b_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_b_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['pct_change_3_1d'] < 0) # self.buy_b_pct_3.value) + & (dataframe['pct_change_5_1d'] < 0) #self.buy_b_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_b_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_b_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + , + ['buy', 'buy_tag']] = (1, 'buy_b') + + for decalage in range(self.buy_decalage_deb_0.value, self.buy_decalage0.value): + #if self.buy_0.value: + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value, + self.buy_decalage0.value + ) + conditions.append(condition1) + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + #& (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_0_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift(decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_1_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_1_bb_lower_5.value) + #& (dataframe['percent_1d'] >= self.buy_1_percent_1d_num.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_1_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_2.value, self.buy_decalage2.value): + #if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_real_num0.value / 2) + #& (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + & (dataframe['bb_lower_5'] <= self.buy_2_bb_lower_5.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + # d = dataframe.tail(1).iloc[0] + # print(metadata['pair'], d['cond1'], d['bb_width'], d['close'], d['sma10'], d['sma20']) + for decalage in range(self.buy_decalage_deb_3.value, self.buy_decalage3.value): + #if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) + #& (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_3_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_3_bb_lower_5.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_3_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + # dataframe.loc[ + # ( + # (dataframe['cond1'] <= 1) + # & (dataframe['close'] < dataframe['sma10']) + # & (dataframe['open'] < dataframe['sma20']) + # & (dataframe['open'] < dataframe['sma10']) + # & (dataframe['close'] <= dataframe['min'] * 1.005) + # & (dataframe['moy200_12'] == dataframe['min200']) + # & (dataframe['volume10'].shift(1) * dataframe['close'].shift(1) / 1000 >= 10) + # & (dataframe['percent20'] >= -0.005) + # + # ), ['buy', 'buy_tag']] = (1, 'buy_near_m50') + + # dataframe.loc[ + # ( + # (dataframe['trend_ichimoku_base'] <= 0.08) + # & (dataframe['rsi'] < 42) + # & (dataframe['rsi_1h'] >= self.buy_rsi.value) + # & (dataframe['close'] < dataframe['sma10']) + # # & (dataframe['min50'].shift(2) == dataframe['min50']) + # ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + # d = dataframe.tail(1) + # print(metadata['pair'], d['percent50'].iloc[0], d['buy'].iloc[0], d['buy_tag'].iloc[0]) + + dataframe.loc[ + ( + ( + (dataframe['trend_ichimoku_base'] <= self.buy_base.value) + | (dataframe['trend_kst_diff'] <= self.buy_diff.value) + ) + & (dataframe['rsi_1h'] < self.buy_rsi_1h.value) + & (dataframe['rsi_1h'] >= self.buy_rsi_max_1h.value) + & (dataframe['rsi'] > self.buy_rsi.value) + & (dataframe['rsi'] <= self.buy_rsi_max.value) + #& (dataframe['close'] < dataframe['sma10']) + # & (dataframe['close'] < dataframe['min200'] * (1 + dataframe['min_max200'] / 5)) + #& (dataframe['sma10'].shift(1) * 1.001 < dataframe['sma10']) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + # & (dataframe['rsi_5_1h'] > 35) + & (dataframe['close'] <= dataframe['min50'] * (1 + dataframe['min_max50'] / 2)) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku_h') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe diff --git a/Zeus_7.json b/Zeus_7.json new file mode 100644 index 0000000..d1963d2 --- /dev/null +++ b/Zeus_7.json @@ -0,0 +1,82 @@ +{ + "strategy_name": "Zeus_7", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_base": 0.12, + "buy_bb_width_n": 4.0, + "buy_diff": 0.3, + "buy_min_max_coef": 1.0, + "buy_min_max_cond1": 1.3, + "buy_min_max_decalage": 2, + "buy_min_max_n": 0.15, + "buy_min_max_nh": 25, + "buy_min_max_rsi": 60, + "buy_rsi": 79, + "buy_rsi_sup": 67 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2, + "sell_h_too_old_percent": 0.01 + }, + "protection": { + "protection_lost_candles": 11, + "protection_lost_percent": 0.14 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-05-24 21:16:25.988317+00:00" +} \ No newline at end of file diff --git a/Zeus_7.py b/Zeus_7.py new file mode 100644 index 0000000..a74ad84 --- /dev/null +++ b/Zeus_7.py @@ -0,0 +1,723 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_7(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", " 0.015) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + & (days < self.sell_b_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > last_candle['bb_width'] / 1.3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + if (current_profit > 0.025) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'h_percent_quick' + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > last_candle['bb_width'] / 0.8) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent'] < - self.sell_h_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '4h') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_5'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_max200_5'] = (dataframe['min200'] * (1 + dataframe['min_max200'] / 5)) + + dataframe['min_n'] = talib.MIN(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['max_n'] = talib.MAX(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / self.buy_bb_width_n.value)) + + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ################### INFORMATIVE BTC 1H + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + # informative["rsi"] = talib.RSI(informative) + # informative["rsi_5"] = talib.RSI(informative, timeperiod=5) + # informative['pct_change_1'] = informative['close'].pct_change(1) + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= self.buy_base.value) + & (dataframe['rsi'] < self.buy_rsi.value) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_lower_width_5']) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + dataframe.loc[ + ( + (dataframe['min_max_n'] >= self.buy_min_max_n.value) + & (dataframe['cond1'].shift(self.buy_min_max_decalage.value) <= self.buy_min_max_cond1.value) + & (dataframe['rsi'] < self.buy_min_max_rsi.value) + & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + & (dataframe['min_n'].shift(self.buy_min_max_decalage.value) == dataframe['min_n']) + # & (dataframe['min50'].shift(self.buy_min_max_decalage.value) == dataframe['min50']) + + ), ['buy', 'buy_tag']] = (1, 'buy_min_max') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe diff --git a/Zeus_8.json b/Zeus_8.json new file mode 100644 index 0000000..09bbe43 --- /dev/null +++ b/Zeus_8.json @@ -0,0 +1,114 @@ +{ + "strategy_name": "Zeus_8", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_b_bb_lowerband": 1.03, + "buy_b_bb_width": 0.02, + "buy_min_max_coef": 1.004, + "buy_min_max_cond1": 0.8, + "buy_min_max_decalage": 2, + "buy_min_max_n": 0.02, + "buy_min_max_nh": 14, + "buy_min_max_rsi": 61, + "buy_b_cat": "R", + "buy_h_pct": 0.016, + "buy_h_real": 0.026, + "buy_0_distance": -0.03, + "buy_0_percent20": -0.03, + "buy_1_bb_lower_5": 0.19, + "buy_1_pente_sma10": 0.48, + "buy_1_pente_sma20": 0.33, + "buy_2_bb_lower_5": 0.59, + "buy_2_distance": 0.08, + "buy_2_pente_sma10": 0.46, + "buy_2_pente_sma20": 0.08, + "buy_2_percent20": 0.06, + "buy_3_bb_lower_5": 0.05, + "buy_3_distance": 0.01, + "buy_3_pente_sma10": 0.12, + "buy_3_pente_sma20": 0.27, + "buy_3_percent20": -0.03, + "buy_4_pente_sma10": 0.35, + "buy_4_pente_sma20": 0.22, + "buy_decalage0": 7, + "buy_decalage2": 6, + "buy_decalage3": 7, + "buy_decalage_deb_0": 1, + "buy_decalage_deb_2": 1, + "buy_decalage_deb_3": 1, + "buy_min_horizon": 192, + "buy_real_num0": 0.61, + "buy_real_num1": 0.34, + "buy_real_num2": 0.35 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2, + "sell_h_too_old_percent": 0.01 + }, + "protection": { + "protection_lost_candles": 11, + "protection_lost_percent": 0.14 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-04-03 14:39:34.819040+00:00" +} diff --git a/Zeus_8.py b/Zeus_8.py new file mode 100644 index 0000000..73553a8 --- /dev/null +++ b/Zeus_8.py @@ -0,0 +1,966 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_8(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", "R", "=R", "R", "=R", " 0.015) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + & (days < self.sell_b_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + if (current_profit > 0.025) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'h_percent_quick' + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent'] < - self.sell_h_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '4h') for pair in pairs] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_1'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_n'] = talib.MIN(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['max_n'] = talib.MAX(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / self.buy_bb_width_n.value)) + + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + dataframe['atr'] = talib.ATR(dataframe, timeperiod=14) + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + ok = False + #if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # ok = (self.market_overview['up'] / (self.market_overview['down'] + self.market_overview['up']) > 0.35) + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_h_real.value + OPR = self.buy_h_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_h_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['pct_change_3_1d'] > 0) + & (dataframe['pct_change_5_1d'] > 0) #self.buy_h_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_h_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_h_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + & (dataframe['trend_ichimoku_base'] <= 0.1) + , + ['buy', 'buy_tag']] = (1, 'buy_h') + + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_b_real.value + OPR = self.buy_b_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_b_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['pct_change_3_1d'] < 0) # self.buy_b_pct_3.value) + & (dataframe['pct_change_5_1d'] < 0) #self.buy_b_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_b_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_b_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + , + ['buy', 'buy_tag']] = (1, 'buy_b') + + for decalage in range(self.buy_decalage_deb_0.value, self.buy_decalage0.value): + #if self.buy_0.value: + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value, + self.buy_decalage0.value + ) + conditions.append(condition1) + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + #& (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_0_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift(decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_1_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_1_bb_lower_5.value) + #& (dataframe['percent_1d'] >= self.buy_1_percent_1d_num.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_1_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_2.value, self.buy_decalage2.value): + #if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_real_num0.value / 2) + #& (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + & (dataframe['bb_lower_5'] <= self.buy_2_bb_lower_5.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + # d = dataframe.tail(1).iloc[0] + # print(metadata['pair'], d['cond1'], d['bb_width'], d['close'], d['sma10'], d['sma20']) + for decalage in range(self.buy_decalage_deb_3.value, self.buy_decalage3.value): + #if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) + #& (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_3_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_3_bb_lower_5.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_3_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= self.buy_base.value) + & (dataframe['rsi'] < self.buy_rsi.value) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_lower_width_5']) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + + dataframe.loc[ + ( + (dataframe['min_max_n'] >= self.buy_min_max_n.value) + & (dataframe['cond1'].shift(self.buy_min_max_decalage.value) <= self.buy_min_max_cond1.value) + & (dataframe['rsi'] < self.buy_min_max_rsi.value) + & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + & (dataframe['min_n'].shift(self.buy_min_max_decalage.value) == dataframe['min_n']) + & (dataframe['pct_change_1_1d'] > 0) + # & (dataframe['pct_change_3_1d'] > 0) + # & (dataframe['pct_change_5_1d'] > 0) + # & (dataframe['min50'].shift(self.buy_min_max_decalage.value) == dataframe['min50']) + + ), ['buy', 'buy_tag']] = (1, 'buy_min_max') + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe diff --git a/Zeus_8_1.json b/Zeus_8_1.json new file mode 100644 index 0000000..bd917bf --- /dev/null +++ b/Zeus_8_1.json @@ -0,0 +1,114 @@ +{ + "strategy_name": "Zeus_8_1", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_b_bb_lowerband": 1.03, + "buy_b_bb_width": 0.02, + "buy_min_max_coef": 1.004, + "buy_min_max_cond1": 0.8, + "buy_min_max_decalage": 2, + "buy_min_max_n": 0.02, + "buy_min_max_nh": 14, + "buy_min_max_rsi": 61, + "buy_b_cat": "R", + "buy_h_pct": 0.016, + "buy_h_real": 0.026, + "buy_0_distance": -0.03, + "buy_0_percent20": -0.03, + "buy_1_bb_lower_5": 0.19, + "buy_1_pente_sma10": 0.48, + "buy_1_pente_sma20": 0.33, + "buy_2_bb_lower_5": 0.59, + "buy_2_distance": 0.08, + "buy_2_pente_sma10": 0.46, + "buy_2_pente_sma20": 0.08, + "buy_2_percent20": 0.06, + "buy_3_bb_lower_5": 0.05, + "buy_3_distance": 0.01, + "buy_3_pente_sma10": 0.12, + "buy_3_pente_sma20": 0.27, + "buy_3_percent20": -0.03, + "buy_4_pente_sma10": 0.35, + "buy_4_pente_sma20": 0.22, + "buy_decalage0": 7, + "buy_decalage2": 6, + "buy_decalage3": 7, + "buy_decalage_deb_0": 1, + "buy_decalage_deb_2": 1, + "buy_decalage_deb_3": 1, + "buy_min_horizon": 192, + "buy_real_num0": 0.61, + "buy_real_num1": 0.34, + "buy_real_num2": 0.35 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3000, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2000, + "sell_h_too_old_percent": 0.01 + }, + "protection": { + "protection_lost_candles": 11, + "protection_lost_percent": 0.14 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-04-03 14:39:34.819040+00:00" +} diff --git a/Zeus_8_1.py b/Zeus_8_1.py new file mode 100644 index 0000000..79321dc --- /dev/null +++ b/Zeus_8_1.py @@ -0,0 +1,1013 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_8_1(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", "R", "=R", "R", "=R", " 0.0) & (last_candle['percent'] < -0.01): + return 'percent_quick' + if (self.market_overview_pct5 < 0) | (last_candle['pct_change_1_4h'] < 0): + if (current_profit > 0.015) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + & (days < self.sell_b_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + if (current_profit > 0.025) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'h_percent_quick' + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent'] < - self.sell_h_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '4h') for pair in pairs] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_1'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_n'] = talib.MIN(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['max_n'] = talib.MAX(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / self.buy_bb_width_n.value)) + + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + dataframe['atr'] = talib.ATR(dataframe, timeperiod=14) + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + ok = False + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # ok = (self.market_overview['up'] / (self.market_overview['down'] + self.market_overview['up']) > 0.35) + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_h_real.value + OPR = self.buy_h_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_h_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['pct_change_3_1d'] > 0) + & (dataframe['pct_change_5_1d'] > 0) #self.buy_h_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_h_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_h_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + & (dataframe['trend_ichimoku_base'] <= 0.1) + , + ['buy', 'buy_tag']] = (1, 'buy_h') + + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_b_real.value + OPR = self.buy_b_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_b_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['pct_change_3_1d'] < 0) # self.buy_b_pct_3.value) + & (dataframe['pct_change_5_1d'] < 0) #self.buy_b_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_b_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_b_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + , + ['buy', 'buy_tag']] = (1, 'buy_b') + + for decalage in range(self.buy_decalage_deb_0.value, self.buy_decalage0.value): + #if self.buy_0.value: + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value, + self.buy_decalage0.value + ) + conditions.append(condition1) + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_0_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift(decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_1_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_1_bb_lower_5.value) + #& (dataframe['percent_1d'] >= self.buy_1_percent_1d_num.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_1_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_2.value, self.buy_decalage2.value): + #if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_real_num0.value / 2) + & (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + & (dataframe['bb_lower_5'] <= self.buy_2_bb_lower_5.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + # d = dataframe.tail(1).iloc[0] + # print(metadata['pair'], d['cond1'], d['bb_width'], d['close'], d['sma10'], d['sma20']) + for decalage in range(self.buy_decalage_deb_3.value, self.buy_decalage3.value): + #if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) + & (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_3_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_3_bb_lower_5.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_3_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= self.buy_base.value) + & (dataframe['rsi'] < self.buy_rsi.value) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_lower_width_5']) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + + dataframe.loc[ + ( + (dataframe['min_max_n'] >= self.buy_min_max_n.value) + & (dataframe['cond1'].shift(self.buy_min_max_decalage.value) <= self.buy_min_max_cond1.value) + & (dataframe['rsi'] < self.buy_min_max_rsi.value) + & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + & (dataframe['min_n'].shift(self.buy_min_max_decalage.value) == dataframe['min_n']) + & (dataframe['pct_change_1_1d'] > 0) + # & (dataframe['pct_change_3_1d'] > 0) + # & (dataframe['pct_change_5_1d'] > 0) + # & (dataframe['min50'].shift(self.buy_min_max_decalage.value) == dataframe['min50']) + + ), ['buy', 'buy_tag']] = (1, 'buy_min_max') + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + if (len(dataframe) < 1): + return None + last_candle = dataframe.iloc[-1].squeeze() + last_candle_5 = dataframe.iloc[-5].squeeze() + + filled_buys = trade.select_filled_orders('buy') + count_of_buys = len(filled_buys) + days = (current_time - trade.open_date_utc).days + minutes = (current_time - trade.open_date_utc).seconds / 60 + + + # condition = (last_candle['cond1'] <= 0.75) & (last_candle['bb_width'] > 0.018) & ( + # last_candle['rsi'] < 72) & (last_candle['close'] < last_candle['min50'] * 1.006)# & (last_candle['min_max_close'] > 2) + + condition = (last_candle['min50'] == last_candle_5['min50']) + + # print(reduce(lambda x, y: x & y, condition)) + if (0 < count_of_buys <= self.max_dca_orders) & (current_profit < -0.05 * count_of_buys) \ + & (condition): + try: + # print(last_candle['cond1'], last_candle['bb_width'], last_candle['rsi'], last_candle['close'], last_candle['percent5'], + # last_candle['trend_ichimoku_base']) + # This returns first order stake size + stake_amount = self.config['stake_amount'] * (count_of_buys ) # filled_buys[0].cost + # This then calculates current safety order size + # stake_amount = stake_amount * pow(1.5, count_of_buys) + print("-----------" + trade.pair + " " + str(current_profit) + " " + str(count_of_buys) + " " + str(stake_amount) + "---------------------") + # print("-----------" + trade.pair + " " + str(current_profit) + "---------------------") + # print("count_of_buys = " + str(count_of_buys)) + # print("stake_amount = " + str(stake_amount)) + return stake_amount + except Exception as exception: + print(exception) + return None + return None diff --git a/Zeus_8_2.json b/Zeus_8_2.json new file mode 100644 index 0000000..400734a --- /dev/null +++ b/Zeus_8_2.json @@ -0,0 +1,123 @@ +{ + "strategy_name": "Zeus_8_2", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_0_distance": -0.03, + "buy_0_percent20": 0.09, + "buy_1_bb_lower_5": 0.28, + "buy_2_bb_lower_5": 0.55, + "buy_2_distance": 0.08, + "buy_2_percent20": 0.09, + "buy_3_bb_lower_5": 0.26, + "buy_3_distance": 0.04, + "buy_3_percent20": 0.09, + "buy_b_bb_lowerband": 1.01, + "buy_b_bb_width": 0.08, + "buy_b_cat": ">R", + "buy_b_pct": 0.009, + "buy_b_pct_1": -0.15, + "buy_b_pct_3": 0.18, + "buy_b_pct_5": 0.01, + "buy_b_real": 0.8482, + "buy_b_sma": "sma3_1d", + "buy_b_sma_close": "sma3_1d", + "buy_base": 0.09, + "buy_bb_width_n": 9.6, + "buy_decalage0": 7, + "buy_decalage2": 6, + "buy_decalage3": 7, + "buy_decalage_deb_0": 1, + "buy_decalage_deb_2": 3, + "buy_decalage_deb_3": 3, + "buy_h_bb_lowerband": 1.02, + "buy_h_bb_width": 0.08, + "buy_h_cat": "=R", + "buy_h_pct": 0.013, + "buy_h_pct_1": -0.15, + "buy_h_pct_3": 0.15, + "buy_h_pct_5": -0.18, + "buy_h_real": 0.2665, + "buy_h_sma": "sma10_1d", + "buy_h_sma_close": "sma5_1d", + "buy_min_horizon": 102, + "buy_min_max_coef": 1.004, + "buy_min_max_cond1": 1.3, + "buy_min_max_decalage": 6, + "buy_min_max_n": 0.03, + "buy_min_max_nh": 31, + "buy_min_max_rsi": 69, + "buy_real_num0": 0.84, + "buy_real_num1": 0.99, + "buy_real_num2": 1.68, + "buy_rsi": 55, + "decalage_b": 1, + "decalage_h": 1 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3000, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2000, + "sell_h_too_old_percent": 0.01 + }, + "protection": { + "protection_lost_candles": 11, + "protection_lost_percent": 0.14, + "protection_nb_buy_lost": 4, + "protection_percent_buy_lost": 5 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-08-01 01:52:55.970812+00:00" +} \ No newline at end of file diff --git a/Zeus_8_2.py b/Zeus_8_2.py new file mode 100644 index 0000000..c8c8f5b --- /dev/null +++ b/Zeus_8_2.py @@ -0,0 +1,1006 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_8_2(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", "R", "=R", "R", "=R", " 0.0) & (last_candle['percent'] < -0.01): + return 'percent_quick' + if (self.market_overview_pct5 < 0) | (last_candle['pct_change_1_4h'] < 0): + if (current_profit > 0.015) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + & (days < self.sell_b_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + if (current_profit > 0.025) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'h_percent_quick' + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent'] < - self.sell_h_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '4h') for pair in pairs] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_1'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_n'] = talib.MIN(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['max_n'] = talib.MAX(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / self.buy_bb_width_n.value)) + + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + dataframe['atr'] = talib.ATR(dataframe, timeperiod=14) + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + ok = False + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # ok = (self.market_overview['up'] / (self.market_overview['down'] + self.market_overview['up']) > 0.35) + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_h_real.value + OPR = self.buy_h_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_h_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['pct_change_3_1d'] > 0) + & (dataframe['pct_change_5_1d'] > 0) #self.buy_h_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_h_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_h_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + & (dataframe['trend_ichimoku_base'] <= 0.1) + , + ['buy', 'buy_tag']] = (1, 'buy_h') + + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_b_real.value + OPR = self.buy_b_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_b_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['pct_change_3_1d'] < 0) # self.buy_b_pct_3.value) + & (dataframe['pct_change_5_1d'] < 0) #self.buy_b_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_b_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_b_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + , + ['buy', 'buy_tag']] = (1, 'buy_b') + + for decalage in range(self.buy_decalage_deb_0.value, self.buy_decalage0.value): + #if self.buy_0.value: + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value, + self.buy_decalage0.value + ) + conditions.append(condition1) + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_0_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift(decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_1_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_1_bb_lower_5.value) + #& (dataframe['percent_1d'] >= self.buy_1_percent_1d_num.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_1_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_2.value, self.buy_decalage2.value): + #if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_real_num0.value / 2) + & (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + & (dataframe['bb_lower_5'] <= self.buy_2_bb_lower_5.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + # d = dataframe.tail(1).iloc[0] + # print(metadata['pair'], d['cond1'], d['bb_width'], d['close'], d['sma10'], d['sma20']) + for decalage in range(self.buy_decalage_deb_3.value, self.buy_decalage3.value): + #if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) + & (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_3_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_3_bb_lower_5.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_3_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= self.buy_base.value) + & (dataframe['rsi'] < self.buy_rsi.value) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_lower_width_5']) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + + dataframe.loc[ + ( + (dataframe['min_max_n'] >= self.buy_min_max_n.value) + & (dataframe['cond1'].shift(self.buy_min_max_decalage.value) <= self.buy_min_max_cond1.value) + & (dataframe['rsi'] < self.buy_min_max_rsi.value) + & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + & (dataframe['min_n'].shift(self.buy_min_max_decalage.value) == dataframe['min_n']) + & (dataframe['pct_change_1_1d'] > 0) + # & (dataframe['pct_change_3_1d'] > 0) + # & (dataframe['pct_change_5_1d'] > 0) + # & (dataframe['min50'].shift(self.buy_min_max_decalage.value) == dataframe['min50']) + + ), ['buy', 'buy_tag']] = (1, 'buy_min_max') + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + if (len(dataframe) < 1): + return None + last_candle = dataframe.iloc[-1].squeeze() + filled_buys = trade.select_filled_orders('buy') + count_of_buys = len(filled_buys) + condition = (last_candle['cond1'] <= 0.75) & (last_candle['bb_width'] > 0.018) & ( + last_candle['rsi'] < 72) & (last_candle['close'] < last_candle['min50'] * 1.006)# & (last_candle['min_max_close'] > 2) + # print(reduce(lambda x, y: x & y, condition)) + days = (current_time - trade.open_date_utc).days + + if (days > 1) & (0 < count_of_buys <= self.protection_nb_buy_lost.value) & (current_profit < - self.protection_percent_buy_lost.value / 100) & (condition): + try: + print(last_candle['cond1'], last_candle['bb_width'], last_candle['rsi'], last_candle['close'], last_candle['percent5'], + last_candle['trend_ichimoku_base']) + # This returns first order stake size + stake_amount = self.config['stake_amount'] * (count_of_buys ) # filled_buys[0].cost + # This then calculates current safety order size + # stake_amount = stake_amount * pow(1.5, count_of_buys) + print("-----------" + trade.pair + " " + str(current_profit) + "---------------------") + print("count_of_buys = " + str(count_of_buys)) + print("stake_amount = " + str(stake_amount)) + return stake_amount + except Exception as exception: + print(exception) + return None + return None diff --git a/Zeus_8_3.json b/Zeus_8_3.json new file mode 100644 index 0000000..938bcfb --- /dev/null +++ b/Zeus_8_3.json @@ -0,0 +1,121 @@ +{ + "strategy_name": "Zeus_8_3", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_0_distance": -0.03, + "buy_0_percent20": 0.09, + "buy_1_bb_lower_5": 0.28, + "buy_2_bb_lower_5": 0.55, + "buy_2_distance": 0.08, + "buy_2_percent20": 0.09, + "buy_3_bb_lower_5": 0.26, + "buy_3_distance": 0.04, + "buy_3_percent20": 0.09, + "buy_b_bb_lowerband": 1.01, + "buy_b_bb_width": 0.08, + "buy_b_cat": ">R", + "buy_b_pct": 0.009, + "buy_b_pct_1": -0.15, + "buy_b_pct_3": 0.18, + "buy_b_pct_5": 0.01, + "buy_b_real": 0.8482, + "buy_b_sma": "sma3_1d", + "buy_b_sma_close": "sma3_1d", + "buy_base": 0.12, + "buy_bb_width_n": 9.6, + "buy_decalage0": 7, + "buy_decalage2": 6, + "buy_decalage3": 7, + "buy_decalage_deb_0": 1, + "buy_decalage_deb_2": 3, + "buy_decalage_deb_3": 3, + "buy_h_bb_lowerband": 1.02, + "buy_h_bb_width": 0.08, + "buy_h_cat": "=R", + "buy_h_pct": 0.013, + "buy_h_pct_1": -0.15, + "buy_h_pct_3": 0.15, + "buy_h_pct_5": -0.18, + "buy_h_real": 0.2665, + "buy_h_sma": "sma10_1d", + "buy_h_sma_close": "sma5_1d", + "buy_min_horizon": 102, + "buy_min_max_coef": 1.004, + "buy_min_max_cond1": 1.3, + "buy_min_max_decalage": 6, + "buy_min_max_n": 0.03, + "buy_min_max_nh": 31, + "buy_min_max_rsi": 69, + "buy_real_num0": 0.84, + "buy_real_num1": 0.99, + "buy_real_num2": 1.68, + "buy_rsi": 55, + "decalage_b": 1, + "decalage_h": 1 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3000, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2000, + "sell_h_too_old_percent": 0.01 + }, + "protection": { + "protection_nb_buy_lost": 5, + "protection_percent_buy_lost": 3 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-08-13 05:53:44.097978+00:00" +} \ No newline at end of file diff --git a/Zeus_8_3.py b/Zeus_8_3.py new file mode 100644 index 0000000..70f7299 --- /dev/null +++ b/Zeus_8_3.py @@ -0,0 +1,1022 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_8_3(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", "R", "=R", "R", "=R", " 0.0) & (last_candle['percent'] < -0.01): + return 'percent_quick' + if (self.market_overview_pct5 < 0) | (last_candle['pct_change_1_4h'] < 0): + if (current_profit > 0.015) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + & (days < self.sell_b_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + if (current_profit > 0.025) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'h_percent_quick' + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent'] < - self.sell_h_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '4h') for pair in pairs] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_1'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_n'] = talib.MIN(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['max_n'] = talib.MAX(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + # dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / self.buy_bb_width_n.value)) + + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + dataframe['atr'] = talib.ATR(dataframe, timeperiod=14) + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + ok = False + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # ok = (self.market_overview['up'] / (self.market_overview['down'] + self.market_overview['up']) > 0.35) + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_h_real.value + OPR = self.buy_h_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_h_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['pct_change_3_1d'] > 0) + & (dataframe['pct_change_5_1d'] > 0) #self.buy_h_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_h_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_h_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + & (dataframe['trend_ichimoku_base'] <= 0.1) + , + ['buy', 'buy_tag']] = (1, 'buy_h') + + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_b_real.value + OPR = self.buy_b_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_b_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['pct_change_3_1d'] < 0) # self.buy_b_pct_3.value) + & (dataframe['pct_change_5_1d'] < 0) #self.buy_b_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_b_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_b_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + , + ['buy', 'buy_tag']] = (1, 'buy_b') + + for decalage in range(self.buy_decalage_deb_0.value, self.buy_decalage0.value): + #if self.buy_0.value: + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value, + self.buy_decalage0.value + ) + conditions.append(condition1) + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_0_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift(decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_1_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_1_bb_lower_5.value) + #& (dataframe['percent_1d'] >= self.buy_1_percent_1d_num.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_1_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_2.value, self.buy_decalage2.value): + #if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_real_num0.value / 2) + & (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + & (dataframe['bb_lower_5'] <= self.buy_2_bb_lower_5.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + # d = dataframe.tail(1).iloc[0] + # print(metadata['pair'], d['cond1'], d['bb_width'], d['close'], d['sma10'], d['sma20']) + for decalage in range(self.buy_decalage_deb_3.value, self.buy_decalage3.value): + #if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) + & (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_3_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_3_bb_lower_5.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_3_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= self.buy_base.value) + & (dataframe['rsi'] < self.buy_rsi.value) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_lower_width_5']) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + + dataframe.loc[ + ( + (dataframe['min_max_n'] >= self.buy_min_max_n.value) + & (dataframe['cond1'].shift(self.buy_min_max_decalage.value) <= self.buy_min_max_cond1.value) + & (dataframe['rsi'] < self.buy_min_max_rsi.value) + & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + & (dataframe['min_n'].shift(self.buy_min_max_decalage.value) == dataframe['min_n']) + & (dataframe['pct_change_1_1d'] > 0) + # & (dataframe['pct_change_3_1d'] > 0) + # & (dataframe['pct_change_5_1d'] > 0) + # & (dataframe['min50'].shift(self.buy_min_max_decalage.value) == dataframe['min50']) + + ), ['buy', 'buy_tag']] = (1, 'buy_min_max') + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + if (len(dataframe) < 1): + return None + last_candle = dataframe.iloc[-1].squeeze() + last_candle_5 = dataframe.iloc[-3].squeeze() + + min_d = min(last_candle['sma3_4h'], last_candle['close_1d']) + + filled_buys = trade.select_filled_orders('buy') + count_of_buys = len(filled_buys) + days = (current_time - trade.open_date_utc).days + minutes = (current_time - trade.open_date_utc).seconds / 60 + # condition = (last_candle['cond1'] <= 0.75) & (last_candle['bb_width'] > 0.018) & ( + # last_candle['rsi'] < 72) & (last_candle['close'] < last_candle['min50'] * 1.006)# & (last_candle['min_max_close'] > 2) + + condition = (last_candle['min50'] == last_candle_5['min50']) & (last_candle['close'] <= min_d) + p = self.protection_percent_buy_lost.value + percents = [p, p * 2, p * 3, p * 4, p * 5, p * 6, p * 7, p * 8, p * 9] + + if (0 < count_of_buys <= self.protection_nb_buy_lost.value) \ + & (current_profit < - (percents[count_of_buys - 1] / 100)) & (condition): + try: + p = self.config['stake_amount'] + factors = [p, p, p, 2 * p, 3 * p, 4 * p, 5 * p, 6 * p] + + stake_amount = factors[count_of_buys - 1]# filled_buys[0].cost + # This then calculates current safety order size + # stake_amount = stake_amount * pow(1.5, count_of_buys) + print("-----------" + trade.pair + " " + str(current_profit) + " " + str(count_of_buys) + " " + str(stake_amount) + "---------------------") + return stake_amount + except Exception as exception: + print(exception) + return None + return None diff --git a/Zeus_8_3_1.json b/Zeus_8_3_1.json new file mode 100644 index 0000000..031fd68 --- /dev/null +++ b/Zeus_8_3_1.json @@ -0,0 +1,114 @@ +{ + "strategy_name": "Zeus_8_3_1", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_b_bb_lowerband": 1.03, + "buy_b_bb_width": 0.02, + "buy_min_max_coef": 1.004, + "buy_min_max_cond1": 0.8, + "buy_min_max_decalage": 2, + "buy_min_max_n": 0.02, + "buy_min_max_nh": 14, + "buy_min_max_rsi": 61, + "buy_b_cat": "R", + "buy_h_pct": 0.016, + "buy_h_real": 0.026, + "buy_0_distance": -0.03, + "buy_0_percent20": -0.03, + "buy_1_bb_lower_5": 0.19, + "buy_1_pente_sma10": 0.48, + "buy_1_pente_sma20": 0.33, + "buy_2_bb_lower_5": 0.59, + "buy_2_distance": 0.08, + "buy_2_pente_sma10": 0.46, + "buy_2_pente_sma20": 0.08, + "buy_2_percent20": 0.06, + "buy_3_bb_lower_5": 0.05, + "buy_3_distance": 0.01, + "buy_3_pente_sma10": 0.12, + "buy_3_pente_sma20": 0.27, + "buy_3_percent20": -0.03, + "buy_4_pente_sma10": 0.35, + "buy_4_pente_sma20": 0.22, + "buy_decalage0": 7, + "buy_decalage2": 6, + "buy_decalage3": 7, + "buy_decalage_deb_0": 1, + "buy_decalage_deb_2": 1, + "buy_decalage_deb_3": 1, + "buy_min_horizon": 192, + "buy_real_num0": 0.61, + "buy_real_num1": 0.34, + "buy_real_num2": 0.35 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3000, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2000, + "sell_h_too_old_percent": 0.01 + }, + "protection": { + "protection_nb_buy_lost": 5, + "protection_percent_buy_lost": 3 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-08-13 05:53:44.097978+00:00" +} \ No newline at end of file diff --git a/Zeus_8_3_1.py b/Zeus_8_3_1.py new file mode 100644 index 0000000..6689504 --- /dev/null +++ b/Zeus_8_3_1.py @@ -0,0 +1,1044 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_8_3_1(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", "R", "=R", "R", "=R", " 0.0) & (last_candle['percent'] < -0.01): + return 'percent_quick' + if (self.market_overview_pct5 < 0) | (last_candle['pct_change_1_4h'] < 0): + if (current_profit > 0.015) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + & (days < self.sell_b_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + if (current_profit > 0.025) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'h_percent_quick' + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent'] < - self.sell_h_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '4h') for pair in pairs] + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_1'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_n'] = talib.MIN(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['max_n'] = talib.MAX(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + # dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / self.buy_bb_width_n.value)) + + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + dataframe['atr'] = talib.ATR(dataframe, timeperiod=14) + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ######################## INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + ok = False + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # ok = (self.market_overview['up'] / (self.market_overview['down'] + self.market_overview['up']) > 0.35) + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_h_real.value + OPR = self.buy_h_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_h_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['pct_change_3_1d'] > 0) + & (dataframe['pct_change_5_1d'] > 0) #self.buy_h_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_h_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_h_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + & (dataframe['trend_ichimoku_base'] <= 0.1) + , + ['buy', 'buy_tag']] = (1, 'buy_h') + + conditions = [] + IND = 'trend_ichimoku_base' + REAL = self.buy_b_real.value + OPR = self.buy_b_cat.value + DFIND = dataframe[IND] + # print(DFIND.mean()) + if OPR == ">R": + conditions.append(DFIND > REAL) + elif OPR == "=R": + conditions.append(np.isclose(DFIND, REAL)) + elif OPR == "= 10) + & (dataframe['pct_change'] < - self.buy_b_pct.value) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['pct_change_3_1d'] < 0) # self.buy_b_pct_3.value) + & (dataframe['pct_change_5_1d'] < 0) #self.buy_b_pct_5.value) + #& (dataframe['close_1d'] < dataframe['bb_lowerband_1d'] * self.buy_b_bb_lowerband.value) + & (dataframe['bb_width_1d'] >= self.buy_b_bb_width.value) + & (dataframe['close'] <= dataframe['sma5_1d']) + & (dataframe['sma10_1d'].shift(1) <= dataframe['sma10_1d']) + & (dataframe['cond1'] <= 0.45) # self.buy_real_num0.value / 2) + , + ['buy', 'buy_tag']] = (1, 'buy_b') + + for decalage in range(self.buy_decalage_deb_0.value, self.buy_decalage0.value): + #if self.buy_0.value: + conditions = list() + condition1, dataframe = condition_generator( + dataframe, + buy_operator0, + buy_indicator0, + buy_crossed_indicator0, + self.buy_real_num0.value, + self.buy_decalage0.value + ) + conditions.append(condition1) + dataframe.loc[ + ( + reduce(lambda x, y: x & y, conditions) + & (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['bb_middleband']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_0_percent20.value) + # & (dataframe['min20'] == dataframe['min50']) + & (dataframe['distance_min'] <= self.buy_0_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift(decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_1_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_1_bb_lower_5.value) + #& (dataframe['percent_1d'] >= self.buy_1_percent_1d_num.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_1_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_1_' + str(decalage)) + for decalage in range(self.buy_decalage_deb_2.value, self.buy_decalage2.value): + #if self.buy_2.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.45) #self.buy_real_num0.value / 2) + & (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + & (dataframe['percent20'].shift(decalage) <= self.buy_2_percent20.value) + & (dataframe['distance_min'] <= self.buy_2_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + & (dataframe['bb_lower_5'] <= self.buy_2_bb_lower_5.value) + ), ['buy', 'buy_tag']] = (1, 'buy_2_' + str(decalage)) + # d = dataframe.tail(1).iloc[0] + # print(metadata['pair'], d['cond1'], d['bb_width'], d['close'], d['sma10'], d['sma20']) + for decalage in range(self.buy_decalage_deb_3.value, self.buy_decalage3.value): + #if self.buy_3.value: + dataframe.loc[ + ( + (dataframe['cond1'].shift(decalage) <= 0.2) + & (ok) + & (dataframe['volume10'].shift(decalage) * dataframe['close'].shift(decalage) / 1000 >= 10) + # & (dataframe['sma10'].shift(1) <= dataframe['sma10']) + & (dataframe['bb_width'] >= 0.05) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['open'] < dataframe['sma20']) + & (dataframe['open'] < dataframe['sma10']) + & (dataframe['min50'].shift(decalage) == dataframe['min50']) + #& (dataframe['min10'] <= dataframe['min50'] * 1.02) + & (dataframe['percent20'].shift(decalage) <= self.buy_3_percent20.value) + & (dataframe['distance_min'] <= self.buy_3_distance.value) + & ((dataframe['close'] - dataframe['open'].shift(decalage)) / dataframe['open'].shift( + decalage) <= 0.005) + # & (dataframe['bb_lower_var_5'] <= self.buy_3_bb_lower_var_5.value) + & (dataframe['bb_lower_5'] <= self.buy_3_bb_lower_5.value) + #& (dataframe['percent_4h'] > 0) + #& (dataframe['percent3_4h'] <= self.buy_3_percent_4h_num.value) + ), ['buy', 'buy_tag']] = (1, 'buy_3_' + str(decalage)) + + # min_d = min(dataframe['sma3_4h'], dataframe['close_1d']) + # condition = (dataframe['min50'] == dataframe['min50'].shift(3)) & (dataframe['close'] <= min_d) + + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= self.buy_base.value) + & (dataframe['rsi'] < self.buy_rsi.value) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_lower_width_5']) + # & (dataframe['min50'] == dataframe['min50'].shift(3)) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + # & (dataframe['close'] <= dataframe['close_1d']) + & (dataframe['close'] <= dataframe['close_1h']) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + + dataframe.loc[ + ( + (dataframe['min_max_n'] >= self.buy_min_max_n.value) + & (dataframe['cond1'].shift(self.buy_min_max_decalage.value) <= self.buy_min_max_cond1.value) + & (dataframe['rsi'] < self.buy_min_max_rsi.value) + & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + & (dataframe['min_n'].shift(self.buy_min_max_decalage.value) == dataframe['min_n']) + & (dataframe['pct_change_1_1d'] > 0) + # & (dataframe['min50'] == dataframe['min50'].shift(3)) + # & (dataframe['close'] <= dataframe['close_1d']) + & (dataframe['close'] <= dataframe['close_1h']) + ), ['buy', 'buy_tag']] = (1, 'buy_min_max') + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + if (len(dataframe) < 1): + return None + last_candle = dataframe.iloc[-1].squeeze() + last_candle_5 = dataframe.iloc[-3].squeeze() + + min_d = min(last_candle['sma3_4h'], last_candle['close_1d']) + + filled_buys = trade.select_filled_orders('buy') + count_of_buys = len(filled_buys) + days = (current_time - trade.open_date_utc).days + minutes = (current_time - trade.open_date_utc).seconds / 60 + # condition = (last_candle['cond1'] <= 0.75) & (last_candle['bb_width'] > 0.018) & ( + # last_candle['rsi'] < 72) & (last_candle['close'] < last_candle['min50'] * 1.006)# & (last_candle['min_max_close'] > 2) + + condition = (last_candle['min50'] == last_candle_5['min50']) & (last_candle['close'] <= last_candle['close_1h']) + p = self.protection_percent_buy_lost.value + percents = [p, p * 2, p * 3, p * 4, p * 5, p * 6, p * 7, p * 8, p * 9] + + if (0 < count_of_buys <= self.protection_nb_buy_lost.value) \ + & (current_profit < - (percents[count_of_buys - 1] / 100)) & (condition): + try: + p = self.config['stake_amount'] + factors = [p, p, p, 2 * p, 3 * p, 4 * p, 5 * p, 6 * p] + + stake_amount = factors[count_of_buys - 1]# filled_buys[0].cost + # This then calculates current safety order size + # stake_amount = stake_amount * pow(1.5, count_of_buys) + print("-----------" + trade.pair + " " + str(current_profit) + " " + str(count_of_buys) + " " + str(stake_amount) + "---------------------") + return stake_amount + except Exception as exception: + print(exception) + return None + return None diff --git a/Zeus_8_3_1_1.json b/Zeus_8_3_1_1.json new file mode 100644 index 0000000..075f6b2 --- /dev/null +++ b/Zeus_8_3_1_1.json @@ -0,0 +1,114 @@ +{ + "strategy_name": "Zeus_8_3_1_1", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_b_bb_lowerband": 1.03, + "buy_b_bb_width": 0.02, + "buy_min_max_coef": 1.004, + "buy_min_max_cond1": 0.8, + "buy_min_max_decalage": 2, + "buy_min_max_n": 0.02, + "buy_min_max_nh": 14, + "buy_min_max_rsi": 61, + "buy_b_cat": "R", + "buy_h_pct": 0.016, + "buy_h_real": 0.026, + "buy_0_distance": -0.03, + "buy_0_percent20": -0.03, + "buy_1_bb_lower_5": 0.19, + "buy_1_pente_sma10": 0.48, + "buy_1_pente_sma20": 0.33, + "buy_2_bb_lower_5": 0.59, + "buy_2_distance": 0.08, + "buy_2_pente_sma10": 0.46, + "buy_2_pente_sma20": 0.08, + "buy_2_percent20": 0.06, + "buy_3_bb_lower_5": 0.05, + "buy_3_distance": 0.01, + "buy_3_pente_sma10": 0.12, + "buy_3_pente_sma20": 0.27, + "buy_3_percent20": -0.03, + "buy_4_pente_sma10": 0.35, + "buy_4_pente_sma20": 0.22, + "buy_decalage0": 7, + "buy_decalage2": 6, + "buy_decalage3": 7, + "buy_decalage_deb_0": 1, + "buy_decalage_deb_2": 1, + "buy_decalage_deb_3": 1, + "buy_min_horizon": 192, + "buy_real_num0": 0.61, + "buy_real_num1": 0.34, + "buy_real_num2": 0.35 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3000, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2000, + "sell_h_too_old_percent": 0.01 + }, + "protection": { + "protection_nb_buy_lost": 5, + "protection_percent_buy_lost": 3 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-08-13 05:53:44.097978+00:00" +} \ No newline at end of file diff --git a/Zeus_8_3_1_1.py b/Zeus_8_3_1_1.py new file mode 100644 index 0000000..9a04c0f --- /dev/null +++ b/Zeus_8_3_1_1.py @@ -0,0 +1,998 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_8_3_1_1(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", "R", "=R", "R", "=R", " bool: + allow_to_buy = True + informative, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + # info_previous_last_candle = informative.iloc[-2].squeeze() + + if self.stop_buying.get(pair, None) is None: + print("enable buying tag", pair) + self.stop_buying[pair] = False + + if ((info_last_candle['rsi_1h'] >= 72) | (info_last_candle['close_1h'] >= info_last_candle['bb_upperband_1h'])) \ + & (self.stop_buying[pair] is False): + logger.info("1 - Disable buying %s date %s", pair, info_last_candle['date']) + self.stop_buying[pair] = True + + if self.stop_buying[pair] is True: + if ((info_last_candle['rsi_1h'] <= 35) | (info_last_candle['close_1h'] < info_last_candle['bb_lowerband_1h'])): + logger.info("2 - Enable buying %s date %s", pair, info_last_candle['date']) + self.stop_buying[pair] = False + + if self.stop_buying[pair]: + allow_to_buy = False + logger.info("3 - cancel buying %s date %s", pair, info_last_candle['date']) + else: + logger.info("3 - accept buying %s date %s", pair, info_last_candle['date']) + return allow_to_buy + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + expected_profit = 0.01 + #print(last_candle['buy_tag']) + + days = (current_time - trade.open_date_utc).days + ###### + + informative, _ = self.dp.get_analyzed_dataframe(pair='BTC/USDT', timeframe="5m") + btc_last_candle = informative.iloc[-1].squeeze() + + if (self.stop_buying[pair] is True) & (current_profit > -0.02): + return "force_sell_no_buy" + + if (self.stop_buying[pair] is False) & (btc_last_candle['percent3'] < -0.02) & (current_profit > -0.02): + self.stop_buying[pair] = True + return "force_sell_btc_fall" + + + # if last_candle['rsi_1h'] > 72: + # self.stop_buy[pair] = True + # return False + + # bb_width_lim = last_candle['bb_width'] / 4 + # bb_width_up = last_candle['bb_upperband'] * (1 - last_candle['bb_width'] / 5) + + if (last_candle['mrsi3_1h'] <= 0): #(self.market_overview_pct5 < 0) | (last_candle['pct_change_1_4h'] < 0): + max_percent = 0.004 # last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0.004 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > 0.01) & \ + (last_candle['percent10'] < -0.005) & ((current_time - trade.open_date_utc).seconds >= 3600): + return 'b_percent10' + if (current_profit > max_profit) & \ + ((last_candle['percent'] < - max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + & (days < self.sell_b_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= max_profit) & ( + last_candle['percent3'] < -max_percent): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > max_percent) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + max_percent = 0.005 # last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0.01 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > max_profit) & ( + (last_candle['percent'] < -max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'h_percent_quick' + + # if (last_candle['bb_width'] < 0.02) & (current_profit > 0) & (last_candle['close'] > bb_width_up) & \ + # ((last_candle['percent'] < - bb_width_lim) | (last_candle['percent3'] < - bb_width_lim) | (last_candle['percent5'] < - bb_width_lim)): + # return 'h_bb_width_max' + + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= max_profit) & ( + last_candle['percent3'] < -max_percent): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > max_percent) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_h_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle[ + 'max200']): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_1'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_n'] = talib.MIN(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['max_n'] = talib.MAX(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + # dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / self.buy_bb_width_n.value)) + + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + # dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + # dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + dataframe['min_max_close'] = ( + (dataframe['max200'] - dataframe['close']) / (dataframe['close'] - dataframe['min200'])) + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + dataframe['atr'] = talib.ATR(dataframe, timeperiod=14) + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ######################## INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative["mrsi3"] = informative["rsi"].rolling(3).mean() + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= self.buy_base.value) + & (dataframe['rsi'] < self.buy_rsi.value) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_lower_width_5']) + # & (dataframe['min50'] == dataframe['min50'].shift(3)) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + # & (dataframe['close'] <= dataframe['close_1d']) + & (dataframe['close'] <= dataframe['close_1h']) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + + dataframe.loc[ + ( + (dataframe['min_max_n'] >= self.buy_min_max_n.value) + & (dataframe['cond1'].shift(self.buy_min_max_decalage.value) <= self.buy_min_max_cond1.value) + & (dataframe['rsi'] < self.buy_min_max_rsi.value) + & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + & (dataframe['min_n'].shift(self.buy_min_max_decalage.value) == dataframe['min_n']) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['close'] <= dataframe['close_1h']) + ), ['buy', 'buy_tag']] = (1, 'buy_min_max') + return dataframe + + # def bot_loop_start(self, **kwargs) -> None: + # pairs = self.dp.current_whitelist() + # print("Calcul des pairs informatives") + # for pairname in pairs: + # self.stop_buying[pairname] = True + # print("Fin Calcul des pairs informatives") + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + if (len(dataframe) < 1): + return None + + # dataframe = self.populate_buy_trend(dataframe, {'pair': trade.pair}) + # if not (self.populate_buy_trend(dataframe, {'pair': trade.pair})): + # return None + if (self.stop_buying.get(trade.pair) == None): + print("-----------" + trade.pair + " Init stop buying " + str(current_profit) + " " + str(current_time) + "---------------------") + + self.stop_buying[trade.pair] = True + + if (self.stop_buying[trade.pair] == True): + print("-----------" + trade.pair + " Canceled " + str(current_profit) + " " + str(current_time) + "---------------------") + return None + last_candle = dataframe.iloc[-1].squeeze() + last_candle_5 = dataframe.iloc[-3].squeeze() + + min_d = min(last_candle['sma3_4h'], last_candle['close_1d']) + + filled_buys = trade.select_filled_orders('buy') + count_of_buys = len(filled_buys) + # days = (current_time - trade.open_date_utc).days + # minutes = (current_time - trade.open_date_utc).seconds / 60 + condition = ((last_candle['min50'] == last_candle_5['min50']) & (last_candle['close'] <= last_candle['close_1h'])) + p = self.protection_percent_buy_lost.value + percents = [p, p * 2, p * 3, p * 4, p * 5, p * 6, p * 7, p * 8, p * 9] + + if (0 < count_of_buys <= self.protection_nb_buy_lost.value) \ + & (current_profit < - (percents[count_of_buys - 1] / 100)) & (condition): + try: + p = self.config['stake_amount'] + factors = [p, p, p, p, 2 * p, 4 * p, 5 * p, 6 * p] + + stake_amount = factors[count_of_buys - 1]# filled_buys[0].cost + # This then calculates current safety order size + print("-----------" + trade.pair + " " + str(current_profit) + " " + str(count_of_buys) + " " + str(stake_amount) + + " " + str(current_time) + "---------------------") + return stake_amount + except Exception as exception: + print(exception) + return None + return None diff --git a/Zeus_8_3_2.json b/Zeus_8_3_2.json new file mode 100644 index 0000000..c1df837 --- /dev/null +++ b/Zeus_8_3_2.json @@ -0,0 +1,114 @@ +{ + "strategy_name": "Zeus_8_3_2", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_b_bb_lowerband": 1.03, + "buy_b_bb_width": 0.02, + "buy_min_max_coef": 1.004, + "buy_min_max_cond1": 0.8, + "buy_min_max_decalage": 2, + "buy_min_max_n": 0.02, + "buy_min_max_nh": 14, + "buy_min_max_rsi": 61, + "buy_b_cat": "R", + "buy_h_pct": 0.016, + "buy_h_real": 0.026, + "buy_0_distance": -0.03, + "buy_0_percent20": -0.03, + "buy_1_bb_lower_5": 0.19, + "buy_1_pente_sma10": 0.48, + "buy_1_pente_sma20": 0.33, + "buy_2_bb_lower_5": 0.59, + "buy_2_distance": 0.08, + "buy_2_pente_sma10": 0.46, + "buy_2_pente_sma20": 0.08, + "buy_2_percent20": 0.06, + "buy_3_bb_lower_5": 0.05, + "buy_3_distance": 0.01, + "buy_3_pente_sma10": 0.12, + "buy_3_pente_sma20": 0.27, + "buy_3_percent20": -0.03, + "buy_4_pente_sma10": 0.35, + "buy_4_pente_sma20": 0.22, + "buy_decalage0": 7, + "buy_decalage2": 6, + "buy_decalage3": 7, + "buy_decalage_deb_0": 1, + "buy_decalage_deb_2": 1, + "buy_decalage_deb_3": 1, + "buy_min_horizon": 192, + "buy_real_num0": 0.61, + "buy_real_num1": 0.34, + "buy_real_num2": 0.35 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3000, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2000, + "sell_h_too_old_percent": 0.01 + }, + "protection": { + "protection_nb_buy_lost": 5, + "protection_percent_buy_lost": 3 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-08-13 05:53:44.097978+00:00" +} \ No newline at end of file diff --git a/Zeus_8_3_2.py b/Zeus_8_3_2.py new file mode 100644 index 0000000..e7e1167 --- /dev/null +++ b/Zeus_8_3_2.py @@ -0,0 +1,947 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), + real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_8_3_2(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", "R", "=R", "R", "=R", " -0.03): + # self.stop_buy_for_all = True + # return "btc_fall" + + # bb_width_lim = last_candle['bb_width'] / 4 + # bb_width_up = last_candle['bb_upperband'] * (1 - last_candle['bb_width'] / 5) + + if (last_candle['mrsi3_1h'] <= 0): #(self.market_overview_pct5 < 0) | (last_candle['pct_change_1_4h'] < 0): + max_percent = 0.004 # last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0.004 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > 0.01) & \ + (last_candle['percent10'] < -0.005) & ((current_time - trade.open_date_utc).seconds >= 3600): + return 'b_percent10' + if (current_profit > max_profit) & \ + ((last_candle['percent'] < - max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + & (days < self.sell_b_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= max_profit) & ( + last_candle['percent3'] < -max_percent): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > max_percent) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + max_percent = 0.005 # last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0.01 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > max_profit) & ( + (last_candle['percent'] < -max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'h_percent_quick' + + # if (last_candle['bb_width'] < 0.02) & (current_profit > 0) & (last_candle['close'] > bb_width_up) & \ + # ((last_candle['percent'] < - bb_width_lim) | (last_candle['percent3'] < - bb_width_lim) | (last_candle['percent5'] < - bb_width_lim)): + # return 'h_bb_width_max' + + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= max_profit) & ( + last_candle['percent3'] < -max_percent): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > max_percent) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_h_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle[ + 'max200']): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_1'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_n'] = talib.MIN(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['max_n'] = talib.MAX(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + # dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / self.buy_bb_width_n.value)) + + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + # dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + # dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + dataframe['min_max_close'] = ( + (dataframe['max200'] - dataframe['close']) / (dataframe['close'] - dataframe['min200'])) + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + dataframe['atr'] = talib.ATR(dataframe, timeperiod=14) + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ######################## INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative["mrsi3"] = informative["rsi"].rolling(3).mean() + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= self.buy_base.value) + & (dataframe['rsi'] < self.buy_rsi.value) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_lower_width_5']) + # & (dataframe['min50'] == dataframe['min50'].shift(3)) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + # & (dataframe['close'] <= dataframe['close_1d']) + & (dataframe['close'] <= dataframe['close_1h']) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + + dataframe.loc[ + ( + (dataframe['min_max_n'] >= self.buy_min_max_n.value) + & (dataframe['cond1'].shift(self.buy_min_max_decalage.value) <= self.buy_min_max_cond1.value) + & (dataframe['rsi'] < self.buy_min_max_rsi.value) + & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + & (dataframe['min_n'].shift(self.buy_min_max_decalage.value) == dataframe['min_n']) + & (dataframe['pct_change_1_1d'] > 0) + # & (dataframe['min50'] == dataframe['min50'].shift(3)) + # & (dataframe['close'] <= dataframe['close_1d']) + & (dataframe['close'] <= dataframe['close_1h']) + ), ['buy', 'buy_tag']] = (1, 'buy_min_max') + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + if (True) | (len(dataframe) < 1): + return None + last_candle = dataframe.iloc[-1].squeeze() + last_candle_5 = dataframe.iloc[-3].squeeze() + + min_d = min(last_candle['sma3_4h'], last_candle['close_1d']) + + filled_buys = trade.select_filled_orders('buy') + count_of_buys = len(filled_buys) + days = (current_time - trade.open_date_utc).days + minutes = (current_time - trade.open_date_utc).seconds / 60 + # condition = (last_candle['cond1'] <= 0.75) & (last_candle['bb_width'] > 0.018) & ( + # last_candle['rsi'] < 72) & (last_candle['close'] < last_candle['min50'] * 1.006)# & (last_candle['min_max_close'] > 2) + + condition = (last_candle['min50'] == last_candle_5['min50']) & (last_candle['close'] <= last_candle['close_1h']) + p = self.protection_percent_buy_lost.value + percents = [p, p * 2, p * 3, p * 4, p * 5, p * 6, p * 7, p * 8, p * 9] + + if (0 < count_of_buys <= self.protection_nb_buy_lost.value) \ + & (current_profit < - (percents[count_of_buys - 1] / 100)) & (condition): + try: + p = self.config['stake_amount'] + factors = [p, p, p, 2 * p, 3 * p, 4 * p, 5 * p, 6 * p] + + stake_amount = factors[count_of_buys - 1]# filled_buys[0].cost + # This then calculates current safety order size + # stake_amount = stake_amount * pow(1.5, count_of_buys) + print("-----------" + trade.pair + " " + str(current_profit) + " " + str(count_of_buys) + " " + str(stake_amount) + "---------------------") + return stake_amount + except Exception as exception: + print(exception) + return None + return None diff --git a/Zeus_8_3_2_1.json b/Zeus_8_3_2_1.json new file mode 100644 index 0000000..1e7e578 --- /dev/null +++ b/Zeus_8_3_2_1.json @@ -0,0 +1,81 @@ +{ + "strategy_name": "Zeus_8_3_2_1", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_base": 0.13, + "buy_bb_width_n": 5.2, + "buy_min_horizon": 173, + "buy_min_max_coef": 1.002, + "buy_min_max_cond1": 2.0, + "buy_min_max_decalage": 3, + "buy_min_max_n": 0.04, + "buy_min_max_nh": 35, + "buy_min_max_rsi": 86, + "buy_rsi": 64 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3000, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2000, + "sell_h_too_old_percent": 0.01 + }, + "protection": { + "hours_sell": 44, + "percent_sell_stop": -0.4 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-27 08:51:29.097780+00:00" +} \ No newline at end of file diff --git a/Zeus_8_3_2_1.py b/Zeus_8_3_2_1.py new file mode 100644 index 0000000..b2950a0 --- /dev/null +++ b/Zeus_8_3_2_1.py @@ -0,0 +1,1010 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), + real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_8_3_2_1(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", "R", "=R", "R", "=R", "= self.hours_sell.value) & (last_candle['percent_1h'] <= -0.05): + return 'sell_stop' + # if ( + # (btc_last_candle['percent'] < -0.02) | (btc_last_candle['percent5'] < -0.04)) & (current_profit > -0.03): + # self.stop_buy_for_all = True + # return "btc_fall" + + # bb_width_lim = last_candle['bb_width'] / 4 + # bb_width_up = last_candle['bb_upperband'] * (1 - last_candle['bb_width'] / 5) + + if (last_candle['mrsi3_1h'] <= 0): #(self.market_overview_pct5 < 0) | (last_candle['pct_change_1_4h'] < 0): + max_percent = 0.004 # last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0.004 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > 0.01) & \ + (last_candle['percent10'] < -0.005) & ((current_time - trade.open_date_utc).seconds >= 3600): + return 'b_percent10' + if (current_profit > max_profit) & \ + ((last_candle['percent'] < - max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + & (days < self.sell_b_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= max_profit) & ( + last_candle['percent3'] < -max_percent): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > max_percent) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + max_percent = 0.005 # last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0.01 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > max_profit) & ( + (last_candle['percent'] < -max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'h_percent_quick' + + # if (last_candle['bb_width'] < 0.02) & (current_profit > 0) & (last_candle['close'] > bb_width_up) & \ + # ((last_candle['percent'] < - bb_width_lim) | (last_candle['percent3'] < - bb_width_lim) | (last_candle['percent5'] < - bb_width_lim)): + # return 'h_bb_width_max' + + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= max_profit) & ( + last_candle['percent3'] < -max_percent): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > max_percent) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_h_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle[ + 'max200']): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + # informative_pairs += [(pair, '1M') for pair in pairs] + # informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_1'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['min_max_50'] = (dataframe['max50'] - dataframe['min50']) / dataframe['min50'] + + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_n'] = talib.MIN(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['max_n'] = talib.MAX(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma5_inv'] = (dataframe['sma5'].shift(2) > dataframe['sma5'].shift(1)) & (dataframe['sma5'].shift(1) < dataframe['sma5']) + dataframe['sma5_sell_1'] = (dataframe['sma5'].shift(2) < dataframe['sma5'].shift(1)) & (dataframe['sma5'].shift(1) > dataframe['sma5']) \ + & ( + (dataframe['close'] >= dataframe['max_n']) | + (dataframe['close'].shift(1) >= dataframe['max_n']) | + (dataframe['close'].shift(2) >= dataframe['max_n']) + ) + dataframe['sma5_buy_1'] = (dataframe['sma5'].shift(2) > dataframe['sma5'].shift(1)) & (dataframe['sma5'].shift(1) < dataframe['sma5']) \ + & ( + (dataframe['close'] <= dataframe['min_n']) | + (dataframe['close'].shift(1) <= dataframe['min_n']) | + (dataframe['close'].shift(2) <= dataframe['min_n']) + ) + dataframe['sma5_pct'] = dataframe['sma5'].pct_change(1) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["zero"] = 0 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + # dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / self.buy_bb_width_n.value)) + + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + # dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + # dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + dataframe['min_max_close'] = ( + (dataframe['max200'] - dataframe['close']) / (dataframe['close'] - dataframe['min200'])) + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + dataframe['atr'] = talib.ATR(dataframe, timeperiod=14) + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['min5'] = talib.MIN(informative['close'], timeperiod=5) + informative['max3'] = talib.MAX(informative['close'], timeperiod=3) + informative['max5'] = talib.MAX(informative['close'], timeperiod=5) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + # informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ######################## INFORMATIVE 4h + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + # informative["rsi"] = talib.RSI(informative) + # informative['pct_change_1'] = informative['close'].pct_change(1) + # informative['pct_change_3'] = informative['close'].pct_change(3) + # informative['pct_change_5'] = informative['close'].pct_change(5) + # informative['sma3'] = talib.SMA(informative, timeperiod=3) + # informative['sma5'] = talib.SMA(informative, timeperiod=5) + # informative['sma10'] = talib.SMA(informative, timeperiod=10) + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ######################## INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative["mrsi3"] = informative["rsi"].rolling(3).mean() + informative['percent'] = informative['close'].pct_change(1) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + ################### INFORMATIVE 1M + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1M") + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1M", ffill=True) + + # dataframe['support'] = min(dataframe['close_1d'], dataframe['sma3_1d'], dataframe['sma3_1d']) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= self.buy_base.value) + & (dataframe['rsi'] < self.buy_rsi.value) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_lower_width_5']) + # & (dataframe['min50'] == dataframe['min50'].shift(3)) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + & (dataframe['close'] < dataframe['min_n'] * (1 + dataframe['min_max_50'] / 5) ) + # & (dataframe['close'] <= dataframe['min_200_001']) + & (dataframe['close'] <= dataframe['close_1h']) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + + dataframe.loc[ + ( + (dataframe['min_max_n'] >= self.buy_min_max_n.value) + & (dataframe['cond1'].shift(self.buy_min_max_decalage.value) <= self.buy_min_max_cond1.value) + & (dataframe['rsi'] < self.buy_min_max_rsi.value) + # & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + & (dataframe['close'] < dataframe['min_n'] * (1 + dataframe['min_max_50'] / 5)) + & (dataframe['min_n'].shift(self.buy_min_max_decalage.value) == dataframe['min_n']) + & (dataframe['pct_change_1_1d'] > 0) + # & (dataframe['close'] <= dataframe['min_200_001']) + # & (dataframe['min50'] == dataframe['min50'].shift(3)) + # & (dataframe['close'] <= dataframe['close_1d']) + & (dataframe['close'] <= dataframe['close_1h']) + ), ['buy', 'buy_tag']] = (1, 'buy_min_max') + + # conditions = [ + # (dataframe['percent10'] <= -0.06) + # ] + # # GUARDS AND TRENDS + # if conditions: + # dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + # ['buy', 'buy_tag']] = (1, 'buy_5') + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + + # def adjust_trade_position(self, trade: Trade, current_time: datetime, + # current_rate: float, current_profit: float, min_stake: float, + # max_stake: float, **kwargs): + # dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + # if (len(dataframe) < 1): + # return None + # last_candle = dataframe.iloc[-1].squeeze() + # last_candle_5 = dataframe.iloc[-3].squeeze() + # + # min_d = min(last_candle['sma3_4h'], last_candle['close_1d']) + # + # filled_buys = trade.select_filled_orders('buy') + # count_of_buys = len(filled_buys) + # days = (current_time - trade.open_date_utc).days + # minutes = (current_time - trade.open_date_utc).seconds / 60 + # # condition = (last_candle['cond1'] <= 0.75) & (last_candle['bb_width'] > 0.018) & ( + # # last_candle['rsi'] < 72) & (last_candle['close'] < last_candle['min50'] * 1.006)# & (last_candle['min_max_close'] > 2) + # + # condition = (last_candle['min50'] == last_candle_5['min50']) & (last_candle['close'] <= last_candle['close_1h']) + # p = self.protection_percent_buy_lost.value + # percents = [p, p * 2, p * 3, p * 4, p * 5, p * 6, p * 7, p * 8, p * 9] + # + # if (0 < count_of_buys <= self.protection_nb_buy_lost.value) \ + # & (current_profit < - (percents[count_of_buys - 1] / 100)) & (condition): + # try: + # p = self.config['stake_amount'] + # factors = [p, p, p, 2 * p, 3 * p, 4 * p, 5 * p, 6 * p] + # + # stake_amount = factors[count_of_buys - 1]# filled_buys[0].cost + # # This then calculates current safety order size + # # stake_amount = stake_amount * pow(1.5, count_of_buys) + # print("-----------" + trade.pair + " " + str(current_profit) + " " + str(count_of_buys) + " " + str(stake_amount) + "---------------------") + # return stake_amount + # except Exception as exception: + # print(exception) + # return None + # return None diff --git a/Zeus_8_3_2_B_1.json b/Zeus_8_3_2_B_1.json new file mode 100644 index 0000000..8097c14 --- /dev/null +++ b/Zeus_8_3_2_B_1.json @@ -0,0 +1,87 @@ +{ + "strategy_name": "Zeus_8_3_2_B_1", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_base": 0.19, + "buy_base_echange": 0.13, + "buy_bb_width_n": 1.1, + "buy_echange": -0.39, + "buy_min_horizon": 186, + "buy_min_max_coef": 1.002, + "buy_min_max_cond1": 0.8, + "buy_min_max_decalage": 5, + "buy_min_max_n": 0.18, + "buy_min_max_nh": 8, + "buy_min_max_rsi": 77, + "buy_rsi": 58, + "decalage_min50": 0, + "pct_change_1h": 0.002, + "percent_close_50": 1.002, + "stake_factor": 4 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": false, + "profit_b_quick_gain_3": true, + "profit_b_quick_lost": true, + "profit_b_short_loss": false, + "profit_b_sma10": true, + "profit_b_sma20": false, + "profit_b_sma5": true, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": false, + "profit_h_over_rsi": true, + "profit_h_quick_gain": true, + "profit_h_quick_gain_3": false, + "profit_h_quick_lost": true, + "profit_h_short_loss": true, + "profit_h_sma10": true, + "profit_h_sma20": true, + "profit_h_sma5": true, + "profit_h_very_old_sma10": false, + "sell_b_RSI": 87, + "sell_b_RSI2": 82, + "sell_b_RSI2_percent": 0.007, + "sell_b_RSI3": 75, + "sell_b_candels": 23, + "sell_b_percent": 0.014, + "sell_b_percent3": 0.018, + "sell_b_profit_no_change": 0.003, + "sell_b_profit_percent10": 0.0011, + "sell_b_too_old_day": 10, + "sell_b_too_old_percent": 0.013, + "sell_h_RSI": 82, + "sell_h_RSI2": 75, + "sell_h_RSI2_percent": 0.011, + "sell_h_RSI3": 97, + "sell_h_candels": 6, + "sell_h_percent": 0.009, + "sell_h_percent3": 0.016, + "sell_h_profit_no_change": 0.017, + "sell_h_profit_percent10": 0.0014, + "sell_h_too_old_day": 3, + "sell_h_too_old_percent": 0.004 + }, + "protection": { + "protection_fibo": 9, + "protection_percent_buy_lost": 3 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-12-15 19:58:58.055412+00:00" +} \ No newline at end of file diff --git a/Zeus_8_3_2_B_1.py b/Zeus_8_3_2_B_1.py new file mode 100644 index 0000000..8e5e8c2 --- /dev/null +++ b/Zeus_8_3_2_B_1.py @@ -0,0 +1,1108 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), + real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_8_3_2_B_1(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", " float: + + # dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + # current_candle = dataframe.iloc[-1].squeeze() + # + # min_value = min([current_candle['close_1d'], current_candle['sma3_1d'], current_candle['sma5_1d'], current_candle['sma10_1d']]) + # min_pct = (current_candle['close'] - min_value) / current_candle['close'] + # + # # + # # print("use more stake", pair, " ", proposed_stake * (1 - min_pct)) + # if min_pct < 0: + # proposed_stake = min(200, min(max_stake, proposed_stake * (1 - self.stake_factor.value * min_pct))) + # print(str(current_time), "proposed_stake=", proposed_stake, " max_stake=", max_stake , "min_pct=", min_pct) + + # + # if current_candle['bb_width'] > 0.035: + # print("use more stake", pair, " ", proposed_stake * 1.5) + # return min(max_stake, proposed_stake * 1.5) + + # if current_candle['bb_width'] < 0.020: + # print("use less stake", pair, " ", proposed_stake / 2) + # return min(max_stake, proposed_stake / 2) + # if self.config['stake_amount'] == 'unlimited': + # # Use entire available wallet during favorable conditions when in compounding mode. + # return max_stake + # else: + # # Compound profits during favorable conditions instead of using a static stake. + # return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] + + + # Use default stake amount. + return proposed_stake + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + expected_profit = 0.01 + #print(last_candle['buy_tag']) + + days = (current_time - trade.open_date_utc).days + hours = (current_time - trade.open_date_utc).seconds / 3600 + + # fibo_fact = fibo[hours] + ###### + + if (last_candle['pct_change_1_1h'] <= 0): #(self.market_overview_pct5 < 0) | (last_candle['pct_change_1_4h'] < 0): + max_percent = 0.004 # last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0.004 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > 0.01) & \ + (last_candle['percent10'] < -0.005) & ((current_time - trade.open_date_utc).seconds >= 3600): + return 'b_percent10' + if (current_profit > max_profit) & \ + ((last_candle['percent'] < - max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + & (days < self.sell_b_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= max_profit) & ( + last_candle['percent3'] < -max_percent): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > max_profit) and (last_candle['echange*pct5'] < 0.0005): + return "b_nochange_pct" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > max_percent) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + + max_percent = 0.005 # last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0.01 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > max_profit) & ( + (last_candle['percent'] < -max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'h_percent_quick' + + # if (last_candle['bb_width'] < 0.02) & (current_profit > 0) & (last_candle['close'] > bb_width_up) & \ + # ((last_candle['percent'] < - bb_width_lim) | (last_candle['percent3'] < - bb_width_lim) | (last_candle['percent5'] < - bb_width_lim)): + # return 'h_bb_width_max' + + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= max_profit) & ( + last_candle['percent3'] < -max_percent): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > max_profit) and (last_candle['echange*pct12'] < 0.001): + return "h_nochange_pct" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > max_percent) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): + # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_h_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle[ + 'max200']): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min50_005'] = dataframe['min50'] * 1.005 + dataframe['min200_005'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['min_max_50'] = (dataframe['max50'] - dataframe['min50']) / dataframe['min50'] + + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_n'] = talib.MIN(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['max_n'] = talib.MAX(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + # dataframe['sma5_inv'] = (dataframe['sma5'].shift(2) > dataframe['sma5'].shift(1)) & (dataframe['sma5'].shift(1) < dataframe['sma5']) + # dataframe['sma5_sell_1'] = (dataframe['sma5'].shift(2) < dataframe['sma5'].shift(1)) & (dataframe['sma5'].shift(1) > dataframe['sma5']) \ + # & ( + # (dataframe['close'] >= dataframe['max_n']) | + # (dataframe['close'].shift(1) >= dataframe['max_n']) | + # (dataframe['close'].shift(2) >= dataframe['max_n']) + # ) + # dataframe['sma5_buy_1'] = (dataframe['sma5'].shift(2) > dataframe['sma5'].shift(1)) & (dataframe['sma5'].shift(1) < dataframe['sma5']) \ + # & ( + # (dataframe['close'] <= dataframe['min_n']) | + # (dataframe['close'].shift(1) <= dataframe['min_n']) | + # (dataframe['close'].shift(2) <= dataframe['min_n']) + # ) + # dataframe['sma5_pct'] = dataframe['sma5'].pct_change(1) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + # dataframe["zero"] = 0 + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + # dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + dataframe["volume5"] = dataframe["volume"].rolling(5).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).sum() + + # print(metadata['pair']) + # print(circulation[metadata['pair']]) + if circulation[metadata['pair']]: + dataframe["echange"] = 10000 * dataframe["volume"] / circulation[metadata['pair']] + dataframe["echange*pct"] = dataframe["echange"] * dataframe["percent"] + dataframe["echange5"] = 10000 * dataframe["volume5"] / circulation[metadata['pair']] + dataframe["echange*pct5"] = dataframe["echange5"] * dataframe["percent5"] + dataframe["pct_echange5"] = dataframe["echange5"].pct_change() + dataframe["acc_echange5"] = dataframe["echange*pct5"] / dataframe["echange*pct5"].shift(1) + for i in range(1, 4): + n = i * 12 + dataframe["echange*pct" + str(n)] = 10000 * dataframe["volume"].rolling(n).sum() / circulation[metadata['pair']] \ + * dataframe["percent"].rolling(n).sum() + + + dataframe["pente20"] = (dataframe["close"] - dataframe['close'].shift(20)) / dataframe["close"] + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / self.buy_bb_width_n.value)) + + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min50_1.01'] = 1.01 * dataframe['min50'] + dataframe['min50_1.02'] = 1.02 * dataframe['min50'] + dataframe['min50_1.03'] = 1.03 * dataframe['min50'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + # dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + # dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + dataframe['min_max_close'] = ( + (dataframe['max200'] - dataframe['close']) / (dataframe['close'] - dataframe['min200'])) + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + dataframe['trend_ichimoku_base_50'] = talib.MIN(dataframe['trend_ichimoku_base'], timeperiod=50) + dataframe['trend_ichimoku_base_5'] = talib.MIN(dataframe['trend_ichimoku_base'], timeperiod=5) + dataframe['trend_ichimoku_base_sma5'] = talib.SMA(dataframe['trend_ichimoku_base'], timeperiod=5) + dataframe['trend_ichimoku_base_pct'] = dataframe['trend_ichimoku_base'].pct_change(3) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + dataframe['cond_50'] = talib.MIN(dataframe['cond1'], timeperiod=50) + dataframe['cond_5'] = talib.MIN(dataframe['cond1'], timeperiod=5) + dataframe['cond_sma5'] = talib.SMA(dataframe['cond1'], timeperiod=5) + dataframe['cond_pct'] = dataframe['cond1'].pct_change(3) + dataframe['atr'] = talib.ATR(dataframe, timeperiod=14) + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['min5'] = talib.MIN(informative['close'], timeperiod=5) + informative['max3'] = talib.MAX(informative['close'], timeperiod=3) + informative['max5'] = talib.MAX(informative['close'], timeperiod=5) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + # informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ######################## INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative["mrsi3"] = informative["rsi"].rolling(3).mean() + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['min3'] = talib.MIN(informative['close'], timeperiod=3) + informative['min5'] = talib.MIN(informative['close'], timeperiod=5) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + # dataframe['support'] = min(dataframe['close_1d'], dataframe['sma3_1d'], dataframe['sma3_1d']) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # min_value = min([dataframe['close_1d'], dataframe['sma3_1d'], dataframe['sma5_1d'], + # dataframe['sma10_1d']]) + # min_pct = 100 * (dataframe['close'] - min_value) / dataframe['close'] + + # if self.config['runmode'].value in ('live', 'dry_run'): + # trades = Trade.get_trades([Trade.pair == metadata['pair'], + # Trade.open_date > datetime.utcnow() - timedelta(days=1), + # Trade.is_open.is_(False), + # ]).order_by(Trade.close_date).all() + # # Summarize profit for this pair. + # print("Trades=", trades) + # curdayprofit = sum(trade.close_profit for trade in trades) + # print(curdayprofit) + + decalage = 3 + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'].shift(decalage) <= self.buy_base.value) + & (dataframe['rsi'].shift(decalage) < self.buy_rsi.value) + & (dataframe['close'].shift(decalage) < dataframe['sma10'].shift(decalage)) + & (dataframe['close'].shift(decalage) < dataframe['bb_middleband'].shift(decalage)) + & (dataframe['bb_width'].shift(decalage) > 0.003) + & ((dataframe['close'].shift(decalage) < dataframe['bb_lower_width_5'].shift(decalage))) + & ((dataframe['close'].shift(decalage) <= dataframe['min50'].shift(decalage) * self.percent_close_50.value) | + (dataframe['pct_change_1_1h'].shift(decalage) > - self.pct_change_1h.value) + ) + # & (dataframe['close'] <= dataframe['bb_upperband']) + # & (dataframe['close'] <= dataframe['min_200_001']) + & (dataframe['close'].shift(decalage) <= dataframe['close_1h'].shift(decalage)) + & ((dataframe['close'].shift(decalage) < dataframe['bb_lowerband'].shift(decalage)) | + (dataframe['pct_change_1_1h'].shift(decalage) > - self.pct_change_1h.value) + ) + & (dataframe['close'] < dataframe['bb_upperband']) + & (dataframe['open'] < dataframe['bb_upperband']) + # & (dataframe['open'] < dataframe['sma10']) + # & (dataframe['open'] < dataframe['sma100']) + # & ((dataframe['percent5'] < 0.005) | (dataframe['rsi'] < 50)) + # & (dataframe['close'] < dataframe['bb_upperband']) + # & (dataframe['close'] < dataframe['min50'] * (1 + dataframe['min_max_50'] / 2)) #self.buy_min_max_coef.value) + ), ['buy', 'enter_tag']] = (1, 'buy_ichimoku') + + + dataframe.loc[ + ( + (dataframe['min_max_n'] >= self.buy_min_max_n.value) + & (dataframe['cond1'].shift(self.buy_min_max_decalage.value) <= self.buy_min_max_cond1.value) + & (dataframe['rsi'] < self.buy_min_max_rsi.value) + & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + & (dataframe['min_n'].shift(self.buy_min_max_decalage.value) == dataframe['min_n']) + & (dataframe['pct_change_1_1d'] > 0) + # & (dataframe['close'] <= dataframe['min_200_001']) + # & (dataframe['min50'] == dataframe['min50'].shift(3)) + # & (dataframe['close'] <= dataframe['close_1d']) + & (dataframe['close'] <= dataframe['close_1h']) + ), ['buy', 'enter_tag']] = (1, 'buy_min_max') + + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= self.buy_base_echange.value) + & (dataframe['echange*pct12'] <= self.buy_echange.value) + & (dataframe['echange*pct12'].shift(1) <= dataframe['echange*pct12']) + & ( + (dataframe['close'] < dataframe['bb_lower_width_5']) + # (dataframe['open'] < dataframe['bb_lower_width_5']) + ) + & (dataframe['min50'] == dataframe['min50'].shift(self.decalage_min50.value)) + ), ['buy', 'enter_tag']] = (1, 'buy_echange') + + # dataframe.loc[ + # ( + # (dataframe['close'] <= dataframe['min200']) + # & (dataframe['min50'] == dataframe['min200']) + # & (dataframe['min_max_50'] >= 0.015) + # ), ['buy', 'enter_tag']] = (1, 'buy_min_max') + + # dataframe.loc[ + # ( + # (dataframe['percent20'] <= -0.015) + # # & (dataframe['rsi'] < self.buy_min_max_rsi.value) + # & (dataframe['close'] <= dataframe['min50'] * 1.005) + # & (dataframe['min50'].shift(5) == dataframe['min50']) + # & (dataframe['close'] <= dataframe['close_1h']) + # ), ['buy', 'buy_tag']] = (1, 'buy_percent20') + # dataframe.loc[ + # ( + # (dataframe['percent10'] <= -0.015) + # # & (dataframe['rsi'] < self.buy_min_max_rsi.value) + # & (dataframe['close'] <= dataframe['min50'] * 1.005) + # & (dataframe['min50'].shift(3) == dataframe['min50']) + # & (dataframe['close'] <= dataframe['close_1h']) + # ), ['buy', 'buy_tag']] = (1, 'buy_percent10') + + # conditions = [ + # (dataframe['percent10'] <= -0.06) + # ] + # # GUARDS AND TRENDS + # if conditions: + # dataframe.loc[(reduce(lambda x, y: x & y, conditions)), + # ['buy', 'buy_tag']] = (1, 'buy_5') + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + if True: #(len(dataframe) < 1): + return None + # print(dataframe) + last_candle = dataframe.iloc[-1].squeeze() + last_candle_5 = dataframe.iloc[-3].squeeze() + + min_d = min(last_candle['sma3_4h'], last_candle['close_1d']) + + filled_buys = trade.select_filled_orders('buy') + count_of_buys = len(filled_buys) + days = (current_time - trade.open_date_utc).days + minutes = (current_time - trade.open_date_utc).seconds / 60 + # condition = (last_candle['cond1'] <= 0.75) & (last_candle['bb_width'] > 0.018) & ( + # last_candle['rsi'] < 72) & (last_candle['close'] < last_candle['min50'] * 1.006)# & (last_candle['min_max_close'] > 2) + + condition = (last_candle['enter_long'] == 1) # & (last_candle['close'] <= last_candle['close_1h']) + p = self.protection_percent_buy_lost.value + percents = [p, p * 2, p * 3, p * 4, p * 5, p * 6, p * 7, p * 8, p * 9] + + # self.protection_nb_buy_lost.value + if (0 < count_of_buys <= 2) \ + & (current_profit < - (percents[count_of_buys - 1] / 100)) & (condition): + try: + p = self.config['stake_amount'] + if (last_candle['close'] < last_candle['min5_1h']): + factors = [1.25 * p, 1.75 * p, 2 * p, 2 * p, 3 * p, 4 * p, 5 * p, 6 * p] + else: + factors = [1 * p, 1.25 * p, 2 * p, 2 * p, 3 * p, 4 * p, 5 * p, 6 * p] + stake_amount = factors[count_of_buys - 1]# filled_buys[0].cost + # This then calculates current safety order size + # stake_amount = stake_amount * pow(1.5, count_of_buys) + # print("-----------" + trade.pair + " " + str(current_profit) + " " + str(count_of_buys) + " " + str(stake_amount) + "---------------------") + return stake_amount + except Exception as exception: + print(exception) + return None + return None diff --git a/Zeus_8_3_2_B_3.json b/Zeus_8_3_2_B_3.json new file mode 100644 index 0000000..4bc1b23 --- /dev/null +++ b/Zeus_8_3_2_B_3.json @@ -0,0 +1,75 @@ +{ + "strategy_name": "Zeus_8_3_2_B_3", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "max_open_trades": { + "max_open_trades": 8 + }, + "buy": { + "buy_base": 0.33 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": false, + "profit_b_quick_gain_3": true, + "profit_b_quick_lost": true, + "profit_b_short_loss": false, + "profit_b_sma10": true, + "profit_b_sma20": false, + "profit_b_sma5": true, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": false, + "profit_h_over_rsi": true, + "profit_h_quick_gain": true, + "profit_h_quick_gain_3": false, + "profit_h_quick_lost": true, + "profit_h_short_loss": true, + "profit_h_sma10": true, + "profit_h_sma20": true, + "profit_h_sma5": true, + "profit_h_very_old_sma10": false, + "sell_b_RSI": 87, + "sell_b_RSI2": 82, + "sell_b_RSI2_percent": 0.007, + "sell_b_RSI3": 75, + "sell_b_candels": 23, + "sell_b_percent": 0.014, + "sell_b_percent3": 0.018, + "sell_b_profit_no_change": 0.003, + "sell_b_profit_percent10": 0.0011, + "sell_b_too_old_day": 10, + "sell_b_too_old_percent": 0.013, + "sell_h_RSI": 82, + "sell_h_RSI2": 75, + "sell_h_RSI2_percent": 0.011, + "sell_h_RSI3": 97, + "sell_h_candels": 6, + "sell_h_percent": 0.009, + "sell_h_percent3": 0.016, + "sell_h_profit_no_change": 0.017, + "sell_h_profit_percent10": 0.0014, + "sell_h_too_old_day": 3, + "sell_h_too_old_percent": 0.004 + }, + "protection": { + "protection_fibo": 9, + "protection_percent_buy_lost": 3 + } + }, + "ft_stratparam_v": 1, + "export_time": "2023-02-16 05:58:02.440989+00:00" +} \ No newline at end of file diff --git a/Zeus_8_3_2_B_3.py b/Zeus_8_3_2_B_3.py new file mode 100644 index 0000000..322feb7 --- /dev/null +++ b/Zeus_8_3_2_B_3.py @@ -0,0 +1,1023 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), + real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_8_3_2_B_3(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", "R", "=R", " 0.01) & \ + (last_candle['percent10'] < -0.005) & ((current_time - trade.open_date_utc).seconds >= 3600): + return 'b_percent10' + if (current_profit > max_profit) & \ + ((last_candle['percent'] < - max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + & (days < self.sell_b_too_old_day.value * 2)\ + & (last_candle['sma10_s2'] > last_candle['sma10']) & (last_candle['percent3'] < 0) \ + & (last_candle['echange*pct12'] < 0.0005): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + & (days < self.sell_b_too_old_day.value * 3) \ + & (last_candle['sma10_s2'] > last_candle['sma10']) & (last_candle['percent3'] < 0)\ + & (last_candle['echange*pct12'] < 0.0005): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (last_candle['sma10_s2'] > last_candle['sma10']) & (last_candle['percent3'] < 0)\ + & (last_candle['echange*pct12'] < 0.0005): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= max_profit) & ( + last_candle['percent3'] < -max_percent): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > max_profit) and (last_candle['echange*pct5'] < 0.0005): + return "b_nochange_pct" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((last_candle['sma5_s5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((last_candle['sma10_s5'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > max_percent) \ + & (last_candle['sma10_s2'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((last_candle['sma20_s2'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (last_candle['rsi_s2'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (last_candle['rsi_s2'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (last_candle['rsi_s2'] > self.sell_b_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (last_candle['rsi_s2'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (last_candle['rsi_s2'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (last_candle['rsi_s2'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (last_candle['percent10_s2'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): #| (last_candle['rsi_s2'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + + max_percent = 0.005 # last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0.01 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > max_profit) & ( + (last_candle['percent'] < -max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'h_percent_quick' + + # if (last_candle['bb_width'] < 0.02) & (current_profit > 0) & (last_candle['close'] > bb_width_up) & \ + # ((last_candle['percent'] < - bb_width_lim) | (last_candle['percent3'] < - bb_width_lim) | (last_candle['percent5'] < - bb_width_lim)): + # return 'h_bb_width_max' + + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (last_candle['sma10_s2'] > last_candle['sma10']) & (last_candle['percent3'] < 0)\ + & (last_candle['echange*pct12'] < 0.001): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (last_candle['sma10_s2'] > last_candle['sma10']) & (last_candle['percent3'] < 0)\ + & (last_candle['echange*pct12'] < 0.001): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (last_candle['sma10_s2'] > last_candle['sma10']) & (last_candle['percent3'] < 0)\ + & (last_candle['echange*pct12'] < 0.001): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= max_profit) & ( + last_candle['percent3'] < -max_percent): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > max_profit) and (last_candle['echange*pct12'] < 0.001): + return "h_nochange_pct" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((last_candle['sma5_s5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((last_candle['sma10_s5'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > max_percent) \ + & (last_candle['sma10_s2'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((last_candle['sma20_s2'] > last_candle['sma20']) & + ((last_candle['percent'] < 0) & (last_candle['percent5'] < 0) & (last_candle['percent10'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (last_candle['rsi_s2'] > self.sell_h_RSI.value): + # & (last_candle['percent'] < 0): #| (last_candle['rsi_s2'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (last_candle['rsi_s2'] > self.sell_h_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_h_RSI2_percent.value): # | (last_candle['rsi_s2'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (last_candle['rsi_s2'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle[ + 'max200']): # | (last_candle['rsi_s2'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (last_candle['percent10_s2'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): #| (last_candle['rsi_s2'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min50_005'] = dataframe['min50'] * 1.005 + dataframe['min200_005'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['mean10'] = dataframe['close'].rolling(10).mean() + dataframe['mean20'] = dataframe['close'].rolling(20).mean() + dataframe['mean50'] = dataframe['close'].rolling(50).mean() + dataframe['mean100'] = dataframe['close'].rolling(100).mean() + + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['min_max_50'] = (dataframe['max50'] - dataframe['min50']) / dataframe['min50'] + + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi_pente'] = dataframe['rsi'].pct_change(1) + dataframe['rsi_acc'] = dataframe['rsi_pente'].pct_change(1) + + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe["volume5"] = dataframe["volume"].rolling(5).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).sum() + + dataframe['sma5_s2'] = dataframe['sma5'].shift(1) + dataframe['sma10_s2'] = dataframe['sma10'].shift(1) + dataframe['sma20_s2'] = dataframe['sma20'].shift(1) + dataframe['percent10_s2'] = dataframe['percent10'].shift(1) + dataframe['rsi_s2'] = dataframe['rsi'].shift(1) + + dataframe['sma5_s5'] = dataframe['sma5'].shift(4) + dataframe['sma10_s5'] = dataframe['sma10'].shift(4) + dataframe['sma20_s5'] = dataframe['sma20'].shift(4) + # print(metadata['pair']) + # print(circulation[metadata['pair']]) + if circulation[metadata['pair']]: + dataframe["echange"] = 10000 * dataframe["volume"] / circulation[metadata['pair']] + dataframe["echange*pct"] = dataframe["echange"] * dataframe["percent"] + dataframe["echange5"] = 10000 * dataframe["volume5"] / circulation[metadata['pair']] + dataframe["echange*pct5"] = dataframe["echange5"] * dataframe["percent5"] + dataframe["pct_echange5"] = dataframe["echange5"].pct_change() + dataframe["acc_echange5"] = dataframe["echange*pct5"] / dataframe["echange*pct5"].shift(1) + for i in range(1, 4): + n = i * 12 + dataframe["echange*pct" + str(n)] = 10000 * dataframe["volume"].rolling(n).sum() / circulation[metadata['pair']] \ + * dataframe["percent"].rolling(n).sum() + + dataframe["pente20"] = (dataframe["close"] - dataframe['close'].shift(20)) / dataframe["close"] + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_ecart"] = ((dataframe["bb_upperband"] - dataframe["bb_lowerband"])) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + # dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / self.buy_bb_width_n.value)) + dataframe["bb_pente"] = (dataframe["bb_lowerband"] - dataframe['bb_lowerband'].shift(3)) / dataframe["bb_lowerband"] + dataframe["bb_pente_inv"] = (dataframe["bb_pente"].shift(2) > dataframe["bb_pente"].shift(1)) & \ + (dataframe["bb_pente"] > dataframe["bb_pente"].shift(1)) + dataframe["bb_max_width"] = (dataframe["bb_ecart"].shift(2) < dataframe["bb_ecart"].shift(1)) & \ + (dataframe["bb_ecart"] < dataframe["bb_ecart"].shift(1)) + dataframe["bb_tag"] = dataframe["bb_pente_inv"] & dataframe["bb_max_width"] + # dataframe['bb_min'] = talib.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min50_1.01'] = 1.01 * dataframe['min50'] + dataframe['min50_1.02'] = 1.02 * dataframe['min50'] + dataframe['min50_1.03'] = 1.03 * dataframe['min50'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + # dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + # dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + dataframe['min_max_close'] = ( + (dataframe['max200'] - dataframe['close']) / (dataframe['close'] - dataframe['min200'])) + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = normalize(tib) #(tib-tib.min())/(tib.max()-tib.min()) + dataframe["trend_ichimoku_pente"] = (dataframe["trend_ichimoku_base"] - dataframe['trend_ichimoku_base'].shift(3)) / dataframe["trend_ichimoku_base"] + + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = normalize(tkd) #(tkd-tkd.min())/(tkd.max()-tkd.min()) + + # dataframe['trend_ichimoku_base_50'] = talib.MIN(dataframe['trend_ichimoku_base'], timeperiod=50) + # dataframe['trend_ichimoku_base_5'] = talib.MIN(dataframe['trend_ichimoku_base'], timeperiod=5) + # dataframe['trend_ichimoku_base_sma5'] = talib.SMA(dataframe['trend_ichimoku_base'], timeperiod=5) + # dataframe['trend_ichimoku_base_pct'] = dataframe['trend_ichimoku_base'].pct_change(3) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + dataframe['cond_50'] = talib.MIN(dataframe['cond1'], timeperiod=50) + dataframe['cond_5'] = talib.MIN(dataframe['cond1'], timeperiod=5) + dataframe['cond_sma5'] = talib.SMA(dataframe['cond1'], timeperiod=5) + dataframe['cond_pct'] = dataframe['cond1'].pct_change(3) + dataframe['atr'] = talib.ATR(dataframe, timeperiod=14) + + #FreqAI / Rewards + # dataframe["%-raw_close"] = dataframe["close"] + # dataframe["%-raw_open"] = dataframe["open"] + # dataframe["%-raw_high"] = dataframe["high"] + # dataframe["%-raw_low"] = dataframe["low"] + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['min5'] = talib.MIN(informative['close'], timeperiod=5) + informative['max3'] = talib.MAX(informative['close'], timeperiod=3) + informative['max5'] = talib.MAX(informative['close'], timeperiod=5) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + # informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ######################## INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative["mrsi3"] = informative["rsi"].rolling(3).mean() + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['min3'] = talib.MIN(informative['close'], timeperiod=3) + informative['min5'] = talib.MIN(informative['close'], timeperiod=5) + informative['rsi_pente'] = informative['rsi'].pct_change(1) + informative['rsi_acc'] = informative['rsi_pente'].pct_change(1) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + # dataframe['support'] = min(dataframe['close_1d'], dataframe['sma3_1d'], dataframe['sma3_1d']) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # enter_long_conditions = [dataframe["do_predict"] == 1] #, dataframe["&-s_close"] > dataframe["target_roi"]] + # + # if enter_long_conditions: + # dataframe.loc[ + # reduce(lambda x, y: x & y, enter_long_conditions), ["enter_long", "enter_tag"] + # ] = (1, "long") + + base = 0.33 #self.buy_base.value + # base = base / dataframe[self.buy_rsi_ichimoku.value] * self.buy_rsi_divisor.value + base = base / dataframe['rsi'] * 52 + + decalage = 3 + dataframe.loc[ + ( + # (reduce(lambda x, y: x & y, enter_long_conditions)) + (dataframe['trend_ichimoku_base'].shift(decalage) <= base) + # & (dataframe['close'] > dataframe['close_1d']) + & (dataframe['rsi'].shift(decalage) < 60) + & (dataframe['close'].shift(decalage) < dataframe['sma10'].shift(decalage)) + & (dataframe['close'].shift(decalage) < dataframe['bb_middleband'].shift(decalage)) + & (dataframe['bb_width'].shift(decalage) > 0.003) + # & (dataframe['close'].shift(decalage) < dataframe['bb_lower_width_5'].shift(decalage)) + & ((dataframe['close'].shift(decalage) <= dataframe['min50'].shift(decalage) * 1.002) | + (dataframe['pct_change_1_1h'].shift(decalage) > - 0.002) + ) + # & (dataframe['close'] <= dataframe['bb_upperband']) + # & (dataframe['close'] <= dataframe['min_200_001']) + & (dataframe['close'].shift(decalage) <= dataframe['close_1h'].shift(decalage)) + & ((dataframe['close'].shift(decalage) < dataframe['bb_lowerband'].shift(decalage)) | + (dataframe['pct_change_1_1h'].shift(decalage) > - 0.002) + ) + & (dataframe['close'] < dataframe['bb_upperband']) + & (dataframe['open'] < dataframe['bb_upperband']) + & ((dataframe['bb_pente_inv'] == 1) | (dataframe['bb_pente_inv'].shift(1) == 1) | (dataframe['bb_pente_inv'].shift(2) == 1) + | (dataframe['bb_pente_inv'].shift(3) == 1)) + ), ['buy', 'enter_tag']] = (1, 'buy_ichimoku') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + if (len(dataframe) < 1): + return None + # print(dataframe) + last_candle = dataframe.iloc[-1].squeeze() + # last_candle_5 = dataframe.iloc[-3].squeeze() + + min_d = min(last_candle['sma3_4h'], last_candle['close_1d']) + + filled_buys = trade.select_filled_orders('buy') + count_of_buys = len(filled_buys) + days = (current_time - trade.open_date_utc).days + minutes = (current_time - trade.open_date_utc).seconds / 60 + # condition = (last_candle['cond1'] <= 0.75) & (last_candle['bb_width'] > 0.018) & ( + # last_candle['rsi'] < 72) & (last_candle['close'] < last_candle['min50'] * 1.006)# & (last_candle['min_max_close'] > 2) + + condition = (last_candle['enter_long'] == 1) # & (last_candle['close'] <= last_candle['close_1h']) + p = self.protection_percent_buy_lost.value + percents = [p, p * 2, p * 3, p * 4, p * 5, p * 6, p * 7, p * 8, p * 9] + + # self.protection_nb_buy_lost.value + if (0 < count_of_buys <= 2) \ + & (current_profit < - (percents[count_of_buys - 1] / 100)) & (condition): + try: + p = self.config['stake_amount'] + if last_candle['close'] < last_candle['min5_1h']: + factors = [1.5 * p, 1.75 * p, 2 * p, 2 * p, 3 * p, 4 * p, 5 * p, 6 * p] + else: + if last_candle['close'] < last_candle['min3_1h']: + factors = [1.25 * p, 1.5 * p, 2 * p, 2 * p, 3 * p, 4 * p, 5 * p, 6 * p] + else: + factors = [1 * p, 1.25 * p, 2 * p, 2 * p, 3 * p, 4 * p, 5 * p, 6 * p] + stake_amount = factors[count_of_buys - 1]# filled_buys[0].cost + # This then calculates current safety order size + # stake_amount = stake_amount * pow(1.5, count_of_buys) + # print("-----------" + trade.pair + " " + str(current_profit) + " " + str(count_of_buys) + " " + str(stake_amount) + "---------------------") + return stake_amount + except Exception as exception: + print(exception) + return None + return None diff --git a/Zeus_8_3_2_B_4.json b/Zeus_8_3_2_B_4.json new file mode 100644 index 0000000..02db540 --- /dev/null +++ b/Zeus_8_3_2_B_4.json @@ -0,0 +1,83 @@ +{ + "strategy_name": "Zeus_8_3_2_B_4", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "max_open_trades": { + "max_open_trades": 8 + }, + "buy": { + "buy_rsi_1d": 45, + "buy_rsi_1h": 49, + "buy_sum_rsi_1d": 17.9, + "buy_sum_rsi_1h": 11.5 + }, + "sell": { + "pHSL": -0.99, + "pPF_1": 0.022, + "pSL_1": 0.021, + "pPF_2": 0.08, + "pSL_2": 0.04, + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": false, + "profit_b_quick_gain_3": true, + "profit_b_quick_lost": true, + "profit_b_short_loss": false, + "profit_b_sma10": true, + "profit_b_sma20": false, + "profit_b_sma5": true, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": false, + "profit_h_over_rsi": true, + "profit_h_quick_gain": true, + "profit_h_quick_gain_3": false, + "profit_h_quick_lost": true, + "profit_h_short_loss": true, + "profit_h_sma10": true, + "profit_h_sma20": true, + "profit_h_sma5": true, + "profit_h_very_old_sma10": false, + "sell_b_RSI": 87, + "sell_b_RSI2": 82, + "sell_b_RSI2_percent": 0.007, + "sell_b_RSI3": 75, + "sell_b_candels": 23, + "sell_b_percent": 0.014, + "sell_b_percent3": 0.018, + "sell_b_profit_no_change": 0.003, + "sell_b_profit_percent10": 0.0011, + "sell_b_too_old_day": 10, + "sell_b_too_old_percent": 0.013, + "sell_h_RSI": 82, + "sell_h_RSI2": 75, + "sell_h_RSI2_percent": 0.011, + "sell_h_RSI3": 97, + "sell_h_candels": 6, + "sell_h_percent": 0.009, + "sell_h_percent3": 0.016, + "sell_h_profit_no_change": 0.017, + "sell_h_profit_percent10": 0.0014, + "sell_h_too_old_day": 3, + "sell_h_too_old_percent": 0.004 + }, + "protection": { + "protection_fibo": 9, + "protection_percent_buy_lost": 3 + } + }, + "ft_stratparam_v": 1, + "export_time": "2023-02-18 16:52:23.048460+00:00" +} \ No newline at end of file diff --git a/Zeus_8_3_2_B_4.jsonKeep b/Zeus_8_3_2_B_4.jsonKeep new file mode 100644 index 0000000..2bbf04f --- /dev/null +++ b/Zeus_8_3_2_B_4.jsonKeep @@ -0,0 +1,80 @@ +{ + "strategy_name": "Zeus_8_3_2_B_4", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "max_open_trades": { + "max_open_trades": 8 + }, + "buy": { + "buy_percent_max3": 0.98 + }, + "sell": { + "pHSL": -0.99, + "pPF_1": 0.022, + "pPF_2": 0.08, + "pSL_1": 0.021, + "pSL_2": 0.04, + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": false, + "profit_b_quick_gain_3": true, + "profit_b_quick_lost": true, + "profit_b_short_loss": false, + "profit_b_sma10": true, + "profit_b_sma20": false, + "profit_b_sma5": true, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": false, + "profit_h_over_rsi": true, + "profit_h_quick_gain": true, + "profit_h_quick_gain_3": false, + "profit_h_quick_lost": true, + "profit_h_short_loss": true, + "profit_h_sma10": true, + "profit_h_sma20": true, + "profit_h_sma5": true, + "profit_h_very_old_sma10": false, + "sell_b_RSI": 87, + "sell_b_RSI2": 82, + "sell_b_RSI2_percent": 0.007, + "sell_b_RSI3": 75, + "sell_b_candels": 23, + "sell_b_percent": 0.014, + "sell_b_percent3": 0.018, + "sell_b_profit_no_change": 0.003, + "sell_b_profit_percent10": 0.0011, + "sell_b_too_old_day": 10, + "sell_b_too_old_percent": 0.013, + "sell_h_RSI": 82, + "sell_h_RSI2": 75, + "sell_h_RSI2_percent": 0.011, + "sell_h_RSI3": 97, + "sell_h_candels": 6, + "sell_h_percent": 0.009, + "sell_h_percent3": 0.016, + "sell_h_profit_no_change": 0.017, + "sell_h_profit_percent10": 0.0014, + "sell_h_too_old_day": 3, + "sell_h_too_old_percent": 0.004 + }, + "protection": { + "protection_fibo": 9, + "protection_percent_buy_lost": 3 + } + }, + "ft_stratparam_v": 1, + "export_time": "2023-02-18 23:02:43.700998+00:00" +} \ No newline at end of file diff --git a/Zeus_8_3_2_B_4.py b/Zeus_8_3_2_B_4.py new file mode 100644 index 0000000..42c546c --- /dev/null +++ b/Zeus_8_3_2_B_4.py @@ -0,0 +1,1080 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +from freqtrade.strategy import stoploss_from_open, merge_informative_pair, DecimalParameter, IntParameter, CategoricalParameter + +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), + real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_8_3_2_B_4(IStrategy): + + # ROI table: + minimal_roi = { + "0": 0.564, + "567": 0.273, + "2814": 0.12, + "7675": 0 + } + + # Stoploss: + stoploss = -1 #0.256 + # Custom stoploss + use_custom_stoploss = True + + # Buy hypers + timeframe = '5m' + + max_open_trades = 5 + max_amount = 40 + + # DCA config + position_adjustment_enable = True + + plot_config = { + "main_plot": { + "min200": { + "color": "#86c932" + }, + "max50": { + "color": "white" + }, + "max200": { + "color": "yellow" + }, + "sma3_1d": { + "color": "pink" + }, + "sma5_1d": { + "color": "blue" + }, + "sma10_1d": { + "color": "orange" + }, + "close_1d": { + "color": "#73e233", + }, + "bb_lowerband": { + "color": "#da59a6"}, + "bb_upperband": { + "color": "#da59a6", + }, + "sar": { + "color": "#4f9f51", + }, + "min5_1d": { + "color": "#6aa123", + }, + "max5_1d": { + "color": "red" + }, + "max3_1d": { + "color": "blue" + } + }, + "subplots": { + # "Ind": { + # "trend_ichimoku_base": { + # "color": "#dd1384" + # }, + # "trend_kst_diff": { + # "color": "#850678" + # } + # }, + # "BB": { + # "bb_width": { + # "color": "white" + # }, + # "bb_lower_5": { + # "color": "yellow" + # } + # }, + # "Cond": { + # "cond1": { + # "color": "yellow" + # } + # }, + "Rsi": { + "rsi": { + "color": "pink" + }, + "rsi_1d": { + "color": "yellow" + } + }, + "Percent": { + "max_min": { + "color": "#74effc" + }, + "pct_change_1_1d": { + "color": "green" + }, + "pct_change_3_1d": { + "color": "orange" + }, + "pct_change_5_1d": { + "color": "red" + }, + "sma5_pct": { + "color": "yellow" + } + } + } + } + trades = list() + # buy_real = DecimalParameter(0.001, 0.999, decimals=4, default=0.5392, space='buy') + # buy_cat = CategoricalParameter([">R", "=R", " float: + + # hard stoploss profit + HSL = self.pHSL.value + PF_1 = self.pPF_1.value + SL_1 = self.pSL_1.value + PF_2 = self.pPF_2.value + SL_2 = self.pSL_2.value + + # For profits between PF_1 and PF_2 the stoploss (sl_profit) used is linearly interpolated + # between the values of SL_1 and SL_2. For all profits above PL_2 the sl_profit value + # rises linearly with current profit, for profits below PF_1 the hard stoploss profit is used. + + if current_profit > PF_2: + sl_profit = SL_2 + (current_profit - PF_2) + elif current_profit > PF_1: + sl_profit = SL_1 + ((current_profit - PF_1) * (SL_2 - SL_1) / (PF_2 - PF_1)) + else: + sl_profit = HSL + + return stoploss_from_open(sl_profit, current_profit) + + def custom_exit(self, pair: str, trade: Trade, current_time, current_rate, current_profit, **kwargs): + + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + candle_12 = dataframe.iloc[-13].squeeze() + + expected_profit = 0.01 + #print(last_candle['buy_tag']) + + days = (current_time - trade.open_date_utc).days + hours = (current_time - trade.open_date_utc).seconds / 3600 + + # fibo_fact = fibo[hours] + ###### + # if last_candle['percent5'] < -0.05: + # return "stop_loss_005" + + if last_candle['pct_change_1_1h'] <= 0: #(self.market_overview_pct5 < 0) | (last_candle['pct_change_1_4h'] < 0): + max_percent = 0.004 # last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0.004 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > 0.01) & \ + (last_candle['percent10'] < -0.005) & ((current_time - trade.open_date_utc).seconds >= 3600): + return 'b_percent10' + if (current_profit > max_profit) & \ + ((last_candle['percent'] < - max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + & (days < self.sell_b_too_old_day.value * 2)\ + & (last_candle['sma10_s2'] > last_candle['sma10']) & (last_candle['percent3'] < 0) \ + & (last_candle['echange*pct12'] < 0.0005): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + & (days < self.sell_b_too_old_day.value * 3) \ + & (last_candle['sma10_s2'] > last_candle['sma10']) & (last_candle['percent3'] < 0)\ + & (last_candle['echange*pct12'] < 0.0005): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (last_candle['sma10_s2'] > last_candle['sma10']) & (last_candle['percent3'] < 0)\ + & (last_candle['echange*pct12'] < 0.0005): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= max_profit) & ( + last_candle['percent3'] < -max_percent): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((last_candle['sma5_s5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((last_candle['sma10_s5'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > max_percent) \ + & (last_candle['sma10_s2'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((last_candle['sma20_s2'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if False: #self.profit_b_over_rsi.value: + if (current_profit > 0) & (last_candle['rsi_s2'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (last_candle['rsi_s2'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (last_candle['rsi_s2'] > self.sell_b_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (last_candle['rsi_s2'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (last_candle['rsi_s2'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (last_candle['rsi_s2'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (last_candle['percent10_s2'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): #| (last_candle['rsi_s2'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + + max_percent = 0.005 # last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0.01 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > max_profit) & ( + (last_candle['percent'] < -max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'h_percent_quick' + + # if (last_candle['bb_width'] < 0.02) & (current_profit > 0) & (last_candle['close'] > bb_width_up) & \ + # ((last_candle['percent'] < - bb_width_lim) | (last_candle['percent3'] < - bb_width_lim) | (last_candle['percent5'] < - bb_width_lim)): + # return 'h_bb_width_max' + + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (last_candle['sma10_s2'] > last_candle['sma10']) & (last_candle['percent3'] < 0)\ + & (last_candle['echange*pct12'] < 0.001): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (last_candle['sma10_s2'] > last_candle['sma10']) & (last_candle['percent3'] < 0)\ + & (last_candle['echange*pct12'] < 0.001): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (last_candle['sma10_s2'] > last_candle['sma10']) & (last_candle['percent3'] < 0)\ + & (last_candle['echange*pct12'] < 0.001): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= max_profit) & ( + last_candle['percent3'] < -max_percent): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((last_candle['sma5_s5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((last_candle['sma10_s5'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > max_percent) \ + & (last_candle['sma10_s2'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((last_candle['sma20_s2'] > last_candle['sma20']) & + ((last_candle['percent'] < 0) & (last_candle['percent5'] < 0) & (last_candle['percent10'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if False: #self.profit_h_over_rsi.value: + if (current_profit > 0) & (last_candle['rsi_s2'] > self.sell_h_RSI.value): + # & (last_candle['percent'] < 0): #| (last_candle['rsi_s2'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (last_candle['rsi_s2'] > self.sell_h_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_h_RSI2_percent.value): # | (last_candle['rsi_s2'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (last_candle['rsi_s2'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle[ + 'max200']): # | (last_candle['rsi_s2'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (last_candle['percent10_s2'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): #| (last_candle['rsi_s2'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min50_005'] = dataframe['min50'] * 1.005 + dataframe['min200_005'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['mean10'] = dataframe['close'].rolling(10).mean() + dataframe['mean20'] = dataframe['close'].rolling(20).mean() + dataframe['mean50'] = dataframe['close'].rolling(50).mean() + dataframe['mean100'] = dataframe['close'].rolling(100).mean() + + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['min_max_50'] = (dataframe['max50'] - dataframe['min50']) / dataframe['min50'] + + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi_pente'] = dataframe['rsi'].pct_change(1) + dataframe['rsi_acc'] = dataframe['rsi_pente'].pct_change(1) + dataframe['sum_rsi'] = (dataframe['rsi'].rolling(5).sum() - 250) / 5 + + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe["volume5"] = dataframe["volume"].rolling(5).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).sum() + + dataframe['sma5_s2'] = dataframe['sma5'].shift(1) + dataframe['sma10_s2'] = dataframe['sma10'].shift(1) + dataframe['sma20_s2'] = dataframe['sma20'].shift(1) + dataframe['percent10_s2'] = dataframe['percent10'].shift(1) + dataframe['rsi_s2'] = dataframe['rsi'].shift(1) + + dataframe['sma5_s5'] = dataframe['sma5'].shift(4) + dataframe['sma10_s5'] = dataframe['sma10'].shift(4) + dataframe['sma20_s5'] = dataframe['sma20'].shift(4) + # print(metadata['pair']) + # print(circulation[metadata['pair']]) + if circulation[metadata['pair']]: + dataframe["echange"] = 10000 * dataframe["volume"] / circulation[metadata['pair']] + dataframe["echange*pct"] = dataframe["echange"] * dataframe["percent"] + dataframe["echange5"] = 10000 * dataframe["volume5"] / circulation[metadata['pair']] + dataframe["echange*pct5"] = dataframe["echange5"] * dataframe["percent5"] + dataframe["pct_echange5"] = dataframe["echange5"].pct_change() + dataframe["acc_echange5"] = dataframe["echange*pct5"] / dataframe["echange*pct5"].shift(1) + for i in range(1, 4): + n = i * 12 + dataframe["echange*pct" + str(n)] = 10000 * dataframe["volume"].rolling(n).sum() / circulation[metadata['pair']] \ + * dataframe["percent"].rolling(n).sum() + + dataframe["pente20"] = (dataframe["close"] - dataframe['close'].shift(20)) / dataframe["close"] + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_ecart"] = ((dataframe["bb_upperband"] - dataframe["bb_lowerband"])) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + # dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / self.buy_bb_width_n.value)) + dataframe["bb_pente"] = (dataframe["bb_lowerband"] - dataframe['bb_lowerband'].shift(3)) / dataframe["bb_lowerband"] + dataframe["bb_pente_inv"] = (dataframe["bb_pente"].shift(2) > dataframe["bb_pente"].shift(1)) & \ + (dataframe["bb_pente"] > dataframe["bb_pente"].shift(1)) + dataframe["bb_max_width"] = (dataframe["bb_ecart"].shift(2) < dataframe["bb_ecart"].shift(1)) & \ + (dataframe["bb_ecart"] < dataframe["bb_ecart"].shift(1)) + dataframe["bb_tag"] = dataframe["bb_pente_inv"] & dataframe["bb_max_width"] + # dataframe['bb_min'] = talib.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min50_1.01'] = 1.01 * dataframe['min50'] + dataframe['min50_1.02'] = 1.02 * dataframe['min50'] + dataframe['min50_1.03'] = 1.03 * dataframe['min50'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + # dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + # dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + dataframe['min_max_close'] = ( + (dataframe['max200'] - dataframe['close']) / (dataframe['close'] - dataframe['min200'])) + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = normalize(tib) #(tib-tib.min())/(tib.max()-tib.min()) + dataframe["trend_ichimoku_pente"] = (dataframe["trend_ichimoku_base"] - dataframe['trend_ichimoku_base'].shift(3)) / dataframe["trend_ichimoku_base"] + + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = normalize(tkd) #(tkd-tkd.min())/(tkd.max()-tkd.min()) + + # dataframe['trend_ichimoku_base_50'] = talib.MIN(dataframe['trend_ichimoku_base'], timeperiod=50) + # dataframe['trend_ichimoku_base_5'] = talib.MIN(dataframe['trend_ichimoku_base'], timeperiod=5) + # dataframe['trend_ichimoku_base_sma5'] = talib.SMA(dataframe['trend_ichimoku_base'], timeperiod=5) + # dataframe['trend_ichimoku_base_pct'] = dataframe['trend_ichimoku_base'].pct_change(3) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + dataframe['cond_50'] = talib.MIN(dataframe['cond1'], timeperiod=50) + dataframe['cond_5'] = talib.MIN(dataframe['cond1'], timeperiod=5) + dataframe['cond_sma5'] = talib.SMA(dataframe['cond1'], timeperiod=5) + dataframe['cond_pct'] = dataframe['cond1'].pct_change(3) + dataframe['atr'] = talib.ATR(dataframe, timeperiod=14) + + #FreqAI / Rewards + # dataframe["%-raw_close"] = dataframe["close"] + # dataframe["%-raw_open"] = dataframe["open"] + # dataframe["%-raw_high"] = dataframe["high"] + # dataframe["%-raw_low"] = dataframe["low"] + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative, timeperiod=7) + informative['sum_rsi'] = (informative['rsi'].rolling(5).sum() - 250) / 5 + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['min5'] = talib.MIN(informative['close'], timeperiod=5) + informative['max3'] = talib.MAX(informative['close'], timeperiod=3) + informative['max5'] = talib.MAX(informative['close'], timeperiod=5) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + # informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ######################## INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative["mrsi3"] = informative["rsi"].rolling(3).mean() + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['min3'] = talib.MIN(informative['close'], timeperiod=3) + informative['min5'] = talib.MIN(informative['close'], timeperiod=5) + informative['rsi_pente'] = informative['rsi'].pct_change(1) + informative['rsi_acc'] = informative['rsi_pente'].pct_change(1) + informative['sum_rsi'] = (informative['rsi'].rolling(5).sum() - 250) / 5 + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + # dataframe['support'] = min(dataframe['close_1d'], dataframe['sma3_1d'], dataframe['sma3_1d']) + return dataframe + + # def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + # current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + # + # # {'symbol': 'FTM/USDT', 'timestamp': 1646494199570, 'datetime': '2022-03-05T15:29:59.570Z', 'high': 1.7489, + # # 'low': 1.6084, 'bid': 1.6505, 'bidVolume': 2135.0, 'ask': 1.6508, 'askVolume': 2815.0, 'vwap': 1.66852198, + # # 'open': 1.7313, 'close': 1.6505, 'last': 1.6505, 'previousClose': '1.73170000', 'change': -0.0808, + # # 'percentage': -4.667, 'average': 1.6909, 'baseVolume': 124656725.0, 'quoteVolume': 207992485.7799, + # # 'info': + # # {'symbol': 'FTMUSDT', 'priceChange': '-0.08080000', 'priceChangePercent': '-4.667', + # # 'weightedAvgPrice': '1.66852198', 'prevClosePrice': '1.73170000', 'lastPrice': '1.65050000', + # # 'lastQty': '143.00000000', 'bidPrice': '1.65050000', 'bidQty': '2135.00000000', + # # 'askPrice': '1.65080000', 'askQty': '2815.00000000', 'openPrice': '1.73130000', + # # 'highPrice': '1.74890000', 'lowPrice': '1.60840000', 'volume': '124656725.00000000', + # # 'quoteVolume': '207992485.77990000', 'openTime': '1646407799570', 'closeTime': '1646494199570', + # # 'firstId': '137149614', 'lastId': '137450289', 'count': '300676'}} - 0.9817468621938484 + # + # allow_to_buy = True + # max_gain = -100 + # sum_gain = 0 + # max_time = 0 + # + # if self.dp: + # if self.dp.runmode.value in ('live', 'dry_run'): + # if len(self.trades) == 0: + # print('search') + # self.trades = Trade.get_open_trades() + # + # if len(self.trades) >= self.config['max_open_trades'] / 2: + # for trade in self.trades: + # ticker = self.dp.ticker(trade.pair) + # last_price = ticker['last'] + # gain = (last_price - trade.open_rate) / trade.open_rate + # max_gain = max(max_gain, gain) + # sum_gain += gain + # max_time = max(max_time, datetime.timestamp(trade.open_date)) + # print(trade.pair, ticker['datetime'], ticker['timestamp'] / 1000, datetime.timestamp(trade.open_date), + # datetime.timestamp(trade.open_date) - int(ticker['timestamp'] / 1000)) + # now = datetime.now() + # diff = (datetime.timestamp(now) - max_time / 3600) + # if (max_gain <= -0.05) & (len(self.trades) >= self.config['max_open_trades'] / 2) & (diff < 6): + # print("allow_to_buy=false") + # allow_to_buy = False + # print(pair, allow_to_buy, len(self.trades), + # "max gain=", max_gain, + # "sum_gain=", sum_gain, + # "now=", now, + # "max=", max_time, + # "diff=", datetime.timestamp(now) - max_time) + # + # if allow_to_buy: + # self.trades = list() + # + # return allow_to_buy + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # enter_long_conditions = [dataframe["do_predict"] == 1] #, dataframe["&-s_close"] > dataframe["target_roi"]] + # + # if enter_long_conditions: + # dataframe.loc[ + # reduce(lambda x, y: x & y, enter_long_conditions), ["enter_long", "enter_tag"] + # ] = (1, "long") + + base = 0.33 #self.buy_base.value + # base = base / dataframe[self.buy_rsi_ichimoku.value] * self.buy_rsi_divisor.value + base = base / dataframe['rsi'] * 52 + + decalage = 3 + dataframe.loc[ + ( + # (reduce(lambda x, y: x & y, enter_long_conditions)) + (dataframe['trend_ichimoku_base'].shift(decalage) <= base) + # & (dataframe['close'] > dataframe['close_1d']) + & (dataframe['rsi'].shift(decalage) < 60) + & (dataframe['close'].shift(decalage) < dataframe['sma10'].shift(decalage)) + & (dataframe['close'].shift(decalage) < dataframe['bb_middleband'].shift(decalage)) + & (dataframe['bb_width'].shift(decalage) > 0.025) + # & (dataframe['close'].shift(decalage) < dataframe['bb_lower_width_5'].shift(decalage)) + & ((dataframe['close'].shift(decalage) <= dataframe['min50'].shift(decalage) * 1.002) | + (dataframe['pct_change_1_1h'].shift(decalage) > - 0.002) + ) + # & (dataframe['close'] <= dataframe['bb_upperband']) + # & (dataframe['close'] <= dataframe['min_200_001']) + & (dataframe['close'].shift(decalage) <= dataframe['close_1h'].shift(decalage)) + & ((dataframe['close'].shift(decalage) < dataframe['bb_lowerband'].shift(decalage)) | + (dataframe['pct_change_1_1h'].shift(decalage) > - 0.002) + ) + & (dataframe['close'] < dataframe['bb_upperband']) + & (dataframe['open'] < dataframe['bb_upperband']) + & ((dataframe['bb_pente_inv'] == 1) | (dataframe['bb_pente_inv'].shift(1) == 1) | (dataframe['bb_pente_inv'].shift(2) == 1) + | (dataframe['bb_pente_inv'].shift(3) == 1)) + # & (dataframe['close_1d'] < dataframe['bb_upperband_1d']) + ), ['buy', 'enter_tag']] = (1, 'buy_ichimoku') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + if True: #(len(dataframe) < 1): + return None + # print(dataframe) + last_candle = dataframe.iloc[-1].squeeze() + # last_candle_5 = dataframe.iloc[-3].squeeze() + + min_d = min(last_candle['sma3_4h'], last_candle['close_1d']) + + filled_buys = trade.select_filled_orders('buy') + count_of_buys = len(filled_buys) + days = (current_time - trade.open_date_utc).days + minutes = (current_time - trade.open_date_utc).seconds / 60 + # condition = (last_candle['cond1'] <= 0.75) & (last_candle['bb_width'] > 0.018) & ( + # last_candle['rsi'] < 72) & (last_candle['close'] < last_candle['min50'] * 1.006)# & (last_candle['min_max_close'] > 2) + + condition = (last_candle['enter_long'] == 1) # & (last_candle['close'] <= last_candle['close_1h']) + p = self.protection_percent_buy_lost.value + percents = [p, p * 2, p * 3, p * 4, p * 5, p * 6, p * 7, p * 8, p * 9] + + # self.protection_nb_buy_lost.value + if (0 < count_of_buys <= 2) \ + & (current_profit < - (percents[count_of_buys - 1] / 100)) & (condition): + try: + p = self.config['stake_amount'] + if last_candle['close'] < last_candle['min5_1h']: + factors = [1.5 * p, 1.75 * p, 2 * p, 2 * p, 3 * p, 4 * p, 5 * p, 6 * p] + else: + if last_candle['close'] < last_candle['min3_1h']: + factors = [1.25 * p, 1.5 * p, 2 * p, 2 * p, 3 * p, 4 * p, 5 * p, 6 * p] + else: + factors = [1 * p, 1.25 * p, 2 * p, 2 * p, 3 * p, 4 * p, 5 * p, 6 * p] + stake_amount = factors[count_of_buys - 1]# filled_buys[0].cost + # This then calculates current safety order size + # stake_amount = stake_amount * pow(1.5, count_of_buys) + # print("-----------" + trade.pair + " " + str(current_profit) + " " + str(count_of_buys) + " " + str(stake_amount) + "---------------------") + return stake_amount + except Exception as exception: + print(exception) + return None + return None diff --git a/Zeus_8_3_2_B_4_2.json b/Zeus_8_3_2_B_4_2.json new file mode 100644 index 0000000..f28e416 --- /dev/null +++ b/Zeus_8_3_2_B_4_2.json @@ -0,0 +1,83 @@ +{ + "strategy_name": "Zeus_8_3_2_B_4_2", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "max_open_trades": { + "max_open_trades": 3 + }, + "buy": { + "buy_rsi_1d": 45, + "buy_rsi_1h": 49, + "buy_sum_rsi_1d": 17.9, + "buy_sum_rsi_1h": 11.5 + }, + "sell": { + "pHSL": -0.99, + "pPF_1": 0.022, + "pSL_1": 0.015, + "pPF_2": 0.05, + "pSL_2": 0.03, + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": false, + "profit_b_quick_gain_3": true, + "profit_b_quick_lost": true, + "profit_b_short_loss": false, + "profit_b_sma10": true, + "profit_b_sma20": false, + "profit_b_sma5": true, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": false, + "profit_h_over_rsi": true, + "profit_h_quick_gain": true, + "profit_h_quick_gain_3": false, + "profit_h_quick_lost": true, + "profit_h_short_loss": true, + "profit_h_sma10": true, + "profit_h_sma20": true, + "profit_h_sma5": true, + "profit_h_very_old_sma10": false, + "sell_b_RSI": 87, + "sell_b_RSI2": 82, + "sell_b_RSI2_percent": 0.007, + "sell_b_RSI3": 75, + "sell_b_candels": 23, + "sell_b_percent": 0.014, + "sell_b_percent3": 0.018, + "sell_b_profit_no_change": 0.003, + "sell_b_profit_percent10": 0.0011, + "sell_b_too_old_day": 10, + "sell_b_too_old_percent": 0.013, + "sell_h_RSI": 82, + "sell_h_RSI2": 75, + "sell_h_RSI2_percent": 0.011, + "sell_h_RSI3": 97, + "sell_h_candels": 6, + "sell_h_percent": 0.009, + "sell_h_percent3": 0.016, + "sell_h_profit_no_change": 0.017, + "sell_h_profit_percent10": 0.0014, + "sell_h_too_old_day": 300, + "sell_h_too_old_percent": 0.004 + }, + "protection": { + "protection_fibo": 9, + "protection_percent_buy_lost": 3 + } + }, + "ft_stratparam_v": 1, + "export_time": "2023-02-18 16:52:23.048460+00:00" +} diff --git a/Zeus_8_3_2_B_4_2.py b/Zeus_8_3_2_B_4_2.py new file mode 100644 index 0000000..ce529e0 --- /dev/null +++ b/Zeus_8_3_2_B_4_2.py @@ -0,0 +1,1365 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from freqtrade.persistence import Trade +from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter, stoploss_from_open, + IntParameter, IStrategy, merge_informative_pair, informative, stoploss_from_absolute) +import pandas as pd +import numpy as np +from pandas import DataFrame +from typing import Optional, Union, Tuple + +import logging +import configparser +from technical import pivots_points +# -------------------------------- + +# Add your lib to import here +import ta +import talib.abstract as talib +import freqtrade.vendor.qtpylib.indicators as qtpylib +import requests + +logger = logging.getLogger(__name__) + +from tabulate import tabulate + + +def pprint_df(dframe): + print(tabulate(dframe, headers='keys', tablefmt='psql', showindex=False)) + + +def normalize(df): + df = (df - df.min()) / (df.max() - df.min()) + return df + + +def get_limit_from_config(section, pair): + file_path = '/HOME/home/souti/freqtrade2/user_data/strategies/Zeus_8_3_2_B_4_2.txt' + # Créez un objet ConfigParser + config = configparser.ConfigParser() + + try: + # Lisez le fichier avec les valeurs + config.read(file_path) + + # Vérifiez si la section existe + if config.has_section(section): + # Obtenez les valeurs à partir de la section et de la clé (pair) + limit = config.get(section, pair) + return limit + else: + raise ValueError(f"La section '{section}' n'existe pas dans le fichier de configuration.") + except Exception as e: + print(f"Erreur lors de la lecture du fichier de configuration : {e}") + return None + + +class Zeus_8_3_2_B_4_2(IStrategy): + levels = [1, 2, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20] + + # ROI table: + minimal_roi = { + "0": 0.564, + "567": 0.273, + "2814": 0.12, + "7675": 0 + } + + # Stoploss: + stoploss = -1 # 0.256 + # Custom stoploss + use_custom_stoploss = True + + # Buy hypers + timeframe = '5m' + + max_open_trades = 5 + max_amount = 40 + + # DCA config + position_adjustment_enable = True + + plot_config = { + "main_plot": { + "min200": { + "color": "#86c932" + }, + "max50": { + "color": "white" + }, + "max200": { + "color": "yellow" + }, + "bb_lowerband": { + "color": "#da59a6"}, + "bb_upperband": { + "color": "#da59a6", + } + }, + "subplots": { + "Rsi": { + "rsi": { + "color": "pink" + } + }, + "Percent": { + "max_min": { + "color": "#74effc" + } + } + } + } + + # 20 20 40 60 100 160 260 420 + # 50 50 100 300 500 + # fibo = [1, 1, 2, 3, 5, 8, 13, 21] + # my fibo + # 50 50 50 100 100 150 200 250 350 450 600 1050 + fibo = [1, 1, 1, 2, 2, 3, 4, 5, 7, 9, 12, 16, 21] + baisse = [1, 2, 3, 5, 7, 10, 14, 19, 26, 35, 47, 63, 84] + # Ma suite 1 1 1 2 2 3 4 5 7 9 12 16 21 + # Mise 50 50 50 100 100 150 200 250 350 450 600 800 1050 + # Somme Mises 50 100 150 250 350 500 700 950 1300 1750 2350 3150 4200 + # baisse 1 2 3 5 7 10 14 19 26 35 47 63 84 + + trades = list() + max_profit_pairs = {} + + profit_b_no_change = BooleanParameter(default=True, space="sell") + profit_b_quick_lost = BooleanParameter(default=True, space="sell") + profit_b_sma5 = BooleanParameter(default=True, space="sell") + profit_b_sma10 = BooleanParameter(default=True, space="sell") + profit_b_sma20 = BooleanParameter(default=True, space="sell") + profit_b_quick_gain = BooleanParameter(default=True, space="sell") + profit_b_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_b_old_sma10 = BooleanParameter(default=True, space="sell") + profit_b_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_b_over_rsi = BooleanParameter(default=True, space="sell") + profit_b_short_loss = BooleanParameter(default=True, space="sell") + + sell_b_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_b_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_b_candels = IntParameter(0, 48, default=12, space='sell') + + sell_b_too_old_day = IntParameter(0, 10, default=300, space='sell') + sell_b_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + sell_b_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') + sell_b_profit_percent12 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') + + sell_b_RSI = IntParameter(70, 98, default=88, space='sell') + sell_b_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_b_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_b_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + # sell_b_expected_profit = DecimalParameter(0, 0.01, decimals=3, default=0.01, space='sell') + + profit_h_no_change = BooleanParameter(default=True, space="sell") + profit_h_quick_lost = BooleanParameter(default=True, space="sell") + profit_h_sma5 = BooleanParameter(default=True, space="sell") + profit_h_sma10 = BooleanParameter(default=True, space="sell") + profit_h_sma20 = BooleanParameter(default=True, space="sell") + profit_h_quick_gain = BooleanParameter(default=True, space="sell") + profit_h_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_h_old_sma10 = BooleanParameter(default=True, space="sell") + profit_h_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_h_over_rsi = BooleanParameter(default=True, space="sell") + profit_h_short_loss = BooleanParameter(default=True, space="sell") + + sell_h_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_candels = IntParameter(0, 48, default=12, space='sell') + + sell_h_too_old_day = IntParameter(0, 10, default=300, space='sell') + sell_h_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + sell_h_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') + sell_h_profit_percent12 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') + + sell_h_RSI = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_h_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + protection_percent_buy_lost = IntParameter(1, 10, default=5, space='protection') + # protection_nb_buy_lost = IntParameter(1, 2, default=2, space='protection') + + protection_fibo = IntParameter(1, 10, default=2, space='protection') + + # trailing stoploss hyperopt parameters + # hard stoploss profit + sell_allow_decrease = DecimalParameter(0.005, 0.02, default=0.2, decimals=2, space='sell', optimize=True, load=True) + + pHSL = DecimalParameter(-0.200, -0.040, default=-0.08, decimals=3, space='sell', optimize=False, load=True) + # profit threshold 1, trigger point, SL_1 is used + pPF_1 = DecimalParameter(0.008, 0.020, default=0.016, decimals=3, space='sell', optimize=True, load=True) + pSL_1 = DecimalParameter(0.008, 0.020, default=0.011, decimals=3, space='sell', optimize=True, load=True) + + # profit threshold 2, SL_2 is used + pPF_2 = DecimalParameter(0.040, 0.100, default=0.080, decimals=3, space='sell', optimize=True, load=True) + pSL_2 = DecimalParameter(0.020, 0.070, default=0.040, decimals=3, space='sell', optimize=True, load=True) + + def min_max_scaling(self, series: pd.Series) -> pd.Series: + """Normaliser les données en les ramenant entre 0 et 100.""" + return 100 * (series - series.min()) / (series.max() - series.min()) + + def z_score_scaling(self, series: pd.Series) -> pd.Series: + """Normaliser les données en utilisant Z-Score Scaling.""" + return (series - series.mean()) / series.std() + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + # count_buys = 0 + # trade = self.getTrade(pair) + # if trade: + # filled_buys = trade.select_filled_orders('buy') + # count_buys = len(filled_buys) + + print('entry_tag' + str(entry_tag)) + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + # last_candle_12 = dataframe.iloc[-13].squeeze() + limit = get_limit_from_config('Achats', pair) + + # allow_to_buy = True #(not self.stop_all) #& (not self.all_down) + allow_to_buy = True # (rate <= float(limit)) | (entry_tag == 'force_entry') + # allow_to_buy = rate <= dataframe['lbp_3'] + self.trades = list() + dispo = round(self.wallets.get_available_stake_amount()) + logger.info(f"{pair} allow_to_buy {allow_to_buy} limit={limit} Buy {entry_tag} {current_time} dispo={dispo}") + + return allow_to_buy + + def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, rate: float, + time_in_force: str, + exit_reason: str, current_time, **kwargs, ) -> bool: + # allow_to_sell = (minutes > 30) + limit = get_limit_from_config('Ventes', pair) + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + + allow_to_sell = (last_candle['percent'] < 0) # rate > float(limit) + string = "" + + if allow_to_sell: + self.trades = list() + logger.info('Sell trade ' + exit_reason + ' ' + str(current_time) + ' ' + pair + " dispo=" + str( + round(self.wallets.get_available_stake_amount())) # "+ str(amount) + ' ' + str(rate) + + " open_rate=" + str(trade.open_rate) + " rate=" + str(rate) + " profit=" + str( + trade.calc_profit(rate, amount)) + + " " + string) + # del self.max_profit_pairs[pair] + else: + logger.info('Cancel Sell trade ' + exit_reason + ' ' + str(current_time) + ' ' + pair) + return (allow_to_sell) | (exit_reason == 'force_exit') + + def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, + proposed_stake: float, min_stake: float, max_stake: float, + **kwargs) -> float: + + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + current_candle = dataframe.iloc[-1].squeeze() + adjusted_stake_amount = self.adjust_stake_amount(pair, current_candle) + + logger.info(f"{pair} adjusted_stake_amount{adjusted_stake_amount}") + + # Use default stake amount. + return adjusted_stake_amount + + def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, + current_rate: float, current_profit: float, **kwargs) -> float: + + # # hard stoploss profit + # HSL = self.pHSL.value + # PF_1 = self.pPF_1.value + # SL_1 = self.pSL_1.value + # PF_2 = self.pPF_2.value + # SL_2 = self.pSL_2.value + # + # # For profits between PF_1 and PF_2 the stoploss (sl_profit) used is linearly interpolated + # # between the values of SL_1 and SL_2. For all profits above PL_2 the sl_profit value + # # rises linearly with current profit, for profits below PF_1 the hard stoploss profit is used. + # + # if current_profit > PF_2: + # sl_profit = SL_2 + (current_profit - PF_2) + # elif current_profit > PF_1: + # sl_profit = SL_1 + ((current_profit - PF_1) * (SL_2 - SL_1) / (PF_2 - PF_1)) + # else: + # sl_profit = HSL + + #print(f"entry_tag={trade.entry_tag} max={trade.max_rate} min={trade.min_rate} ") + if current_profit > 0.0125: + sl_profit = 0.75 * current_profit # 75% du profit en cours + else: + sl_profit = self.pHSL.value # Hard stop-loss + stoploss = stoploss_from_open(sl_profit, current_profit) + logger.info(f"stoploss={stoploss}") + return stoploss + + # + # dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + # last_candle = dataframe.iloc[-1].squeeze() + # limit = get_limit_from_config('Ventes', pair) + # + # print(pair + " " + str(current_rate) + " " + str(limit)) + # + # #stop_loss = self.adjust_stop_loss(dataframe.iloc[-1]) + # + # #if current_rate < float(limit): + # # return -1 + # + # # "pHSL": -0.99, + # # "pPF_1": 0.022, + # # "pSL_1": 0.021, + # # "pPF_2": 0.08, + # # "pSL_2": 0.04, + # # + # # hard stoploss profit + # HSL = self.pHSL.value + # PF_1 = self.pPF_1.value + # SL_1 = self.pSL_1.value + # PF_2 = self.pPF_2.value + # SL_2 = self.pSL_2.value + # + # # For profits between PF_1 and PF_2 the stoploss (sl_profit) used is linearly interpolated + # # between the values of SL_1 and SL_2. For all profits above PL_2 the sl_profit value + # # rises linearly with current profit, for profits below PF_1 the hard stoploss profit is used. + # + # # 0.04 + # if current_profit > PF_2: + # # 0.04 + (current_profit - 0.08) + # sl_profit = SL_2 + (current_profit - PF_2) + # # 0.022 + # elif current_profit > PF_1: + # # 0.021 + ((current_profit - 0.022) * (0.04 - 0.021) / (0.08 - 0.022)) + # sl_profit = SL_1 + ((current_profit - PF_1) * (SL_2 - SL_1) / (PF_2 - PF_1)) + # else: + # sl_profit = HSL + # + # slfo = stoploss_from_open(sl_profit, current_profit) + # print('current_profit=' + str(current_profit) + ' stop from open=' + str(slfo)) + # return slfo + + # def custom_exit(self, pair: str, trade: Trade, current_time, current_rate, current_profit, **kwargs): + # + # dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + # last_candle = dataframe.iloc[-1].squeeze() + # + # # self.analyze_conditions(pair, dataframe) + # + # print("---------------" + pair + "----------------") + # expected_profit = self.expectedProfit(pair, last_candle) + # + # dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + # last_candle = dataframe.iloc[-1] + # + # # Calcul du prix cible basé sur l'ATR + # atr_take_profit = trade.open_rate + (last_candle['atr'] * 2) # Prendre profit à 2x l'ATR + # + # logger.info(f"{pair} Custom exit atr_take_profit={atr_take_profit:.4f}") + # # if current_rate >= atr_take_profit: + # # return 'sell_atr_take_profit' + # + # if (last_candle['percent3'] < -0.002) & (last_candle['percent12'] < 0) & ( + # current_profit > last_candle['min_max200'] / 2): + # self.trades = list() + # return 'min_max200' + # if (last_candle['percent12'] <= -0.01) & (current_profit >= expected_profit): + # self.trades = list() + # return 'profit' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + pair = metadata['pair'] + + dataframe['achats'] = get_limit_from_config('Achats', pair) + dataframe['ventes'] = get_limit_from_config('Ventes', pair) + + heikinashi = qtpylib.heikinashi(dataframe) + dataframe['haopen'] = heikinashi['open'] + dataframe['haclose'] = heikinashi['close'] + dataframe['hapercent'] = (dataframe['haclose'] - dataframe['haopen']) / dataframe['haclose'] + + dataframe['close_02'] = dataframe['haclose'] * 1.02 + + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min12'] = talib.MIN(dataframe['close'], timeperiod=12) + + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max144'] = talib.MAX(dataframe['close'], timeperiod=144) + dataframe['min_max50'] = (dataframe['max50'] - dataframe['min50']) / dataframe['min50'] + + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['max200_diff'] = (dataframe['max200'] - dataframe['close']) / dataframe['close'] + dataframe['max50_diff'] = (dataframe['max50'] - dataframe['close']) / dataframe['close'] + + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent3"] = (dataframe["close"] - dataframe["open"].shift(3)) / dataframe["open"].shift(3) + dataframe["percent5"] = (dataframe["close"] - dataframe["open"].shift(5)) / dataframe["open"].shift(5) + dataframe["percent12"] = (dataframe["close"] - dataframe["open"].shift(12)) / dataframe["open"].shift(12) + dataframe["percent24"] = (dataframe["close"] - dataframe["open"].shift(24)) / dataframe["open"].shift(24) + dataframe["percent48"] = (dataframe["close"] - dataframe["open"].shift(48)) / dataframe["open"].shift(48) + dataframe["percent_max_144"] = (dataframe["close"] - dataframe["max144"]) / dataframe["close"] + + dataframe['sma10_s2'] = dataframe['sma10'].shift(1) + dataframe['sma20_s2'] = dataframe['sma20'].shift(1) + dataframe['percent12_s2'] = dataframe['percent12'].shift(1) + + dataframe['sma5_s5'] = dataframe['sma5'].shift(4) + dataframe['sma10_s5'] = dataframe['sma10'].shift(4) + dataframe['sma20_s5'] = dataframe['sma20'].shift(4) + # print(metadata['pair']) + dataframe['rsi'] = talib.RSI(dataframe['close'], length=14) + + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + + # Normalization + + dataframe['average_line'] = dataframe['close'].mean() + dataframe['average_line_50'] = talib.MIDPOINT(dataframe['close'], timeperiod=50) + + dataframe['average_line_288'] = talib.MIDPOINT(dataframe['close'], timeperiod=288) + dataframe['average_line_288_098'] = dataframe['average_line_288'] * 0.98 + dataframe['average_line_288_099'] = dataframe['average_line_288'] * 0.99 + # Sort the close prices to find the 4 lowest values + sorted_close_prices = dataframe['close'].tail(576).sort_values() + lowest_4 = sorted_close_prices.head(20) + + dataframe['lowest_4_average'] = lowest_4.mean() + # Propagate this mean value across the entire dataframe + # dataframe['lowest_4_average'] = dataframe['lowest_4_average'].iloc[0] + + # # Sort the close prices to find the 4 highest values + sorted_close_prices = dataframe['close'].tail(288).sort_values(ascending=False) + highest_4 = sorted_close_prices.head(20) + + # # Calculate the mean of the 4 highest values + dataframe['highest_4_average'] = highest_4.mean() + + # # Propagate this mean value across the entire dataframe + # dataframe['highest_4_average'] = dataframe['highest_4_average'].iloc[0] + + dataframe['volatility'] = talib.STDDEV(dataframe['close'], timeperiod=144) / dataframe['close'] + dataframe['atr'] = talib.ATR(dataframe['high'], dataframe['low'], dataframe['close'], timeperiod=144) / \ + dataframe['close'] + # dataframe['pct_average'] = (dataframe['highest_4_average'] - dataframe['close']) / dataframe['lowest_4_average'] + # dataframe['highest_4_average_1'] = dataframe['highest_4_average'] * 0.99 + # dataframe['highest_4_average_2'] = dataframe['highest_4_average'] * 0.98 + # dataframe['highest_4_average_3'] = dataframe['highest_4_average'] * 0.97 + # dataframe['highest_4_average_4'] = dataframe['highest_4_average'] * 0.96 + # dataframe['highest_4_average_5'] = dataframe['highest_4_average'] * 0.95 + + # Normaliser les données de 'close' + # normalized_close = self.min_max_scaling(dataframe['close']) + ################### INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative['volatility'] = talib.STDDEV(informative['close'], timeperiod=14) / informative['close'] + informative['atr'] = (talib.ATR(informative['high'], informative['low'], informative['close'], timeperiod=14)) / informative['close'] + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + ################### INFORMATIVE 1d + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + sorted_close_prices = informative['close'].tail(365).sort_values() + lowest_4 = sorted_close_prices.head(4) + informative['lowest_4'] = lowest_4.mean() + + sorted_close_prices = informative['close'].tail(365).sort_values(ascending=False) + highest_4 = sorted_close_prices.head(4) + informative['highest_4'] = highest_4.mean() + + last_14_days = informative.tail(14) + + # Récupérer le minimum et le maximum de la colonne 'close' des 14 derniers jours + min_14_days = last_14_days['close'].min() + max_14_days = last_14_days['close'].max() + informative['lowest'] = min_14_days + informative['highest'] = max_14_days + informative['pct_min_max'] = (max_14_days - min_14_days) / min_14_days + informative['mid_min_max'] = min_14_days + (max_14_days - min_14_days) / 2 + informative['middle'] = informative['lowest_4'] + (informative['highest_4'] - informative['lowest_4']) / 2 + informative['mid_min_max_0.98'] = informative['mid_min_max'] * 0.98 + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + dataframe['count_buys'] = 0 + + dataframe['last_price'] = dataframe['close'] + dataframe['first_price'] = dataframe['close'] + dataframe['mid_price'] = (dataframe['last_price'] + dataframe['first_price']) / 2 + dataframe['close01'] = dataframe.iloc[-1]['close'] * 1.01 + dataframe['amount'] = 0 + dataframe['limit'] = dataframe['close'] + count_buys = 0 + if self.dp: + if self.dp.runmode.value in ('live', 'dry_run'): + self.getOpenTrades() + + for trade in self.trades: + if trade.pair != pair: + continue + print(trade) + filled_buys = trade.select_filled_orders('buy') + dataframe['count_buys'] = len(filled_buys) + count = 0 + amount = 0 + for buy in filled_buys: + if count == 0: + dataframe['first_price'] = buy.price + dataframe['close01'] = buy.price * 1.01 + + # Order(id=2396, trade=1019, order_id=29870026652, side=buy, filled=0.00078, price=63921.01, + # status=closed, date=2024-08-26 02:20:11) + dataframe['last_price'] = buy.price + print(buy) + count = count + 1 + amount += buy.price * buy.filled + dataframe['mid_price'] = (dataframe['last_price'] + dataframe['first_price']) / 2 + count_buys = count + dataframe['limit'] = dataframe['last_price'] * (1 - self.baisse[count] / 100) + dataframe['amount'] = amount + print(f"amount= {amount}") + # trades = Trade.get_trades([Trade.is_open is False]).all() + trades = Trade.get_trades_proxy(is_open=False, pair=metadata['pair']) + if trades: + trade = trades[-1] + print('closed trade pair is : ') + print(trade) + dataframe['expected_profit'] = (1 + self.expectedProfit(pair, dataframe.iloc[-1])) * dataframe[ + 'last_price'] + dataframe['lbp'] = dataframe['last_price'] + dataframe['lbp_3'] = dataframe['lbp'] * 0.97 # 3 + dataframe['lbp_6'] = dataframe['lbp'] * 0.94 # 6 + dataframe['lbp_9'] = dataframe['lbp'] * 0.90 # 10 + dataframe['lbp_12'] = dataframe['lbp'] * 0.85 # 15 + dataframe['lbp_20'] = dataframe['lbp'] * 0.8 # 20 + dataframe['fbp'] = trade.open_rate + # else: + # last_trade = self.get_trades(pair=pair).order_by('-close_date').first() + # filled_buys = last_trade.select_filled_orders('buy') + # print(last_trade) + # for buy in filled_buys: + # print(filled_buys) + + dataframe['buy_level'] = dataframe['lowest_4_average'] * (1 - self.levels[count_buys] / 100) + # ---------------------------------------------------------- + # Calcul de la variation entre deux bougies successives + dataframe['price_change'] = dataframe['close'].diff() + + # Marquer les bougies en baisse + dataframe['is_down'] = dataframe['price_change'] < 0 + + # Identifier les blocs consécutifs de baisses + # dataframe['drop_id'] = (dataframe['is_down'] != dataframe['is_down'].shift(1)).cumsum() + dataframe['drop_id'] = np.where(dataframe['is_down'], + (dataframe['is_down'] != dataframe['is_down'].shift(12)).cumsum(), np.nan) + + # Identifier uniquement les blocs de baisse + dataframe['drop_id'] = dataframe['drop_id'].where(dataframe['is_down']) + # # Grouper par les chutes détectées + # drop_info = dataframe.groupby('drop_id').agg( + # start=('close', 'first'), # Prix au début de la chute + # end=('close', 'last'), # Prix à la fin de la chute + # start_index=('close', 'idxmin'), # Début de la chute (index) + # end_index=('close', 'idxmax'), # Fin de la chute (index) + # ) + # + # # Calcul de l'ampleur de la chute en % + # drop_info['drop_amplitude_pct'] = ((drop_info['end'] - drop_info['start']) / drop_info['start']) * 100 + # # Filtrer les chutes avec une amplitude supérieure à 3% + # drop_info = drop_info[drop_info['drop_amplitude_pct'] < -3] + + # ************** + + # Identifier le prix de début et de fin de chaque chute + drop_stats = dataframe.groupby('drop_id').agg( + start_price=('close', 'first'), # Prix au début de la chute + end_price=('close', 'last'), # Prix à la fin de la chute + ) + + # Calculer l'amplitude en % + drop_stats['amplitude_pct'] = ((drop_stats['end_price'] - drop_stats['start_price']) / drop_stats[ + 'start_price']) * 100 + # drop_stats = drop_stats[drop_stats['amplitude_pct'] < -1] + # Associer les amplitudes calculées à chaque drop_id dans dataframe + dataframe = dataframe.merge(drop_stats[['amplitude_pct']], on='drop_id', how='left') + # Remplir les lignes sans drop_id par 0 + dataframe['amplitude_pct'] = dataframe['amplitude_pct'].fillna(0) + dataframe['amplitude_pct_60'] = dataframe['amplitude_pct'].rolling(60).sum() + # ---------------------------------------------------------- + + self.getBinanceOrderBook(pair, dataframe) + + return dataframe + + def getOpenTrades(self): + # if len(self.trades) == 0: + print('search open trades') + self.trades = Trade.get_open_trades() + return self.trades + + def getTrade(self, pair): + trades = self.getOpenTrades() + trade_for_pair = None + for trade in trades: + if trade.pair == pair: + trade_for_pair = trade + break + return trade_for_pair + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + pair = metadata['pair'] + + self.getOpenTrades() + expected_profit = self.expectedProfit(pair, dataframe.iloc[-1]) + # self.getBinanceOrderBook(pair, dataframe) + last_candle = dataframe.iloc[-1].squeeze() + # limit = last_candle['first_price'] * (1 - self.baisse[last_candle['count_buys']] / 100) + + # self.updateLastValue(dataframe, 'expected_profit', expected_profit) + print("---------------" + pair + "----------------") + print('adjust stake amount ' + str(self.adjust_stake_amount(pair, dataframe.iloc[-1]))) + print('adjust exit price ' + str(self.adjust_exit_price(dataframe.iloc[-1]))) + print('calcul expected_profit ' + str(expected_profit)) + + buy_level = dataframe['buy_level'] # self.get_buy_level(pair, dataframe) + + dataframe.loc[ + ( + (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['percent_max_144'] <= -0.012) + & (dataframe['haopen'] < buy_level) + & (dataframe['open'] < dataframe['average_line_288']) + & (dataframe['min50'].shift(3) == dataframe['min50']) + ), ['buy', 'enter_tag']] = (1, 'buy_fractal') + + dataframe.loc[ + ( + (dataframe['max200_diff'].shift(4) >= 0.015) + & (dataframe['close'] <= dataframe['lowest_4_average'] * 1.002) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['max50_diff'].shift(4) >= 0.01) + & (dataframe['haclose'] < dataframe['bb_middleband']) + & (dataframe['close'] < buy_level) + & (dataframe['open'] < dataframe['average_line_288']) + & (dataframe['min50'].shift(3) == dataframe['min50']) + ), ['buy', 'enter_tag']] = (1, 'buy_max_diff_015') + + dataframe.loc[ + ( + (dataframe['max200_diff'] >= 0.018) + & (dataframe['close'] <= dataframe['lowest_4_average'] * 1.002) + & (dataframe['max50_diff'] >= 0.009) + & (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['haclose'] < dataframe['bb_middleband']) + & (dataframe['close'] < buy_level) + & (dataframe['open'] < dataframe['average_line_288']) + & (dataframe['min50'].shift(3) == dataframe['min50']) + ), ['buy', 'enter_tag']] = (1, 'buy_max_diff_018') + + # dataframe.loc[ + # ( + # (dataframe['max200_diff'] >= 0.018) + # & (dataframe['open'] < dataframe['average_line_288']) + # & (dataframe['close'] < dataframe['min12'] * 1.002) + # & (dataframe['min12'].shift(2) == dataframe['min12']) + # ), ['buy', 'enter_tag']] = (1, 'buy_min_max200') + dataframe.loc[ + ( + (dataframe['max200_diff'] >= 0.025) + & (dataframe['percent12'] < -0.002) + & (dataframe['pct_change'] < 0) + & (dataframe['open'] < dataframe['average_line_288_099']) + & (dataframe['open'] < dataframe['average_line_50']) + & (dataframe['count_buys'] == 0 | + ((dataframe['count_buys'] > 0) & (dataframe['close'] <= dataframe['limit'])) + ) + & (dataframe['percent'] >= -0.0005) + & (dataframe['min12'].shift(2) == dataframe['min12']) + ), ['buy', 'enter_tag']] = (1, 'buy_min_max200_2') + dataframe.loc[ + ( + ((dataframe['count_buys'] > 0) & (dataframe['close'] <= dataframe['limit'])) + & (dataframe['close'] < dataframe['min200'] * 1.002) + # & (dataframe['percent'] >= -0.0005) + & ( + (dataframe['min12'].shift(2) == dataframe['min12']) | + (dataframe['min200'].shift(60) >= dataframe['min200'] * 1.03) + ) + ), ['buy', 'enter_tag']] = (1, 'buy_count_buy') + + dataframe.loc[ + ( + ( + (dataframe['percent12'] < -0.015) | + (dataframe['percent24'] < -0.022) | + (dataframe['percent48'] < -0.030) + ) + & (dataframe['count_buys'] == 0) + & (dataframe['close'] <= dataframe['min50'] * 1.002) + & (dataframe['open'] < dataframe['average_line_50']) + & ( + (dataframe['close'] < dataframe['min12'] * 1.002) + | (dataframe['percent12'] < -0.022) + | (dataframe['percent24'] < -0.022) + ) + & ( + (dataframe['min50'].shift(2) == dataframe['min50']) + | (dataframe['percent12'] < -0.022) + | (dataframe['percent24'] < -0.022) + ) + ), ['buy', 'enter_tag']] = (1, 'buy_0_percent12') + dataframe.loc[ + ( + # (dataframe['percent12'] < -0.015) + ((dataframe['count_buys'] > 0) & (dataframe['close'] <= dataframe['limit'])) + & (dataframe['open'] < dataframe['average_line_50']) + & (dataframe['close'] < dataframe['min12'] * 1.002) + & (dataframe['min12'].shift(2) == dataframe['min12']) + ), ['buy', 'enter_tag']] = (1, 'buy_percent12') + + dataframe.loc[ + ( + (dataframe['percent_max_144'] <= -0.02) + & (dataframe['close'] <= dataframe['lowest_4_average'] * 1.002) + & (dataframe['haopen'] < buy_level) + & (dataframe['min50'].shift(3) == dataframe['min50']) + & (dataframe['close'] <= dataframe['min50'] * 1.002) + & (dataframe['open'] < dataframe['average_line_288']) + ), ['buy', 'enter_tag']] = (1, 'buy_percent_max_144') + dataframe.loc[ + ( + (dataframe['close'] <= dataframe['min200'] * 1.002) + & (dataframe['min_max200'] > 0.015) + & (dataframe['pct_change'] < 0) + & (dataframe['haopen'] < buy_level) + & (dataframe['open'] < dataframe['average_line_288']) + ), ['buy', 'enter_tag']] = (1, 'buy_min_max_200') + dataframe.loc[ + ( + (dataframe['percent_max_144'] <= -0.02) + & (dataframe['close'] <= dataframe['lowest_4_average'] * 1.002) + & (dataframe['close_02'] < dataframe['max144']) + & (dataframe['haopen'] < buy_level) + & (dataframe['close'] <= dataframe['average_line_288_099']) + & (dataframe['min50'].shift(3) == dataframe['min50']) + & (dataframe['close'] <= dataframe['min50'] * 1.002) + ), ['buy', 'enter_tag']] = (1, 'buy_close_02') + dataframe.loc[ + ( + (dataframe['close'] <= dataframe['lowest_4_average'] * 1.002) + & (dataframe['haopen'] >= dataframe['lbp_3']) + & (dataframe['haclose'] <= dataframe['lbp_3']) + & (dataframe['haopen'] < buy_level) + ), ['buy', 'enter_tag']] = (1, 'buy_lbp_3') + dataframe.loc[ + ( + (dataframe['close'] <= dataframe['lowest_4_average'] * 1.002) + & (dataframe['haopen'] >= dataframe['average_line_288_098']) + & (dataframe['haclose'] <= dataframe['average_line_288_098']) + & (dataframe['haopen'] < buy_level) + ), ['buy', 'enter_tag']] = (1, 'buy_average_line_288_098') + dataframe.loc[ + ( + (dataframe['close'].shift(2) <= dataframe['min200']) + & (dataframe['pct_change'] < 0) + & (dataframe['min200'].shift(2) == dataframe['min200']) + & (dataframe['close'] < dataframe['lowest_4_average']) + & (dataframe['count_buys'] == 0 | + ((dataframe['count_buys'] > 0) & (dataframe['close'] <= dataframe['limit'])) + ) + ), ['buy', 'enter_tag']] = (1, 'buy_min200') + + dataframe['test'] = np.where(dataframe['buy'] == 1, dataframe['close'] * 1.01, np.nan) + + return dataframe + + # def get_buy_level(self, pair, dataframe): + # limit = get_limit_from_config('Achats', pair) + # + # filled_buys = {} + # for trade in self.trades: + # if trade.pair != pair: + # continue + # filled_buys = trade.select_filled_orders('buy') + # print('populate_buy_trend filled_buys : ' + str(len(filled_buys))) + # # Affichez les valeurs + # print(pair, limit) + # # BUY_LEVELS = { + # # 'BTC/USDT': [int(btc_limit), 42600, 41000, 40000, 39000, 38000, 37000, 36000, 35000], + # # 'ETH/USDT': [int(eth_limit), 2290, 1900, 1800, 1700, 1600, 1500, 1400, 1300], + # # 'ETC/USDT': [int(eth_limit), 2290, 1900, 1800, 1700, 1600, 1500, 1400, 1300], + # # 'DOGE/USDT': [int(eth_limit), 2290, 1900, 1800, 1700, 1600, 1500, 1400, 1300], + # # # Ajoutez d'autres paires avec leurs niveaux d'achat ici... + # # } + # count_of_buys = len(filled_buys) + # buy_level = dataframe['lbp'] * (1 - self.levels[count_of_buys] / 100) # float(limit) #BUY_LEVELS.get(pair, [])[0] #dataframe['lbp_3'] #" + # return buy_level + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + return dataframe + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + # print(dataframe) + last_candle = dataframe.iloc[-1].squeeze() + last_candle_12 = dataframe.iloc[-13].squeeze() + + if (len(dataframe) < 1): + return None + pair = trade.pair + if pair not in ('BTC/USDT', 'DOGE/USDT', 'ETH/USDT'): + return None + max_buys = 7 + + filled_buys = trade.select_filled_orders('buy') + count_of_buys = len(filled_buys) + if count_of_buys >= max_buys: + return None + + if 'buy' in last_candle: + condition = (last_candle['buy'] == 1) + else: + condition = False + # self.protection_nb_buy_lost.value + # limits = ['lbp_3', 'lbp_6', 'lbp_9', 'lbp_12', 'lbp_20'] + # limit = last_candle[limits[count_of_buys]] + limit = last_candle['limit'] + stake_amount = min(200, self.adjust_stake_amount(pair, last_candle) * self.fibo[count_of_buys]) + + # print("Adjust " + trade.pair + " time=" + str(current_time) + ' rate=' + str(current_rate) + " buys=" + str(count_of_buys) + " limit=" + str(limit) + " stake=" + str(stake_amount)) + logger.info( + f"Adjust price={trade.pair} buy={condition} rate={current_rate:.4f} buys={count_of_buys} limit={limit:.4f} stake={stake_amount:.4f}") + + if (0 < count_of_buys <= max_buys) & (current_rate <= limit) & (condition): + try: + + # This then calculates current safety order size + # stake_amount = stake_amount * pow(1.5, count_of_buys) + # print("Effective Adjust " + trade.pair + " time=" + str(current_time) + ' rate=' + str(current_rate) + " buys=" + str(count_of_buys) + " limit=" + str(limit) + " stake=" + str(stake_amount)) + logger.info( + f"Effective Adjust price={trade.pair} rate={current_rate:.4f} buys={count_of_buys} limit={limit:.4f} stake={stake_amount:.4f}") + + return stake_amount + except Exception as exception: + print(exception) + return None + return None + + def adjust_stake_amount(self, pair: str, dataframe: DataFrame): + # Calculer le minimum des 14 derniers jours + current_price = dataframe['close'] + + # trade = self.getTrade(pair) + # if trade: + # current_price = trade.open_rate + base_stake_amount = self.config.get('stake_amount', 50) # Montant de base configuré + + # Calculer le max des 14 derniers jours + min_14_days_4 = dataframe['lowest_4_1d'] + max_14_days_4 = dataframe['highest_4_1d'] + percent_4 = 1 - (current_price - min_14_days_4) / (max_14_days_4 - min_14_days_4) + factor_4 = 1 / ((current_price - min_14_days_4) / (max_14_days_4 - min_14_days_4)) + max_min_4 = max_14_days_4 / min_14_days_4 + + # min_14_days = dataframe['lowest_1d'] + # max_14_days = dataframe['highest_1d'] + # percent = 1 - (current_price - min_14_days) / (max_14_days - min_14_days) + # factor = 1 / ((current_price - min_14_days) / (max_14_days - min_14_days)) + # max_min = max_14_days / min_14_days + # Stack amount ajusté price=2473.47 min_max=0.15058074985054215 percent=0.8379141364642171 amount=20.0 + + adjusted_stake_amount = max(base_stake_amount / 2.5, min(75, base_stake_amount * percent_4)) + # if pair in ('BTC/USDT', 'ETH/USDT'): + # if percent_4 > 0.5: + # adjusted_stake_amount = 300 + + # adjusted_stake_amount_2 = max(base_stake_amount / 2.5, min(75, base_stake_amount * percent)) + + print( + f"Stack amount ajusté price={current_price} max_min={max_min_4:.4f} min_14={min_14_days_4:.4f} max_14={max_14_days_4:.4f} factor={factor_4:.4f} percent={percent_4:.4f} amount={adjusted_stake_amount:.4f}") + # print(f"Stack amount ajusté price={current_price} max_min={max_min:.4f} min_14={min_14_days:.4f} max_14={max_14_days:.4f} factor={factor:.4f} percent={percent:.4f} amount={adjusted_stake_amount_2:.4f}") + + return adjusted_stake_amount + + def adjust_exit_price(self, dataframe: DataFrame): + # Calculer le max des 14 derniers jours + min_14_days = dataframe['lowest_1d'] + max_14_days = dataframe['highest_1d'] + entry_price = dataframe['fbp'] + current_price = dataframe['close'] + percent = 0.5 * (max_14_days - min_14_days) / min_14_days + exit_price = (1 + percent) * entry_price + + print(f"Exit price ajusté price={current_price:.4f} max_14={max_14_days:.4f} exit_price={exit_price:.4f}") + + return exit_price + + def adjust_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, + current_rate: float, current_profit: float, **kwargs) -> float: + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + # print(dataframe) + last_candle = dataframe.iloc[-1].squeeze() + + # Utiliser l'ATR pour ajuster le stoploss + atr_stoploss = current_rate - (last_candle['atr'] * 1.5) # Stoploss à 1.5x l'ATR + + # Retourner le stoploss dynamique en pourcentage du prix actuel + return (atr_stoploss / current_rate) - 1 + + def expectedProfit(self, pair: str, dataframe: DataFrame): + + current_price = dataframe['last_price'] # dataframe['close'] + + # trade = self.getTrade(pair) + # if trade: + # current_price = trade.open_rate + + # Calculer le max des 14 derniers jours + min_14_days = dataframe['lowest_1d'] + max_14_days = dataframe['highest_1d'] + percent = (max_14_days - current_price) / (min_14_days) + + min_max = dataframe['pct_min_max_1d'] # (max_14_days - min_14_days) / min_14_days + expected_profit = min(0.1, max(0.01, dataframe['min_max200'] * 0.5)) + + print( + f"Expected profit price={current_price:.4f} min_max={min_max:.4f} min_14={min_14_days:.4f} max_14={max_14_days:.4f} percent={percent:.4f} expected_profit={expected_profit:.4f}") + + # self.analyze_conditions(pair, dataframe) + return expected_profit + + # def adjust_exit_price(self, dataframe: DataFrame): + # # Calculer le max des 14 derniers jours + # min_14_days = dataframe['lowest_1d'] + # max_14_days = dataframe['highest_1d'] + # entry_price = dataframe['fbp'] + # current_price = dataframe['close'] + # percent = 0.5 * (max_14_days - min_14_days) / min_14_days + # exit_price = (1 + percent) * entry_price + # + # print(f"Exit price ajusté price={current_price} max_14={max_14_days} exit_price={exit_price}") + # + # return exit_price + + # def adjust_entry_price(self, dataframe: DataFrame): + # # Calculer le max des 14 derniers jours + # min_14_days = dataframe['lowest_1d'] + # max_14_days = dataframe['highest_1d'] + # current_price = dataframe['close'] + # percent = 0.5 * (max_14_days - min_14_days) / min_14_days + # entry_price = (1 + percent) * entry_price + # + # print(f"Entry price ajusté price={current_price} max_14={max_14_days} exit_price={entry_price}") + # + # return entry_price + + # def adjust_stake_amount(self, dataframe: DataFrame): + # # Calculer le minimum des 14 derniers jours + # middle = dataframe['middle_1d'] + # + # # Récupérer la dernière cotation actuelle (peut être le dernier point de la série) + # current_price = dataframe['close'] + # + # # Calculer l'écart entre la cotation actuelle et le minimum des 14 derniers jours + # difference = middle - current_price + # # Ajuster la stake_amount en fonction de l'écart + # # Par exemple, augmenter la stake_amount proportionnellement à l'écart + # base_stake_amount = self.config.get('stake_amount', 100) # Montant de base configuré + # + # multiplier = 1 - (difference / current_price) # Exemple de logique d'ajustement + # + # adjusted_stake_amount = max(base_stake_amount / 2.5, base_stake_amount * multiplier) + # + # # difference = 346.07000000000016 + # # price = 2641.75 + # # min_14 = 2295.68 + # # amount = 56.5500141951358 + # + # print(f"Stack amount ajusté difference={difference} price={current_price} middle={middle} multiplier={multiplier} amount={adjusted_stake_amount}") + # + # return adjusted_stake_amount + + def analyze_conditions(self, pair: str, row: DataFrame): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + + if dataframe is None or dataframe.empty: + return + if row is None or row.empty: + return + # Créer un tableau pour stocker les résultats de l'analyse + results = [] + # row = dataframe.iloc[-1].squeeze() + # result = {'triggered': False, 'conditions_failed': []} + try: + buy_level = row['buy_level'] + except Exception as exception: + print(exception) + return None + # Première condition : 'buy_fractal' + print('------------------------------------------------') + print('Test buy fractal ' + pair + ' buy_level=' + str(buy_level)) + if not ( + (row['close'] <= (row['min200'] * 1.002)) and + (row['percent_max_144'] <= -0.012) and + (row['haopen'] < buy_level) and + (row['open'] < row['average_line_288']) and + (dataframe['min50'].shift(3).iloc[-1] == row['min50']) + ): + failed_conditions = [] + if row['close'] > (row['min200'] * 1.002): + print('close > min200 * 1.002') + if row['percent_max_144'] > -0.012: + print('percent_max_144 > -0.012') + if row['haopen'] >= buy_level: + print('haopen >= buy_level') + if row['open'] >= row['average_line_288']: + print('open >= average_line_288') + if dataframe['min50'].shift(3).iloc[-1] != row['min50']: + print('min50.shift(3) != min50') + # result['conditions_failed'].append({'buy_fractal': failed_conditions}) + print('------------------------------------------------') + print('Test buy_max_diff_015 ' + pair + ' buy_level=' + str(buy_level)) + # Deuxième condition : 'buy_max_diff_015' + if not ( + (dataframe['max200_diff'].shift(4).iloc[-1] >= 0.015) and + (row['close'] <= row['lowest_4_average'] * 1.002) and + (row['close'] <= row['min200'] * 1.002) and + (dataframe['max50_diff'].shift(4).iloc[-1] >= 0.01) and + (row['haclose'] < row['bb_middleband']) and + (row['close'] < buy_level) and + (row['open'] < row['average_line_288']) and + (dataframe['min50'].shift(3).iloc[-1] == row['min50']) + ): + if dataframe['max200_diff'].shift(4).iloc[-1] < 0.015: + print('max200_diff.shift(4) < 0.015') + if row['close'] > row['lowest_4_average'] * 1.002: + print('close > lowest_4_average * 1.002') + if row['close'] > row['min200'] * 1.002: + print('close > min200 * 1.002') + if dataframe['max50_diff'].shift(4).iloc[-1] < 0.01: + print('max50_diff.shift(4) < 0.01') + if row['haclose'] >= row['bb_middleband']: + print('haclose >= bb_middleband') + if row['close'] >= buy_level: + print('close >= buy_level') + if row['open'] >= row['average_line_288']: + print('open >= average_line_288') + if dataframe['min50'].shift(3).iloc[-1] != row['min50']: + print('min50.shift(3) != min50') + print('------------------------------------------------') + print('Test buy_min_max_200 ' + pair + ' buy_level=' + str(buy_level)) + if not ( + (row['close'] <= row['min200'] * 1.002) + and (row['min_max200'] > 0.015) + and (row['haopen'] < buy_level) + and (row['open'] < row['average_line_288']) + ): + if row['close'] > row['min200'] * 1.002: + print('close > row[min200] * 1.002') + if row['min_max200'] <= 0.015: + print('row[min_max200] <= 0.015') + if row['haopen'] < buy_level: + print('row[haopen] < buy_level') + if row['open'] < row['average_line_288']: + print('row[open] >= row[average_line_288]') + print('------------------------------------------------') + + # Ajouter le résultat à la liste des résultats + # results.append(result) + + # print(result) + + def getBinanceOrderBook(self, pair, dataframe: DataFrame): + """Fetch the order book (depth) from Binance.""" + # print(dataframe) + last_candle = dataframe.iloc[-1].squeeze() + symbol = pair.replace('/', '') + + try: + url = f"https://api.binance.com/api/v3/depth?symbol={symbol}&limit=5000" + response = requests.get(url) + data = response.json() + + # Extract bids and asks from the order book + asks, bids = self.calculateSMA(20, data['asks'], data['bids']) # Ventes List of [price, quantity] + # bids = data['bids'] + # asks = data['asks'] # Achats List of [price, quantity] + + # Process the depth data as you need it + # bid_volume = sum([float(bid[1]) for bid in bids]) # Sum of all bid volumes + # $ * nb / $ => nb + bid_volume = sum([float(bid[0]) * float(bid[1]) / float(last_candle['close']) for bid in bids[:10]]) + # ask_volume = sum([float(ask[1]) for ask in asks]) # Sum of all ask volumes + ask_volume = sum([float(ask[0]) * float(ask[1]) / float(last_candle['close']) for ask in asks[:10]]) + + # Example: add the difference in volumes as an indicator + if bid_volume and ask_volume: + self.updateLastValue(dataframe, 'depth_bid_ask_diff', round(bid_volume - ask_volume, 2)) + else: + self.updateLastValue(dataframe, 'depth_bid_ask_diff', 0) + + # probabilité baisse hausse sur les n premiers élements + for start in [0, 50, 100, 150]: + self.updateLastValue(dataframe, 'prob_hausse_' + str(start + 50), + self.calculateProbaNb(asks, bids, start, start + 50)) + # dataframe['prob_hausse_' + str(nb)] = self.calculateProbaNb(asks, bids, nb) + # Analyse des prix moyens pondérés par les volumes (VWAP) : + # + # Le VWAP (Volume Weighted Average Price) peut être utilisé pour comprendre la pression directionnelle. + # Si le VWAP basé sur les ordres d'achat est plus élevé que celui des ordres de vente, + # cela peut indiquer une probabilité de hausse. + nb = 50 + + bid_vwap = sum([float(bid[0]) * float(bid[1]) for bid in bids[:nb]]) / sum( + [float(bid[1]) for bid in bids[:nb]]) + ask_vwap = sum([float(ask[0]) * float(ask[1]) for ask in asks[:nb]]) / sum( + [float(ask[1]) for ask in asks[:nb]]) + + if bid_vwap > ask_vwap: + self.updateLastValue(dataframe, 'vwap_hausse', + round(100 * (bid_vwap - ask_vwap) / (bid_vwap + ask_vwap), 2)) + else: + self.updateLastValue(dataframe, 'vwap_hausse', + - round(100 * (ask_vwap - bid_vwap) / (bid_vwap + ask_vwap), 2)) + + current_price = last_candle['close'] # le prix actuel du marché + + # Calcul du seuil de variation de 1% + lower_threshold = current_price * 0.99 + upper_threshold = current_price * 1.01 + + # Volumes d'achat (bids) sous 1% du prix actuel + bid_volume_1percent = sum( + [float(bid[1]) for bid in bids if current_price >= float(bid[0]) >= lower_threshold]) + + # Volumes de vente (asks) au-dessus de 1% du prix actuel + ask_volume_1percent = sum( + [float(ask[1]) for ask in asks if current_price <= float(ask[0]) <= upper_threshold]) + + # Estimation de la probabilité basée sur le déséquilibre des volumes + total_volume = bid_volume_1percent + ask_volume_1percent + if total_volume > 0: + prob_hausse = bid_volume_1percent / total_volume + prob_baisse = ask_volume_1percent / total_volume + else: + prob_hausse = prob_baisse = 0 + + self.updateLastValue(dataframe, 'proba_hausse_1%', round(prob_hausse * 100, 2)) + self.updateLastValue(dataframe, 'proba_baisse_1%', round(prob_baisse * 100, 2)) + print(f"Probabilité de hausse de 1%: {prob_hausse * 100:.2f}%") + print(f"Probabilité de baisse de 1%: {prob_baisse * 100:.2f}%") + + self.calculateResistance(pair, asks, dataframe) + self.calculateSupport(pair, bids, dataframe) + + dataframe['r_s'] = 100 * (dataframe['r_min'] - dataframe['s_min']) / dataframe['s_min'] + + except Exception as e: + logger.error(f"Error fetching order book: {e}") + return None, None + + def calculateProbaNb(self, asks, bids, start, nb): + top_bids = sum([float(bid[1]) for bid in bids[start:nb]]) + top_asks = sum([float(ask[1]) for ask in asks[start:nb]]) + if top_bids > top_asks: + proba = round(100 * (top_bids - top_asks) / (top_bids + top_asks), 2) + else: + proba = - round(100 * (top_asks - top_bids) / (top_bids + top_asks), 2) + return proba + + def calculateResistance(self, pair, asks, dataframe: DataFrame): + last_candle = dataframe.iloc[-1].squeeze() + + # Filtrage +-5% + current_price = float(last_candle['close']) + lower_bound = current_price * 0.95 + upper_bound = current_price * 1.05 + ask_prices = [float(ask[0]) for ask in asks] + ask_volumes = [float(ask[1]) for ask in asks] + ask_df = pd.DataFrame({'price': ask_prices, 'volume': ask_volumes}) + filtered_ask_df = ask_df[(ask_df['price'] >= lower_bound) & (ask_df['price'] <= upper_bound)] + # Trier le DataFrame sur la colonne 'volume' en ordre décroissant + sorted_ask_df = filtered_ask_df.sort_values(by='volume', ascending=False) + + # Ne garder que les 3 premières lignes (les 3 plus gros volumes) + top_3_asks = sorted_ask_df.head(3) + print(top_3_asks) + + # Convertir les ordres de vente en numpy array pour faciliter le traitement + asks_array = np.array(filtered_ask_df, dtype=float) + + # Détecter les résistances : on peut définir qu'une résistance est un niveau de prix où la quantité est élevée + # Ex: seuil de résistance à partir des 10% des plus grosses quantités + resistance_threshold = np.percentile(asks_array[:, 1], 90) + resistances = asks_array[asks_array[:, 1] >= resistance_threshold] + + # Afficher les résistances trouvées + # print(f"{pair} Niveaux de résistance détectés:") + # for resistance in resistances: + # print(f"{pair} Prix: {resistance[0]}, Quantité: {resistance[1]}") + + # Exemple : somme des quantités sur des plages de prix + # Intervalles de 10 USDT + step = last_candle['close'] / 100 + price_intervals = np.arange(asks_array[:, 0].min(), asks_array[:, 0].max(), step=step) + + for start_price in price_intervals: + end_price = start_price + step + mask = (asks_array[:, 0] >= start_price) & (asks_array[:, 0] < end_price) + volume_in_range = asks_array[mask, 1].sum() + amount = volume_in_range * end_price + print( + f"Prix entre {start_price:.6f} et {end_price:.6f}: Volume total = {volume_in_range:.2f} amount={amount:.2f}") + + # Trier les asks par quantité en ordre décroissant + asks_sorted = asks_array[asks_array[:, 1].argsort()][::-1] + + # Sélectionner les trois plus gros resistances + top_3_resistances = asks_sorted[:3] + + # Afficher les trois plus gros resistances + print("Les trois plus grosses resistances détectées : ") + self.updateLastValue(dataframe, 'r3', top_3_resistances[0][0]) + self.updateLastValue(dataframe, 'r2', top_3_resistances[1][0]) + self.updateLastValue(dataframe, 'r1', top_3_resistances[2][0]) + self.updateLastValue(dataframe, 'r_min', + min(top_3_resistances[0][0], top_3_resistances[1][0], top_3_resistances[2][0])) + for resistance in top_3_resistances: + print(f"{pair} Prix: {resistance[0]}, Quantité: {resistance[1]}") + + def calculateSupport(self, pair, bids, dataframe: DataFrame): + last_candle = dataframe.iloc[-1].squeeze() + + # Convert to pandas DataFrame to apply moving average + current_price = float(last_candle['close']) + lower_bound = current_price * 0.95 + upper_bound = current_price * 1.05 + bid_prices = [float(bid[0]) for bid in bids] + bid_volumes = [float(bid[1]) for bid in bids] + bid_df = pd.DataFrame({'price': bid_prices, 'volume': bid_volumes}) + filtered_bid_df = bid_df[(bid_df['price'] >= lower_bound) & (bid_df['price'] <= upper_bound)] + # Trier le DataFrame sur la colonne 'volume' en ordre décroissant + sorted_bid_df = filtered_bid_df.sort_values(by='volume', ascending=False) + + # Ne garder que les 3 premières lignes (les 3 plus gros volumes) + top_3_bids = sorted_bid_df.head(3) + print(top_3_bids) + + # Convertir les ordres d'achat en numpy array pour faciliter le traitement + bids_array = np.array(filtered_bid_df, dtype=float) + + # Détecter les supports : on peut définir qu'un support est un niveau de prix où la quantité est élevée + # Ex: seuil de support à partir des 10% des plus grosses quantités + support_threshold = np.percentile(bids_array[:, 1], 90) + supports = bids_array[bids_array[:, 1] >= support_threshold] + + # Afficher les supports trouvés + # print(f"{pair} Niveaux de support détectés:") + # for support in supports: + # print(f"{pair} Prix: {support[0]}, Quantité: {support[1]}") + + step = last_candle['close'] / 100 + # Exemple : somme des quantités sur des plages de prix pour les bids + price_intervals = np.arange(bids_array[:, 0].min(), bids_array[:, 0].max(), step=step) # Intervalles de 10 USDT + + for start_price in price_intervals: + end_price = start_price + step + mask = (bids_array[:, 0] >= start_price) & (bids_array[:, 0] < end_price) + volume_in_range = bids_array[mask, 1].sum() + amount = volume_in_range * end_price + print( + f"Prix entre {start_price:.6f} et {end_price:.6f}: Volume total = {volume_in_range:.2f} amount={amount:.2f}") + + # Trier les bids par quantité en ordre décroissant + bids_sorted = bids_array[bids_array[:, 1].argsort()][::-1] + + # Sélectionner les trois plus gros supports + top_3_supports = bids_sorted[:3] + + # Afficher les trois plus gros supports + print("Les trois plus gros supports détectés:") + + self.updateLastValue(dataframe, 's1', top_3_supports[0][0]) + self.updateLastValue(dataframe, 's2', top_3_supports[1][0]) + self.updateLastValue(dataframe, 's3', top_3_supports[2][0]) + self.updateLastValue(dataframe, 's_min', max(top_3_supports[0][0], top_3_supports[1][0], top_3_supports[2][0])) + + for support in top_3_supports: + print(f"{pair} Prix: {support[0]}, Quantité: {support[1]}") + + def updateLastValue(self, df: DataFrame, col, value): + if col in df.columns: + print(f"update last col {col} {value}") + df.iloc[-1, df.columns.get_loc(col)] = value + else: + print(f"update all col {col} {value}") + df[col] = value + + # def update_last_record(self, dataframe: DataFrame, new_data): + # # Vérifiez si de nouvelles données ont été reçues + # if new_data is not None: + # # Ne mettez à jour que la dernière ligne du dataframe + # last_index = dataframe.index[-1] # Sélectionne le dernier enregistrement + # dataframe.loc[last_index] = new_data # Met à jour le dernier enregistrement avec les nouvelles données + # return dataframe + + def calculateSMA(self, nb, asks, bids): + # Prepare data for plotting + bid_prices = [float(bid[0]) for bid in bids] + bid_volumes = [float(bid[1]) for bid in bids] + + ask_prices = [float(ask[0]) for ask in asks] + ask_volumes = [float(ask[1]) for ask in asks] + + # Convert to pandas DataFrame to apply moving average + bid_df = pd.DataFrame({'price': bid_prices, 'volume': bid_volumes}) + ask_df = pd.DataFrame({'price': ask_prices, 'volume': ask_volumes}) + + # Apply a rolling window to calculate a 10-value simple moving average (SMA) + bid_df['volume_sma'] = bid_df['volume'].rolling(window=nb).mean() + ask_df['volume_sma'] = ask_df['volume'].rolling(window=nb).mean() + + # Pour bid_df + bid_df = bid_df.dropna(subset=['volume_sma']) + bids_with_sma = list(zip(bid_df['price'], bid_df['volume_sma'])) + + # Pour ask_df + ask_df = ask_df.dropna(subset=['volume_sma']) + asks_with_sma = list(zip(ask_df['price'], ask_df['volume_sma'])) + + # print(bids_with_sma) + # print(asks_with_sma) + + return asks_with_sma, bids_with_sma diff --git a/Zeus_8_3_2_B_4_2.txt b/Zeus_8_3_2_B_4_2.txt new file mode 100644 index 0000000..5df2352 --- /dev/null +++ b/Zeus_8_3_2_B_4_2.txt @@ -0,0 +1,15 @@ +[Achats] +BTC/USDT=63400 +ETH/USDT=2570 +ETC/USDT=10 +DOGE/USDT=0.106 +SOL/USDT=150 +XRP/USDT=0.584 + +[Ventes] +BTC/USDT=63979 +ETH/USDT=2542 +ETC/USDT=70 +DOGE/USDT=0.122 +SOL/USDT=150.24 +XRP/USDT=0.6 diff --git a/Zeus_8_3_2_B_4_2.txtold b/Zeus_8_3_2_B_4_2.txtold new file mode 100644 index 0000000..e678a37 --- /dev/null +++ b/Zeus_8_3_2_B_4_2.txtold @@ -0,0 +1,11 @@ +[Achats] +BTC/USDT=52669 +ETH/USDT=2375 +ETC/USDT=17.05 +DOGE/USDT=0.0901 + +[Ventes] +BTC/USDT=66331 +ETH/USDT=3321 +ETC/USDT=22.424 +DOGE/USDT=0.12660 diff --git a/Zeus_8_3_3.json b/Zeus_8_3_3.json new file mode 100644 index 0000000..b69c006 --- /dev/null +++ b/Zeus_8_3_3.json @@ -0,0 +1,117 @@ +{ + "strategy_name": "Zeus_8_3_3", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_b_bb_lowerband": 1.03, + "buy_b_bb_width": 0.02, + "buy_min_max_coef": 1.004, + "buy_min_max_cond1": 1, + "buy_base": 0.1, + "buy_min_max_decalage": 2, + "buy_min_max_n": 0.02, + "buy_min_max_nh": 14, + "buy_min_max_rsi": 61, + "buy_b_cat": "R", + "buy_h_pct": 0.016, + "buy_h_real": 0.026, + "buy_0_distance": -0.03, + "buy_0_percent20": -0.03, + "buy_1_bb_lower_5": 0.19, + "buy_1_pente_sma10": 0.48, + "buy_1_pente_sma20": 0.33, + "buy_2_bb_lower_5": 0.59, + "buy_2_distance": 0.08, + "buy_2_pente_sma10": 0.46, + "buy_2_pente_sma20": 0.08, + "buy_2_percent20": 0.06, + "buy_3_bb_lower_5": 0.05, + "buy_3_distance": 0.01, + "buy_3_pente_sma10": 0.12, + "buy_3_pente_sma20": 0.27, + "buy_3_percent20": -0.03, + "buy_4_pente_sma10": 0.35, + "buy_4_pente_sma20": 0.22, + "buy_decalage0": 7, + "buy_decalage2": 6, + "buy_decalage3": 7, + "buy_decalage_deb_0": 1, + "buy_decalage_deb_2": 1, + "buy_decalage_deb_3": 1, + "buy_min_horizon": 192, + "buy_real_num0": 0.61, + "buy_real_num1": 0.34, + "buy_real_num2": 0.35 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3000, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2000, + "sell_h_too_old_percent": 0.01 + }, + "protection": { + "protection_nb_buy_lost": 3, + "protection_percent_buy_lost": 3, + "protection_strategy": 1, + "protection_strategy_amount": 2 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-08-17 14:09:36.720609+00:00" +} \ No newline at end of file diff --git a/Zeus_8_3_3.py b/Zeus_8_3_3.py new file mode 100644 index 0000000..aafe7e9 --- /dev/null +++ b/Zeus_8_3_3.py @@ -0,0 +1,1014 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), + real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_8_3_3(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", "R", "=R", "R", "=R", " bool: + allow_to_buy = True + informative, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + # info_previous_last_candle = informative.iloc[-2].squeeze() + + if self.stop_buying.get(pair, None) is None: + print("enable buying tag", pair) + self.stop_buying[pair] = False + + if ((info_last_candle['rsi_1h'] >= 76) & (info_last_candle['close_1h'] >= info_last_candle['bb_upperband_1h'])) \ + & (self.stop_buying[pair] is False): + logger.info("1 - Disable buying %s date %s", pair, info_last_candle['date']) + self.stop_buying[pair] = True + + if self.stop_buying[pair] is True: + if ((info_last_candle['rsi_1h'] <= 35) | (info_last_candle['close_1h'] < info_last_candle['bb_lowerband_1h'])): + logger.info("2 - Enable buying %s date %s", pair, info_last_candle['date']) + self.stop_buying[pair] = False + + if self.stop_buying[pair]: + allow_to_buy = False + logger.info("3 - cancel buying %s date %s", pair, info_last_candle['date']) + else: + logger.info("3 - accept buying %s date %s", pair, info_last_candle['date']) + return allow_to_buy + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + expected_profit = 0.01 + #print(last_candle['buy_tag']) + + days = (current_time - trade.open_date_utc).days + ###### + + if (last_candle['mrsi3_1h'] <= 0): #(self.market_overview_pct5 < 0) | (last_candle['pct_change_1_4h'] < 0): + max_percent = 0.004 # last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0.004 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > 0.01) & \ + (last_candle['percent10'] < -0.005) & ((current_time - trade.open_date_utc).seconds >= 3600): + return 'b_percent10' + if (current_profit > max_profit) & \ + ((last_candle['percent'] < - max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + & (days < self.sell_b_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= max_profit) & ( + last_candle['percent3'] < -max_percent): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > max_percent) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + max_percent = 0.005 # last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0.01 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > max_profit) & ( + (last_candle['percent'] < -max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'h_percent_quick' + + # if (last_candle['bb_width'] < 0.02) & (current_profit > 0) & (last_candle['close'] > bb_width_up) & \ + # ((last_candle['percent'] < - bb_width_lim) | (last_candle['percent3'] < - bb_width_lim) | (last_candle['percent5'] < - bb_width_lim)): + # return 'h_bb_width_max' + + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= max_profit) & ( + last_candle['percent3'] < -max_percent): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > max_percent) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_h_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle[ + 'max200']): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_1'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_n'] = talib.MIN(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['max_n'] = talib.MAX(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + # dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / self.buy_bb_width_n.value)) + + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + # dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + # dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + dataframe['min_max_close'] = ( + (dataframe['max200'] - dataframe['close']) / (dataframe['close'] - dataframe['min200'])) + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + dataframe['atr'] = talib.ATR(dataframe, timeperiod=14) + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ######################## INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative["mrsi3"] = informative["rsi"].rolling(3).mean() + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= 0.12) + & (dataframe['bb_width'] > 0.018) + & (dataframe['rsi'] < 79) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_lower_width_5']) + & (dataframe['close'] < dataframe['min50'] * 1.005) + & (dataframe['percent'].shift(1) > -0.003) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku_h') + # dataframe.loc[ + # ( + # (dataframe['trend_ichimoku_base'] <= self.buy_base.value) + # & (dataframe['rsi'] < self.buy_rsi.value) + # & (dataframe['close'] < dataframe['sma10']) + # & (dataframe['close'] <= dataframe['close_1h']) + # ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + + dataframe.loc[ + ( + (dataframe['min_max_n'] >= self.buy_min_max_n.value) + & (dataframe['cond1'].shift(self.buy_min_max_decalage.value) <= self.buy_min_max_cond1.value) + & (dataframe['rsi'] < self.buy_min_max_rsi.value) + & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + & (dataframe['min_n'].shift(self.buy_min_max_decalage.value) == dataframe['min_n']) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['close'] <= dataframe['close_1h']) + ), ['buy', 'buy_tag']] = (1, 'buy_min_max') + return dataframe + + # def bot_loop_start(self, **kwargs) -> None: + # pairs = self.dp.current_whitelist() + # print("Calcul des pairs informatives") + # for pairname in pairs: + # self.stop_buying[pairname] = True + # print("Fin Calcul des pairs informatives") + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + if (len(dataframe) < 1): + return None + + # dataframe = self.populate_buy_trend(dataframe, {'pair': trade.pair}) + # if not (self.populate_buy_trend(dataframe, {'pair': trade.pair})): + # return None + if (self.stop_buying.get(trade.pair) == None): + print("-----------" + trade.pair + " Init stop buying " + str(current_profit) + " " + str(current_time) + "---------------------") + + self.stop_buying[trade.pair] = True + + if (self.stop_buying[trade.pair] == True): + print("-----------" + trade.pair + " Canceled " + str(current_profit) + " " + str(current_time) + "---------------------") + return None + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + last_candle_5 = dataframe.iloc[-3].squeeze() + last_candle_decalage = dataframe.iloc[- self.buy_min_max_decalage.value].squeeze() + # print(last_candle['buy']) + + condition = ( + ( + (last_candle['trend_ichimoku_base'] <= self.buy_base.value) + & (last_candle['rsi'] < self.buy_rsi.value) + & (last_candle['close'] < last_candle['sma10']) + & (last_candle['close'] < last_candle['bb_lower_width_5']) + & (last_candle['close'] <= last_candle['close_1h']) + ) | ( + (last_candle['min_max_n'] >= self.buy_min_max_n.value) + & (last_candle_decalage['cond1'] <= self.buy_min_max_cond1.value) + & (last_candle['rsi'] < self.buy_min_max_rsi.value) + & (last_candle['close'] < last_candle['min_n'] * self.buy_min_max_coef.value) + & (last_candle_decalage['min_n'] == last_candle['min_n']) + & (last_candle['pct_change_1_1d'] > 0) + & (last_candle['close'] <= last_candle['close_1h']) + ) + ) + + # min_d = min(last_candle['sma3_4h'], last_candle['close_1d']) + + filled_buys = trade.select_filled_orders('buy') + count_of_buys = len(filled_buys) + # days = (current_time - trade.open_date_utc).days + # minutes = (current_time - trade.open_date_utc).seconds / 60 + # condition = condition & ((last_candle['min50'] == last_candle_5['min50']) & (last_candle['close'] <= last_candle['close_1h'])) + p = self.protection_percent_buy_lost.value + percents = [p, p * 2, p * 3, p * 4, p * 5, p * 6, p * 7, p * 8, p * 9] + + if (0 < count_of_buys <= self.protection_nb_buy_lost.value) \ + & (current_profit < - (percents[count_of_buys - 1] / 100)) & (condition): + try: + p = self.config['stake_amount'] + factors = [p, p, p, p, 2 * p, 4 * p, 5 * p, 6 * p] + + stake_amount = factors[count_of_buys - 1]# filled_buys[0].cost + # This then calculates current safety order size + print("-----------" + trade.pair + " " + str(current_profit) + " " + str(count_of_buys) + " " + str(stake_amount) + + " " + str(current_time) + "---------------------") + return stake_amount + except Exception as exception: + print(exception) + return None + return None diff --git a/Zeus_8_3_3_1.json b/Zeus_8_3_3_1.json new file mode 100644 index 0000000..56bdc2b --- /dev/null +++ b/Zeus_8_3_3_1.json @@ -0,0 +1,117 @@ +{ + "strategy_name": "Zeus_8_3_3_1", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_b_bb_lowerband": 1.03, + "buy_b_bb_width": 0.02, + "buy_min_max_coef": 1.004, + "buy_min_max_cond1": 1, + "buy_base": 0.1, + "buy_min_max_decalage": 2, + "buy_min_max_n": 0.02, + "buy_min_max_nh": 14, + "buy_min_max_rsi": 61, + "buy_b_cat": "R", + "buy_h_pct": 0.016, + "buy_h_real": 0.026, + "buy_0_distance": -0.03, + "buy_0_percent20": -0.03, + "buy_1_bb_lower_5": 0.19, + "buy_1_pente_sma10": 0.48, + "buy_1_pente_sma20": 0.33, + "buy_2_bb_lower_5": 0.59, + "buy_2_distance": 0.08, + "buy_2_pente_sma10": 0.46, + "buy_2_pente_sma20": 0.08, + "buy_2_percent20": 0.06, + "buy_3_bb_lower_5": 0.05, + "buy_3_distance": 0.01, + "buy_3_pente_sma10": 0.12, + "buy_3_pente_sma20": 0.27, + "buy_3_percent20": -0.03, + "buy_4_pente_sma10": 0.35, + "buy_4_pente_sma20": 0.22, + "buy_decalage0": 7, + "buy_decalage2": 6, + "buy_decalage3": 7, + "buy_decalage_deb_0": 1, + "buy_decalage_deb_2": 1, + "buy_decalage_deb_3": 1, + "buy_min_horizon": 192, + "buy_real_num0": 0.61, + "buy_real_num1": 0.34, + "buy_real_num2": 0.35 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3000, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2000, + "sell_h_too_old_percent": 0.01 + }, + "protection": { + "protection_nb_buy_lost": 3, + "protection_percent_buy_lost": 3, + "protection_strategy": 1, + "protection_strategy_amount": 2 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-08-17 14:09:36.720609+00:00" +} \ No newline at end of file diff --git a/Zeus_8_3_3_1.py b/Zeus_8_3_3_1.py new file mode 100644 index 0000000..8f4583e --- /dev/null +++ b/Zeus_8_3_3_1.py @@ -0,0 +1,984 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), + real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_8_3_3_1(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", "R", "=R", "R", "=R", " bool: + allow_to_buy = True + informative, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + # info_previous_last_candle = informative.iloc[-2].squeeze() + + if self.stop_buying.get(pair, None) is None: + print("enable buying tag", pair) + self.stop_buying[pair] = False + + if ((info_last_candle['rsi_1h'] >= 72) | (info_last_candle['close_1h'] >= info_last_candle['bb_upperband_1h'])) \ + & (self.stop_buying[pair] is False): + logger.info("1 - Disable buying %s date %s", pair, info_last_candle['date']) + self.stop_buying[pair] = True + + if self.stop_buying[pair] is True: + if ((info_last_candle['rsi_1h'] <= 35) | (info_last_candle['close_1h'] < info_last_candle['bb_lowerband_1h'])): + logger.info("2 - Enable buying %s date %s", pair, info_last_candle['date']) + self.stop_buying[pair] = False + + if self.stop_buying[pair]: + allow_to_buy = False + logger.info("3 - cancel buying %s date %s", pair, info_last_candle['date']) + else: + logger.info("3 - accept buying %s date %s", pair, info_last_candle['date']) + return allow_to_buy + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_5_candle = dataframe.iloc[-5].squeeze() + + expected_profit = 0.01 + #print(last_candle['buy_tag']) + + days = (current_time - trade.open_date_utc).days + ###### + + if (current_profit > 0.0) & (last_candle['percent'] < -0.01): + return 'percent_quick' + if (self.market_overview_pct5 < 0) | (last_candle['pct_change_1_4h'] < 0): + if (current_profit > 0.015) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + & (days < self.sell_b_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + if (current_profit > 0.025) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'h_percent_quick' + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > 0.005) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent'] < - self.sell_h_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_1'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_n'] = talib.MIN(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['max_n'] = talib.MAX(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + # dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / self.buy_bb_width_n.value)) + + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + # dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + # dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + dataframe['min_max_close'] = ( + (dataframe['max200'] - dataframe['close']) / (dataframe['close'] - dataframe['min200'])) + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + dataframe['atr'] = talib.ATR(dataframe, timeperiod=14) + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ######################## INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative["mrsi3"] = informative["rsi"].rolling(3).mean() + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= 0.12) + & (dataframe['bb_width'] > 0.018) + & (dataframe['rsi'] < 79) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_lower_width_5']) + & (dataframe['close'] < dataframe['min50'] * 1.005) + & (dataframe['percent'].shift(1) > -0.003) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku_h') + # dataframe.loc[ + # ( + # (dataframe['trend_ichimoku_base'] <= self.buy_base.value) + # & (dataframe['rsi'] < self.buy_rsi.value) + # & (dataframe['close'] < dataframe['sma10']) + # & (dataframe['close'] <= dataframe['close_1h']) + # ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + + dataframe.loc[ + ( + (dataframe['min_max_n'] >= self.buy_min_max_n.value) + & (dataframe['cond1'].shift(self.buy_min_max_decalage.value) <= self.buy_min_max_cond1.value) + & (dataframe['rsi'] < self.buy_min_max_rsi.value) + & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + & (dataframe['min_n'].shift(self.buy_min_max_decalage.value) == dataframe['min_n']) + & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['close'] <= dataframe['close_1h']) + ), ['buy', 'buy_tag']] = (1, 'buy_min_max') + return dataframe + + # def bot_loop_start(self, **kwargs) -> None: + # pairs = self.dp.current_whitelist() + # print("Calcul des pairs informatives") + # for pairname in pairs: + # self.stop_buying[pairname] = True + # print("Fin Calcul des pairs informatives") + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + if (len(dataframe) < 1): + return None + + # dataframe = self.populate_buy_trend(dataframe, {'pair': trade.pair}) + # if not (self.populate_buy_trend(dataframe, {'pair': trade.pair})): + # return None + if (self.stop_buying.get(trade.pair) == None): + print("-----------" + trade.pair + " Init stop buying " + str(current_profit) + " " + str(current_time) + "---------------------") + + self.stop_buying[trade.pair] = True + + if (self.stop_buying[trade.pair] == True): + print("-----------" + trade.pair + " Canceled " + str(current_profit) + " " + str(current_time) + "---------------------") + return None + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + last_candle_5 = dataframe.iloc[-3].squeeze() + last_candle_decalage = dataframe.iloc[- self.buy_min_max_decalage.value].squeeze() + # print(last_candle['buy']) + + condition = ( + ( + (last_candle['trend_ichimoku_base'] <= self.buy_base.value) + & (last_candle['rsi'] < self.buy_rsi.value) + & (last_candle['close'] < last_candle['sma10']) + & (last_candle['close'] < last_candle['bb_lower_width_5']) + & (last_candle['close'] <= last_candle['close_1h']) + ) | ( + (last_candle['min_max_n'] >= self.buy_min_max_n.value) + & (last_candle_decalage['cond1'] <= self.buy_min_max_cond1.value) + & (last_candle['rsi'] < self.buy_min_max_rsi.value) + & (last_candle['close'] < last_candle['min_n'] * self.buy_min_max_coef.value) + & (last_candle_decalage['min_n'] == last_candle['min_n']) + & (last_candle['pct_change_1_1d'] > 0) + & (last_candle['close'] <= last_candle['close_1h']) + ) + ) + + # min_d = min(last_candle['sma3_4h'], last_candle['close_1d']) + + filled_buys = trade.select_filled_orders('buy') + count_of_buys = len(filled_buys) + # days = (current_time - trade.open_date_utc).days + # minutes = (current_time - trade.open_date_utc).seconds / 60 + # condition = condition & ((last_candle['min50'] == last_candle_5['min50']) & (last_candle['close'] <= last_candle['close_1h'])) + p = self.protection_percent_buy_lost.value + percents = [p, p * 2, p * 3, p * 4, p * 5, p * 6, p * 7, p * 8, p * 9] + + if (0 < count_of_buys <= self.protection_nb_buy_lost.value) \ + & (current_profit < - (percents[count_of_buys - 1] / 100)) & (condition): + try: + p = self.config['stake_amount'] + factors = [p, p, p, p, 2 * p, 4 * p, 5 * p, 6 * p] + + stake_amount = factors[count_of_buys - 1]# filled_buys[0].cost + # This then calculates current safety order size + print("-----------" + trade.pair + " " + str(current_profit) + " " + str(count_of_buys) + " " + str(stake_amount) + + " " + str(current_time) + "---------------------") + return stake_amount + except Exception as exception: + print(exception) + return None + return None diff --git a/Zeus_8_3_3_2.json b/Zeus_8_3_3_2.json new file mode 100644 index 0000000..88bf9e5 --- /dev/null +++ b/Zeus_8_3_3_2.json @@ -0,0 +1,35 @@ +{ + "strategy_name": "Zeus_8_3_3_2", + "params": { + "roi": { + "0": 10 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_min_horizon": 625, + "buy_min_max_n": 0.2, + "buy_rsi": 26 + }, + "sell": { + "sell_b_RSI": 72, + "sell_percent": 0.25, + "sell_profit_percent": 0.1 + }, + "protection": { + "protection_nb_buy_lost": 3, + "protection_percent_buy_lost": 3, + "protection_start_buying_rsi_1d": 5, + "protection_stop_buying_rsi_1d": 91 + }, + "stoploss": { + "stoploss": -0.262 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-03 12:06:46.910000+00:00" +} \ No newline at end of file diff --git a/Zeus_8_3_3_2.py b/Zeus_8_3_3_2.py new file mode 100644 index 0000000..943f7f7 --- /dev/null +++ b/Zeus_8_3_3_2.py @@ -0,0 +1,441 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +class Zeus_8_3_3_2(IStrategy): + + # ROI table: + minimal_roi = { + "0": 10 + # 0.564, + # "567": 0.273, + # "2814": 0.12, + # "7675": 0 + } + + # Stoploss: + stoploss = -1 #0.256 + + # Buy hypers + timeframe = '5m' + + stop_buying = {} + + # DCA config + position_adjustment_enable = True + + plot_config = { + "main_plot": { + "min200": { + "color": "#86c932" + }, + "min50": { + "color": "white" + }, + # "max200": { + # "color": "yellow" + # }, + "sma3_1d": { + "color": "pink" + }, + "sma5_1d": { + "color": "blue" + }, + "sma10_1d": { + "color": "orange" + }, + "close_1d": { + "color": "#73e233", + }, + "low": { + "color": "cyan", + }, + "bb_lowerband": { + "color": "#da59a6"}, + "bb_upperband": { + "color": "#da59a6", + } + }, + "subplots": { + # "Ind": { + # "trend_ichimoku_base": { + # "color": "#dd1384" + # }, + # "trend_kst_diff": { + # "color": "#850678" + # } + # }, + # "BB": { + # "bb_width": { + # "color": "white" + # }, + # "bb_lower_5": { + # "color": "yellow" + # } + # }, + "Rsi": { + "rsi_1d": { + "color": "pink" + }, + # "rsi_1h": { + # "color": "green" + # }, + "rsi5": { + "color": "yellow" + }, + "rsi3_1d": { + "color": "red" + } + }, + # "Percent": { + # "pct_change_1_1d": { + # "color": "green" + # }, + # "pct_change_3_1d": { + # "color": "orange" + # }, + # "pct_change_5_1d": { + # "color": "red" + # } + # } + } + } + trades = list() + + buy_min_horizon = IntParameter(50, 800, default=72, space='buy') + buy_rsi = IntParameter(1, 30, default=12, space='buy') + buy_min_max_n = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + # adx_1d_limit = IntParameter(15, 45, default=18, space='buy') + sell_b_RSI = IntParameter(70, 98, default=60, space='sell') + sell_profit_percent = DecimalParameter(0.1, 1.5, decimals=1, default=0.8, space='sell') + + sell_percent = DecimalParameter(0.01, 0.30, decimals=2, default=0.05, space='sell') + protection_percent_buy_lost = IntParameter(1, 30, default=3, space='protection') + protection_nb_buy_lost = IntParameter(1, 3, default=3, space='protection') + protection_stop_buying_rsi_1d = IntParameter(50, 100, default=76, space='protection') + protection_start_buying_rsi_1d = IntParameter(1, 50, default=30, space='protection') + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + allow_to_buy = True + # info_previous_last_candle = informative.iloc[-2].squeeze() + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_previous_last_candle = dataframe.iloc[-3].squeeze() + + if self.stop_buying.get(pair, None) is None: + print("enable buying tag", pair) + self.stop_buying[pair] = False + + if ((last_candle['rsi5'] >= self.protection_stop_buying_rsi_1d.value) | (last_candle['close'] >= last_candle['bb_upperband'])) \ + & (self.stop_buying[pair] is False): + logger.info("1 - Disable buying %s date %s", pair, last_candle['date']) + self.stop_buying[pair] = True + + if self.stop_buying[pair] is True: + if ((last_candle['rsi5'] <= self.protection_start_buying_rsi_1d.value) & (last_candle['percent5'] >= 0.005)): + logger.info("2 - Enable buying %s date %s", pair, last_candle['date']) + self.stop_buying[pair] = False + + logger.info("Buy ==> %s ", pair + " " + str(current_time) + "---------------------") + + if self.stop_buying[pair]: + allow_to_buy = False + logger.info("3 - cancel buying %s date %s", pair, str(last_candle['date']) + " " + str(last_candle['rsi5'])) + else: + logger.info("3 - accept buying %s date %s", pair, str(last_candle['date']) + " " + str(last_candle['rsi5'])) + return allow_to_buy + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_previous_last_candle = dataframe.iloc[-3].squeeze() + + if (current_profit > self.buy_min_max_n.value * self.sell_profit_percent.value) \ + & (previous_last_candle['rsi5'] > self.sell_b_RSI.value) \ + & (previous_last_candle['rsi5'] >= last_candle['rsi5']) \ + & (previous_last_candle['rsi5'] >= previous_previous_last_candle['rsi5']): + logger.info("Sell ==> %s ", pair + " " + str(current_time) + str(current_profit) + " " + str(current_rate)) + + return 'profit_1' # + str(self.sell_percent.value) + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + # informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + # dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + # dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + # dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + # dataframe['min200_1'] = dataframe['min200'] * 1.005 + # dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_n'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['max_n'] = talib.MAX(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + # dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + # dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + # dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + # dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + # dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + # dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + # dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + # dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + # dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + # dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + # dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / self.buy_bb_width_n.value)) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['min_max_close'] = ( + (dataframe['max200'] - dataframe['close']) / (dataframe['close'] - dataframe['min200'])) + + # dataframe['stop_buying'] = (dataframe['rsi5'] >= self.protection_stop_buying_rsi_1d.value) \ + # & (dataframe['close'] >= dataframe['bb_upperband']) + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["rsi3"] = talib.RSI(informative, 3) + # informative["mrsi3"] = informative["rsi"].rolling(3).mean() + # informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + # informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + # informative['pct_change_1'] = informative['close'].pct_change(1) + # informative['pct_change_3'] = informative['close'].pct_change(3) + # informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + # informative['adx'] = talib.ADX(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + + # informative["bb_width"] = ( + # (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + # ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + # ######################## INFORMATIVE 4h + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + # informative["rsi"] = talib.RSI(informative) + # informative['pct_change_1'] = informative['close'].pct_change(1) + # informative['pct_change_3'] = informative['close'].pct_change(3) + # informative['pct_change_5'] = informative['close'].pct_change(5) + # informative['sma3'] = talib.SMA(informative, timeperiod=3) + # informative['sma5'] = talib.SMA(informative, timeperiod=5) + # informative['sma10'] = talib.SMA(informative, timeperiod=10) + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ######################## INFORMATIVE 1h + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + # informative["rsi"] = talib.RSI(informative) + # informative["rsi3"] = talib.RSI(informative, 3) + # informative["mrsi3"] = informative["rsi"].rolling(3).mean() + # informative['pct_change_1'] = informative['close'].pct_change(1) + # informative['pct_change_3'] = informative['close'].pct_change(3) + # informative['pct_change_5'] = informative['close'].pct_change(5) + # informative['sma3'] = talib.SMA(informative, timeperiod=3) + # informative['sma5'] = talib.SMA(informative, timeperiod=5) + # informative['sma10'] = talib.SMA(informative, timeperiod=10) + # informative['adx'] = talib.ADX(informative) + # bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + # informative['bb_lowerband'] = bollinger['lower'] + # informative['bb_middleband'] = bollinger['mid'] + # informative['bb_upperband'] = bollinger['upper'] + # + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + (dataframe['rsi5'] < self.protection_stop_buying_rsi_1d.value) + & (dataframe['rsi5'] > self.protection_start_buying_rsi_1d.value) + & (dataframe['rsi5'].shift(1) < self.buy_rsi.value) + & (dataframe['rsi5'].shift(1) < dataframe['rsi5']) + & (dataframe['rsi5'].shift(1) < dataframe['rsi5'].shift(2)) + # & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + & (dataframe['min_n'].shift(6) == dataframe['min_n']) + # & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['min_max_n'] >= self.buy_min_max_n.value) + # & (dataframe['close'] <= dataframe['close_1d']) + # & (dataframe['close_1d'] <= dataframe['sma3_1d']) + # & (dataframe['close_1d'] <= dataframe['sma5_1d']) + # & (dataframe['close_1d'] <= dataframe['sma10_1d']) + # & (dataframe['adx_1d'] > self.adx_1d_limit.value) + # & (dataframe['rsi3_1d'].shift(288) <= dataframe['rsi3_1d']) + ), ['buy', 'buy_tag']] = (1, 'buy_adx_inf') + + return dataframe + + # def bot_loop_start(self, **kwargs) -> None: + # pairs = self.dp.current_whitelist() + # print("Calcul des pairs informatives") + # for pairname in pairs: + # self.stop_buying[pairname] = True + # print("Fin Calcul des pairs informatives") + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['rsi5'].shift(1) > self.sell_b_RSI.value) + # & (dataframe['rsi5'].shift(1) >= dataframe['rsi5']) + # & (dataframe['rsi5'].shift(1) >= dataframe['rsi5'].shift(2)) + # & (dataframe['close'] > dataframe['close_1d']) + # & (dataframe['close_1d'] > dataframe['sma3_1d']) + # & (dataframe['close_1d'] > dataframe['sma5_1d']) + # & (dataframe['close_1d'] > dataframe['sma10_1d']) + # # & (dataframe['rsi3_1d'].shift(288) > dataframe['rsi3_1d']) + # # (dataframe['close_1d'] > dataframe['sma3_1d']) + # # & (dataframe['rsi3_1d'] > 72) + # #& (dataframe['last_buy_price'] < (dataframe['close'])) + # #& (dataframe['should_sell'] == True) + # ), ['sell', 'sell_tag']] = (1, 'sell_close_1d') + # print("dans sell" + metadata['pair']) + return dataframe + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + if (len(dataframe) < 1): + return None + + if (self.stop_buying.get(trade.pair, None) == None): + # logger.info("----------- %s ", trade.pair + " Init stop buying " + str(current_profit) + " " + str(current_time) + "---------------------") + self.stop_buying[trade.pair] = False + + if (self.stop_buying[trade.pair] == True): + # logger.info("----------- %s ", trade.pair + " Canceled " + str(current_profit) + " " + str(current_time) + "---------------------") + return None + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_previous_last_candle = dataframe.iloc[-3].squeeze() + last_candle_6 = dataframe.iloc[-7].squeeze() + + # last_candle_5 = dataframe.iloc[-3].squeeze() + # last_candle_decalage = dataframe.iloc[- self.buy_min_max_decalage.value].squeeze() + # print(last_candle['buy']) + + condition = ( + (previous_last_candle['rsi5'] < self.buy_rsi.value) + & (previous_last_candle['rsi5'] < last_candle['rsi5']) + & (previous_last_candle['rsi5'] < previous_previous_last_candle['rsi5']) + & (last_candle_6['min_n'] == last_candle['min_n']) + & (last_candle['min_max_n'] >= self.buy_min_max_n.value) + # & (last_candle['close'] < last_candle['min_n'] * self.buy_min_max_coef.value) + # & (last_candle['close'] <= last_candle['close_1d']) + # & (last_candle['close_1d'] <= last_candle['sma3_1d']) + # & (last_candle['close_1d'] <= last_candle['sma5_1d']) + # & (last_candle['close_1d'] <= last_candle['sma10_1d']) + # & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + ) + if not (condition): + return None + + # min_d = min(last_candle['sma3_4h'], last_candle['close_1d']) + + filled_buys = trade.select_filled_orders('buy') + count_of_buys = len(filled_buys) + # days = (current_time - trade.open_date_utc).days + # minutes = (current_time - trade.open_date_utc).seconds / 60 + # condition = condition & ((last_candle['min50'] == last_candle_5['min50']) & (last_candle['close'] <= last_candle['close_1h'])) + p = self.protection_percent_buy_lost.value + percents = [p, p * 2, p * 3, p * 4, p * 5, p * 6, p * 7, p * 8, p * 9] + + if (0 < count_of_buys <= self.protection_nb_buy_lost.value) \ + & (current_profit < - (percents[count_of_buys - 1] / 100)) & (condition): + try: + p = self.config['stake_amount'] + factors = [p, p, p, p, 2 * p, 4 * p, 5 * p, 6 * p] + + stake_amount = factors[count_of_buys - 1]# filled_buys[0].cost + # This then calculates current safety order size + # logger.info("----------- %s ", trade.pair + " " + str(current_profit) + " " + str(count_of_buys) + " " + str(stake_amount) + + # " " + str(current_time) + "---------------------") + # print("-----------" + trade.pair + " " + str(current_profit) + " " + str(count_of_buys) + " " + str(stake_amount) + + # " " + str(current_time) + "---------------------") + return stake_amount + except Exception as exception: + print(exception) + return None + return None diff --git a/Zeus_8_3_3_3.json b/Zeus_8_3_3_3.json new file mode 100644 index 0000000..b3b89b0 --- /dev/null +++ b/Zeus_8_3_3_3.json @@ -0,0 +1,79 @@ +{ + "strategy_name": "Zeus_8_3_3_3", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_bb_width_n": 5, + "buy_min_horizon": 192, + "buy_min_max_coef": 1.004, + "buy_min_max_decalage": 5, + "buy_min_max_n": 0.02, + "buy_min_max_nh": 14, + "buy_min_max_rsi": 61, + "buy_rsi": 72 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": false, + "profit_b_quick_gain_3": true, + "profit_b_quick_lost": false, + "profit_b_short_loss": false, + "profit_b_sma10": true, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": true, + "profit_h_no_change": false, + "profit_h_old_sma10": false, + "profit_h_over_rsi": true, + "profit_h_quick_gain": true, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": true, + "profit_h_sma10": true, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": false, + "sell_b_RSI": 79, + "sell_b_RSI2": 71, + "sell_b_RSI2_percent": 0.0, + "sell_b_RSI3": 89, + "sell_b_candels": 23, + "sell_b_percent": 0.017, + "sell_b_percent3": 0.003, + "sell_b_profit_no_change": 0.014, + "sell_b_profit_percent10": 0.0008, + "sell_b_too_old_day": 10, + "sell_b_too_old_percent": 0.01, + "sell_h_RSI": 98, + "sell_h_RSI2": 80, + "sell_h_RSI2_percent": 0.008, + "sell_h_RSI3": 95, + "sell_h_candels": 15, + "sell_h_percent": 0.019, + "sell_h_percent3": 0.0, + "sell_h_profit_no_change": 0.016, + "sell_h_profit_percent10": 0.0008, + "sell_h_too_old_day": 0, + "sell_h_too_old_percent": 0.0 + }, + "protection": { + "protection_nb_buy_lost": 3, + "protection_percent_buy_lost": 5 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-08-23 21:11:15.507514+00:00" +} \ No newline at end of file diff --git a/Zeus_8_3_3_3.py b/Zeus_8_3_3_3.py new file mode 100644 index 0000000..7dce27a --- /dev/null +++ b/Zeus_8_3_3_3.py @@ -0,0 +1,756 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +class Zeus_8_3_3_3(IStrategy): + + # ROI table: + minimal_roi = { + "0": 0.564, + "567": 0.273, + "2814": 0.12, + "7675": 0 + } + + # Stoploss: + stoploss = -1 #0.256 + + # Buy hypers + timeframe = '5m' + + market_overview = {'up': 0, 'down': 0} + market_overview_pct5 = 0 + market_overview_pct1 = 0 + max_open_trades = 5 + max_amount = 40 + stop_buy_for_all = False + stop_buying = {} + + # DCA config + position_adjustment_enable = True + #max_dca_orders = 2 # n - 1 + max_dca_multiplier = 7 # (2^n - 1) + dca_trigger = 0 + + plot_config = { + "main_plot": { + "min200": { + "color": "#86c932" + }, + "min50": { + "color": "white" + }, + # "max200": { + # "color": "yellow" + # }, + "sma3_1d": { + "color": "pink" + }, + "sma5_1d": { + "color": "blue" + }, + "sma10_1d": { + "color": "orange" + }, + "close_1d": { + "color": "#73e233", + }, + "low": { + "color": "cyan", + }, + "bb_lowerband": { + "color": "#da59a6"}, + "bb_upperband": { + "color": "#da59a6", + } + }, + "subplots": { + # "Ind": { + # "trend_ichimoku_base": { + # "color": "#dd1384" + # }, + # "trend_kst_diff": { + # "color": "#850678" + # } + # }, + # "BB": { + # "bb_width": { + # "color": "white" + # }, + # "bb_lower_5": { + # "color": "yellow" + # } + # }, + "Rsi": { + "rsi_1d": { + "color": "pink" + }, + # "rsi_1h": { + # "color": "green" + # }, + "rsi5": { + "color": "yellow" + }, + "rsi3_1d": { + "color": "red" + } + }, + # "Percent": { + # "pct_change_1_1d": { + # "color": "green" + # }, + # "pct_change_3_1d": { + # "color": "orange" + # }, + # "pct_change_5_1d": { + # "color": "red" + # } + # } + } + } + trades = list() + + buy_min_horizon = IntParameter(50, 200, default=72, space='buy') + + buy_rsi = IntParameter(20, 90, default=72, space='buy') + buy_bb_width_n = DecimalParameter(1, 10, decimals=1, default=5, space='buy') + + buy_min_max_nh = IntParameter(1, 48, default=24, space='buy') + buy_min_max_n = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + buy_min_max_rsi = IntParameter(50, 90, default=72, space='buy') + buy_min_max_coef = DecimalParameter(1, 1.01, decimals=3, default=1.002, space='buy') + buy_min_max_decalage = IntParameter(2, 10, default=2, space='buy') + + profit_b_no_change = BooleanParameter(default=True, space="sell") + profit_b_quick_lost = BooleanParameter(default=True, space="sell") + profit_b_sma5 = BooleanParameter(default=True, space="sell") + profit_b_sma10 = BooleanParameter(default=True, space="sell") + profit_b_sma20 = BooleanParameter(default=True, space="sell") + profit_b_quick_gain = BooleanParameter(default=True, space="sell") + profit_b_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_b_old_sma10 = BooleanParameter(default=True, space="sell") + profit_b_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_b_over_rsi = BooleanParameter(default=True, space="sell") + profit_b_short_loss = BooleanParameter(default=True, space="sell") + + sell_b_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_b_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_b_candels = IntParameter(0, 48, default=12, space='sell') + + sell_b_too_old_day = IntParameter(0, 10, default=5, space='sell') + sell_b_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + sell_b_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') + sell_b_profit_percent10 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') + + sell_b_RSI = IntParameter(70, 98, default=88, space='sell') + sell_b_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_b_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_b_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + # sell_b_expected_profit = DecimalParameter(0, 0.01, decimals=3, default=0.01, space='sell') + + profit_h_no_change = BooleanParameter(default=True, space="sell") + profit_h_quick_lost = BooleanParameter(default=True, space="sell") + profit_h_sma5 = BooleanParameter(default=True, space="sell") + profit_h_sma10 = BooleanParameter(default=True, space="sell") + profit_h_sma20 = BooleanParameter(default=True, space="sell") + profit_h_quick_gain = BooleanParameter(default=True, space="sell") + profit_h_quick_gain_3 = BooleanParameter(default=True, space="sell") + profit_h_old_sma10 = BooleanParameter(default=True, space="sell") + profit_h_very_old_sma10 = BooleanParameter(default=True, space="sell") + profit_h_over_rsi = BooleanParameter(default=True, space="sell") + profit_h_short_loss = BooleanParameter(default=True, space="sell") + + sell_h_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_percent3 = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + sell_h_candels = IntParameter(0, 48, default=12, space='sell') + + sell_h_too_old_day = IntParameter(0, 10, default=5, space='sell') + sell_h_too_old_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + sell_h_profit_no_change = DecimalParameter(0, 0.02, decimals=3, default=0.005, space='sell') + sell_h_profit_percent10 = DecimalParameter(0, 0.002, decimals=4, default=0.001, space='sell') + + sell_h_RSI = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI2 = IntParameter(70, 98, default=88, space='sell') + sell_h_RSI3 = IntParameter(70, 98, default=80, space='sell') + + sell_h_RSI2_percent = DecimalParameter(0, 0.02, decimals=3, default=0.01, space='sell') + + protection_percent_buy_lost = IntParameter(1, 30, default=3, space='protection') + protection_nb_buy_lost = IntParameter(1, 3, default=3, space='protection') + + def calc_profit(self, price: float, current: float) -> float: + fee = 1.0007 + profit = ((current*fee) - + (price*fee)) + + return float(f"{profit:.8f}") + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + allow_to_buy = True + # info_previous_last_candle = informative.iloc[-2].squeeze() + + if self.stop_buying.get(pair, None) is None: + print("enable buying tag", pair) + self.stop_buying[pair] = False + + informative, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + info_last_candle = informative.iloc[-1].squeeze() + + if ((info_last_candle['rsi_1h'] >= 76) & (info_last_candle['close_1h'] >= info_last_candle['bb_upperband_1h'])) \ + & (self.stop_buying[pair] is False): + logger.info("1 - Disable buying %s date %s", pair, info_last_candle['date']) + self.stop_buying[pair] = True + + if self.stop_buying[pair] is True: + if ((info_last_candle['rsi_1h'] <= 35) | (info_last_candle['close_1h'] < info_last_candle['bb_lowerband_1h'])): + logger.info("2 - Enable buying %s date %s", pair, info_last_candle['date']) + self.stop_buying[pair] = False + + if self.stop_buying[pair]: + allow_to_buy = False + logger.info("3 - cancel buying %s date %s", pair, info_last_candle['date']) + else: + logger.info("3 - accept buying %s date %s", pair, info_last_candle['date']) + return allow_to_buy + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_previous_last_candle = dataframe.iloc[-2].squeeze() + + previous_5_candle = dataframe.iloc[-5].squeeze() + + return (previous_last_candle['rsi5'] > 60) \ + & (previous_last_candle['rsi5'] >= last_candle['rsi5']) \ + & (previous_last_candle['rsi5'] >= previous_previous_last_candle['rsi5']) \ + & (last_candle['close'] > last_candle['close_1d']) & (last_candle['percent'] < 0) + + # expected_profit = 0.01 + # #print(last_candle['buy_tag']) + # + # days = (current_time - trade.open_date_utc).days + # ###### + # + # if (last_candle['mrsi3_1h'] < previous_last_candle['mrsi3_1h']): #(self.market_overview_pct5 < 0) | (last_candle['pct_change_1_4h'] < 0): + # max_percent = 0.004 # last_candle['bb_width'] / 3.5 # 0.005 + # max_profit = 0.004 # last_candle['bb_width'] * 3 / 4 # 0.015 + # + # if (current_profit > 0.01) & \ + # (last_candle['percent10'] < -0.005) & ((current_time - trade.open_date_utc).seconds >= 3600): + # return 'b_percent10' + # if (current_profit > max_profit) & \ + # ((last_candle['percent'] < - max_percent) | (last_candle['percent3'] < -max_percent) | ( + # last_candle['percent5'] < -max_percent)): + # return 'b_percent_quick' + # + # if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + # & (days < self.sell_b_too_old_day.value * 2)\ + # & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + # return "b_too_old_0.01" + # if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + # & (days < self.sell_b_too_old_day.value * 3) \ + # & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + # return "b_too_old_0.02" + # if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + # & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + # return "b_too_old_0.03" + # + # if self.profit_b_quick_lost.value and (current_profit >= max_profit) & ( + # last_candle['percent3'] < -max_percent): + # return "b_quick_lost" + # + # if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + # & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600): + # return "b_no_change" + # + # if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + # & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + # return "b_quick_gain_param" + # + # if self.profit_b_sma5.value: + # if (current_profit > expected_profit) \ + # & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + # | (last_candle['percent3'] < -expected_profit) | ( + # last_candle['percent5'] < -expected_profit)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'b_sma5' + # + # if self.profit_b_sma10.value: + # if (current_profit > expected_profit) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + # | (last_candle['percent3'] < -expected_profit) | ( + # last_candle['percent5'] < -expected_profit)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'b_sma10' + # + # if self.profit_b_sma20.value: + # if (current_profit > max_percent) \ + # & (previous_last_candle['sma10'] > last_candle['sma10']) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600) \ + # & ((previous_last_candle['sma20'] > last_candle['sma20']) & + # ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + # last_candle['percent20'] < 0))): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'b_sma20' + # + # if self.profit_b_over_rsi.value: + # if (current_profit > 0) & (previous_last_candle[ + # 'rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'b_over_rsi' + # + # if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + # (last_candle[ + # 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'b_over_rsi_2' + # + # if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + # (last_candle['close'] >= last_candle['max200']) & (last_candle[ + # 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'b_over_rsi_max' + # + # if self.profit_b_short_loss.value: + # if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + # last_candle['percent'] < 0) \ + # & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'b_short_lost' + # else: + # max_percent = 0.005 # last_candle['bb_width'] / 3.5 # 0.005 + # max_profit = 0.01 # last_candle['bb_width'] * 3 / 4 # 0.015 + # + # if (current_profit > max_profit) & ( + # (last_candle['percent'] < -max_percent) | (last_candle['percent3'] < -max_percent) | ( + # last_candle['percent5'] < -max_percent)): + # return 'h_percent_quick' + # + # # if (last_candle['bb_width'] < 0.02) & (current_profit > 0) & (last_candle['close'] > bb_width_up) & \ + # # ((last_candle['percent'] < - bb_width_lim) | (last_candle['percent3'] < - bb_width_lim) | (last_candle['percent5'] < - bb_width_lim)): + # # return 'h_bb_width_max' + # + # if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + # & (days < self.sell_h_too_old_day.value * 2)\ + # & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + # return "h_too_old_0.01" + # if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + # & (days < self.sell_h_too_old_day.value * 3) \ + # & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + # return "h_too_old_0.02" + # if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + # & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + # return "h_too_old_0.03" + # + # if self.profit_h_quick_lost.value and (current_profit >= max_profit) & ( + # last_candle['percent3'] < -max_percent): + # return "h_quick_lost" + # + # if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + # & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600): + # return "h_no_change" + # + # if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + # & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + # return "h_quick_gain_param" + # + # if self.profit_h_sma5.value: + # if (current_profit > expected_profit) \ + # & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + # | (last_candle['percent3'] < -expected_profit) | ( + # last_candle['percent5'] < -expected_profit)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'h_sma5' + # + # if self.profit_h_sma10.value: + # if (current_profit > expected_profit) \ + # & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + # | (last_candle['percent3'] < -expected_profit) | ( + # last_candle['percent5'] < -expected_profit)) \ + # & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'h_sma10' + # + # if self.profit_h_sma20.value: + # if (current_profit > max_percent) \ + # & (previous_last_candle['sma10'] > last_candle['sma10']) \ + # & ((current_time - trade.open_date_utc).seconds >= 3600) \ + # & ((previous_last_candle['sma20'] > last_candle['sma20']) & + # ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + # last_candle['percent20'] < 0))): + # # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'h_sma20' + # + # if self.profit_h_over_rsi.value: + # if (current_profit > 0) & (previous_last_candle[ + # 'rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'h_over_rsi' + # + # if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + # (last_candle[ + # 'percent'] < - self.sell_h_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'h_over_rsi_2' + # + # if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + # (last_candle['close'] >= last_candle[ + # 'max200']): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'h_over_rsi_max' + # + # if self.profit_h_short_loss.value: + # if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + # last_candle['percent'] < 0) \ + # & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + # return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + # informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_1'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_n'] = talib.MIN(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['max_n'] = talib.MAX(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + # dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / self.buy_bb_width_n.value)) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['min_max_close'] = ( + (dataframe['max200'] - dataframe['close']) / (dataframe['close'] - dataframe['min200'])) + + # profit = False + # profit_percent = False + # percent_lower = False + # current_price = dataframe['close'].iloc[-1] + # + # dataframe['should_sell'] = False + # dataframe['should_buy'] = False + # + # # Get the previous trade + # trade = Trade.get_trades_proxy(is_open=False, pair=metadata['pair']) + # if trade: + # trade = trade[-1] + # lsp = trade.close_rate + # if lsp: + # percent_lower = self.calc_percentage_lower(price=lsp, current=current_price) + # # Found a bug? When force selling it doesnt close it + # else: + # lsp = trade.open_rate + # if lsp: + # percent_lower = self.calc_percentage_lower(price=lsp, current=current_price) + # else: + # lsp = 0.00 + # + # # Get the current Trade + # trade = Trade.get_trades_proxy(is_open=True, pair=metadata['pair']) + # if trade: + # trade = trade[-1] + # lbp = trade.open_rate + # open_trade = True + # profit = self.calc_profit(price=lbp, current=current_price) + # profit_percent = (profit/lbp)*100 + # else: + # lbp = 0.00 + # open_trade = False + # profit = False + # profit_percent = False + # + # print("------------") + # + # print("Last Sold For:", lsp) + # + # if open_trade: + # print("Bought for: ", lbp) + # print("Current Price: ", current_price) + # if profit: + # print("Current Profit: ", profit, " ", float(f"{profit_percent:.8f}"), "%") + # if percent_lower and not open_trade: + # print("Percent Lower: ", float(f"{percent_lower:.8f}"), "%") + # + # # Should we Sell? + # if profit_percent: + # if profit_percent > 1: + # dataframe['should_sell'] = True + # + # # Should we buy? + # if not open_trade: + # if (lsp == 0.00 ) & (lbp == 0.00): + # dataframe['should_buy'] = True + # # Is the percentage of what we sold for and the current price 2% lower + # if percent_lower > 2: + # dataframe['should_buy'] = True + # + # dataframe['last_sell_price'] = lsp + # dataframe['last_buy_price'] = lbp + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["rsi3"] = talib.RSI(informative, 3) + informative["mrsi3"] = informative["rsi"].rolling(3).mean() + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + # ######################## INFORMATIVE 4h + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + # informative["rsi"] = talib.RSI(informative) + # informative['pct_change_1'] = informative['close'].pct_change(1) + # informative['pct_change_3'] = informative['close'].pct_change(3) + # informative['pct_change_5'] = informative['close'].pct_change(5) + # informative['sma3'] = talib.SMA(informative, timeperiod=3) + # informative['sma5'] = talib.SMA(informative, timeperiod=5) + # informative['sma10'] = talib.SMA(informative, timeperiod=10) + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ######################## INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative["rsi3"] = talib.RSI(informative, 3) + informative["mrsi3"] = informative["rsi"].rolling(3).mean() + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + # (dataframe['min_max_n'] >= self.buy_min_max_n.value) + (dataframe['rsi5'].shift(1) < 12) + & (dataframe['rsi5'].shift(1) < dataframe['rsi5']) + & (dataframe['rsi5'].shift(1) < dataframe['rsi5'].shift(2)) + # & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + #& (dataframe['min_n'].shift(self.buy_min_max_decalage.value) == dataframe['min_n']) + # & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['close'] <= dataframe['close_1d']) + & (dataframe['close_1d'] <= dataframe['sma3_1d']) + & (dataframe['close_1d'] <= dataframe['sma5_1d']) + & (dataframe['close_1d'] <= dataframe['sma10_1d']) + & (dataframe['low'] <= dataframe['min200']) + & (dataframe['min'].shift(2) == dataframe['min50']) + # & (dataframe['rsi3_1d'].shift(288) <= dataframe['rsi3_1d']) + ), ['buy', 'buy_tag']] = (1, 'buy_rsi5') + + return dataframe + + # def bot_loop_start(self, **kwargs) -> None: + # pairs = self.dp.current_whitelist() + # print("Calcul des pairs informatives") + # for pairname in pairs: + # self.stop_buying[pairname] = True + # print("Fin Calcul des pairs informatives") + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['rsi5'].shift(1) > 60) + # & (dataframe['rsi5'].shift(1) >= dataframe['rsi5']) + # & (dataframe['rsi5'].shift(1) >= dataframe['rsi5'].shift(2)) + # & (dataframe['close'] > dataframe['close_1d']) + # # & (dataframe['close_1d'] > dataframe['sma3_1d']) + # # & (dataframe['close_1d'] > dataframe['sma5_1d']) + # # & (dataframe['close_1d'] > dataframe['sma10_1d']) + # # & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + # + # # & (dataframe['rsi3_1d'].shift(288) > dataframe['rsi3_1d']) + # # (dataframe['close_1d'] > dataframe['sma3_1d']) + # # & (dataframe['rsi3_1d'] > 72) + # #& (dataframe['last_buy_price'] < (dataframe['close'])) + # #& (dataframe['should_sell'] == True) + # ), ['sell', 'sell_tag']] = (1, 'sell_close_1d') + # print("dans sell" + metadata['pair']) + return dataframe + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + if (len(dataframe) < 1): + return None + + # dataframe = self.populate_buy_trend(dataframe, {'pair': trade.pair}) + # if not (self.populate_buy_trend(dataframe, {'pair': trade.pair})): + # return None + if (self.stop_buying.get(trade.pair, None) == None): + print("-----------" + trade.pair + " Init stop buying " + str(current_profit) + " " + str(current_time) + "---------------------") + self.stop_buying[trade.pair] = True + + if (self.stop_buying[trade.pair] == True): + print("-----------" + trade.pair + " Canceled " + str(current_profit) + " " + str(current_time) + "---------------------") + return None + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_previous_last_candle = dataframe.iloc[-3].squeeze() + + # last_candle_5 = dataframe.iloc[-3].squeeze() + # last_candle_decalage = dataframe.iloc[- self.buy_min_max_decalage.value].squeeze() + # print(last_candle['buy']) + + condition = ( + # (last_candle['min_max_n'] >= self.buy_min_max_n.value) + (previous_last_candle['rsi5'] < 12) + & (previous_last_candle['rsi5'] < last_candle['rsi5']) + & (previous_last_candle['rsi5'] < previous_previous_last_candle['rsi5']) + # & (last_candle['close'] < last_candle['min_n'] * self.buy_min_max_coef.value) + & (last_candle['close'] <= last_candle['close_1d']) + & (last_candle['close_1d'] <= last_candle['sma3_1d']) + & (last_candle['close_1d'] <= last_candle['sma5_1d']) + & (last_candle['close_1d'] <= last_candle['sma10_1d']) + # & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + ) + if not (condition): + return None + + # min_d = min(last_candle['sma3_4h'], last_candle['close_1d']) + + filled_buys = trade.select_filled_orders('buy') + count_of_buys = len(filled_buys) + # days = (current_time - trade.open_date_utc).days + # minutes = (current_time - trade.open_date_utc).seconds / 60 + # condition = condition & ((last_candle['min50'] == last_candle_5['min50']) & (last_candle['close'] <= last_candle['close_1h'])) + p = self.protection_percent_buy_lost.value + percents = [p, p * 2, p * 3, p * 4, p * 5, p * 6, p * 7, p * 8, p * 9] + + if (0 < count_of_buys <= self.protection_nb_buy_lost.value) \ + & (current_profit < - (percents[count_of_buys - 1] / 100)) & (condition): + try: + p = self.config['stake_amount'] + factors = [p, p, p, p, 2 * p, 4 * p, 5 * p, 6 * p] + + stake_amount = factors[count_of_buys - 1]# filled_buys[0].cost + # This then calculates current safety order size + print("-----------" + trade.pair + " " + str(current_profit) + " " + str(count_of_buys) + " " + str(stake_amount) + + " " + str(current_time) + "---------------------") + return stake_amount + except Exception as exception: + print(exception) + return None + return None diff --git a/Zeus_8_4h.json b/Zeus_8_4h.json new file mode 100644 index 0000000..9d3fbf9 --- /dev/null +++ b/Zeus_8_4h.json @@ -0,0 +1,40 @@ +{ + "strategy_name": "Zeus_8_4h", + "params": { + "roi": { + "0": 10 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": null, + "trailing_stop_positive_offset": 0.0, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_min_horizon": 109, + "buy_min_horizon_4h": 30, + "buy_min_max_n": 0.06, + "buy_min_max_n_4h": 0.11, + "buy_percent_4h": 1.02, + "buy_rsi": 26, + "buy_rsi_4h": 24, + "decalage": 9, + "decalage_4h": 10 + }, + "sell": { + "sell_b_RSI": 89, + "sell_percent": 0.25, + "sell_percent3_quick": 0.16, + "sell_profit_percent": 0.9 + }, + "protection": { + "protection_start_buying_rsi_4h": 40, + "protection_stop_buying_rsi_4h": 99 + }, + "stoploss": { + "stoploss": -0.235 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-05 18:50:45.473684+00:00" +} \ No newline at end of file diff --git a/Zeus_8_4h.jsonProfit b/Zeus_8_4h.jsonProfit new file mode 100644 index 0000000..6a5634f --- /dev/null +++ b/Zeus_8_4h.jsonProfit @@ -0,0 +1,44 @@ +{ + "strategy_name": "Zeus_8_4h", + "params": { + "roi": { + "0": 10 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": null, + "trailing_stop_positive_offset": 0.0, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_min_horizon": 238, + "buy_min_horizon_4h": 106, + "buy_min_max_n": 0.16, + "buy_min_max_n_4h": 0.15, + "buy_percent_4h": 1.004, + "buy_rsi": 7, + "buy_rsi_4h": 20, + "decalage_4h": 5 + }, + "sell": { + "sell_b_RSI": 93, + "sell_percent": 0.17, + "sell_percent10_quick": 0.19, + "sell_percent3_quick": 0.07, + "sell_percent5_quick": 0.21, + "sell_percent_quick": 0.1, + "sell_profit_percent": 1.3 + }, + "protection": { + "protection_nb_buy_lost": 2, + "protection_percent_buy_lost": 26, + "protection_start_buying_rsi_4h": 49, + "protection_stop_buying_rsi_4h": 68 + }, + "stoploss": { + "stoploss": -0.234 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-05 01:29:52.992774+00:00" +} \ No newline at end of file diff --git a/Zeus_8_4h.py b/Zeus_8_4h.py new file mode 100644 index 0000000..42ad24f --- /dev/null +++ b/Zeus_8_4h.py @@ -0,0 +1,409 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +class Zeus_8_4h(IStrategy): + + # ROI table: + minimal_roi = { + "0": 10 + # 0.564, + # "567": 0.273, + # "2814": 0.12, + # "7675": 0 + } + + # Stoploss: + stoploss = -1 #0.256 + + # Buy hypers + timeframe = '5m' + + stop_buying = {} + + # DCA config + # position_adjustment_enable = True + + plot_config = { + "main_plot": { + "min200": { + "color": "#86c932" + }, + "min50": { + "color": "white" + }, + # "max200": { + # "color": "yellow" + # }, + "sma3_1d": { + "color": "pink" + }, + "sma5_1d": { + "color": "blue" + }, + "sma10_1d": { + "color": "orange" + }, + "close_1d": { + "color": "#73e233", + }, + "low": { + "color": "cyan", + }, + "bb_lowerband": { + "color": "#da59a6"}, + "bb_upperband": { + "color": "#da59a6", + } + }, + "subplots": { + # "Ind": { + # "trend_ichimoku_base": { + # "color": "#dd1384" + # }, + # "trend_kst_diff": { + # "color": "#850678" + # } + # }, + # "BB": { + # "bb_width": { + # "color": "white" + # }, + # "bb_lower_5": { + # "color": "yellow" + # } + # }, + "Rsi": { + "rsi_1d": { + "color": "pink" + }, + # "rsi_1h": { + # "color": "green" + # }, + "rsi5": { + "color": "yellow" + }, + "rsi3_1d": { + "color": "red" + } + }, + # "Percent": { + # "pct_change_1_1d": { + # "color": "green" + # }, + # "pct_change_3_1d": { + # "color": "orange" + # }, + # "pct_change_5_1d": { + # "color": "red" + # } + # } + } + } + trades = list() + + decalage = IntParameter(1, 12, default=6, space='buy') + buy_min_horizon = IntParameter(50, 288, default=72, space='buy') + buy_rsi = IntParameter(1, 30, default=12, space='buy') + buy_min_max_n = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + # adx_1d_limit = IntParameter(15, 45, default=18, space='buy') + sell_b_RSI = IntParameter(70, 98, default=60, space='sell') + sell_profit_percent = DecimalParameter(0.1, 1.5, decimals=1, default=0.8, space='sell') + + # sell_percent_quick = DecimalParameter(0.01, 0.30, decimals=2, default=0.05, space='sell') + sell_percent3_quick = DecimalParameter(0.01, 0.30, decimals=2, default=0.05, space='sell') + # sell_percent5_quick = DecimalParameter(0.01, 0.30, decimals=2, default=0.05, space='sell') + # sell_percent10_quick = DecimalParameter(0.01, 0.30, decimals=2, default=0.05, space='sell') + + sell_percent = DecimalParameter(0.01, 0.30, decimals=2, default=0.05, space='sell') + # protection_percent_buy_lost = IntParameter(1, 30, default=3, space='protection') + # protection_nb_buy_lost = IntParameter(1, 3, default=3, space='protection') + # protection_stop_buying_rsi_1d = IntParameter(50, 100, default=76, space='protection') + # protection_start_buying_rsi_1d = IntParameter(1, 50, default=30, space='protection') + + # 4h + protection_stop_buying_rsi_4h = IntParameter(50, 100, default=76, space='protection') + protection_start_buying_rsi_4h = IntParameter(1, 50, default=30, space='protection') + buy_min_horizon_4h = IntParameter(1, 120, default=72, space='buy') + buy_rsi_4h = IntParameter(1, 30, default=12, space='buy') + buy_min_max_n_4h = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + buy_percent_4h = DecimalParameter(1.005, 1.02, decimals=3, default=1.01, space='buy') + decalage_4h = IntParameter(1, 12, default=6, space='buy') + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_previous_last_candle = dataframe.iloc[-3].squeeze() + + ret = None + if (current_profit > 0.01) \ + & (previous_last_candle['rsi5'] > self.sell_b_RSI.value) \ + & (previous_last_candle['rsi5'] >= last_candle['rsi5']) \ + & (previous_last_candle['rsi5'] >= previous_previous_last_candle['rsi5']): + ret = 'profit_1' # + str(self.sell_percent.value) + + # if (last_candle['percent'] < - self.sell_percent_quick.value): + # ret = 'quick_percent' + + if (last_candle['percent3'] < - self.sell_percent3_quick.value): + ret = 'quick_percent3' + + # if (last_candle['percent5'] < - self.sell_percent5_quick.value): + # ret = 'quick_percent5' + # + # if (last_candle['percent10'] < - self.sell_percent10_quick.value): + # ret = 'quick_percent10' + + if ret: + logger.info("Sell ==> %s ", ret + " " + pair + " " + str(current_time) + " " + str(current_profit)) + #+ " " + str(self.wallets.get_total_stake_amount())) + return ret + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + # informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs += [(pair, '4h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + # dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + # dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + # dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + # dataframe['min200_1'] = dataframe['min200'] * 1.005 + # dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_n'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['max_n'] = talib.MAX(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + # dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + # dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + # dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + # dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + # dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + # dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + # dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + # dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + # dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + # dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / self.buy_bb_width_n.value)) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['min_max_close'] = ( + (dataframe['max200'] - dataframe['close']) / (dataframe['close'] - dataframe['min200'])) + + # dataframe['stop_buying'] = (dataframe['rsi5'] >= self.protection_stop_buying_rsi_1d.value) \ + # & (dataframe['close'] >= dataframe['bb_upperband']) + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["rsi3"] = talib.RSI(informative, 3) + # informative["mrsi3"] = informative["rsi"].rolling(3).mean() + # informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + # informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + # informative['pct_change_1'] = informative['close'].pct_change(1) + # informative['pct_change_3'] = informative['close'].pct_change(3) + # informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + # informative['adx'] = talib.ADX(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + + # informative["bb_width"] = ( + # (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + # ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + # ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['rsi5'] = talib.RSI(informative, timeperiod=5) + informative['min'] = talib.MIN(informative['close'], timeperiod=self.buy_min_horizon_4h.value) + informative['min_n'] = talib.MIN(informative['close'], timeperiod=self.buy_min_horizon_4h.value) + informative['max_n'] = talib.MAX(informative['close'], timeperiod=self.buy_min_horizon_4h.value) + informative['min_max_n'] = (informative['max_n'] - informative['min_n']) / informative['min_n'] + informative['allow_to_buy'] = (informative['rsi5'] < self.protection_stop_buying_rsi_4h.value) \ + & (informative['rsi5'] > self.protection_start_buying_rsi_4h.value) \ + & (informative['min_n'].shift(self.decalage_4h.value) == informative['min_n'])\ + & (informative['min_max_n'] >= self.buy_min_max_n.value) + # informative['allow_to_sell'] = ( + # (informative['rsi5'] > self.protection_stop_buying_rsi_4h.value) \ + # | (informative['rsi5'] < self.protection_start_buying_rsi_4h.value) + # ) + # & (informative['close'] <= informative['min_n'] * self.buy_percent_4h.value) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + dataframe['allow_to_buy'] = (dataframe['rsi5'].shift(1) < self.buy_rsi.value) \ + & (dataframe['rsi5'].shift(1) < dataframe['rsi5']) \ + & (dataframe['rsi5'].shift(1) < dataframe['rsi5'].shift(2)) \ + & (dataframe['min_n'].shift(6) == dataframe['min_n']) \ + & (dataframe['min_max_n'] >= self.buy_min_max_n.value) + + ######################## INFORMATIVE 1h + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + # informative["rsi"] = talib.RSI(informative) + # informative["rsi3"] = talib.RSI(informative, 3) + # informative["mrsi3"] = informative["rsi"].rolling(3).mean() + # informative['pct_change_1'] = informative['close'].pct_change(1) + # informative['pct_change_3'] = informative['close'].pct_change(3) + # informative['pct_change_5'] = informative['close'].pct_change(5) + # informative['sma3'] = talib.SMA(informative, timeperiod=3) + # informative['sma5'] = talib.SMA(informative, timeperiod=5) + # informative['sma10'] = talib.SMA(informative, timeperiod=10) + # informative['adx'] = talib.ADX(informative) + # bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + # informative['bb_lowerband'] = bollinger['lower'] + # informative['bb_middleband'] = bollinger['mid'] + # informative['bb_upperband'] = bollinger['upper'] + # + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + (dataframe['allow_to_buy_4h']) + # & (dataframe['rsi5'] < self.protection_stop_buying_rsi_1d.value) + # & (dataframe['rsi5'] > self.protection_start_buying_rsi_1d.value) + & (dataframe['rsi5'].shift(1) < self.buy_rsi.value) + & (dataframe['rsi5'].shift(1) < dataframe['rsi5']) + & (dataframe['rsi5'].shift(1) < dataframe['rsi5'].shift(2)) + & (dataframe['min_n'].shift(self.decalage.value) == dataframe['min_n']) + & (dataframe['min_max_n'] >= self.buy_min_max_n.value) + ), ['buy', 'buy_tag']] = (1, 'buy_rsi5') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['rsi5'].shift(1) > self.sell_b_RSI.value) + # & (dataframe['rsi5'].shift(1) >= dataframe['rsi5']) + # & (dataframe['rsi5'].shift(1) >= dataframe['rsi5'].shift(2)) + # & (dataframe['close'] > dataframe['close_1d']) + # & (dataframe['close_1d'] > dataframe['sma3_1d']) + # & (dataframe['close_1d'] > dataframe['sma5_1d']) + # & (dataframe['close_1d'] > dataframe['sma10_1d']) + # # & (dataframe['rsi3_1d'].shift(288) > dataframe['rsi3_1d']) + # # (dataframe['close_1d'] > dataframe['sma3_1d']) + # # & (dataframe['rsi3_1d'] > 72) + # #& (dataframe['last_buy_price'] < (dataframe['close'])) + # #& (dataframe['should_sell'] == True) + # ), ['sell', 'sell_tag']] = (1, 'sell_close_1d') + # print("dans sell" + metadata['pair']) + return dataframe + + # def adjust_trade_position(self, trade: Trade, current_time: datetime, + # current_rate: float, current_profit: float, min_stake: float, + # max_stake: float, **kwargs): + # dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + # if (len(dataframe) < 1) | (current_profit > - 0.2): + # return None + # + # last_candle = dataframe.iloc[-1].squeeze() + # + # condition = ( + # (last_candle['allow_to_buy_4h']) + # & (last_candle['allow_to_buy']) + # ) + # if not (condition): + # return None + # # print(trade.pair, current_profit, last_candle['allow_to_buy_4h'], last_candle['allow_to_buy']) + # + # filled_buys = trade.select_filled_orders('buy') + # count_of_buys = len(filled_buys) + # + # p = self.protection_percent_buy_lost.value + # percents = [p, p * 2, p * 3, p * 4, p * 5, p * 6, p * 7, p * 8, p * 9] + # + # if (0 < count_of_buys <= self.protection_nb_buy_lost.value) \ + # & (current_profit < - 0.2) & (condition): #(percents[count_of_buys - 1] / 100)) + # try: + # p = self.config['stake_amount'] + # factors = [p, p, p, p, 2 * p, 4 * p, 5 * p, 6 * p] + # + # stake_amount = factors[count_of_buys - 1]# filled_buys[0].cost + # return stake_amount + # except Exception as exception: + # print(exception) + # return None + # return None diff --git a/Zeus_8d.json b/Zeus_8d.json new file mode 100644 index 0000000..9a19b42 --- /dev/null +++ b/Zeus_8d.json @@ -0,0 +1,36 @@ +{ + "strategy_name": "Zeus_8d", + "params": { + "roi": { + "0": 5 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.068, + "trailing_stop_positive_offset": 0.155, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_min_horizon": 96, + "buy_min_max_n": 0.14, + "buy_rsi": 30, + "decalage": 7 + }, + "sell": { + "sell_b_RSI": 86, + "sell_percent": 0.13, + "sell_profit_percent": 0.6 + }, + "protection": { + "protection_nb_buy_lost": 3, + "protection_percent_buy_lost": 4, + "protection_start_buying_rsi_1d": 22, + "protection_stop_buying_rsi_1d": 64 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-03 15:13:16.143482+00:00" +} \ No newline at end of file diff --git a/Zeus_8d.jsonstoploss b/Zeus_8d.jsonstoploss new file mode 100644 index 0000000..6f2eb9c --- /dev/null +++ b/Zeus_8d.jsonstoploss @@ -0,0 +1,36 @@ +{ + "strategy_name": "Zeus_8d", + "params": { + "roi": { + "0": 5 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.068, + "trailing_stop_positive_offset": 0.155, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_min_horizon": 33, + "buy_min_max_n": 0.19, + "buy_rsi": 28, + "decalage": 8 + }, + "sell": { + "sell_b_RSI": 86, + "sell_percent": 0.13, + "sell_profit_percent": 0.9 + }, + "protection": { + "protection_nb_buy_lost": 1, + "protection_percent_buy_lost": 5, + "protection_start_buying_rsi_1d": 19, + "protection_stop_buying_rsi_1d": 83 + }, + "stoploss": { + "stoploss": -1 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-03 14:11:59.654713+00:00" +} \ No newline at end of file diff --git a/Zeus_8d.py b/Zeus_8d.py new file mode 100644 index 0000000..5170e34 --- /dev/null +++ b/Zeus_8d.py @@ -0,0 +1,437 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +class Zeus_8d(IStrategy): + + # ROI table: + minimal_roi = { + "0": 10 + # 0.564, + # "567": 0.273, + # "2814": 0.12, + # "7675": 0 + } + + # Stoploss: + stoploss = -1 #0.256 + + # Buy hypers + timeframe = '4h' + + stop_buying = {} + + # DCA config + position_adjustment_enable = True + + plot_config = { + "main_plot": { + "min200": { + "color": "#86c932" + }, + "min50": { + "color": "white" + }, + # "max200": { + # "color": "yellow" + # }, + "sma3_1d": { + "color": "pink" + }, + "sma5_1d": { + "color": "blue" + }, + "sma10_1d": { + "color": "orange" + }, + "close_1d": { + "color": "#73e233", + }, + "low": { + "color": "cyan", + }, + "bb_lowerband": { + "color": "#da59a6"}, + "bb_upperband": { + "color": "#da59a6", + } + }, + "subplots": { + # "Ind": { + # "trend_ichimoku_base": { + # "color": "#dd1384" + # }, + # "trend_kst_diff": { + # "color": "#850678" + # } + # }, + # "BB": { + # "bb_width": { + # "color": "white" + # }, + # "bb_lower_5": { + # "color": "yellow" + # } + # }, + "Rsi": { + "rsi_1d": { + "color": "pink" + }, + # "rsi_1h": { + # "color": "green" + # }, + "rsi5": { + "color": "yellow" + }, + "rsi3_1d": { + "color": "red" + } + }, + # "Percent": { + # "pct_change_1_1d": { + # "color": "green" + # }, + # "pct_change_3_1d": { + # "color": "orange" + # }, + # "pct_change_5_1d": { + # "color": "red" + # } + # } + } + } + trades = list() + + buy_min_horizon = IntParameter(1, 100, default=7, space='buy') + buy_rsi = IntParameter(1, 30, default=12, space='buy') + buy_min_max_n = DecimalParameter(0, 0.2, decimals=2, default=0.05, space='buy') + decalage = IntParameter(1, 12, default=6, space='buy') + # adx_1d_limit = IntParameter(15, 45, default=18, space='buy') + sell_b_RSI = IntParameter(70, 98, default=60, space='sell') + sell_profit_percent = DecimalParameter(0.1, 1.5, decimals=1, default=0.8, space='sell') + + sell_percent = DecimalParameter(0.01, 0.30, decimals=2, default=0.05, space='sell') + protection_percent_buy_lost = IntParameter(1, 30, default=3, space='protection') + protection_nb_buy_lost = IntParameter(1, 3, default=3, space='protection') + protection_stop_buying_rsi_1d = IntParameter(50, 100, default=76, space='protection') + protection_start_buying_rsi_1d = IntParameter(1, 50, default=30, space='protection') + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + allow_to_buy = True + # info_previous_last_candle = informative.iloc[-2].squeeze() + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_previous_last_candle = dataframe.iloc[-3].squeeze() + + if self.stop_buying.get(pair, None) is None: + print("enable buying tag", pair) + self.stop_buying[pair] = False + + if ((last_candle['rsi5'] >= self.protection_stop_buying_rsi_1d.value) | (last_candle['close'] >= last_candle['bb_upperband'])) \ + & (self.stop_buying[pair] is False): + logger.info("1 - Disable buying %s date %s", pair, last_candle['date']) + self.stop_buying[pair] = True + + if self.stop_buying[pair] is True: + if ((last_candle['rsi5'] <= self.protection_start_buying_rsi_1d.value) & (last_candle['percent5'] >= 0.005)): + logger.info("2 - Enable buying %s date %s", pair, last_candle['date']) + self.stop_buying[pair] = False + + logger.info("Buy ==> %s ", pair + " " + str(current_time) + "---------------------") + + if self.stop_buying[pair]: + allow_to_buy = False + logger.info("3 - cancel buying %s date %s", pair, str(last_candle['date']) + " " + str(last_candle['rsi5'])) + else: + logger.info("3 - accept buying %s date %s", pair, str(last_candle['date']) + " " + str(last_candle['rsi5'])) + return allow_to_buy + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_previous_last_candle = dataframe.iloc[-3].squeeze() + + if (current_profit > self.buy_min_max_n.value * self.sell_profit_percent.value) \ + & (previous_last_candle['rsi5'] > self.sell_b_RSI.value) \ + & (previous_last_candle['rsi5'] >= last_candle['rsi5']) \ + & (previous_last_candle['rsi5'] >= previous_previous_last_candle['rsi5']): + logger.info("Sell ==> %s ", pair + " " + str(current_time) + str(current_profit) + " " + str(current_rate)) + + return 'profit_1' # + str(self.sell_percent.value) + + # def informative_pairs(self): + # # get access to all pairs available in whitelist. + # pairs = self.dp.current_whitelist() + # informative_pairs = [(pair, '4h') for pair in pairs] + # # informative_pairs += [(pair, '4h') for pair in pairs] + # informative_pairs += [(pair, '1h') for pair in pairs] + # + # return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + # dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + # dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + # dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + # dataframe['min200_1'] = dataframe['min200'] * 1.005 + # dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_n'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['max_n'] = talib.MAX(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + # dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + # dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + # dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + # dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + # dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + # dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + # dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + # dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + # dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + # dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / self.buy_bb_width_n.value)) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['min_max_close'] = ( + (dataframe['max200'] - dataframe['close']) / (dataframe['close'] - dataframe['min200'])) + + # dataframe['stop_buying'] = (dataframe['rsi5'] >= self.protection_stop_buying_rsi_1d.value) \ + # & (dataframe['close'] >= dataframe['bb_upperband']) + + # ################### INFORMATIVE 1D + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + # informative["rsi"] = talib.RSI(informative) + # informative["rsi3"] = talib.RSI(informative, 3) + # # informative["mrsi3"] = informative["rsi"].rolling(3).mean() + # # informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + # # informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + # # informative['pct_change_1'] = informative['close'].pct_change(1) + # # informative['pct_change_3'] = informative['close'].pct_change(3) + # # informative['pct_change_5'] = informative['close'].pct_change(5) + # informative['sma3'] = talib.SMA(informative, timeperiod=3) + # informative['sma5'] = talib.SMA(informative, timeperiod=5) + # informative['sma10'] = talib.SMA(informative, timeperiod=10) + # # informative['adx'] = talib.ADX(informative) + # + # bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + # informative['bb_lowerband'] = bollinger['lower'] + # informative['bb_middleband'] = bollinger['mid'] + # informative['bb_upperband'] = bollinger['upper'] + # informative["bb_percent"] = ( + # (informative["close"] - informative["bb_lowerband"]) / + # (informative["bb_upperband"] - informative["bb_lowerband"]) + # ) + # + # # informative["bb_width"] = ( + # # (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + # # ) + # + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + # ######################## INFORMATIVE 4h + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + # informative["rsi"] = talib.RSI(informative) + # informative['pct_change_1'] = informative['close'].pct_change(1) + # informative['pct_change_3'] = informative['close'].pct_change(3) + # informative['pct_change_5'] = informative['close'].pct_change(5) + # informative['sma3'] = talib.SMA(informative, timeperiod=3) + # informative['sma5'] = talib.SMA(informative, timeperiod=5) + # informative['sma10'] = talib.SMA(informative, timeperiod=10) + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ######################## INFORMATIVE 1h + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + # informative["rsi"] = talib.RSI(informative) + # informative["rsi3"] = talib.RSI(informative, 3) + # informative["mrsi3"] = informative["rsi"].rolling(3).mean() + # informative['pct_change_1'] = informative['close'].pct_change(1) + # informative['pct_change_3'] = informative['close'].pct_change(3) + # informative['pct_change_5'] = informative['close'].pct_change(5) + # informative['sma3'] = talib.SMA(informative, timeperiod=3) + # informative['sma5'] = talib.SMA(informative, timeperiod=5) + # informative['sma10'] = talib.SMA(informative, timeperiod=10) + # informative['adx'] = talib.ADX(informative) + # bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + # informative['bb_lowerband'] = bollinger['lower'] + # informative['bb_middleband'] = bollinger['mid'] + # informative['bb_upperband'] = bollinger['upper'] + # + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + (dataframe['rsi5'] < self.protection_stop_buying_rsi_1d.value) + & (dataframe['rsi5'] > self.protection_start_buying_rsi_1d.value) + & (dataframe['rsi5'].shift(1) < self.buy_rsi.value) + & (dataframe['rsi5'].shift(1) < dataframe['rsi5']) + & (dataframe['rsi5'].shift(1) < dataframe['rsi5'].shift(2)) + # & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + & (dataframe['min_n'].shift(self.decalage.value) == dataframe['min_n']) + # & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['min_max_n'] >= self.buy_min_max_n.value) + # & (dataframe['percent'] > -0.005) + # & (dataframe['close'] <= dataframe['close_1d']) + # & (dataframe['close_1d'] <= dataframe['sma3_1d']) + # & (dataframe['close_1d'] <= dataframe['sma5_1d']) + # & (dataframe['close_1d'] <= dataframe['sma10_1d']) + # & (dataframe['adx_1d'] > self.adx_1d_limit.value) + # & (dataframe['rsi3_1d'].shift(288) <= dataframe['rsi3_1d']) + ), ['buy', 'buy_tag']] = (1, 'buy_adx_inf') + + return dataframe + + # def bot_loop_start(self, **kwargs) -> None: + # pairs = self.dp.current_whitelist() + # print("Calcul des pairs informatives") + # for pairname in pairs: + # self.stop_buying[pairname] = True + # print("Fin Calcul des pairs informatives") + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['percent10'] < -0.05) + # ), ['sell', 'sell_tag']] = (1, 'sell_percent10') + # dataframe.loc[ + # ( + # (dataframe['percent'] < -0.02) + # ), ['sell', 'sell_tag']] = (1, 'sell_percent') + + # print("dans sell" + metadata['pair']) + return dataframe + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + if (len(dataframe) < 1): + return None + + if (self.stop_buying.get(trade.pair, None) == None): + # logger.info("----------- %s ", trade.pair + " Init stop buying " + str(current_profit) + " " + str(current_time) + "---------------------") + self.stop_buying[trade.pair] = False + + if (self.stop_buying[trade.pair] == True): + # logger.info("----------- %s ", trade.pair + " Canceled " + str(current_profit) + " " + str(current_time) + "---------------------") + return None + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_previous_last_candle = dataframe.iloc[-3].squeeze() + last_candle_6 = dataframe.iloc[-7].squeeze() + + # last_candle_5 = dataframe.iloc[-3].squeeze() + # last_candle_decalage = dataframe.iloc[- self.buy_min_max_decalage.value].squeeze() + # print(last_candle['buy']) + + condition = ( + (previous_last_candle['rsi5'] < self.buy_rsi.value) + & (previous_last_candle['rsi5'] < last_candle['rsi5']) + & (previous_last_candle['rsi5'] < previous_previous_last_candle['rsi5']) + & (last_candle_6['min_n'] == last_candle['min_n']) + & (last_candle['min_max_n'] >= self.buy_min_max_n.value) + # & (last_candle['close'] < last_candle['min_n'] * self.buy_min_max_coef.value) + # & (last_candle['close'] <= last_candle['close_1d']) + # & (last_candle['close_1d'] <= last_candle['sma3_1d']) + # & (last_candle['close_1d'] <= last_candle['sma5_1d']) + # & (last_candle['close_1d'] <= last_candle['sma10_1d']) + # & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + ) + if not (condition): + return None + + # min_d = min(last_candle['sma3_4h'], last_candle['close_1d']) + + filled_buys = trade.select_filled_orders('buy') + count_of_buys = len(filled_buys) + # days = (current_time - trade.open_date_utc).days + # minutes = (current_time - trade.open_date_utc).seconds / 60 + # condition = condition & ((last_candle['min50'] == last_candle_5['min50']) & (last_candle['close'] <= last_candle['close_1h'])) + p = self.protection_percent_buy_lost.value + percents = [p, p * 2, p * 3, p * 4, p * 5, p * 6, p * 7, p * 8, p * 9] + + if (0 < count_of_buys <= self.protection_nb_buy_lost.value) \ + & (current_profit < - (percents[count_of_buys - 1] / 100)) & (condition): + try: + p = self.config['stake_amount'] + factors = [p, p, p, p, 2 * p, 4 * p, 5 * p, 6 * p] + + stake_amount = factors[count_of_buys - 1]# filled_buys[0].cost + # This then calculates current safety order size + # logger.info("----------- %s ", trade.pair + " " + str(current_profit) + " " + str(count_of_buys) + " " + str(stake_amount) + + # " " + str(current_time) + "---------------------") + # print("-----------" + trade.pair + " " + str(current_profit) + " " + str(count_of_buys) + " " + str(stake_amount) + + # " " + str(current_time) + "---------------------") + return stake_amount + except Exception as exception: + print(exception) + return None + return None diff --git a/Zeus_8d_1.py b/Zeus_8d_1.py new file mode 100644 index 0000000..cf19a55 --- /dev/null +++ b/Zeus_8d_1.py @@ -0,0 +1,472 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +class Zeus_8d_1(IStrategy): + + # Buy hyperspace params: + buy_params = { + "buy_min_horizon": 63, + "buy_min_max_n": 0.11, + "buy_rsi": 30, + "decalage": 2, + } + + # Sell hyperspace params: + sell_params = { + "sell_b_RSI": 85, + "sell_percent": 0.08, + "sell_profit_percent": 0.2, + } + + # Protection hyperspace params: + protection_params = { + "protection_nb_buy_lost": 1, + "protection_percent_buy_lost": 24, + "protection_start_buying_rsi_1d": 5, + "protection_stop_buying_rsi_1d": 66, + } + + # ROI table: # value loaded from strategy + minimal_roi = { + "0": 10 + } + + # Stoploss: + stoploss = -0.208 + + # Trailing stop: + trailing_stop = False # value loaded from strategy + trailing_stop_positive = None # value loaded from strategy + trailing_stop_positive_offset = 0.0 # value loaded from strategy + trailing_only_offset_is_reached = False # value loaded from strategy + + # Buy hypers + timeframe = '4h' + + stop_buying = {} + + # DCA config + # position_adjustment_enable = True + + plot_config = { + "main_plot": { + "min200": { + "color": "#86c932" + }, + "min50": { + "color": "white" + }, + # "max200": { + # "color": "yellow" + # }, + "sma3_1d": { + "color": "pink" + }, + "sma5_1d": { + "color": "blue" + }, + "sma10_1d": { + "color": "orange" + }, + "close_1d": { + "color": "#73e233", + }, + "low": { + "color": "cyan", + }, + "bb_lowerband": { + "color": "#da59a6"}, + "bb_upperband": { + "color": "#da59a6", + } + }, + "subplots": { + # "Ind": { + # "trend_ichimoku_base": { + # "color": "#dd1384" + # }, + # "trend_kst_diff": { + # "color": "#850678" + # } + # }, + # "BB": { + # "bb_width": { + # "color": "white" + # }, + # "bb_lower_5": { + # "color": "yellow" + # } + # }, + "Rsi": { + "rsi_1d": { + "color": "pink" + }, + # "rsi_1h": { + # "color": "green" + # }, + "rsi5": { + "color": "yellow" + }, + "rsi3_1d": { + "color": "red" + } + }, + # "Percent": { + # "pct_change_1_1d": { + # "color": "green" + # }, + # "pct_change_3_1d": { + # "color": "orange" + # }, + # "pct_change_5_1d": { + # "color": "red" + # } + # } + } + } + trades = list() + + buy_min_horizon = IntParameter(1, 100, default=7, space='buy') + buy_rsi = IntParameter(1, 30, default=12, space='buy') + buy_min_max_n = DecimalParameter(0.01, 0.2, decimals=2, default=0.05, space='buy') + decalage = IntParameter(1, 12, default=6, space='buy') + # adx_1d_limit = IntParameter(15, 45, default=18, space='buy') + sell_b_RSI = IntParameter(60, 98, default=60, space='sell') + sell_profit_percent = DecimalParameter(0.1, 1.5, decimals=1, default=0.8, space='sell') + + sell_percent = DecimalParameter(0.01, 0.30, decimals=2, default=0.05, space='sell') + protection_percent_buy_lost = IntParameter(1, 30, default=3, space='protection') + protection_nb_buy_lost = IntParameter(1, 3, default=3, space='protection') + protection_stop_buying_rsi_1d = IntParameter(50, 100, default=76, space='protection') + protection_start_buying_rsi_1d = IntParameter(1, 50, default=30, space='protection') + + def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + allow_to_buy = True + # info_previous_last_candle = informative.iloc[-2].squeeze() + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_previous_last_candle = dataframe.iloc[-3].squeeze() + + if self.stop_buying.get(pair, None) is None: + print("enable buying tag", pair) + self.stop_buying[pair] = False + + if ((last_candle['rsi5'] >= self.protection_stop_buying_rsi_1d.value) | (last_candle['close'] >= last_candle['bb_upperband'])) \ + & (self.stop_buying[pair] is False): + logger.info("1 - Disable buying %s date %s", pair, last_candle['date']) + self.stop_buying[pair] = True + + if self.stop_buying[pair] is True: + if ((last_candle['rsi5'] <= self.protection_start_buying_rsi_1d.value) & (last_candle['percent5'] >= 0.005)): + logger.info("2 - Enable buying %s date %s", pair, last_candle['date']) + self.stop_buying[pair] = False + + logger.info("Buy ==> %s ", pair + " " + str(current_time) + "---------------------") + + if self.stop_buying[pair]: + allow_to_buy = False + logger.info("3 - cancel buying %s date %s", pair, str(last_candle['date']) + " " + str(last_candle['rsi5'])) + else: + logger.info("3 - accept buying %s date %s", pair, str(last_candle['date']) + " " + str(last_candle['rsi5'])) + return allow_to_buy + + def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + current_profit: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + last_candle = dataframe.iloc[-1].squeeze() + previous_last_candle = dataframe.iloc[-2].squeeze() + previous_previous_last_candle = dataframe.iloc[-3].squeeze() + + if (current_profit > self.buy_min_max_n.value * self.sell_profit_percent.value): + # \ + # & (previous_last_candle['rsi5'] > self.sell_b_RSI.value) \ + # & (previous_last_candle['rsi5'] >= last_candle['rsi5']) \ + # & (previous_last_candle['rsi5'] >= previous_previous_last_candle['rsi5']): + logger.info("Sell ==> %s ", pair + " " + str(current_time) + " " + str(current_profit) + + " " + str(self.buy_min_max_n.value * self.sell_profit_percent.value) + + " " + str(previous_previous_last_candle['rsi5']) + " " + str( + previous_last_candle['rsi5']) + " " + str(last_candle['rsi5']) + ) + return 'profit_1' # + str(self.sell_percent.value) + # else: + # logger.info("No Sell ==> %s ", pair + " " + str(current_time) + " " + str(current_profit) + # + " " + str(self.buy_min_max_n.value * self.sell_profit_percent.value) + # + " " + str(previous_previous_last_candle['rsi5']) + " " + str(previous_last_candle['rsi5']) + " " + str(last_candle['rsi5']) + # ) + return None + + # def informative_pairs(self): + # # get access to all pairs available in whitelist. + # pairs = self.dp.current_whitelist() + # informative_pairs = [(pair, '4h') for pair in pairs] + # # informative_pairs += [(pair, '4h') for pair in pairs] + # informative_pairs += [(pair, '1h') for pair in pairs] + # + # return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + # dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + # dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + # dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + # dataframe['min200_1'] = dataframe['min200'] * 1.005 + # dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_n'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['max_n'] = talib.MAX(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + # dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + # dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + # dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + # dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + # dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + # dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + # dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + # dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + # dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + # dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + # dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / self.buy_bb_width_n.value)) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['min_max_close'] = ( + (dataframe['max200'] - dataframe['close']) / (dataframe['close'] - dataframe['min200'])) + + # dataframe['stop_buying'] = (dataframe['rsi5'] >= self.protection_stop_buying_rsi_1d.value) \ + # & (dataframe['close'] >= dataframe['bb_upperband']) + + # ################### INFORMATIVE 1D + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + # informative["rsi"] = talib.RSI(informative) + # informative["rsi3"] = talib.RSI(informative, 3) + # # informative["mrsi3"] = informative["rsi"].rolling(3).mean() + # # informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + # # informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + # # informative['pct_change_1'] = informative['close'].pct_change(1) + # # informative['pct_change_3'] = informative['close'].pct_change(3) + # # informative['pct_change_5'] = informative['close'].pct_change(5) + # informative['sma3'] = talib.SMA(informative, timeperiod=3) + # informative['sma5'] = talib.SMA(informative, timeperiod=5) + # informative['sma10'] = talib.SMA(informative, timeperiod=10) + # # informative['adx'] = talib.ADX(informative) + # + # bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + # informative['bb_lowerband'] = bollinger['lower'] + # informative['bb_middleband'] = bollinger['mid'] + # informative['bb_upperband'] = bollinger['upper'] + # informative["bb_percent"] = ( + # (informative["close"] - informative["bb_lowerband"]) / + # (informative["bb_upperband"] - informative["bb_lowerband"]) + # ) + # + # # informative["bb_width"] = ( + # # (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + # # ) + # + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + # ######################## INFORMATIVE 4h + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + # informative["rsi"] = talib.RSI(informative) + # informative['pct_change_1'] = informative['close'].pct_change(1) + # informative['pct_change_3'] = informative['close'].pct_change(3) + # informative['pct_change_5'] = informative['close'].pct_change(5) + # informative['sma3'] = talib.SMA(informative, timeperiod=3) + # informative['sma5'] = talib.SMA(informative, timeperiod=5) + # informative['sma10'] = talib.SMA(informative, timeperiod=10) + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ######################## INFORMATIVE 1h + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + # informative["rsi"] = talib.RSI(informative) + # informative["rsi3"] = talib.RSI(informative, 3) + # informative["mrsi3"] = informative["rsi"].rolling(3).mean() + # informative['pct_change_1'] = informative['close'].pct_change(1) + # informative['pct_change_3'] = informative['close'].pct_change(3) + # informative['pct_change_5'] = informative['close'].pct_change(5) + # informative['sma3'] = talib.SMA(informative, timeperiod=3) + # informative['sma5'] = talib.SMA(informative, timeperiod=5) + # informative['sma10'] = talib.SMA(informative, timeperiod=10) + # informative['adx'] = talib.ADX(informative) + # bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + # informative['bb_lowerband'] = bollinger['lower'] + # informative['bb_middleband'] = bollinger['mid'] + # informative['bb_upperband'] = bollinger['upper'] + # + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + (dataframe['rsi5'] < self.protection_stop_buying_rsi_1d.value) + & (dataframe['rsi5'] > self.protection_start_buying_rsi_1d.value) + & (dataframe['rsi5'].shift(1) < self.buy_rsi.value) + & (dataframe['rsi5'].shift(1) < dataframe['rsi5']) + & (dataframe['rsi5'].shift(1) < dataframe['rsi5'].shift(2)) + # & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + & (dataframe['min_n'].shift(self.decalage.value) == dataframe['min_n']) + # & (dataframe['pct_change_1_1d'] > 0) + & (dataframe['min_max_n'] >= self.buy_min_max_n.value) + # & (dataframe['percent'] > -0.005) + # & (dataframe['close'] <= dataframe['close_1d']) + # & (dataframe['close_1d'] <= dataframe['sma3_1d']) + # & (dataframe['close_1d'] <= dataframe['sma5_1d']) + # & (dataframe['close_1d'] <= dataframe['sma10_1d']) + # & (dataframe['adx_1d'] > self.adx_1d_limit.value) + # & (dataframe['rsi3_1d'].shift(288) <= dataframe['rsi3_1d']) + ), ['buy', 'buy_tag']] = (1, 'buy_adx_inf') + + return dataframe + + # def bot_loop_start(self, **kwargs) -> None: + # pairs = self.dp.current_whitelist() + # print("Calcul des pairs informatives") + # for pairname in pairs: + # self.stop_buying[pairname] = True + # print("Fin Calcul des pairs informatives") + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # dataframe.loc[ + # ( + # (dataframe['percent10'] < -0.05) + # ), ['sell', 'sell_tag']] = (1, 'sell_percent10') + # dataframe.loc[ + # ( + # (dataframe['percent'] < -0.02) + # ), ['sell', 'sell_tag']] = (1, 'sell_percent') + + # print("dans sell" + metadata['pair']) + return dataframe + + # def adjust_trade_position(self, trade: Trade, current_time: datetime, + # current_rate: float, current_profit: float, min_stake: float, + # max_stake: float, **kwargs): + # dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + # if (len(dataframe) < 1): + # return None + # + # if (self.stop_buying.get(trade.pair, None) == None): + # # logger.info("----------- %s ", trade.pair + " Init stop buying " + str(current_profit) + " " + str(current_time) + "---------------------") + # self.stop_buying[trade.pair] = False + # + # if (self.stop_buying[trade.pair] == True): + # # logger.info("----------- %s ", trade.pair + " Canceled " + str(current_profit) + " " + str(current_time) + "---------------------") + # return None + # last_candle = dataframe.iloc[-1].squeeze() + # previous_last_candle = dataframe.iloc[-2].squeeze() + # previous_previous_last_candle = dataframe.iloc[-3].squeeze() + # last_candle_6 = dataframe.iloc[-7].squeeze() + # + # # last_candle_5 = dataframe.iloc[-3].squeeze() + # # last_candle_decalage = dataframe.iloc[- self.buy_min_max_decalage.value].squeeze() + # # print(last_candle['buy']) + # + # condition = ( + # (previous_last_candle['rsi5'] < self.buy_rsi.value) + # & (previous_last_candle['rsi5'] < last_candle['rsi5']) + # & (previous_last_candle['rsi5'] < previous_previous_last_candle['rsi5']) + # & (last_candle_6['min_n'] == last_candle['min_n']) + # & (last_candle['min_max_n'] >= self.buy_min_max_n.value) + # # & (last_candle['close'] < last_candle['min_n'] * self.buy_min_max_coef.value) + # # & (last_candle['close'] <= last_candle['close_1d']) + # # & (last_candle['close_1d'] <= last_candle['sma3_1d']) + # # & (last_candle['close_1d'] <= last_candle['sma5_1d']) + # # & (last_candle['close_1d'] <= last_candle['sma10_1d']) + # # & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + # ) + # if not (condition): + # return None + # + # # min_d = min(last_candle['sma3_4h'], last_candle['close_1d']) + # + # filled_buys = trade.select_filled_orders('buy') + # count_of_buys = len(filled_buys) + # # days = (current_time - trade.open_date_utc).days + # # minutes = (current_time - trade.open_date_utc).seconds / 60 + # # condition = condition & ((last_candle['min50'] == last_candle_5['min50']) & (last_candle['close'] <= last_candle['close_1h'])) + # p = self.protection_percent_buy_lost.value + # percents = [p, p * 2, p * 3, p * 4, p * 5, p * 6, p * 7, p * 8, p * 9] + # + # if (0 < count_of_buys <= self.protection_nb_buy_lost.value) \ + # & (current_profit < - (percents[count_of_buys - 1] / 100)) & (condition): + # try: + # p = self.config['stake_amount'] + # factors = [p, p, p, p, 2 * p, 4 * p, 5 * p, 6 * p] + # + # stake_amount = factors[count_of_buys - 1]# filled_buys[0].cost + # # This then calculates current safety order size + # # logger.info("----------- %s ", trade.pair + " " + str(current_profit) + " " + str(count_of_buys) + " " + str(stake_amount) + + # # " " + str(current_time) + "---------------------") + # # print("-----------" + trade.pair + " " + str(current_profit) + " " + str(count_of_buys) + " " + str(stake_amount) + + # # " " + str(current_time) + "---------------------") + # return stake_amount + # except Exception as exception: + # print(exception) + # return None + # return None diff --git a/Zeus_8d_2.json b/Zeus_8d_2.json new file mode 100644 index 0000000..b466e6c --- /dev/null +++ b/Zeus_8d_2.json @@ -0,0 +1,38 @@ +{ + "strategy_name": "Zeus_8d_2", + "params": { + "roi": { + "0": 0.636, + "541": 0.182, + "2571": 0.063, + "7661": 0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.169, + "trailing_stop_positive_offset": 0.206, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_min_horizon": 46, + "buy_min_max_n": 0.2, + "buy_percent": 1.012, + "buy_rsi": 28, + "decalage": 9 + }, + "sell": { + "sell_b_RSI": 87, + "sell_percent": 0.17, + "sell_profit_percent": 0.3 + }, + "protection": { + "protection_start_buying_rsi_1d": 14, + "protection_stop_buying_rsi_1d": 69 + }, + "stoploss": { + "stoploss": -0.268 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-09-03 22:41:55.642221+00:00" +} \ No newline at end of file diff --git a/Zeus_8d_2.py b/Zeus_8d_2.py new file mode 100644 index 0000000..092d984 --- /dev/null +++ b/Zeus_8d_2.py @@ -0,0 +1,277 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +class Zeus_8d_2(IStrategy): + # Buy hyperspace params: + buy_params = { + "buy_min_horizon": 23, + "buy_min_max_n": 0.16, + "buy_rsi": 10, + "decalage": 8, + } + + # Sell hyperspace params: + sell_params = { + "sell_b_RSI": 75, + "sell_percent": 0.1, + "sell_profit_percent": 0.1, + } + + # Protection hyperspace params: + protection_params = { + "protection_nb_buy_lost": 3, + "protection_percent_buy_lost": 17, + "protection_start_buying_rsi_1d": 17, + "protection_stop_buying_rsi_1d": 51, + } + + # ROI table: # value loaded from strategy + minimal_roi = { + "0": 10 + } + + # Stoploss: + stoploss = -1.0 # value loaded from strategy + + # Trailing stop: + trailing_stop = False # value loaded from strategy + trailing_stop_positive = None # value loaded from strategy + trailing_stop_positive_offset = 0.0 # value loaded from strategy + trailing_only_offset_is_reached = False # value loaded from strategy + + # Buy hypers + timeframe = '4h' + + stop_buying = {} + + # DCA config + # position_adjustment_enable = True + + plot_config = { + "main_plot": { + "min200": { + "color": "#86c932" + }, + "min50": { + "color": "white" + }, + # "max200": { + # "color": "yellow" + # }, + "sma3_1d": { + "color": "pink" + }, + "sma5_1d": { + "color": "blue" + }, + "sma10_1d": { + "color": "orange" + }, + "close_1d": { + "color": "#73e233", + }, + "low": { + "color": "cyan", + }, + "bb_lowerband": { + "color": "#da59a6"}, + "bb_upperband": { + "color": "#da59a6", + } + }, + "subplots": { + # "Ind": { + # "trend_ichimoku_base": { + # "color": "#dd1384" + # }, + # "trend_kst_diff": { + # "color": "#850678" + # } + # }, + # "BB": { + # "bb_width": { + # "color": "white" + # }, + # "bb_lower_5": { + # "color": "yellow" + # } + # }, + "Rsi": { + "rsi_1d": { + "color": "pink" + }, + # "rsi_1h": { + # "color": "green" + # }, + "rsi5": { + "color": "yellow" + }, + "rsi3_1d": { + "color": "red" + } + }, + # "Percent": { + # "pct_change_1_1d": { + # "color": "green" + # }, + # "pct_change_3_1d": { + # "color": "orange" + # }, + # "pct_change_5_1d": { + # "color": "red" + # } + # } + } + } + + buy_min_horizon = IntParameter(1, 100, default=7, space='buy') + buy_rsi = IntParameter(1, 30, default=12, space='buy') + buy_min_max_n = DecimalParameter(0.01, 0.2, decimals=2, default=0.05, space='buy') + buy_percent = DecimalParameter(1.005, 1.02, decimals=3, default=1.01, space='buy') + + decalage = IntParameter(1, 12, default=6, space='buy') + sell_b_RSI = IntParameter(60, 98, default=60, space='sell') + sell_profit_percent = DecimalParameter(0.1, 1.5, decimals=1, default=0.8, space='sell') + + sell_percent = DecimalParameter(0.01, 0.30, decimals=2, default=0.05, space='sell') + # protection_percent_buy_lost = IntParameter(1, 30, default=3, space='protection') + # protection_nb_buy_lost = IntParameter(1, 3, default=3, space='protection') + protection_stop_buying_rsi_1d = IntParameter(50, 100, default=76, space='protection') + protection_start_buying_rsi_1d = IntParameter(1, 50, default=30, space='protection') + + # def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, + # current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: + # allow_to_buy = True + # # info_previous_last_candle = informative.iloc[-2].squeeze() + # # dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + # # last_candle = dataframe.iloc[-1].squeeze() + # + # return allow_to_buy + + # def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, + # current_profit: float, **kwargs): + # dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + # last_candle = dataframe.iloc[-1].squeeze() + # previous_last_candle = dataframe.iloc[-2].squeeze() + # previous_previous_last_candle = dataframe.iloc[-3].squeeze() + # + # if (current_profit > self.buy_min_max_n.value * self.sell_profit_percent.value): + # # \ + # # & (previous_last_candle['rsi5'] > self.sell_b_RSI.value) \ + # # & (previous_last_candle['rsi5'] > last_candle['rsi5']) \ + # # & (previous_last_candle['rsi5'] > previous_previous_last_candle['rsi5']): + # logger.info("Sell ==> %s ", pair + " " + str(current_time) + " " + str(current_profit) + # + " " + str(self.buy_min_max_n.value * self.sell_profit_percent.value) + # + " " + str(previous_previous_last_candle['rsi5']) + " " + str( + # previous_last_candle['rsi5']) + " " + str(last_candle['rsi5']) + # ) + # return 'profit_1' # + str(self.sell_percent.value) + # + # return None + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + # dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + # dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + # dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + # dataframe['min200_1'] = dataframe['min200'] * 1.005 + # dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_n'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['max_n'] = talib.MAX(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + # dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + # dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + # dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + # dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + # dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + # dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + # dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + # dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + # dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + dataframe['min_max_close'] = ( + (dataframe['max200'] - dataframe['close']) / (dataframe['close'] - dataframe['min200'])) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + (dataframe['rsi5'] < self.protection_stop_buying_rsi_1d.value) + & (dataframe['rsi5'] > self.protection_start_buying_rsi_1d.value) + # & (dataframe['rsi5'].shift(1) < self.buy_rsi.value) + # & (dataframe['rsi5'].shift(1) < dataframe['rsi5']) + # & (dataframe['rsi5'].shift(1) < dataframe['rsi5'].shift(2)) + & (dataframe['min_n'].shift(self.decalage.value) == dataframe['min_n']) + & (dataframe['min_max_n'] >= self.buy_min_max_n.value) + & (dataframe['close'] <= dataframe['min_n'] * self.buy_percent.value) + + ), ['buy', 'buy_tag']] = (1, 'buy_adx_inf') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + return dataframe diff --git a/Zeus_9.json b/Zeus_9.json new file mode 100644 index 0000000..17a3321 --- /dev/null +++ b/Zeus_9.json @@ -0,0 +1,81 @@ +{ + "strategy_name": "Zeus_9", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_base": 0.0, + "buy_diff": 0.4, + "buy_min_max_coef": 1.004, + "buy_min_max_cond1": 0.8, + "buy_min_max_decalage": 2, + "buy_min_max_n": 0.02, + "buy_min_max_nh": 14, + "buy_min_max_rsi": 61, + "buy_rsi": 26, + "buy_rsi_sup": 79 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2, + "sell_h_too_old_percent": 0.01 + }, + "protection": { + "protection_lost_candles": 11, + "protection_lost_percent": 0.14 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-05-22 20:03:28.505444+00:00" +} \ No newline at end of file diff --git a/Zeus_9.py b/Zeus_9.py new file mode 100644 index 0000000..eb8a15b --- /dev/null +++ b/Zeus_9.py @@ -0,0 +1,707 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = (qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = (qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_9(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", " 0.015) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + & (days < self.sell_b_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > last_candle['bb_width'] / 1.3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle['percent'] < - self.sell_b_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + if (current_profit > 0.025) & ((last_candle['percent'] < -0.005) | (last_candle['percent3'] < -0.005) | (last_candle['percent5'] < -0.005)): + return 'h_percent_quick' + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= 0.015) & (last_candle['percent3'] < -0.005): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | (last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > last_candle['bb_width'] / 0.8) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle['percent'] < - self.sell_h_RSI2_percent.value): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '4h') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + + # dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + # dataframe['high'], + # dataframe['low'], + # window1=9, + # window2=26, + # visual=False, + # fillna=False + # ) + # KST = ta.trend.KSTIndicator( + # close=dataframe['close'], + # roc1=10, + # roc2=15, + # roc3=20, + # roc4=30, + # window1=10, + # window2=10, + # window3=10, + # window4=15, + # nsig=9, + # fillna=False + # ) + # + # dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_5'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_max200_5'] = (dataframe['min200'] * (1 + dataframe['min_max200'] / 5)) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma10xpct+'] = dataframe['sma10'] * 1.0075 + dataframe['sma10xpct-'] = dataframe['sma10'] * 0.9925 + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + # dataframe["bb_percent"] = ( + # (dataframe["close"] - dataframe["bb_lowerband"]) / + # (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + # ) + # dataframe["bb_width"] = ( + # (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + # ) + # + # dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + # dataframe['min1.1'] = 1.01 * dataframe['min'] + + # dataframe['sar'] = talib.SAR(dataframe) + # # Normalization + # tib = dataframe['trend_ichimoku_base'] + # dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + # tkd = dataframe['trend_kst_diff'] + # dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ################### INFORMATIVE BTC 1H + # informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + # informative["rsi"] = talib.RSI(informative) + # informative["rsi_5"] = talib.RSI(informative, timeperiod=5) + # informative['pct_change_1'] = informative['close'].pct_change(1) + # dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['close'] <= dataframe['sma10xpct-']) + & (dataframe['close'].shift(1) <= dataframe['close']) + & (dataframe['min50'].shift(2) == dataframe['min50']) + ), ['buy', 'buy_tag']] = (1, 'buy_sma10') + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe diff --git a/Zeus_AI.json b/Zeus_AI.json new file mode 100644 index 0000000..b0efce1 --- /dev/null +++ b/Zeus_AI.json @@ -0,0 +1,116 @@ +{ + "strategy_name": "Zeus_AI", + "params": { + "roi": { + "0": 10 + }, + "stoploss": { + "stoploss": -1.0 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": 0.254, + "trailing_stop_positive_offset": 0.323, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_b_bb_lowerband": 1.03, + "buy_b_bb_width": 0.02, + "buy_min_max_coef": 1.004, + "buy_min_max_cond1": 0.8, + "buy_min_max_decalage": 2, + "buy_min_max_n": 0.02, + "buy_min_max_nh": 14, + "buy_min_max_rsi": 61, + "buy_b_cat": "R", + "buy_h_pct": 0.016, + "buy_h_real": 0.026, + "buy_0_distance": -0.03, + "buy_0_percent20": -0.03, + "buy_1_bb_lower_5": 0.19, + "buy_1_pente_sma10": 0.48, + "buy_1_pente_sma20": 0.33, + "buy_2_bb_lower_5": 0.59, + "buy_2_distance": 0.08, + "buy_2_pente_sma10": 0.46, + "buy_2_pente_sma20": 0.08, + "buy_2_percent20": 0.06, + "buy_3_bb_lower_5": 0.05, + "buy_3_distance": 0.01, + "buy_3_pente_sma10": 0.12, + "buy_3_pente_sma20": 0.27, + "buy_3_percent20": -0.03, + "buy_4_pente_sma10": 0.35, + "buy_4_pente_sma20": 0.22, + "buy_decalage0": 7, + "buy_decalage2": 6, + "buy_decalage3": 7, + "buy_decalage_deb_0": 1, + "buy_decalage_deb_2": 1, + "buy_decalage_deb_3": 1, + "buy_min_horizon": 192, + "buy_real_num0": 0.61, + "buy_real_num1": 0.34, + "buy_real_num2": 0.35 + }, + "sell": { + "profit_b_no_change": false, + "profit_b_old_sma10": false, + "profit_b_over_rsi": true, + "profit_b_quick_gain": true, + "profit_b_quick_gain_3": false, + "profit_b_quick_lost": false, + "profit_b_short_loss": true, + "profit_b_sma10": false, + "profit_b_sma20": false, + "profit_b_sma5": false, + "profit_b_very_old_sma10": false, + "profit_h_no_change": false, + "profit_h_old_sma10": true, + "profit_h_over_rsi": false, + "profit_h_quick_gain": false, + "profit_h_quick_gain_3": true, + "profit_h_quick_lost": false, + "profit_h_short_loss": false, + "profit_h_sma10": false, + "profit_h_sma20": false, + "profit_h_sma5": false, + "profit_h_very_old_sma10": true, + "sell_b_RSI": 92, + "sell_b_RSI2": 72, + "sell_b_RSI2_percent": 0.002, + "sell_b_RSI3": 75, + "sell_b_candels": 2, + "sell_b_percent": 0.012, + "sell_b_percent3": 0.008, + "sell_b_profit_no_change": 0.01, + "sell_b_profit_percent10": 0.0, + "sell_b_too_old_day": 3000, + "sell_b_too_old_percent": 0.016, + "sell_h_RSI": 98, + "sell_h_RSI2": 82, + "sell_h_RSI2_percent": 0.007, + "sell_h_RSI3": 72, + "sell_h_candels": 24, + "sell_h_percent": 0.013, + "sell_h_percent3": 0.02, + "sell_h_profit_no_change": 0.018, + "sell_h_profit_percent10": 0.0002, + "sell_h_too_old_day": 2000, + "sell_h_too_old_percent": 0.01 + }, + "protection": { + "protection_nb_buy_lost": 3, + "protection_percent_buy_lost": 3, + "protection_strategy": 1, + "protection_strategy_amount": 2 + } + }, + "ft_stratparam_v": 1, + "export_time": "2022-08-17 14:09:36.720609+00:00" +} \ No newline at end of file diff --git a/Zeus_AI.py b/Zeus_AI.py new file mode 100644 index 0000000..65e68bd --- /dev/null +++ b/Zeus_AI.py @@ -0,0 +1,1017 @@ +# Zeus Strategy: First Generation of GodStra Strategy with maximum +# AVG/MID profit in USDT +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: INSTALL TA BEFOUR RUN(pip install ta) +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell roi --strategy Zeus +# --- Do not remove these libs --- +from datetime import timedelta, datetime +from typing import Optional + +from freqtrade import data +from freqtrade.persistence import Trade +from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter + +from numpy.lib import math +from freqtrade.strategy.interface import IStrategy +import pandas +from pandas import DataFrame +import time +import logging +import calendar +from freqtrade.loggers import setup_logging +from freqtrade.strategy.strategy_helper import merge_informative_pair + +# -------------------------------- + +# Add your lib to import here +import ta +from functools import reduce +import numpy as np +import talib.abstract as talib +from freqtrade.strategy.strategy_helper import merge_informative_pair +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from random import shuffle + +logger = logging.getLogger(__name__) + +operators = [ + "D", # Disabled gene + ">", # Indicator, bigger than cross indicator + "<", # Indicator, smaller than cross indicator + "=", # Indicator, equal with cross indicator + "C", # Indicator, crossed the cross indicator + "CA", # Indicator, crossed above the cross indicator + "CB", # Indicator, crossed below the cross indicator + ">R", # Normalized indicator, bigger than real number + "=R", # Normalized indicator, equal with real number + "R", # Normalized indicator devided to cross indicator, bigger than real number + "/=R", # Normalized indicator devided to cross indicator, equal with real number + "/ 10) + + # TODO : it ill callculated in populate indicators. + + dataframe[indicator] = gene_calculator(dataframe, indicator) + dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator) + + indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}" + if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]: + dataframe[indicator_trend_sma] = gene_calculator(dataframe, indicator_trend_sma) + + if operator == ">": + condition = (dataframe[indicator].shift(decalage) > dataframe[crossed_indicator].shift(decalage)) + elif operator == "=": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "<": + condition = (dataframe[indicator].shift(decalage) < dataframe[crossed_indicator].shift(decalage)) + elif operator == "C": + condition = ( + (qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) | + (qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[crossed_indicator].shift(decalage))) + ) + elif operator == "CA": + condition = ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == "CB": + condition = ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), dataframe[crossed_indicator].shift(decalage))) + elif operator == ">R": + condition = (dataframe[indicator].shift(decalage) > real_num) + elif operator == "=R": + condition = (np.isclose(dataframe[indicator].shift(decalage), real_num)) + elif operator == "R": + condition = (dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)) > real_num) + elif operator == "/=R": + condition = (np.isclose(dataframe[indicator].shift(decalage).div(dataframe[crossed_indicator].shift(decalage)), + real_num)) + elif operator == "/ dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "DT": + condition = (dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage)) + elif operator == "OT": + condition = (np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage))) + elif operator == "CUT": + condition = ( + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) > dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "CDT": + condition = ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) & + ( + dataframe[indicator].shift(decalage) < dataframe[indicator_trend_sma].shift(decalage) + ) + ) + elif operator == "COT": + condition = ( + ( + ( + qtpylib.crossed_below(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) | + ( + qtpylib.crossed_above(dataframe[indicator].shift(decalage), + dataframe[indicator_trend_sma].shift(decalage)) + ) + ) & + ( + np.isclose(dataframe[indicator].shift(decalage), dataframe[indicator_trend_sma].shift(decalage)) + ) + ) + return condition, dataframe + +class Zeus_AI(IStrategy): + + # * 1/43: 86 trades. 72/6/8 Wins/Draws/Losses. Avg profit 12.66%. Median profit 11.99%. Total profit 0.10894395 BTC ( 108.94Σ%). Avg duration 3 days, 0:31:00 min. Objective: -48.48793 + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "4h", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_b_params = { + "buy_b_cat": "R", "=R", "R", "=R", "R", "=R", " -0.03): + # self.stop_buy_for_all = True + # return "btc_fall" + + # bb_width_lim = last_candle['bb_width'] / 4 + # bb_width_up = last_candle['bb_upperband'] * (1 - last_candle['bb_width'] / 5) + + if (last_candle['mrsi3_1h'] <= 0): #(self.market_overview_pct5 < 0) | (last_candle['pct_change_1_4h'] < 0): + max_percent = 0.004 # last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0.004 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > 0.01) & \ + (last_candle['percent10'] < -0.005) & ((current_time - trade.open_date_utc).seconds >= 3600): + return 'b_percent10' + if (current_profit > max_profit) & \ + ((last_candle['percent'] < - max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'b_percent_quick' + + if (current_profit >= - self.sell_b_too_old_percent.value) & (days >= self.sell_b_too_old_day.value)\ + & (days < self.sell_b_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.01" + if (current_profit >= - self.sell_b_too_old_percent.value * 2) & (days >= self.sell_b_too_old_day.value * 2)\ + & (days < self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.02" + if (current_profit >= - self.sell_b_too_old_percent.value * 3) & (days >= self.sell_b_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "b_too_old_0.03" + + if self.profit_b_quick_lost.value and (current_profit >= max_profit) & ( + last_candle['percent3'] < -max_percent): + return "b_quick_lost" + + if self.profit_b_no_change.value and (current_profit > self.sell_b_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_b_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "b_no_change" + + if (current_profit > self.sell_b_percent.value) & (last_candle['percent3'] < - self.sell_b_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_b_candels.value): + return "b_quick_gain_param" + + if self.profit_b_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma5' + + if self.profit_b_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma10' + + if self.profit_b_sma20.value: + if (current_profit > max_percent) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_sma20' + + if self.profit_b_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_b_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_b_RSI3.value) & \ + (last_candle['close'] >= last_candle['max200']) & (last_candle[ + 'percent'] < - self.sell_b_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_over_rsi_max' + + if self.profit_b_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'b_short_lost' + else: + max_percent = 0.005 # last_candle['bb_width'] / 3.5 # 0.005 + max_profit = 0.01 # last_candle['bb_width'] * 3 / 4 # 0.015 + + if (current_profit > max_profit) & ( + (last_candle['percent'] < -max_percent) | (last_candle['percent3'] < -max_percent) | ( + last_candle['percent5'] < -max_percent)): + return 'h_percent_quick' + + # if (last_candle['bb_width'] < 0.02) & (current_profit > 0) & (last_candle['close'] > bb_width_up) & \ + # ((last_candle['percent'] < - bb_width_lim) | (last_candle['percent3'] < - bb_width_lim) | (last_candle['percent5'] < - bb_width_lim)): + # return 'h_bb_width_max' + + if (current_profit >= - self.sell_h_too_old_percent.value) & (days >= self.sell_h_too_old_day.value)\ + & (days < self.sell_h_too_old_day.value * 2)\ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.01" + if (current_profit >= - self.sell_h_too_old_percent.value * 2) & (days >= self.sell_h_too_old_day.value * 2)\ + & (days < self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.02" + if (current_profit >= - self.sell_h_too_old_percent.value * 3) & (days >= self.sell_h_too_old_day.value * 3) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) & (last_candle['percent3'] < 0): + return "h_too_old_0.03" + + if self.profit_h_quick_lost.value and (current_profit >= max_profit) & ( + last_candle['percent3'] < -max_percent): + return "h_quick_lost" + + if self.profit_h_no_change.value and (current_profit > self.sell_h_profit_no_change.value) \ + & (last_candle['percent10'] < self.sell_h_profit_percent10.value) & (last_candle['percent5'] < 0) \ + & ((current_time - trade.open_date_utc).seconds >= 3600): + return "h_no_change" + + if (current_profit > self.sell_h_percent.value) & (last_candle['percent3'] < - self.sell_h_percent3.value) \ + & ((current_time - trade.open_date_utc).seconds <= 300 * self.sell_h_candels.value): + return "h_quick_gain_param" + + if self.profit_h_sma5.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma5'] > last_candle['sma5']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma5' + + if self.profit_h_sma10.value: + if (current_profit > expected_profit) \ + & ((previous_5_candle['sma10'] > last_candle['sma10']) \ + | (last_candle['percent3'] < -expected_profit) | ( + last_candle['percent5'] < -expected_profit)) \ + & ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma10' + + if self.profit_h_sma20.value: + if (current_profit > max_percent) \ + & (previous_last_candle['sma10'] > last_candle['sma10']) \ + & ((current_time - trade.open_date_utc).seconds >= 3600) \ + & ((previous_last_candle['sma20'] > last_candle['sma20']) & + ((last_candle['percent5'] < 0) | (last_candle['percent10'] < 0) | ( + last_candle['percent20'] < 0))): + # print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_sma20' + + if self.profit_h_over_rsi.value: + if (current_profit > 0) & (previous_last_candle[ + 'rsi'] > self.sell_h_RSI.value): # & (last_candle['percent'] < 0): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI2.value) & \ + (last_candle[ + 'percent'] < - self.sell_h_RSI2_percent.value): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_2' + + if (current_profit > 0) & (previous_last_candle['rsi'] > self.sell_h_RSI3.value) & \ + (last_candle['close'] >= last_candle[ + 'max200']): # | (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_over_rsi_max' + + if self.profit_h_short_loss.value: + if (current_profit > -expected_profit) & (previous_last_candle['percent10'] > 0.04) & ( + last_candle['percent'] < 0) \ + & (days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)): + # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate) + return 'h_short_lost' + + def informative_pairs(self): + # get access to all pairs available in whitelist. + pairs = self.dp.current_whitelist() + informative_pairs = [(pair, '1d') for pair in pairs] + informative_pairs += [(pair, '4h') for pair in pairs] + informative_pairs += [(pair, '1h') for pair in pairs] + + corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"] + for tf in self.config["freqai"]["feature_parameters"]["include_timeframes"]: + for pair in pairs: + informative_pairs.append((pair, tf)) + for pair in corr_pairs: + if pair in pairs: + continue # avoid duplication + informative_pairs.append((pair, tf)) + + return informative_pairs + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # Add all ta features + dataframe = self.freqai.start(dataframe, metadata, self) + return dataframe + + def populate_any_indicators(self, pair, dataframe, tf, informative=None, set_generalized_indicators=False): + dataframe['trend_ichimoku_base'] = ta.trend.ichimoku_base_line( + dataframe['high'], + dataframe['low'], + window1=9, + window2=26, + visual=False, + fillna=False + ) + KST = ta.trend.KSTIndicator( + close=dataframe['close'], + roc1=10, + roc2=15, + roc3=20, + roc4=30, + window1=10, + window2=10, + window3=10, + window4=15, + nsig=9, + fillna=False + ) + + dataframe['trend_kst_diff'] = KST.kst_diff() + dataframe['pct_change'] = dataframe['close'].pct_change(5) + dataframe['min'] = talib.MIN(dataframe['close'], timeperiod=self.buy_min_horizon.value) + dataframe['min10'] = talib.MIN(dataframe['close'], timeperiod=10) + dataframe['min20'] = talib.MIN(dataframe['close'], timeperiod=20) + dataframe['min50'] = talib.MIN(dataframe['close'], timeperiod=50) + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['min200_1'] = dataframe['min200'] * 1.005 + dataframe['moy200_12'] = dataframe['min200'].rolling(12).mean() + + dataframe['max50'] = talib.MAX(dataframe['close'], timeperiod=50) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] + dataframe['min_n'] = talib.MIN(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['max_n'] = talib.MAX(dataframe['close'], timeperiod=12 * self.buy_min_max_nh.value) + dataframe['min_max_n'] = (dataframe['max_n'] - dataframe['min_n']) / dataframe['min_n'] + dataframe['rsi'] = talib.RSI(dataframe) + dataframe['rsi5'] = talib.RSI(dataframe, timeperiod=5) + dataframe['sma5'] = talib.SMA(dataframe, timeperiod=5) + dataframe['sma10'] = talib.SMA(dataframe, timeperiod=10) + dataframe['sma20'] = talib.SMA(dataframe, timeperiod=20) + dataframe['sma50'] = talib.SMA(dataframe, timeperiod=50) + dataframe['sma100'] = talib.SMA(dataframe, timeperiod=100) + dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] + dataframe["percent5"] = dataframe["percent"].rolling(5).sum() + dataframe["percent3"] = dataframe["percent"].rolling(3).sum() + dataframe["percent10"] = dataframe["percent"].rolling(10).sum() + dataframe["percent20"] = dataframe["percent"].rolling(20).sum() + dataframe["percent50"] = dataframe["percent"].rolling(50).sum() + # dataframe['percent_lost_n'] = dataframe["percent"].rolling(self.protection_lost_candles.value).sum() + dataframe["volume10"] = dataframe["volume"].rolling(10).mean() + # 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'] + dataframe["bb_percent"] = ( + (dataframe["close"] - dataframe["bb_lowerband"]) / + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) + ) + dataframe["bb_width"] = ( + (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"] + ) + dataframe['bb_lower_var_5'] = (dataframe['bb_lowerband'] - dataframe['min50']).rolling(5).var() + dataframe['bb_lower_5'] = 100 * ((dataframe['bb_lowerband'].rolling(5).mean() / dataframe['bb_lowerband']) - 1) + dataframe['bb_lower_width_5'] = (dataframe['bb_lowerband'] * (1 + dataframe['bb_width'] / self.buy_bb_width_n.value)) + + # dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36) + + dataframe['distance_min'] = (dataframe['close'] - dataframe['min']) / dataframe['close'] + dataframe['min1.1'] = 1.01 * dataframe['min'] + dataframe['normal'] = 100 * (dataframe['close'] / dataframe['close'].rolling(200).mean()) + # dataframe['normal_var_20'] = dataframe['normal'].rolling(20).var() + # dataframe['normal_var_50'] = dataframe['normal'].rolling(50).var() + + dataframe['min_max_close'] = ( + (dataframe['max200'] - dataframe['close']) / (dataframe['close'] - dataframe['min200'])) + dataframe['sar'] = talib.SAR(dataframe) + # Normalization + tib = dataframe['trend_ichimoku_base'] + dataframe['trend_ichimoku_base'] = (tib-tib.min())/(tib.max()-tib.min()) + tkd = dataframe['trend_kst_diff'] + dataframe['trend_kst_diff'] = (tkd-tkd.min())/(tkd.max()-tkd.min()) + + dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0) + dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1) + dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2) + + dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0) + dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1) + dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2) + + dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0]) + dataframe['atr'] = talib.ATR(dataframe, timeperiod=14) + + ################### INFORMATIVE 1D + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + informative["rsi"] = talib.RSI(informative) + informative["max3"] = talib.MAX(informative['close'], timeperiod=3) + informative["min3"] = talib.MIN(informative['close'], timeperiod=3) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + informative['sar'] = talib.SAR(informative) + + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(informative), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] + informative["bb_percent"] = ( + (informative["close"] - informative["bb_lowerband"]) / + (informative["bb_upperband"] - informative["bb_lowerband"]) + ) + informative["bb_width"] = ( + (informative["bb_upperband"] - informative["bb_lowerband"]) / informative["bb_middleband"] + ) + + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) + + ######################## INFORMATIVE 4h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="4h") + informative["rsi"] = talib.RSI(informative) + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "4h", ffill=True) + + ######################## INFORMATIVE 1h + informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1h") + informative["rsi"] = talib.RSI(informative) + informative["mrsi3"] = informative["rsi"].rolling(3).mean() + informative['pct_change_1'] = informative['close'].pct_change(1) + informative['pct_change_3'] = informative['close'].pct_change(3) + informative['pct_change_5'] = informative['close'].pct_change(5) + informative['sma3'] = talib.SMA(informative, timeperiod=3) + informative['sma5'] = talib.SMA(informative, timeperiod=5) + informative['sma10'] = talib.SMA(informative, timeperiod=10) + dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1h", ffill=True) + + """ + Function designed to automatically generate, name and merge features + from user indicated timeframes in the configuration file. User controls the indicators + passed to the training/prediction by prepending indicators with `'%-' + coin ` + (see convention below). I.e. user should not prepend any supporting metrics + (e.g. bb_lowerband below) with % unless they explicitly want to pass that metric to the + model. + :param pair: pair to be used as informative + :param df: strategy dataframe which will receive merges from informatives + :param tf: timeframe of the dataframe which will modify the feature names + :param informative: the dataframe associated with the informative pair + :param coin: the name of the coin which will modify the feature names. + """ + + coin = pair.split('/')[0] + + if informative is None: + informative = self.dp.get_pair_dataframe(pair, tf) + + # first loop is automatically duplicating indicators for time periods + for t in self.freqai_info["feature_parameters"]["indicator_periods_candles"]: + t = int(t) + informative[f"%-{coin}rsi-period_{t}"] = ta.RSI(informative, timeperiod=t) + informative[f"%-{coin}mfi-period_{t}"] = ta.MFI(informative, timeperiod=t) + informative[f"%-{coin}adx-period_{t}"] = ta.ADX(informative, window=t) + + indicators = [col for col in informative if col.startswith("%")] + # This loop duplicates and shifts all indicators to add a sense of recency to data + for n in range(self.freqai_info["feature_parameters"]["include_shifted_candles"] + 1): + if n == 0: + continue + informative_shift = informative[indicators].shift(n) + informative_shift = informative_shift.add_suffix("_shift-" + str(n)) + informative = pandas.concat((informative, informative_shift), axis=1) + + dataframe = merge_informative_pair(dataframe, informative, self.config["timeframe"], tf, ffill=True) + skip_columns = [ + (s + "_" + tf) for s in ["date", "open", "high", "low", "close", "volume"] + ] + dataframe = dataframe.drop(columns=skip_columns) + + # Add generalized indicators here (because in live, it will call this + # function to populate indicators during training). Notice how we ensure not to + # add them multiple times + if set_generalized_indicators: + + # user adds targets here by prepending them with &- (see convention below) + # If user wishes to use multiple targets, a multioutput prediction model + # needs to be used such as templates/CatboostPredictionMultiModel.py + dataframe["&-s_close"] = ( + dataframe["close"] + .shift(-self.freqai_info["feature_parameters"]["label_period_candles"]) + .rolling(self.freqai_info["feature_parameters"]["label_period_candles"]) + .mean() + / dataframe["close"] + - 1 + ) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['trend_ichimoku_base'] <= self.buy_base.value) + & (dataframe['rsi'] < self.buy_rsi.value) + & (dataframe['close'] < dataframe['sma10']) + & (dataframe['close'] < dataframe['bb_lower_width_5']) + # & (dataframe['min50'] == dataframe['min50'].shift(3)) + # & (dataframe['min50'].shift(2) == dataframe['min50']) + # & (dataframe['close'] <= dataframe['close_1d']) + & (dataframe['close'] <= dataframe['close_1h']) + ), ['buy', 'buy_tag']] = (1, 'buy_ichimoku') + + dataframe.loc[ + ( + (dataframe['min_max_n'] >= self.buy_min_max_n.value) + & (dataframe['cond1'].shift(self.buy_min_max_decalage.value) <= self.buy_min_max_cond1.value) + & (dataframe['rsi'] < self.buy_min_max_rsi.value) + & (dataframe['close'] < dataframe['min_n'] * self.buy_min_max_coef.value) + & (dataframe['min_n'].shift(self.buy_min_max_decalage.value) == dataframe['min_n']) + & (dataframe['pct_change_1_1d'] > 0) + # & (dataframe['min50'] == dataframe['min50'].shift(3)) + # & (dataframe['close'] <= dataframe['close_1d']) + & (dataframe['close'] <= dataframe['close_1h']) + ), ['buy', 'buy_tag']] = (1, 'buy_min_max') + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + return dataframe + + def adjust_trade_position(self, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs): + dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) + if (len(dataframe) < 1): + return None + last_candle = dataframe.iloc[-1].squeeze() + last_candle_5 = dataframe.iloc[-3].squeeze() + + min_d = min(last_candle['sma3_4h'], last_candle['close_1d']) + + filled_buys = trade.select_filled_orders('buy') + count_of_buys = len(filled_buys) + days = (current_time - trade.open_date_utc).days + minutes = (current_time - trade.open_date_utc).seconds / 60 + # condition = (last_candle['cond1'] <= 0.75) & (last_candle['bb_width'] > 0.018) & ( + # last_candle['rsi'] < 72) & (last_candle['close'] < last_candle['min50'] * 1.006)# & (last_candle['min_max_close'] > 2) + + condition = (last_candle['min50'] == last_candle_5['min50']) & (last_candle['close'] <= last_candle['close_1h']) + p = self.protection_percent_buy_lost.value + percents = [p, p * 2, p * 3, p * 4, p * 5, p * 6, p * 7, p * 8, p * 9] + + if (0 < count_of_buys <= self.protection_nb_buy_lost.value) \ + & (current_profit < - (percents[count_of_buys - 1] / 100)) & (condition): + try: + p = self.config['stake_amount'] + factors = [p, p, p, p, 2 * p, 4 * p, 5 * p, 6 * p] + + stake_amount = factors[count_of_buys - 1]# filled_buys[0].cost + # This then calculates current safety order size + # stake_amount = stake_amount * pow(1.5, count_of_buys) + print("-----------" + trade.pair + " " + str(current_profit) + " " + str(count_of_buys) + " " + str(stake_amount) + "---------------------") + return stake_amount + except Exception as exception: + print(exception) + return None + return None diff --git a/__pycache__/BB_RTR_dca.cpython-39.pyc b/__pycache__/BB_RTR_dca.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..09ad8aae4f3852d4316b5abdd8eebf29953b0759 GIT binary patch literal 24407 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 B6)yk) literal 0 HcmV?d00001 diff --git a/__pycache__/BinHV45.cpython-39.pyc b/__pycache__/BinHV45.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39ba8f5106ce97a24ca451e5e9af8f1d46dd2bca GIT binary patch literal 2356 zcmZuy&6Ctb6z_C8ACt-K$1uXKY+0*l=~U?sDCj{11Q*$Zg9Q|aTvD0o%x>0v(aAvV z)^ZXL9=NN;VfSJce}(@?U%fbgftHH+UMI{9uu0|ReRRL>e!ur#2d$Pv;Bl|_N0)&6 z9fkRafp7)BA_l?<*J6@ro~9vBfu|l-ydLWb^O)jUYe3Qf97pN4WJwsLQZGv0HN9q0X2b9NTtvfAHmkvY5$%W4Nymww zjeHm<-FmLCrZ!(lVDg+edk_kL5TyJm5iQv4-=Ftx-27&u7h_-pA=(nWE)|Qw4^c7ySRh=1 zulNZ_NIZ=bk8;h^Ipun!d5km9-~erCaT5XK4es#fW5csSV{vy*(*O<1voQ{D@dduf zm!9ZeGjzjN=CcZ-okPEV7zyyYe*e}E!j@Ay(29ij)HMqN~6dFz|PL{GD-lr zNe8E4JSNLXR)GNgO>p5420V<*KrWy#jI$!FWEOfE(1Q5rH8 z8e8cbY7BUs>K&yAO|{aYel)W+=WOoWoEGXK{0n_)s8dfqfb9wlPnQZjgJh7)g*3)_ z9tx=^5toKaY-zoS8O!o8^&C|xRn(kW1jUMSqC&`|b+63#;%J~!RqDY2JQq9BsO&OW zD2zwqU0nY?oYR=*yDFZFETH8?p5@~hdWR1s91Vjq6GaycMH>jwI@HzhU4eHEzWvkF zDtdk1OTb}k!TeCb{R#{R*i?g|s{|X&A;5QpP%@Ay{1HO3tM9T7;pS;_q+P-A16*@f z;}xq7RE>EwX5}TAE@Px5v}2K`T}!Xe24@qM0|wgeIU^va46A3 z@hp;4K)MaAm!bFscuBWQ@isEX{U9EP;vJ=oOL3-R!l$~xPCG;9RXEyHI8?ESFy$9u zLV=!$L;qxSNn3kP>Dqr(UwcMb=U-}c{-GBAi$15${+q9%Gj;PCI9OTN(Dz9o6*y2) z^ap@t8Vt6-?yT2Onyo`t#p^}1od)q#tzy(y85ego@fkQ17m;9>J~q+Ni(lf=uELN4 z_d!_uxYU}B?01)??fWS-4BwZI?P-QK#{ zm(3(fqa=uZkwwymn<$5tRLBN2vQTv(0jLs%n*yu?%fZ0qc#ReUcPJTDM=3tVpB_NnV1vXV%V>)}z7}FSSYw$9v!TZcK+itt1#&(yy_Vnu7sPC&ifvsEZ zOqw}ddqKSo11?Osz-XGwg))Hh-+;o0J8oD?d&*I0re|h8??b3Fu~GMnjai{fvoeWu ju$Xja2Gvv4?v6X6v@cfSzl57C@YGe8uyyDZx^4dtoEu9O literal 0 HcmV?d00001 diff --git a/__pycache__/BreakEven.cpython-39.pyc b/__pycache__/BreakEven.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d59b7e086b71cfc16aa14e9c75a0e582954a062f GIT binary patch literal 1964 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 Pt#_+#r#s|6DmniLAJ*Lm literal 0 HcmV?d00001 diff --git a/__pycache__/DevilStra2.cpython-39.pyc b/__pycache__/DevilStra2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7dfae848e4cc1f70684c49513689ac388230e114 GIT binary patch literal 10358 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 QRn~cQkJA(K+^RVL2jw{%TL1t6 literal 0 HcmV?d00001 diff --git a/__pycache__/Diamond.cpython-39.pyc b/__pycache__/Diamond.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ce6c6b1f4cbc5e5440d41c3324c2e7b6a745c401 GIT binary patch literal 2765 zcmdT`OK%%h6rQ)ouRI+0k)&y9`ZT3>ACwAF)s6nt&11n59 zDajjw9j2X(rqAKCAq$AF-s+?1ZMN-!llA`7{-5DjutWvU9;H^qY z7sbt1ltV_rxT%EZVSTKB`XNbY=;^!H1uNS~ig7@97iaK=l#vuHd6lEo{&M+NmDrDQoDw&c9 z#GsFC?;(XXUUoq?cmfW{M!9JDd8*_8zjCZlPQ07V^wZo$Msj*9~WU4YG^u z24rFlvWM&iWRmQIjO-VwGrkwb5fMhqZ?;x2T*8`&?&ZpJpTg5x)ep>vX${_Iy zyh%MIw8KXo?bu;QKYaX9^ZvINY9p7w`SMcFRF*33!7jD&yUp} z^JjHM2YChE2_3Eng=~{<$#Rh*wBdDvM3}to)oDp%Q=rGT11Xuph}$$`X+%cR+^h#N zrz{J+lL|d~f63q-g@N}2?`Z-R-oOeMHnfO`A{KhmVNo5=vicy8$2tv*V~;PXv&ynM zvy9D#-WqHgP__%n-c|DVOO;n%z3_6S6^FFK<4)pN8jN0n4kPqPr32@4@r@PU?Kz+F zO0QVTZK#2E-0lR>jIJLMNOBT0-T~KK1EN@qlYQMM_0}C8&x>Uz4}Te$rgBwZ(r+n= zdP`eUmy|hu&KOs2nZQ|d#%)z%DZ~J==ZvCqTf42Q%97455!jteCmHPT>wsL-h|$na z$bgj088!j_>+*!a;DbD_Sy8W^58nN?BofZKw_+Qdg4pLg3d#zRqIKDRI48 z3{{>3HDS(sL5D(hwfsiH@&K_RB*VA~O;u+Y!)1f*Lv$JmW?N+H(A<9F#}R-0{Dpo7 z`-i*=CLC*4Q4RcQMYZs+_QG%A9<(54QcYQ1{>>ywtB3=fv zc<_n4%JK8Gr0xWpQkGTF{t%EsHft1`n-y^52$G{n#*h?|Y(nxp63iq!hNR!C-HIFZ z+~5fBgWg6dORYYbKNT)FIe5dLTTd%n~SV8 zUll?>!cVu$Vqe%iiQ6#WxX8og@B=?;x(JEQ>qNI5b12MtDyBBZ-MG=Glb>w``(r$H+L#u*+1)*ju0?3>aexE1~vr zBrhTvWI%G*_zcIy3$XY_AhxZk^7oIG-hhM7k(~s+WatHTM72%hfo0j6@z64)jGW$+ z?ZT+^UMh&G{wyx{2Jk{zMjQVtaV9S%Z;KZd+cwblBhYNX$B3DzVzs@9c$~?*4#8*J zua&(T`mQI!?sFP^W73UIR8QhK;IbCQu7US?m{c(<_eNEDF#F+OgWT#CeP*yBFSJn{ YVSsk)?BoFqCXTrxjF_AOOWmsf2ay@yNdN!< literal 0 HcmV?d00001 diff --git a/__pycache__/Empty.cpython-39.pyc b/__pycache__/Empty.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ef73efeabdf60dadb1b8e1f88a97e36059ee326f GIT binary patch literal 2240 zcmb7FJC7Vi5T5tWzVFuO^PZmp(FWXFMwURb@QXWRA+NzU1e3I8r+fF>nwPtKd|K-u z!Gr|l4j{aLK*(<(>&*6MED&_3EDLuBvb1cDrH0Xl;)s_dAyL zI||2#2f_`QG6g~{YNu9aMV5`cliHDE-frZ<+fCifk9=eE(jcovwS`}p)uYCOZ)PnA z)@N-)J5k%V?pV~P!E=iStoE%Nb*b~v8q^Npi`Kx_A&nJNJYype>Yv0a-WGAjP`LY0 z2{2A)3Hlx%J_t8p%3pvmE3&B-IbTBjkxL!wavLJ{BA@zEK!d18Yf%V%orY0^)}toy zEqJ$Kc4#B&vK6*Uo7XJbqHS(3Xy=7PyK~;?Rt#MQ+5^iem}@Vbs7FuJ_2*V}imkEJ zY>l3I;nF_cc)g7f z{hOdcp`OJ&CuLeF9m;Bqj7)43`nIKRp@leidf-^ZZm$q5X^eWFMg~(#XpE6R$T*y24 zMZ8YSd3I**_mktZ%LUknT$i_VHc?@7=H6J(qeL)}BrMYZmZ<;cJEu4POV|rj5DJhSf)@ek7ek84B@DEl9Nu%h+0I zgF9@G4%Q$F5deWq1iL(k48ff$Dz$G?Eui5o?Pi?nHl!HdP#-4$zDd65qTNr@LNeU| z4=9L)ASzj(V zO0PJFUprM6WtBot6NneKP=%16fkCbVv0CeP3m>OrcMhGyz+XQMyk{GWG{3#_jSOM7 zaGDI^(W$*>KL!|E+?YE$z)~DICMNBUtJ%J--Ly#d-4h2m8FgOd46 z5I_}x020eX#2gETph~a5A(FC?9Q#yXc(-f`{ z_in!wMZ%;c#x-ZV3pA9v3&26lVI`mDajI900R9uMjJxiGp%4rYLHLAV*5(s+JmB2;(e4qKynqyQOXR5jUW(9dNAdO zKpLLsd#}B~vlox=x$vOmw*0^jUVF9h(D4lSkM}a@gUz&)Fs%i-}2`FoD>nJ1T}=-;BrTkN}`BIR_x|y`v4DLiHD7 va{&&p&3}EYgSklzaKFZp!G!s)ThQUPd9i(rAxLc6mJ`|`AVAA&*?sq4h|*|! literal 0 HcmV?d00001 diff --git a/__pycache__/FractalAtr.cpython-39.pyc b/__pycache__/FractalAtr.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cf01ef41762a1023a7b1246e401fbe59078b240a GIT binary patch literal 9884 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 D91q8I literal 0 HcmV?d00001 diff --git a/__pycache__/FractalAtr2.cpython-39.pyc b/__pycache__/FractalAtr2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9a0cbd64e8c64181d868f29ed56489247ace0b5f GIT binary patch literal 11967 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 literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD.cpython-39.pyc b/__pycache__/GodStraJD.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..67652836123ec94ea6be3ce2ec61b171131d20e6 GIT binary patch literal 10056 zcmdT~du&`+TEDNkb7wq$#CDw6ahgf`NW5D+w$pa0NRy6dCic|xVrHDABhWDRy|yQv zhkNgJTAK-rHr=I*s8k9pY}+kyTMGRH5D2ZxO1q1MSXfx06%yL&h)1^tfnix#Ay(Aw zCj7p0@7x*3PU*kg(f#iCI^Q|xJLfz1JLgWhyE`c03SCGq-@hsd?-9{?1wcHC+j>V7 z1YOX@s!$V)qDZ(@m1=TPt|>)@({j~UQ;RCcm8!p{6*Z3gs)1Ut805HG?W%=}-7el! z>y-pisP%DppxDRx{%U`1pg7>_3>FWfF0DFLJ5)R*3af%1cufQha@fUTh{J9UdpPXn zuS6u( zGeYrR{e*rup7-hZ==b7za#qmq(@#Dl=qC;Bx?DV^t9fDU)HaR%n8*~pY?sF*HW)42 z#!ADiES0O-vRSSfwqdfqn6Xr;b#SOet!%I28A#-9lL~DR5rdfvR4ffjH4S}z$pGfJ zR@ax8tH!wjRL7K9JQZQed<0M?R7)jd*-Ro`*i=(sj!!Z<54Nw6OQa$$Z)#JUnTe!h z`SB@8BRP+pW=esKgyssRLN1<;B@^j5lfY4-7QbBZG$)x>O)lh1u@sZDSro}-qD);# z7Bcy0yXa&~D?vR=lYW9JP|b0eZCQg^Ud1^2#*quCK;t#-vNweu<`S2goXVtHMJ7Gd zDW*hXmvc;tUG~gOI*mc+(RTPW8pE^QA9yfzA%R{;0Zb8&qVt?{CSQoaL+N~Gp)kj7LYsKpW;B_BY&I3iEzD$!3tT>zn4gcp zUI_N$>Dh(k1!%A?P}xL07i|%9iP^bJk&AJ501_BXCgB-W9M7e>=~O0{hHfnLm{T2f z=M36`K5)~8NPb~Hf$nev$xM1S8K0ev$EW}cKsk|4$I%I@_zW7)3-*GD#4bkC(fCX< z11WdHOcK3yoI;J6&7oPCw&^&xdYktgQRh*W}6=jSqr+u3YlHl9iI(VB;C8WM5@W=nLA z8k@Pq`!W+xI@5<*5z9qpGwBP-$GI=atKO)SV+wP*1vC_vs2Vsq7SGLO3WZFHPooH} zaOBaM$jswx04kT=*?1-uMeBUbs4bOe>-ZVX30&xgo$@yz2C4hSL|`dB14kKkE|MKe@6c@UN53N%l+ z@A-rVagOp9)=T_IE*GCIp-ag)-T(!7_+qCZpUh;j5Eb$lSinN^8@8`dnlvJ}3sUSA@fWqt-;Fs~DMhQMbDe2&1+6L^-u=K;pFZ66-nDuEDzUINnu zK1Dz$PzM+rU}B7k=a_hwiH|T5%X>6}duB&Em)nsZ!F@KjBcBE#p9UqLK8rWeLIH0h z+?fL2F(Bk9C^?!bY}4Amjm%PD;`p|N=MoXEi*VA2;MTf$*_id!CaM7Rb# zFT5$Hk+jIQN*$p5G%^iIUw%_Ce+OlE9yyILj-Ot|@SL_9>vrYzvS~btWJNcoPp?~s zS)!!pwB@8P7|PSL4V{vi$6^y}8!S-AXq2m!P2(0dhuS5GqoVnHcrH=XTUQql@+5Ed zS14^3o8nc$Jk=C$AZgwbuSvSxlyv1Rs2gIFIC4|I1}#^vG!@-9EL`*1Qd7AuUQ?SQ z`YUeG*743kW2 zYc@6A|D3c-TZ8uZxwh(Rw@U_YFL^~nS}uH6Y6g}iw6(L=08dwEI_quvkl;#9jS}2z z!P{-2Prm4?1eS༛%^pA?CBz5Z+9=(x3@h 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 literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD1.cpython-39.pyc b/__pycache__/GodStraJD1.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0059b69bb09a9ebf9b4cbf8ddd8ef49fe43d73ac GIT binary patch literal 14525 zcmdU03v3+6dEVFUJ$QUm4^onKde{;jlOicOepHrpygQK>9q*XCqhudaT#wu(dD1=T z-KAu)CnSiJG-+$Vb=#y#oK%X_Gy>8VZG+Yb+9Yk!^pO?~ilR*iMdQRRP&H}VBtQ`> zw$<;S-P_ya!&ctt5&Q4_GynWE^Z)bD{PWMU)Y%yl@C%<#FE-vI2rm)h{RM%X!ef3z z6a-Ds#EMWA3!+GAsUlV7f?QP!3diM&uc{VQo>nUUYM>C{XtyVg!;XNHw=^4>@k3OXB9iYA+)OWeLAJKR5++E1s?dE<;-_3J( zYY}bVHKA~?wqHAd_kG$y?OwbOO$pk4+M#QLc1RCgl?#V~AJ&F}4_}oE!c zGhtX{idM2p!x9^al`MU!Zj={FmD!R}s_K?*u%5WSP_BA%s7AG9E#nqTmmC>e^GCCpOd3u5 zXH#>zVtj_lv$Lp@&BT~Gm&#{yu~yZwCRLI~mL~cnQ$U*KaJI<}Y8S+T$!AX}L>xF5OU;r3FyOOOPAdPztdp8a#Cht3AH-`NQ0?q=G?&R@YPfoy z$;17S zVk$a25zA)I;GL=Co7s6x;qqT)Xotes_TbQ=#1n1>P7EjQCCbNm? zndrIb6TH=<=cbeSL=FYv!JUWv=Bk>&1#MDZh##Cs_ zjAfv%Xo_m)&Y_S~Lh;0a9d57|iX0J7K0)RH zBO=wuquDbsp1F7|LyeOJ(O5Q5^MuQuPiP<~$ctg!lpoDz6H`SDDV4wukcWiNdJA%? z%q$k7eC{+0y5vlaWl$}}l*vRMo6=brZogYv#LfhhOw8to1Gl>~7j>gjT8ZSWRjsU> zxBF-6RlR1dRwK!phJDkj8|Llav-QeqRrh9XLsrQuSIX99Z+7=AHd{TCH%c{gv2Ijv z_rxwQRN89x&z3A~!?j3OU#KtD%2v5vyWO9&z|FME3z5^SwFSyEjdwx{##I8>2)v8H zy9xXp0eF9c@m_%8z%3tMw^RaQ0^I~A2s}hUBTxev?q_10i6@wNoQaPy5zBilif8h+ zbRv6OdJNC;>}~l75cvo&`N(nXM00uUMtCxL>@h&(7%(}O$=?bPWB8c@6Gv`IcrOsr z{85^AhZr6+0?@$Ub>W7vliuPD7uzLVK{_Yg6w`28E_|mZ6>_W_{HvA6Yc?C*iDU`oxh{Q#Xp_^c*p5_XVmvGF8{enR!cme01e93)Y~H zQl-46-=Y3c#{_Y=X#5V|3)J`KuLB7ZCGYm9$%_^n;zhwY+z_wBhrS_Rku

Y07b6 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 literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD2.cpython-39.pyc b/__pycache__/GodStraJD2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..78ac846e87e22404bbf2ccdc095776f48e9b5f01 GIT binary patch literal 14525 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 literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_1.cpython-39.pyc b/__pycache__/GodStraJD3_1.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..519fc55c670e5f1be5e48533cb9833af1aa0a154 GIT binary patch literal 15938 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 literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_2.cpython-39.pyc b/__pycache__/GodStraJD3_2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f570fc3950d10f207a488d72e8f7b04c19d23314 GIT binary patch literal 16524 zcmeHOd2k!&b>Ceq76-wbqDbn3CD{^eQ{rL8jw?$70fAx_ zfQOGXP5&t{c>CV_-uJ%yeeZkUBHP~X74Y#LkIg^*8A13K5zQYDh-3KaiX;fCpo#^d zD5M3EaH${`rL@FxN5N5)(=x~9g0rZkm7*){;cBZ>5yt~*Vp^RcLhkfZ@&i53y6#LWtmdw`l0P5f^3>JsdL!z)KsBNDR0eu{{ zbJ)RQCx=}ec5~SC2~q96>`rgfwm%}IcW67bZED}35U=A~n(+Q6d}|Xv(1Z^*;X_UM zHWObE)a@S^(>w9pf#)uDXWFmsO5dXG)^Al)cJcrV^YJ0BS zrtSTRp!&62KO%h8aa9zB$HfPi@SD^2QBHN&S?O_+UD0-^xAaln_h~zA?&r0goVyd; zT{icxwOyRMOWm#Rc~nU6SNE#7;u%nHQ}^L{`=p@mR|Ag5e-F-?57$zLNjs0F%h4wdS-&5@bT!G z;jyYWnTm&|63OWBNYxcg#N#m(?V6oFnaqS|49Dy&vLxamLpeE}iYG&jtRr=)D77p` z@=-&EXoADpx;CigMGT{D9662x+`Oa~eWPe$B6`Mf%*1EvSw?KtGlm=tpGg=}_>8S) z#HY* zj~;PQCpv>+RNIh(VZ#xhIAt>BSTL0^lvE-Zn~b2GRN{zC!tT?d=~)^8H2Bn{g{AJF zwXm5;m}5s=5U#diXlJK_$#?=&!yf0EcsOEEQaCglNkrq}W;wJ0DM?HP^sbGL7+zBz z$|6VZql$P#;pv%RYAQY*HhO@coJ~fcCi#2B227+s)4@sqJs zBoR!df}sx4A%xX#dY9BGp?!Nkdlc={wypNgJ15rjS;^hIKm zC#R1?!e|3E8;vAFbz&+yIdwXCDq?ga1q!F9(K9GGl8ABDGx0=dfZWCGQ~w2ei$E*DrFp$1JPBEb{E)4}_BspF@oqNzv{J&cwmc~N$Ksmqg* z*&sR-&EWmas}(n^MFmA;ry_||Bs>+InTePk8aWkdX*CT>G8GM-NWwy#j)tbVj#Ti( z$wZWQ6S^aY^x*V(V^l3Ae3YmsrcjBQs6maNieueQ&PFFA@faVi6VOdVLLGtG5}KmQ z#!vIMOhl&5=|i;$CxVml*zxK6d0$Yk+M`Y#lbTALL`9*Aia{rbBZ-N4DixpM(zL#lc$l$B{A@%fPQ=s%m$cq zhBv$&WlUjhqDnKt7*9^lVorcg#bOWOZ2>D=$rtjK3(f4#S!}kN9~*E5d`JDgwN-0M?ojrTweZf#3oGh#4%)BlUV+PfRlF#vj+&&(he2HtYs7KX92X&FrOP#0bq)#_ zofWAjKO$aKY9gBKxGF%)DRhH4h~}!oMORHx<+Bods3KRKHRmHzO@OqkqSRcEh)Dsd z3Q|8+aY3IvNNMhdWX-L*E=euYZb*NRODmRiBd2F=&LuaTci}Oq=9!mJ*XB|^+^L;t zF1O}{8!Xk_%$!dw0>-_v?wX^hl>{6WT zOKGj~%ay63j{|Knny=IB8phKQN7$$FWHd9`uK`>W4KF#*%Nom<)m3tQR%u?acLD@_ zhEre03OD4XGR?_?Rx;$X+6CQkEofM;6=q14ET2DyBVU4CzEIAwF61?o`Fx>J${M}g ziOqzjuq|go*bk;7$v~Uop~*T=uC?LG*A0!)&5P62OoP>4(#|)L@`6@E7J``)<#uzF zY6kyYUnx|KPR?nx%J6aqRB8_yo%&*yEjJYMwPk|eqH3F&aa_o)z`T_iPLG1Vh!jC` zi(U~wNtV38-ICAY2JQwf-IN`igLFxhZYVOil27#2X@~bk89Xm|9Tv|k4{_Qj_BBbl zW%gB+l_Psr|B%mUuf}g;oz}1(2Qe4V%2;2ZY7(uRnuPgBl!H@_!{A^pIVvKbu~>ns zjMX>IWfU%>Kt@!ZSTl>rhc!UC2L;mmxKvt38m+#!R-`MA=Jcx)PoG7)3+bP1PCJV= zkGyvwjn?AVEAka*bNW>Y9Ho^rVdkN_VBN7Fua|&MvGh%(n`>xjy;d&q$B<~_tL!eT z1ev!*EPWCwKAw_jYK_cWB9`8a)OMa)u=7!!#8Lq%9X#c6Gv$g4PxR4Mu~yeT1({AR z^JGJJvz%3mA97t>&e%W~$}8P~L^qfCkyQ^^Kyy4wPa~}dX%g#cNuzvUL0T_Q8*9`6 zQpihXmp+B`KAt|;(ziUlMVow#^qqtJ7B2rpOHZ$t7r6X!$oF&k*74@@>r0#v*4L8s z)G$-#CD^R3F+(}WJ&sk13{{=OI6n^u6m}I9`#6D55cnj4Um@@*0`DX60RrzQ@Ie9} zBJg1XA0cp=z()yujKD7wxI*A1H3!^$RkANa(!jznTzNfLDC?Txda$y5p^%?5+H#$sNTA_TtL1yR5=QP8i=FbIO>`Rp2U52qG)55+?WOqGd!{oV{mMvsTD@DVTE5kOD zGi3DI>6r@CN@_+gX4e|8URyjvCXbhLf(+Q7qa;D{$sI6H@=dqHG(v>ArH-2lU=N2D zF3O_sri8EJgo)$0BE#_6m>1Gsp3^ItcpvBZMCB$7AmxO7AIu_2+0fB*LzcY`SyV*j zhNN^n2gBL6j+X1`*7G^3!@_<5&0E<&qAfXUQ1gcz=41HjmnA{agtVv%X-O4TI4UY! z5mixLmzA_bb*E+3lXj}!w4%19U8*nb2JTVY)s8%jfZEAn7l+*(_B6p>;C+`}kTbcI zXUk>Drp^;WohST#H#c<=+8%=Ve9Rby-S#wKmPC`qf+1 z-IpBcc24cFs18o;wWv-`-D*)?oVv}Tx;eGaqIy8}BFbj!?Bi6xqPB4Ac8luQwxah$ zZ9u&PoI5W$(}Ol=*y0S?oC6kT8!zpkI>PI`ol~P0wS!Y*7PXU8;}*4xQ+HXEpHqh{ z>K0JFYQUm)bLuq~wTDx8Thv~=KKFn_b-vZ+yw>8}W^?YfIQz8y>d&c%ABCHl4rsTl z_o+v;JG490qnDlPF*W$8jM(FZ8bX9}SUaE{)DEa&jAukWj+Bum&ZIg8&S(=Ss=f}K zG4%wD+qB_pcFeg$wxBP*`@6NHe|mJ`nb$wL^Re%I@#w#O_(Syh{W@~(D0kXm1Ahmz z+;F^RWF#$*ju9NYYh=Wb$2sTjv3p*-dJ`W*x@$4DFwQ?;t}FSAqlPnAE|i(!KrGX6 zE-;Ney1BxNW+)f50$DES`Ne!iGvwTbY$@O}d~bxLK0CxYJUUi&Q!tkJXri7sPcLs5QYw3?{|Dnj#^? zqo^=Njm$vF@FDhIst4`83*Ue0iEqVLpFFzo&a0OfPv7$IM;Dy=uY7#k zx`M3{n`$P9NnOx%qirVpP?%-UQ^wU1WLfP4VBez5PKFyL!Pr_Tm(R{+b7wP{5jZO~ z^712irjRe@E6hoveK@;kvb37u52%(}zE|V8Mg2OzcH?eaX61@zMm?snf$3DdAk$ew zb^@B;=jG?C1I@PMKvOoxk8Q|{8e`gL^UQGani;;_3WLW`$rulp& zN9TeH7 zWlpu#oH*&rHN*{;oCCs=f^&!Sm{4m|;U(5Q1XWib&K}@-R5vL3EOvC@o;uDxTyd#h zlrO2I&40n=hZ09=JPDCBLYED^j2xkTM4S3tQ&SE=D7~;~A znpf@Nbpcd+sVaz%jab&Q(T})6CR@rDE>vl4 zG-3cWH+eXK{RvgYr9Zf$X;sZ=hqqiJK`paV$$^iUVM)~u=Q2aUA>d=Ux)oR_0bJY) zteZd&0o>Oj3UT&)mM!R}=PhQ8wnA2~SWA~0qMgf^SM*FXx7+3(vIN(y*4zrrNHIee z@?ErAr%+`<>ciO{-z|#RaZ{3dZb&%wOS1O`+1V$`k|KI<%8-$rLsAcL(@mD$ls3R~ zblgxJed3xG-c-~&vxo+oW|5Zu0;UT43=p%nKI#IB8=8Q}HoEgA3dzFsozpU8A@skb z+LO%?#Olr#%d$>1;INJ_u%uZUIEuGaR#++Lp#0W{;uiGd)>?w*3bzxL#b^gAbd*Yp?~9t|fQHg*c#i710&7 zU)_4iLGe))*Y%JbL@tMlD|+DDIKG|ZJGeHpUAWBW?b>1HAc||>$z__jq{AwCmtAw@ z#&N;y<6C%ryzA;i5n8j{-Owppb=$*nvu=Bv>a)kH&u(5HPVxHO%4M3ksLx)jKDSXC z?AN{+w=sMxW}n04U}RL7n`S84FZMV9_Zbh3uqTM|UCuC1*fJRa)3Q-a(ll=#%PSij zS*4>bcRdX!{7ZO4+yiyd77UsPIcfhtkK;4ETUoD=@!%dU`wkU@XuE2(bMI7zFTfpJ zC--sLGn7t#qE#Au7L3)sRNxONsr@|C8tyPB8@_S}r9ydKX&cd&Cc{Gn_lyH=?2ky} zK>}R_o+sT79mjtMuI&JVXV~8nuXE1ySIM%&tm{U5<-#((+K_=U%xT8Jc`d)NSfL<9 zs~~(BZYIA(>H}mKJ+_3=vp&~a!_m83UZ!xU)`*+xUnSGQ4c|~ldymM!?(Jf~`IY*` zKKv*I>XTp;c`TtHF@k=a{(X3^OHr!+76Y_Fku8>n4*cY;a7*(|AQj;e0jo?{5|^Zl zGM)}R5m8!_@kGRN$%!Y$_{Ex1!(NRW1}A(NH*PH)h}OC3)fPy(t=t|fx7W(u_KI@* ztlaHZ?hY$==PStVfIk8|xzttZCJXr}kL0TIykxB-XN+&jEjKk}yXlw2PbX)shBbGr zWh=NQ8RA2Rcwz0Jx(^q!#W^*5kP9 z+N#nb>ncarXc^l?%UG)xi>zzGxsUERC@vZBtX)=KI{=5p+0q%tmJK@rC+em?FsXV; z>>p6Z+EMGV*9B$dk-+^JKRpduK|&-CmkIQ>4-KO9yxYYuEQl{{!e1wk+luF8#YeHc z>yo#+>t$xf|1Zr?qyJy+(qvQXy);Gv+b1xjY#K;)Y~zk6*K^Y~Nw>R!9yWsq?z>2` zu_jrZxLY}QgC*I)6B=vMr0X-%T$C)if#T84bYj)D=&aLqoug~3IJTLlu{E0NbY0UJ zdjJ*NcxAFTQk~73*qveYZ`=(=drOTivR=%F%d)XU@~nJa&NZz|x-fjHb-8Z+Z6Yrt z9PolH`6TH@tW4)-duF2&vB0?D&hV?!OvdnLGR3mGQqX|=GMNWgvbdrx64eH0iQb7s zOe~Mc6`~}#-_Y3(>b7>o5%8i%X(2;=qrZjBESGiMP~ehd$Hx3jd468kD#k85xm+s1 z?sIM?uV>J)xkZE{+bCF;fd!q0C_EVO*NmZut}ol2JLQJ_ zZTW}FK?yfD3U&n>I+}2kkJ}=Af#as98KORRE=zJV4&6IJ7P{B#@CNAu-FMjS=y^lW zy}ZpiG`HnL2i#-$>2CsP-0q|unrymCh}b)UBP7V9_X=>__;B0>+^u>z?grkkwyC~H zMda{++KStl4o-PNb*f!lt_^s%+QV@l@Lsi#Gh?|2!(k zQ1$FNtatz2ett#Zx8kJ!d=S`%K37JSE(L;{TLZpa(o3(hd6{|M)lSW#^%7fe4F>@ zJaoPC{xl`gzj9Mw1Lv_z#HtaA5a>N1dmMmMnvX_8seZujw3iu)Qn}(UmN7Dw#catx z{xcpIevD?HN6)ag&{(~VK*M73vDsECV{>6zrm~1@rPfjUER7N!Zmm(0h7cVa!WLFN zxY206gr?2M?eBjk<7SVy(ln;PTgKbflm@&d4d+aIE&y*y^E9Oar!>qGFMBg}ck4=N z8j;_lVd1fnOTbp?vSjm7>eaP#2&lHKp+BI}@gv)s(UFHqAgvg1F;P+WZsDC!EFZlMqh z!5APGWZ7XyP25UvZds{#hn3TGE5rB2&%IJ>e2*Vn`A=_Gv^G)N`0n-d{SRhdt9u6i zxeNZ1U9)>%W((z`bxkt2&$n)1r#opcMD^=C=+pKN>Kzhy(+;|4qgB2p*XCpK+5hoa z*yAOy883_Gw`lb-Mf|>HOf9-a1~I=D8H={Y-Nk#} ze;0YP{PN5+Shk{{vPWp{bDKIH0}aQnj5mTC)7pz8gYH9bHJPEC)jK!p{moQtJ_>JI zI|@xErrEr#+1`JOn;6WMi@oO+PTP&VwLw1TOgEsl=LV?f`200J>bkE+-oVZ3@J2nj zsjgSig|_`(cWS|8ZCHe+?jUG@i{4-u|C(L^slfKf?08xc=y16`F-B1n3m`TWCc9j+=1*I z6_dj&V|9$*2^xDE>fp4iWp==N-S~hV!_@JnG*2?G2)#`So_YEs6<&wZc}&!B^2)Nm z<+Z^7v($2twGq*dca<`Bs11=TMC&{ax~=h73Y|SnprJ%x6?_cleW8^PB*)m?o8+Gt7-R-ut}TlY`c@t zwY9(R+=~l<58LUq+i8KpchC2obH4NV&iT&wednUk+Z&MZ30_JrHh)u+{(_Lsj~~cI z{H#7jk_^d^%Th(o$rULl$%HFqrK09kkyguE#gp@hv{u$D-ki7M%lQQEDf=sdTtKAt za!)0g3s!n_y#n`^`zrmpev$T-2P%7VgHC#=GOVDT%3c9Sa(e~cU*1>QpWE-0IglGg zKLX{0l|#8hva~E2J)e;Qg97#n*e77WfCB>V5peJ`vN3eempg1ebXUq9F^`ysjo|}Q zs+Hc`ksj$t@9RkK??@l$NRM`;58CM^$vE_BITyn7FrG(^hjPb^Be~<|!{(8@GTMI_ zy*ObWiAmq}NbV6cgy(4PQS-$8$IM4QAsHd_(N9R9RM%x$dRcy+;djFfQ%&Q@P32{o z{fv3UI66#y51S#E`gt=Xs3A~~y3{{0j|%EhkhOq>&$I-^Q!yf0&x%h6@C zcr&}YQYQZX82DczHc4PQGmX_E(Y@C4>f&PAJU@i)aV-{~i*W671W=``+AbYau#9YLoO*DK{Q;}pWGd6+TNZLgwxt1j+!I^A6n~o=Avx#J!E1>8o z7Qdc#OOA6TdxopmS}mp%(fIkts7GpaF2&WU)O^D~7m3C$C9aQ6HUgP!Dms(SB*w-Y z-bgx?N}_4+{OpxXJ~qeI`FT`Hr=ncHGMi0hqV1~Vtx^dZS(3^pxQ3!>0q0xXz*aOd zhOvpnB{bj@Ep_pmzzEZc>s*~n&9$nGyR7H97KvR?b0v1&WixJf27^c6F{jZOp6&5q z2Inp%FzP4(Dg(@?+Epgn$w}_HmYAJQW2Bjc!)cOxX)F`tE!Gp~RhmRkB!+HtB@*Lm zYU-*@)RK{Gn(NtgBsm>NJK6MkjSBm&MQ7(p02uJqX(yF^Zr(}F#bY9M-iyMG9*A~+ zCXz{|u{2yc&!u8HHi8*S zW>QzOGlC~d6HV}p&ZdwzJr_w|nM&oZi2Rwv<;xN93xZ!fIelgJ5=!tMVDpK1I@&^J z64Nu+B3I*l4|2e8b`~>(hU4j^U_F;gCs8(*`VPA}8qOK?17i@Zvyse|%LxoeFqlmx zr)T5S)A1Np;C>WNB$IIrf*L-9&WnouKq9fLkz_PJHJd`Nn1rcWjMknM>dbT+-NLd> z#sx1gNFqT4no7qbmm}9A&xuygT$@Q`;~C5_MwSsxx&5Un&&1~=m`n^q%(LiL%I+35 zlt^BUr?c_cOk{2@Zck|ZYP@UIB$P}x5xty&hPal9&Ilga$mJ{PgqS8wM-usw*)wga zI!uI=XeMURiMa%)!Ox_yZ>Q%I)A3YNNb53qlSpVHuv(%s)Y;TEF_x+Lti61w7qN6? zI+eUM`<$2ynpIcoG%?wk^c8dzoTwR0ax9*nN@cUDIkAi)q~T~rry^6&@gWqs?ySZ$ zb8$2h%LpQtR{Ro{LKiWbg192HR5No8nW7{FPYmcMgkTTAUNd6gy=Y?wdlOxnizG#M zW*%z-cs4aJdU++8c+ZvinREhT5CyPpFK1>V(4$FFATyIn(U>}BQbcu#h@2^&dXBaOHW7(F7D->m=D8A!rl@h6K{S@m(mD}yFBUYA^JEqAexgUx z>G*UWLz<1_1ju5BuXbi+W>fP}MA^(G?stoGJ(@zbS*}gRvpAHlV&e|Exp|yS*pl)2 zY}mKipSoqTa$zNusjnI()7l)Gt5wWueYFxwR1KV?^%}D__gt-&S1V>GX)j2HdZ}Eh zuXU39=W+O&Ason6Yq7>En*-6cV!5m4(0rkeW4Rhio5k8vwNx+Fs+&WZI=ET&QZaOC zwOS;a#l8=-z`mcr2MK(Lzz-1kK>|NS;3a^tZ^MJfhE5c7>ln;?3Yn@^Wtf2AfpwB;@MI{aoD(e%b-b^R-JheTMfX zrK_|LrTOW&(B{fi3v+S#gK*t=7m?KVBIkd>y7EaW?Zt-LU% zz&WX|*FE?Bls+Kc4;Y?xx!$v`I3o!Np1-kM?;QSqw-wL!R=Ruqc5ND%<8V)s*6S?$ zImnaC@VI zbIs!3B@_F#&bU%9i1owOQWfP&wa6$*iIZ!F-i!`$Up@2K(GvE!TO!ptMKD1A+jS z<_o;vS}w4aHbb$uZ15}8ZM!nuhvF*KTa9^Xrz|5!Qhahi#!u0d0Mb4ssQQrhA+0>n zR6#+$A}ep|8mLN84z_SL@P-Cj0JJ`b7SKio9+ZbWO8GSQCA3u}eb)Mr&FJpNKf^w4 zVm}_hTD+-Ye*tSMv~QXU)*oT2z|?b~U@fV2S*%#>Ktsdsn-yhrQAS4@+3;Y`ETbOw z0M$Mq5#N`U>I(8`_kFOg+*dpE*Ak24( zH)YDMLw&)%W8d6f0zBpFpCRAgL!;ZdiV~kki5{`bo^)E!#8~9&XOR;WIq8nkXksLC z_4|<9D{{+jJ?fKOEhDE--1p*%Ia-&SE#5OI(=W=r*5=(=&S@otas#3q-+>p} ztG+}N}(3VH_&yhEXJcSK*r!yn; z7dP!9@w4aG$_B zflm{7OU_}R*p=*yD9N!gxVK~#%Qeg7-skHpYvs}c?=7;LWkGW-nC05-Hdjq zlx~H+?Dwd?uLfmHs)hYNp?g{t+bYk;%tAR|U9E6`u?F2p#tDQs0< zLvHarsXWojWl~`O0WC>NQ0s$w(jNF!TM1Fhr}RD00S5(i(y}H8A1L_h9;i5x)-@_K{|4uM?7w3yMH*1&ht|nO{H%{cL7GxdHl&=m%D1NI|5aL|eU5}`P_tYJzL!b_WzPBU` zt|J2O1KcmTbhb`h4xs$#J>58XPss($9y4ehG7cLLy`<)P1$M;2`UDnouzrCZb+7?} z9docf0z2+tgTRImj z-Ts^fh5CHLrM%0bJmONGaww0Qj~P!J?|u;`W-e?#ZaicB8}p=j(l~d|V?1k|e^JXl zVO%gSB5*loo-$9Hr;G@sG-X7QGu}ao8F5f1Iw+TnX;3DO87Q^{4|W>m?ANhkg+;dU z03WVAxqNBqjQD)L389Un$5sJ2BJ@&4~UXK{hcl~ksCobE;%+a+3-;W zmFO6vRpQ!8sd}^FJ^u7l=95n$s(8FmJY!B4(6s0?5;k$C5 zlQ`q1&W3faOigh$feFz7CJ_QeQ(lJ~`#+ch3I|sTrD}eqTmutpb%7GrrjEGA*d#i) zu)2maUc+20kf90VxKK2ajcC}|yEe(g?+C(-ogLrw(z%7%-t>vEBavw4+7Sw{wa*`M zCW&*3Z1bhY|LWI2^}erP2&)Kq+tF)_Js?@(wnAFE^VNU1`Qdl}`GuuF8u<6W{n|@^ zdSQoIK0D4e3(KPs5JBh(>PLe@4o-LwG8+cAB|r$-QVgpjnlUzUcDzB6A3NAFc9wmZ z`f;DS`Hio<@)yaC*Dfsm(E7dQYe)a`!jh--2cO>f`qwUObA-uNj^bw>0wBd~s8D|J z5)B<`ui-QN_w*$?{R0%YsnyCx?RIru_$BN=qYh?XM?8PIX7IgMy|$7!R&l`LXv`O} z%FCw3d*%u+#8}}rRooay72)D6y>Ivzr@zwp?1f7(ApDp;zOP)X-CQUXZ|1R1;P$i` z2yZ%HE>%i(hN~or55o=57if>+Xg3_*yth^@uTkavN}&r_ZfdVL_IK)rQymooK%DLDclz*Vm={-gOE0N(120d#A4%G(7vHJHhAYn;v-jTC>N1 zRoL_s1m-uq@ML^9r74`=!2H0po0K9tQWx=MpEd%B1=#g*SR*HR6P7Cbk9D8Xi#{tx zA9DKu4a!{wGGZ@g`L3#_IsK+bbO4uOWl|Er7E+Ui^a40DK>;HI|2F zAzeu(*2H+;Fs&l{264I$0Q-%KKu5;N--9}qRmWi($dREt>C9_aN79xDv~b-175;!`N`ca}FFUSZ~~Qf<}BcT)Gb)U&OEIBr(S znJ<#l?{&JeZDAKzu?s7CQZetNg&o3>BxM*5F21l8N94dhT^@a)C?l}?H2*Lh-7QPJ zgT*#|f#KNtg2E9?kSF^EB{e&7TgL7SM8MuP7A;bAr@FZ6p(80(Rmkqe$ zB2N<-f@T7vU>BTk2P20Z?zV$@1?B}N-B3Y?a(s#MDa2$`dK^m7rSv+KK9|z(PzKJC zYrO~C4i@SGiav_xL3$2}ydm1i#_+m)--DaX%^vMhXS65m8iWb%kBBlIRP^Y$)AFNk&rut8uRZf&(Vw1e{h>go-R|Sy zsX2W+DbjY|p6KY$NvA)Li~a~q^k+7b%NPdNQKMR`zXr;|bnu{W>+8E)t$Mp0a* z63__Xy$DI1lVoPfd<*kS8gOM(%-S23T8?pjzTe*Fd3+64RJ%l@)xStSV z(}siW3&e4Pz?TRN64(Ubss)!@#Z?(V<|dsAI1()0TVGqD*9-E{$VHRy zzipP5mh0sBcNYwMY+X%0KO2W=xZf?o2e;SSs?Ua2YAY0iG~2OC>%Wjjy9$QVXkWnA zvjXaaviSNALF+nSWkepvkAjdv1*L>HKLRg(1papr&uuw+Bh;lOcj!QuI;1-)vMGc* zq=Tp~-IcIGr5o}M<&K7@iYH<^H#9sE^Sj}}lcMZ$Q*UaB_IU4lU?uo)Z=fPx=A-vc zP|D}j_B*u$PVJs|R6FR@?saPSIko%WL2VW9xD3)=$BhAOK#GS#dBaYjCvG1QDfPfg zftnUM$RCyr??JL(V2#xGz{L-|DB`bEct>avhk)3)!K58@f0FhmZ33~dmtK45d%yOc z*FJxN{U?w%zKVlJ?8~l;7&^V^m%+~>d#wqU7rPF_R~0V5z~!~A>&JJlT&OGC`zy?+az?JQ8+Y_9p6u2f0?p)f_x7lyt*Ta3OCM$O#=G^0p|} zICeSctzegfwjx{OB8tQH0{l{VWZj%6c40Zu&C)?zEGM>bncRiTWH%QFZE=BLNLLti zix~EA-N6Ajz6L$eH5q*Gj*|f2yJHS)tkn|xuV`bd)CS#6M-@e&?-|I?T0~h%L7)zI z1@v|O23dLC=N0!6L|1m9Z!53+ly7NJ3jtxCDO&J#MOQWvUJEpiyv-`vT?c#VIl z#^lZdA;WvueM>rp357FH{KbJ!S6Sgfb`=j?gHi4FUS`*cx=&Dd*vfq(qrI1HytR_; z&CK4r1LKL^cw)D9@oeEOo)cTUIJq0A$t|2(c#G2{Gtse~cQjk1KHEKUCxegdJPo|J ztH%!7YG%jn+&;ubRk5Gzw4cA*-p_Q8_*VOQ+rHhU{2tA#Y-+e&9C@H<%Inz2-rWxK zc1Mz7#K-dD7B!#efqcGFGgiwc(!qTG`PBk$Z!3iLz_6p&ClProAu5K@3$8va7NV)^ zMHm9_fK-?AMCT)2Xnv(;;qn6aABT6Q=WC0LmRaXV+-wn8v8nlzmB-{3ml5>rp+H<7 z+IO+E#K`902bK`Gp-T=6c3pj0#6Mu|A)3RUBXNH>fdvBB2z(cT0s!~7Rdrs(43(dlt*JLYD*A683O&R4a+)5kjuOEMhc%nRcZmqwKC zgo2JUCon~K;K6G?bJ(D`9XVL<*lA*Y}7n2%4RyfEc>3 zZGOmYgF0-IC5F*hq0(aLHaf@9^};Re%g|FHabXa0f~28R6@2NOan?|14S`UPwOm@PzbcEEO2_K8<23c#wY=z3EA09OQix>S zO8!2I7d>d4*g-a@w$^e07bHpOjgPpK_BN8J z*6N{34U(xZ7pkE%|DWX|Tyxu}f0+dKO1EYavN>3-;SgYEzP^l0rfw9@1TTa8%v3fOJ=$zatN6%EKxN zupgo6?%pXK68S9*+>X5b2vQq#65-z$yGUusGH;pHZt46tl1>QKoITW%jyC%Dif0tY z{phCR9mRcn(T0*Zv zTyL?MMEI(EF@1v;6UE)$W-<9jzq#9j+JURMec9$7kuev=&$xF~YZ z!=}8CUcPeAL>s37j5rs6?VW1lr$ac(|M5;m=MbfX@0pM||Dfj0mSqrHSPNC%o;~w6 zN2rk2tBQSm{^$;Rx}VNMbiZ|isxYqX6ErX?AEOiW_)fcgORe2yL2rh(X8a!}i&kwz z+afOqCwaMLJX^{*cvl$&4ZGy-;EvqA?4xh5Uxh;b*bdVQy@=o1*mEaot8rp4c+ci5?y`Ct^P|wQ;6n&y zhn^&BR$K_$3d?2mk8Baz=iIKx+5Wa+SHnxa?PYzEq(FhV4mI?k@x)HEzZ;9)rSO5R zQs_`Io$6)F@xE0GZ(}05(%Zq*fV%Rsd+%`B?qsbU5NpmhrMqYD-R}CeE$U8d;6Y<- zry1Op*S9f+?H#?yEvnYVQc?G4~b2}(-Ydd1+eb^X*YnlP7??8 zu3N#DDD41}XQz{t_yWrW{VAttzjecY zj0*fXfe#b-2!W3i_ymDZ68IE>pCqtT%;JZU@mpkDJc7;Vlj;4JKreoM_zmJWq{u-$ zgToHJUy&Kr37;14zAy#VxZnNP3&Wg9e6Ne2f*Wt)p2Eb76XJ~-ez=`yIiC0_aZuCW zf-Mn95>cO=A1~XktewJ(yo?-CerSh^c>ls=i+JIS(Zv@FP&s`CinL(av75d+{#&zf zs<1$974ZUDDbi< zWA7|h*A_8X5SBhKUFx< zHTIj7OOajnJCvfo3fb4D^cJp&^av5{Bya9JXGavz>nLkg0S3gMA_xx1K|Lt%7k`lm zs`@^CO!wfc>wZ0=e?a<*{(fmp*TfyAeTOOTF%kY8L4Zs1sk#W89`ewe%+62sd3-V7 N`z6o7`z88T{vYwXHDLKmZ~UkpK;VlJ-th49Igyg2ID) z07@%{8h5;#O!`o|Nz=56m5mg;Zrw*ZZPIj`>2#;#KBv<*Z8M$BnYNRpo!U&(Ox;XU zTehWs|9>w)fYRCl=KlBmkMp1R`Tz5uTk7qNX!u2MWL8i9lBWH4LOOq8AlLCUE*P37 zG@)0ts_y7j&Cztijfzn<9h1{$#i|CJ0H>{rT@5X+aCO8PVP2$ipgQV|`f>-Whn&NH`bhOP z19hq%W%#;tl<7T{W7Xr%abMVSO=`xy=}9Ar4eaG2r9 z7j<#qamYFCzWJW!oN>>%r^V=DE!#>T>_{K#NFVMT0REj(0enZ%R`7fRH50yFqF*z58ots&cTgzO}kqajy=eWy(sX7UIgf83!~e8Cpmu7qjV1VcT8+acn}F zd5{BzTzVny(29M#B7EM%2AlU>{nFT@kc8|gb^Q`?bzA)A=X<;e zC3WeWL<@82JIY+hF0`^tv{f%CE1tZQQ%3SmTg`-588jYsN1rB=c<;3bJ-BcqjaDZB zkQrbxyO(8hFFBpUUAf%Y19L4VAX|qe(7c!&0z)e+05*GYIZi2qzozy z;dCaGLL;c)X;hvw_5+D0Z^bi-)XaPqT<(OKd9>E+6spW@4%Nc6&7@dY5F}1egJyE6 z_|5q3_}g6S^zFHHA(clDqh)z6s$E~|@_cGBj?P3gxSzRNS+81DP&#ufl`Eu@bMb|R zl-HrDTdA&A)1c%F>BP-Ec8J^Q#2o7=#BVO;(%enxjtuzm`RTn;^_B1_QBTaF5({ZX zji1Y6-OetiXH(e>kJe4-rXit@z-&p(QDw8Yxh*rPd2jkqEt0wTY&LUa{%!6H>ecqB zQ^yqMa!aTvG*L0=KgvuwB3KF>qsck#^I zv>dRAXy}vi+)XT=rDP&Yg;Niruv~%W3HLouXdqX~-BSHTkLPl!*&>=WpMnJ_poedD zGV=4;MQlWc{0$Xuiz_>kMYefm&7=x2O1H3ZhuhpDEEASwYOxRtJ?qcjb7iHpc_QE3 z5@omXYWOqszyvqzvhi&2R=u)SbvsE1K!SO!l$#Ga$^DD4+wKY2@>+u| z=(B;ugOy5G&f&#U6BfO8BImBu*J|Zvxn6rVoNq!~qgh@#abv5tLbQhbv*-o+=Lr0H z0)K(PBLY7`;4c#R03a6F4dAs)9_;Q2fhht4AU3S@q|&b{{fg2*qjc=&i8!8_XU5gs zGvhOOuH>GX<3P-DVCMK0n4YBq%nzPy0Y(LgnE+-cvW49cDTW8EDSd2L$9si9>qlua z2MIil697B-Khd6Qhv=<8ZDX%#50TDmU(qvgjLd4e22gqzu7x31zoN;%hrG|Ok7M1Y z#@8_(95t_|*&n?dNa4ihi5C%F?6!AC}ybO_SF&(gLpy_Yk*Kj%gW zUcdIE#!h(EKwUda4YM~q(^>9L0RFSF6C(flVPwB9v?+2$Yue4glQ5+ZYfmB~@KkU1 zJT?55L|Esqep2mx{o!^gftO3^uJQLPQ=pGyJsFy>^YU+DJe58n|2E!|W~Tg`0AJBn zgdFcpSC;Eympq+aniujX0b)@VXl!ChD{G@pbF$*rly%pA&``lO7Ynp0mC-Em{847P z2Dx&jz9J7Gud-Lml}fFohS_T^Cgxy=iwPKj`BXmEqrx;j-gs(9URRF=@A`|VHxxe8BsHYbO?0gg=I1YbVE0u+ZL!sRFAfB zGxA*vvW3!A68C?XhX4a7pYk+be)=2LMMr{*3T75s> zG@h89{HF%<7r_sL|D{geU95TJy#yYu#ougNPXe9%rv@l?H)Y1lLv_Krliz$<0y_2D zZ-MWvp(8JAWr=?Ri5^~Mm;4eeZi`;~5;#%j)T_~^i91?>p@kf3=umgJIQTrY6 zhQKrAP#2H#{R{AhnK!jp14tn+m0kNX_#@0;?&@3ScWJY(lfHY9Kfv-o)z#B4%WEuu z1M;IR-#y+e|8j{l+RL>hJv*2ws|NP0gBfMtV;4jI6ej<6UPIL1?rM#?qS7g1>z|OVoR_gcn(9&}Ko~ukzz84G1e@6L3b!@h@wa9-? z=wK`3-j)}VZmCkNZBOrpQR0q3f7FM z)u;Cv-Uej72x0r`dtn0(G4#{ArAJ>F_}c+&Z=AL*Ye(D?0d{F)uv}1)D)A=JW z>^h#t8ElTO=IBCm45167tUIPK9k>ooKm;5(6HZVBosbAQVG$OQGBzC1!!XLQmth}Z z|Kk971B{d0;A5jpb_n<|C?jQ^WjeKx{DH@&7=3JsgO3ddj)2pHCyH`=@$}*82R?vj z5a}U2!$^;~2i#F{NE{YN9+}QT#$NNWLyR5uvBQkL?qf$7JLY4rF?QU?jskmKoba(@ zjJ@Gw#~C~6V<+4<#3^ywJt^J<<;r+m*Dd&93n{7%A!9L1$2EoEp;=H&3 z{KzBAIkyjwF+SGjzs0yAF8cE4-3ww|OuP?g4*|@JVp2@G<4az7QVIepj3+m zRCIY6QN;VMTrSlFV_Tb8_l((Cx`;2qxuQ3}rtrGIUT(VZ|3k=nzbu;T%0>!@ftax! z@dI;XQxn^C?Dh`-W0z5rMp;>#<=Wktse&kB1*bI?@)OhT)MZ3cjhPw8OxJ{C<{D-B zf6(HXff#(LTq|x?>d4jDTBbx}+s4ryMJaQ6>j7#M6z*z?Tr0SPr4<)!1boImwM~cE zjyS{EWs3P>r>5Xph$Cx6qQJr4AslMG9hY}u-kI4OCBJlS`+xM?AO6ViT|-+!!VB;= zrIyrit;Z* zc6WlnzoM+<=e~3p~r9H0zs1v4tHOdwFpMbE4ulRL?@` z-J~qtr;ImX-kzf_>R_c_zq?#oxm&~_!YSXA=CfY0Qm&Sp@&!bl5gZbWC7O3IJi=F` zdh4~y1Io;iYSo)Z*)Ud(*{zZkyN8fb{v&``fXkD(3ecoMfOJaS*=R9ag<7>xfqNxP zaF$U0d%awwCaJ?6gs#d6b*O_yvQ!1mgk!DMa4W0(tyj-phQ<>kqaMbuQ$K9ZDp5(b5FKg0v`XuL9hMD!Q^IVMLonIwkC3bSa?v zT0Cf4EjL%l^=|#5NRZN-cp5X%O!EZ?V4g8gjX#CM=)=%XaKj`A{fYHddl+U6t~Fy3 z#^63O*0U49;Ww~@qx44LkhWpN6%Sm8d)dbAyD)4!5fOxI9wvAf4jNJ+xc_0|$sZFD z9FqfZdpq^m`}9P=ubzJ=dRb3jm!2r=`5xut$|xlIkyG0UBK;O>5{3q^h67Y8QN{QTBkZH9DF+c?c5cid}fXBns&G>IT*%g69Pw_l@6+ z&`o`dKH&1-Xr-^yXh6F-!6o&5U`m=fVdRYn>erJryC9*&DM)m3PW$tYYTv`XkG$ed z*3%C?S0Rn#5cE*VXF8<2N5U3oc~p@@oTC5`cF$Nw{x>YA-RH2oyWa*_^EUPOKjt6= zZ>5qh4dpiokg?c~HtY2wHr(RG#feE@{-;0Q?cdLvTKnkOkN&UEe)QqTU0J4m@=tv1 zMBdpdW2?pH)b#Zy|{zaqRd=e`w8IV08I`8DEsdHl3Z(e$1J_*^i)cXrf)u- zkxl8|E7!Lg#ZD?3>QgWKf-hO6l`UAWY7rKz@(TvGeF`ya#s~sA`1g$JeJ>27@3~<_ zo?AHGyb{-OPNGl|xCEyFkwVz#48u?BmK}mM>w}y@&kMsG{jMD#Sx`OwW}klYh4Ee6 zwg#aG+D@{@W8mB7J{#CkEM5v?ze>bu0(jB3CMi?XT3U zn^!}q(;tAVZBKQX@%jf+SCMFCjv4X@4g6rsWU~uR?3l@OMFq&}V?Mv6 zZr?D=Dd@3Kmj;`AMCST}+sD3ljZlVnpof!JqLoGw$mW);tx!L=ex#2gcnLg>x4=RC zjobx&gQ9kRiX(TFHrrIB+ElAeMXF5=w5doT5>Ep`TpO_`9q}n$n1pSFkU}*65lRz? z`1zEePYJatVV}ZrY|e#4E9Z({L7hb}j18h;hw1u)->=cTpLzXoX>eSAsy~U~S_8-B z4(d>wI^03E+SHK_YOqZ`&_NBisiR!m9+ZIEh=YFp4s}smEj!E_@NErhMx{l$X0Neq z?>-GjSr*^hfR3+pxMi(BnNjDvAcy;jxa+%^J=Q_pd)0}>j zwR!yo^UMA9CUY2L?Ppk~gNl+*`6Zuiw}*`PIlq-LuFv30^`W~KuiUqw)AH+fp3`34 zE_BrAykDQUxIT<=ea2X(gNpiG@auCCJZ)oWV;FZnx*U^BUtVRNr+&p5D^_56Wf#cJ}L zg1Rir}~#Y5qLq=RSnhKe6UF$ zffYCDPFWqg@0Qorn-p^G7W8bw5II7$`|mkH65g98snIxVvnxUSx10vS3AdFvLoX{< zJsk#QpXq@6Q{SY^BKk_Kbwxf%rbIxy);kKTkN-hLTYHCe{)djj{&)R>kKmyzqbMS@ zu=$9;j>58!!l#Jh{SqfKVjLwM#>mL_i7rF+it2P(B>I?utP)!!nrKb!Jq^1W+oXpS z0)g$p>ee=3ZE$l-;STSXRo}69EUeVvdjTAELs+FIY)y#1CWKT7Qj|OF=Z^Tfd;VCt zqkisQKX;#>yZ?`o+mv4?>$fq`9Hc-F{200egVn=c6}YQk*Mz+a%gB35FvG5M*L^p+ zHuSJ-Ly@5!$X*93{|=CrH>1C+^auL~KXj#1sxFJt)qjf-m*bd9+mo*z4O&1Px$pF3 zyu412zglrozwTYm{visE4JnJ98F>wAc^S&N|LHIEZ%UiQ#sV0_mq))`a_IK<4;3YGjPuVf1CbZ?1Y?r9-)F?TmLKYbAcMdBEF@@Jc1$$Sb7j|(x2F9wZg0DxBZ=40 z|4a>~8LSzfv>EIPXJS8~LPfqkFBVm#Sgh8?R>eg+TEvO3gj@S6VLh;F^oeaz*2{GznWgGudd=lTXnq6=8&aFEtVTa4D`x6 zLhL;hjxJ*Atd`g08|XEsbquA$Kc6GzS(@Uf37jHug1|WfZxT2I;2iJ|mpD;l;k0wo zCa4atA;e0zWer%#<~m0Nu^JJ0<;BK}N8TY-yb5_i-3F?{trggiIbtsnc$)yZ`zn-7 zCGdUkd|u{>c8h>RfWEzvw8_X2rSSc`CT#)~gGIbvlHTS6iI3soGhp?@JXh+araVtV zbWV{JhQvv|ywa2lgqH{`6WBXwaf%j({I3AN0cPX#NQ5lwg%z<3I;-+O;Kx->`&(9& z?&d6_Jhys7nAyzxuJtwR5AAVZ-pl1dx=-XA#XZRO?ZA*e;QfO*&!*`EiKrXu9r~Qs zH}q+)PwXfjXBxyaU18w{wx}t#{(5g$jJU;>uOrZAkj|2@WSbj#9(#XbuvG;CWz2ElD-F zm!G~9M-62!WjeV>Z&$6AD6YE|w1qto)z`#FSj!hn%fwy5y-O>_UXnVsr{^9@tjml3 zCG*8S{{r{p%<^2NNCzXlO5e-jHlo2hu?leO<$vI2;WIo@ugU=+PA`S2+LIqM&22j2 zHtzK7eL&NYHJV#rMJ`#V6x}JyDu5GeWsv`nxCLVTV@mxp0s54M?Gcapt0t!kUGa9G zt2A=TPO_y9=WEnj!HcLWI%N9jaUBp16&049Kic9)OalDxIpe*i4F z58OWs@&JR9%A{<`Htn;V$gxGsq@^f6$5G-(9Mx6%CAkv2QclXLR7EvOIkKFGS#}&L zP8^BSk@>o3c6Js3b&|_YsA$=G{@>mI-~XW0-5pWzi(Z^t?D=g)`4dWbe_KTFqDRG48ACHGjd+xLys^f`wo$R0!3=g>WrWh%nt> z?W#o!QN{z+?pjZwhw)&wx7JtaV?0#tuMHFi9ek*^OG8<;-3<2>b~8O(-CG+j3_CI- zg?*??q`JR$pm0D{mKD+UX%#Tau$y5I!(N7c4Eq@ld|DNQ_d>OoKsxzsNihnRXu91@4`D212A5pfjnW8yjSJl>;|ig-c9 z?bw9-O^6jaDZCkQrb;)6R0Tjh?dnSEpxYvS?{;+R=2%3Q}86j7X+Qm_G#!(NnfmP=dxyzTP z(HvGVlbM^GNl#9uQxFO(!lTt|ngPQpc}OP3O}&^e|eMU4m{J66y%dmgE#wHglERGLfFK zrw`R4l}$`$<}S{>%6&n->W(^fOnxeR85M;lDh8dLN@piB`Fv)Ur%{3|9QEi#V&YY6 z5F*!{**HF%Mj@#j6EU^Y7cmt&h{+6wD=|Ykb63G+NenzGpoa&+Zh$>!xZ&L>V+v~% zRhmuAF*`SpIe~OOGtafWJU9Km%jxm#G=_l%uxu~orV_BDb1aaX%4Dcbo}T29V%_#M z$6Dnf@hmF4yD=QO91oGx#S^d6a=;>@p-&~Um#}y)r;-^eoO%$2W%D#oxbJyFBXN#) zN>(q?6WMHfvWO*Z=k&cXRo6K=X5%NpgzQoYiwH0qBAb4_S7 zo0W3x;#$2-G*kW%dO?1az{d#uFo7Q-@S_Co68JHIxc{LauZOhxd^kkl6afJcAGFkz zrJl3YvzGc^ONBj8Ch$x=(#~ZcY462zHv7nT0tw#i-=^;tq#VA!sSE$Ga?9V;Huc-;t-z*= zM*AKp&~gPGp$?69*pKS)APah^; zs4ck}62ZG#hja+if5FlLN4lLeyfx=aNT3cM(Kf@28tUpTHOyPy32(WZer%Gp%@A#p zZ$-B1LYpFIm1dynzaJ)iSh*h&{s(Ha>w)I9B*Hp>{b{vx^oQM2{M$?EtnoXQDbUC9 zt~r{qGxAN0r=^a`U&LF|+?2ln@TO`-Xg|Gb$Vx*zq`l5VniulN0pd~1Z?0ldTlz|a zW@Xi=Tl#fl-L!&B29{`3T3WNj^T+a4>X55c8)eBG;lN_0TCJC?LEg6(lT+}-#Uxz7 zOgb0uvcfc77il+bg)1%FV)b)zhA6g4-F4%pC#f$Pbz~t}tW)j~qf|3&$<4KD)9PhP zyH!?%2~cUgZuOeWCAr#G$g9f+{{&5TGUEhMUV}wzNSrMda~T{(3#kzmkEUx8;2|yQ z3jq%S*PiG;rhu=h+T(x@suoqFt+X%lwhmeZv>t~R(MLEPRfjxMAzl6f$|{oyYyD`O z=&r^uW1VhdJq}|oUe~d{klNH}-E3-@f0XiZ%6AqN%q3q_IA48&_CH~5(2P~jH9`&z)Hvpa{2Re9^@1KJ=$h=eS8bAtpsqFe^!5?D&LPy^+ zzeAgTf%M&g{4SRNct=lfmseQ+BII|oeCK$x{Pq$jl?Kv;1dM?9)X`G@G}HHN#GuVpC#}q z0zXIKK7ntmIUeFwDSro&78ZsTteE9$!!)emYt7a5YGuLdF3W~#!g4Jb)yB1jdhYLVgG)k)&Mc8+fei5udIG)WBmc(DMx#&8}@}y~TIl z-_Uv->}$}xM_On{nHuB$(LU?~9`m!Bq8Lg+6-q%9s?aKG!6$qLY#j=I;V)n_Q3#4) zAtXYDun{r3L|8;b*Ii#B%BiT6>gH6plj`AAkCW=PKoo^gF3RP7OGz zAx;fCsa?iyF(h^wd&F)~_T2Rs_PUh44rSP-3_Fw&W1rY1G%>BU?R4$p(m4OVcz)?;ewu#2e;!dpoU2DqA&R)LwvOXMP#B9P+Vxrzs!@BTA925Qn{pM5p@p2^nf_W5@=QjV6qT!du3rjsMge_|*6R`T4jH zQCK^qYRdme>@Nc>-TKoXc=U<)eC7PozwG-LZ+-smm(KI{^1pun(B~%qc;hqYt;niu zGz~t=$)7}qhhqeOkFx&x`#%57NB-==dCOmJR2$OrA+&1wm!v_v&4uckVFlKWDp^(k z&E-ndu=MhJsU8nn(S?N~$c9`f)di>4R$*H?6@Ym}ThT4_mcN98u zRz!2z3IO9%mozJuzFMhYMH`n*GiRoakbh& z26JtJ5OdpckPKB4k9iJ&43zL;V(Od9in4<9(Tc{<$53Cv&P)XE1w;_b4QGlKKT{(2 z{7XK2U*D5N=vp*x*5`Tm&XWgK2_W#j+z{3-v)NcJiZ!?aIFMo)om4eUt82FOdP|+`G{GpR%(@|d_tl_IGYwrG#>DF!ck;(H|o`O z%3NG6RixFOL)q|<=HyyQiidlVQT{W4xSz|DxMWbIMu2om9aubIg<7?+{5MJ%c$Tnw z+f%1VO|phPgpSGxYp;hzvQ!1mgoBQ58r8+EF0-qh!MqIjVID$#6rFbQx?dw_#edo( zJq#ND0;={+{Vz90I#=&;j~Ypvcj*I&`!SuUJN(>>RE1EGlck`TipuHbw;9H^1;EIpV1^4v_%B?V`u%qWxgj3kH za;l5xQb2y+rXOc-|7Ms@>e%VShsy60L7cjkmEcN1eh~c7bshXL_z~uRP;@ar1pep1 zkGlDF;%_1N;J#IpuSH$n>#% zbR~c*r!y#_7d7Y-{WzP135tOsoKM7jUH|&xn53-Oai` z;kJ%)Z?C-{x*`HsbkGRs_Jge%*L9SKN@U%i5W84+59@vlx^X%{PF&@Qz1Oj;M)_}v zVV3D-nMaTbKqkVyJ3=z;x_f%EPwcynx*-1}v7cflSo;U&;wJ( z25rf>-+J?F|7Lb!9<+L+SY9sGmy9Jc#P9q3 zr_NXUda%-?wuU9LY~wA(Fy(Es{3;d6Hh$=<=jGRtVuL8Z2FxuCZsmU>{qPzQL)Z_? zA}p=^3Q1Eq!it#74cX+X#`_WVTD5i~zECXHOV#xavNG*h0j(Sk8_3_HoI&%oHN)62 ztbTjh6x)rKAuJTD&!M|*v<5pQ?Z&};tr~SI$0)8f%TSAGWL=nB)RM3jb@H=g@uuLE z$y8zS7F%s}^Scc+>E3ePj7McRS-)NaxSLjFKY;-PxSm$5!1bFYxn$Z$2DNB)RZC{m zvC7uasx)p?8f#|JOGOtr)YFdOHe**_gHfy%VF;@~r@@xIAAP83LkLRH$smMK1Ogfa z^^kf5Cjo|91V(Q_jbL|==ZWUq{dT~QAP8tZPc)n-c0WPL!>{*~9GwV=_Y8Oe-!?Pp zslYbxh-TZ~kpfUlnCJ2z0@ z+l1pj=R5{?S)v(60i@=dte44jw|-=H>0!4$1P+Blg+`$kGQbD}r~;vuCY(1g1U>K4 z8-W#v5_BjbmjZLkG88Vfb9FhC=vfMZbi?Ez=rBxiVt((VcQ5mLVZwc)?}18&9Mpaf zb-<+#dZ@Zf9r93vE_IiO8g{9>xo&=xfZB*XPW|?FP+Kh2pJN#h6*W5Sl>EHg9^}UN2==(dxjtRn>O&!IyWAI{Q+Mk2661E=j(O_y zl2e}-xjvlY`i!!Shl=_fbLw*(B}dt*cdzsyKu7K!m`m+-slZ%npGyViQu|#hFqb;u zQh~YDL6-`Q>SvdW5glS2rE$PZ6(<}nO5%2nQ5p{E`$U?SU8I=5I;?Ne~ z@?#$lck{0Z57{9k?EZPTTLi9_ zs&87|&Gl9KFs8UlS8~?gn?_}6xk>S=PC*WISv@#hwqxXDwkzFD#BI^4u`R+Ut)1mg zZjn3n6uDFK-=YCHC$N9~0yX+H54T%UYhZijw|ct4)y66X@QrpW%s(UVumFWh43V$y z0r)Tch4yQ^)s8=;hwxDNJ__#!ABR}jZg{iZh+akU-p0{A`L$=%K7HfR7Von|b+)+~ z+VQiSq1oP4ZY#GCyXOU{tswrsqBHa}46OKW`?2rWap&ektQm$^RUGLMON1Fl7 zz3G#`Le6X@+U%y2=p7hYGCFk6cE1PZ^{(J!FgwkAdz?qNaF8aB2rR-OvLOi0@s6(J zHWA&f(38Vs(h+zQvKKo1JC>vRx}~mf9mdevYN@s$O6S(#d1-+u8{<#wS(=?389ja$ zmr$juc@Bu3+crC|km>j%=!o}OdKK5p98JVRN5)o6)}HF}GKpTHX~*1FJa)_qln`M+ zu%RQcS^Yp z{{TA4JOf!p3uyLPl~1qV33lRW5)Z*S9QZd7XNU_j2tGU3<~e2Gt(S4^o?`6twBW)0b5{!(9A( ztWc5f28%^2QY_XQVy$Wbj}~z{DB(`BMyW2i0{SknC>s@oY@AquJjjEH<2pWOs4v;^ zhTR>>;%dW0NFGuB13S};jm1UNXj;#@Y>tiC)MCXfVmg(V4Iyd4Tf<9;wotU*gz;}K zb7%lA0Wlb!fIO>YfqIK4CC{{1sgmK=oHZqFrR5^m+&h_1NxC|;LaB5T-x|*3q#(H^ zSFSa!Pf1#pa)r2b-zmccx(EabP@JZ~E`c2q-As%X`lzIZ1$J0Wk@T60JVF@`6NnRd zk-!TCjsm!+F@&w?{^I1AwaYm%;ZTTG*doi9D9bU*(nX=PA_`utEFl6XsnyMOiEn%1 zsA&!R7psj@Q;t)PmkCg`1gES@xhX$D=_-L5f%bfJC!PHFkp4q3%{xFsy8c9u=$Z~o z&wq4d!gnTZ$X-M18yryk>_2Tl8?b$X?K4`?-wGb_`x|-`@5lPC5SAHJ-q!y_|6hSq zI8{y46>@Ys zr;I8_WQHv6#`_~N=Vw-GtN74onVGRNj;Us>*@)qUig^pHl#Jmvu!7&ZK3b)_zU`(@d@>Zz6{^(ZrPG+;sDwS*1^nV-#CFvz6$^Ej%3i zjM*m0Gc+^bLx5r-Z>q8a9Ir1R<(aYek+*rXQRE7>0<^zR_K}va67Eq)uvOVOy2HpE z-pKDdZzGQa?zbZB2M=_94VXH7quladpu-O8<^Vsjods+Q?(gH}W<{>%>l@ z^=&jgecWi<{I@@D?sx}0V+y=uyb)e-=K}B!UdY1(-oZlv!p#Sqc$g&-d55~YbDelb zgzgpid!>iuP#==dlTRI`LDRT_NMFYq`Y$v(F=TVrP-}Gbk#A}Kk#A~&kw;o^1Q*sD z&+jx^J7|3RD18;B{Qoyf?yQJ-Mi96&f?XTmvAuoe|J}-V=aKvv&9@&W@DDIt-)1uL zA-Z!ieS;>`k$0XjYZ9?s!mBTCBPF3!S@q#<6?&ECL9s|O0$I!CFzD;!xeYxa% zL_1A)59}3x?K`!`uf*WU|K@z6<3j0xbSB1!Bba%kwJ(S*tjFqZ&Ca}o1Le_rQ?uPC zxzeq6x|e(*s^9XU-Pl^$9yBtd9wHBVc&AmqCD+r(g5prE9{=l&MJqRMhC9d0N%LJ% zYfKTO?HE%h-60d$BIBf;aSzJ7?`&V$p#Inn-PyN@9h)DFSd9eP{S z58;Hlab%~y-Kyg0=5xyJTze9K`>kHPBR!LzHYfIk_doi$JFVVf`%&^t_!t6ev6pEt z%Xcug!EzP-4etkSKlfC3wzs{pYv7C6_Ow1tqd+Gik57BDacrmFf0~M?kHUwyj)KR; zczPO}?+*9=U2YNG(c59G0dp0UKk*$-+nsi6!#wBgZ9r$wCEfXJ@2I`IfhQZIJN4jG zb$u6I=-P~JXBJFWw=1@Gp_jLHm+j?k?LvD21UJ5m1>ijgZCwJGcYnJJfVbZ4pr5)F z>=C6EK;!A%;N||BBf66={O>3G;?}E2)@c?t2&@8F!D3M~$`o!g>7Q82_QyE#he+U~ z1b&3Tj}rKC0&fubIDwxe@OKEbPZsSs#ceRYNPCOtu=qkMeVh{M#?ymm0MDSNM)8gg zIrLslm6Rtw#-E}{4cV-)`%fbheWF;q-1KoA;!VUvje6PO4_K@NZJz0z!;iD0rhlt7 zIe5kKB|CUT!Jk4Hk)hS~?=n}8t~@u`}m|1!ai5v*y+w1YN1P5gJt z!tv4q6Q?2;`Zwc35l?Xs&M zbF12pOFC(jw#!QZ&edcqG~&KHR&WJ&Gb{%w3C)?%g0t*_eeY#)}&Y+|x{Ug<3XE>4>agIVq%0E^;?i^Q@io(+0 zRh<(or9r6Kv2q$A;LE`-)Rw!)yfi zC)rVU4ELvAP}p&H;=aO8xIOoD=V_LhR|+S0=s6WMkqo`6%UnM!37eHdm(YSL475wQ zn|FALxdmP1XTYq~xmPavFPC_!>V_^CeY0-at9DZ|*K1+SCq>m#xP*K1`h3Vq#rhm! zvrzv$X#rw!mo>}ON(4*I#YNvO3J>((8i&j8V9fax>0?(Eo@>y8Gnk-B+(?rZbK*_)#m_)#L5 zH?NFNO>7vmuYl4kMdt6^nt5?_a&&THBXi^W+_fvWZ(n<1^uokOZf5qz?CmQr+?sp- z#y4hfy>xx_LKzDy{!yEj+zhVZG(ZdgEoEC7rn|ZwW5<+DwC9y~)j7NZz3SBfO0VJx zXl(Iag+GD5pI#n+@z%|2<4g6bJ08@Vp*OzB-Me^*%$*u<1}-nsI~ot#kJWR7@fYfh z9`Z}G=ZhD{8tWoit8=^@Z^OMt7LjR+dR*lN+{^ShI=?g;7A|S{pVD4YTk5L9&$ZMo z?2>JDQ)7BdW5z{bTWX6adQ0DgRKz!01~W&LO*7P5#yxe@YN?pAzO6vZ*YOzC5lojU zn~9dij1`T)8XBS5GVf_E1=5MoY9;Qe^9r~YxbK7s=rcx$XYWb2Y?gRk+b3;9`oE;K z6-n>)OdjmHY-5ipuW7C1q6S;LM@_D1Z>aog_qZ+dP0Y4szp4D5zL`3x3v)_cR6;8> zx02KzQMOXd+*ZT%wieAKC3XJs7g-nSPsXE|hez3O@y{ER;TadwbF}bp2z6Ab6Famd zxddm>dRG-G+TabBdv&&>gS11dV#fq1WP}+sd@mHna-CMB@79E|;;siGQE|gk7;>S7 zCAoTp?$sdY`Sr5U=WkvS)}rV8wUQXPbNkxdY;oqrYcpRd&Rm(By>V^6kQPZ=s*AMW zMbfJ=IFYc8Qn(PMqAr8zy$?- zP1PP)2B^SdTu*&&fR+L+8_A@MqY}@k`A#oL*#^IkvC8!LJHG@P@m~Bfc4-T{aRe)I z#lY?Y*3xL#v^1F(nD3QSL{D#VDH_KGM1FFAfqx9J7x*}umh;~h(dbb z&}t3vXz%?-sBP)p{B2G0Uj{z`{@dNW73_HQeFi+*iGLd!TV^+ZTLZ<~Pr2IeL$+Yo z@&7w40i9~?$Kbbj(9y$MrNsM?NXtF;Of-Ta=c3l$1}7sqx4ULz$eF0MUkA5Ga{ag; z*`(HdaI%u~Mw_#hz#Y$MTWxi8zX+LLDf8Z*?ryngl+%#wlXBuAx-eet0VMjR#7`qT z;Q!k1qxR3><-pT;ZXb{O{W*98k~g(y0i@8E#;&~seqQny_B~t4->1#EO!`(KKPcsY zYu}STEU!rU=OI5N<@djDDSvpxtIA<3NlyzaWl@92Ix;7$)wbC49+ z7$V^XWxpP{B5^lttoz=A=qdAh5WsUSxPECb8jaol}<{|#em`9l=)BPg{c^OxL z*jaHEM`enmF_mea>gY^&5I8s{GaZByPJ$&Io7qm1C0WXY!(nL&GZOYlm<8;8%LK1a z;w0Dqmgc6~(r&ul*V9-IE%X6@0Ij@i4ZdZtp|>?u%=vZt}BMn$GO9OO^qiSe%h?CjzvvWsoW3K>a^1(t ztRo~BW`(;H0xbAVS6J(=PfOh>ua{~CQ)CtviojjIP^vMBH5*vz60@*aCnqKlPS9Q6 zrf^$Z@2rmqv6$73Wm_GUKPgm8k-Qt47ftS9e9+l zR7+m1*zoJ2u!H6THG+14Gm6Po?mn0Lo9{LLep=`Y&2^+LHftv+h+&w!SfYRdK~Aac zR^B`L+Q(mg?;ob4F8?~s=@jM^6JdHrqvUa6%4l0;%1w?<7#1nY*uyBD5!nu}!Rw1& zSf-=9^UI(?O6jo&Tps1i$O9-eUe>m?UqL9lX}1!~Ivwy^#Yw z1Q>KgGE&=R1)+8y>+iCh`W&{somPA{pc5JhmEmz+5Qsk8b8U7#kMEeam?(*3Jzx+|If0M z@wo6v;G81P-jhYU^Hvk=G}(h*HTDRlTkr#C=J+?T{B|B-*YA7=AUBJ!u~*Aj(&sC` zn*XC0{%Pa>2O@R1>6KTose>>ghSMS$$xTjFe)jH9{`%&^%b?Zj#qv_AR_Sb_-=FsS zvN+r_Z4DLLp|RrWj$$Hn23sm}#Qc9x$5vbriD(?|LLeS>YJyKthqjI32O?Sbo4+E^x-d?4?6tAIK=~ob`o-yzA@Z=+>CXZbErf@>F5kDS&*n;k@B;^vTs-6;Toq#f~ZP(^7`Y1npkwh?2QT$xjdb8q$ps zst(EfF}n9kUN3yU&ib}h^8BFocTsaOb)buC#MFEjH4#$>yQs;SIwWl~F#@bX{L;2~ zcptShvk|G`sI-hmv!rFmq-^4VhT~Eeehl>C5k=1I1SIX{WN2@t=y|dPMLw_VR7hf{ zrQ{>%rL!}#eO9&~mF>r*w)XRSBzj(twR;dIwLdOpx~LfWY&7x{@qEymor`8!koKex z*+ZqQ_P9?%rxDrqlx(+cd%DY>ry_fvl=etW+A}I;x~Q<{>BycjjGSqyVGq)BzzEAq zEE{3H62oecl6?~Ei?Du)VNFP0PGUJ4FYLqH86cf9WmVZYQMdAJLayJ*2%D1F`3QSP zViyR*dGQN#?E}JU$1~ES%D9n_VRuOXy(1GWa@0Fh05ZP2FtJ0fotGt{oh=#UC;5FM zlN`TCfc!b%B=AiFuK~pF9!ma#h>sG{L|_R2PZb$qx6{MP3H}(N3Dim{ZsccxAttoe zkS$Sc8ia7erVLAYi6l{4Wuiw$nGEh#MyI9>RQUq&Q($#Yo(c#%G&H9Ikmy+hZ!e~m z=w85^B`%bAl%@%fG>If3GQ#B^R7K(SpGmK=AFOJe~DTNUiW>kRuOuDL{Uc-8d1c#`Pnl&~~?>mnkV$h+3 zhhiCd@Vt*ksN7RFQOCnJ)j`l|5Li+GZ3NPa#1TyH!%@Qqo{sq$ILjUiHd%I&4#Lz{8mWT1 zoMCV?Og(~XliH^CvmCXh0~wf;f!4n73ph>{y5?~OHb4uqm-nCinowUA>iWSKYG3qA z)dg0%G!6y-gRaWPs#5}*;NJ(0e-JY|l^-O>t0kFbmykqLvWppTN*{&Qz+Nr|h+7e*)*G@o zWTL9wc*s^s#RVbA$L-03k2DuA$@r1=;Nb=oj{+F<}?wM}=Zhq>9CAoi%+I?M$%!^D@82byW`Y8hM~8~lh=6=d*$|&o7sg5pG*B5Os7|YWUZ365wTf+A=VMX9cq~2;1Jt@D zt9@vvx){mDX0067>weJQ`lhs$-ykb34mVJ*@*YBx9fuOg8a4knvX8t9{~>|z6ZjBd zzxSEs9}(lv4l$7!4O*K`(IyRA)OIcPJ7;De zBrSIrd*;0Ex%bZ8d+s@Bw$R<3((p;2pDq2#^P2WOB04`7h^O!iTwT+ICiJpa(Q|r5 z%V|2%M%k#CIg@d-Y*p->&A3%|Dv4a8;^y2+GMB8Raw+E9<*rIPmuB24cULmG4C9G% zPo+24%eY(atMumvVtlZ&!$4n^A%;71L(ET>hbtqwkyvL}Za2n~D(|W6&F$5-c};Zv zmY&-u(z*SjJ9j{2atB3E?vUus9Tt7LBceZdR1D;fiNV|>Vn^;#T(e?G?7XApj*DS2 zg6m^qm)MQ#;}t&M{H8uI zp1k^j@oD@{U;V%w2VstbGRIF}RmNwpD`N(~E7z4d4Z@rTWlmqYzTsl_8wS8?>hi_e z&tA*VTpm5OYRxr)MtB zTs?c?%Ix_|&(BmJK)kA;0B)u1L6NPtTydHQm zPfu+;h;FOz1>+ZLf^PCNGbi(>@{?oBD=Jy7Njxxr)tjO*(cmT+snD@d$7)oV?1ztVM^sCVG%eA61uU$T?oRVKIR|{(3`qgu@Gx_PK&rN?O zKYey~=F+)q*)El&#af~*uabUMz_ElisrtA(PvqOI?y7gQgS6(oDw+_?SE;qjD2)t$ zBdC`{)x(_jtW=5_FzLOddV+<5Ty7iWy%m8hrW`khn=97KG^7h)1`7b1;p!}2Lujqp zo$_@99A`6UDr!Sx!LF14vt0u=_3As2M>}ZOcB`!Mr_ku)J@!Q0gT-^vtFJ?nX35o# z*;qUiz4|hw-7GD)+R>QwY8jFYOJ0d2YYAL&kJj}@)ArNQ>0zC>+O|9O;$99wub1`I z$Jj!D)%(!sV~zig#{vHrJ&)=?LDmnMA^SJUsNJ7KHo&qI?GZo=ZK?0-8;}pOd~Vad zW%(v+)>CBf2K0BZ{+Bl0>FxR&>z{}I5bJM#-mJg9$0=?5D9KI(E2U(>W9^((&W&2R zUhy_4yj!(L;Za7jj@$!E?j^8~zbIp0>+RZjun5*6Jlqvih z*@Qep?cExjEx8u?6p?++hHaPUO0Q7PSL+p(EY{!~L%&v411)(zlwMWjgGynm^BQQW z=gH-9FJD9_!M6c4BW-2$j1f5?>wydRm3iO*_A`ue-O|$!4166M?u~KBg41hZDQKs- zWy*-~LFS}&=K-7_wS@j4Tq0?;pxJ-lvQnm{JGyhbYb{<&J<=2fkVy~b`GILE+G;*SGc*PNQ#sX zhav> zLkz^St^JbK!F#6IanBJ$_l#Vx*C%$0VXt3|P#}NT&JDCUdt%ODi?cW8>}YZJiT&Ja z$lD1!2gE_phwfUr;VtwC(}!E~T}&I|NUXox+ap|9KXli~?d`z#b>RCu@BdbXz7i(oiC?=8AAT)*q)p# zR0UJ@Wo&$=9PHi6i3vnkbd^VG1jhiFabf~FfQ!~QeGx7w2N)#;*0Nt+Lid56HB|zZ ziXH+kH%3mjuv1w_8Bywx3?~_$Q0CRZmt}NAiC?AQSM$r|8cYQBIYNRcDjb7C zJne6uQRZBI1^u}q%JdNU39nS3C<~Ecq3F#+n?Et~&Y5?AdhqKP{&Ds7Gb*(#YoS*p z@3kSIv@rpYMWm05Tp`9^5=LR@wzP>dv}ybTa=EmXg?SIzMnhZF7Li9R8VpT_)*@mU z;b7||uvig$FWSsW-LvP-DE`e%BNG+1o7HDI&gI9AyWk*?U8o7QBM58Dc~OVM#9;G9 zJh8GDsIJR}mu6()CN(^d)92?f7PYfnt1ZnHic5Lidc@3aZBE7VWxwKw^5;o)5IJ1F zK#K$Cf`|>N?pn3HLXGpw1z)P}Yv>ziNpPWFkYa<+rW0tLwYfie8@RkffNaW_N#rQE zISOUpC}0(`hU#hG@jOjQjdUMH+c-cf+Gs@>+pD<(#V?VVcRLTU24sST5lh<4O=NO$o&Gg<_%3) z-VwE#!d%&QNer^>F1Gz0*iNBE^Dd+j7m~e>d9>!Rvttg7f8rdzBZj%3ZuDcwPobZ_ zP5q2Kv>%MSb*~uzgnnRI?Baeh+)pl^Pg3m0y!R|2K1T03v6ppvSm(8PEXekuvyXJz z&jNV+UhJnaqWx>)0M$O=n-0#(9G2?FeH=X-9|Fur?lU~2lCf)_oOt(t-~8csFV8&- zUbU7lE)=Ts-uzqpKL7oneCumxWCiWnb^E;;IGyy1bsx@Yp8WgC*xDU9<2GBC&|;pw z^xzN9G*#Y~RWd~9gCu9(>i@)qoTLWvz}{9#xKfHfCnq*&>#58QPp;%!Ot}Q+1_f#e zbs@^*$daodB^@m)6)e%-?|y{mq^}?z`9E9y?i|^LfxZqI8fotS8uYK@+1pQvx_h}&$Q z8ar6HoP2X$s;_CgIjJC`qYd?y#Z#N1P{;x^}8tiy>{@^IYcUm)jv16^sxAj%CC^SdZASk@85_V^ho zxaNL6g=ihW2ZlNHkz*6LU(Y-+kRJ>^Kna664s(v(k9AN0*}`F4lm$@xeb9EyZ4SR< zXxq+t3^h9EspvG19U+eZiDvgJEsW})lFKuFezim;%+SB#@%b5in`Uc3LDN?cY&mMS zDQC?%2gAW%pp`~Nqp&WkMLL9L=Sb8OaQsaqK8y+0gNdsx#%HE zF05%Wh0-WfRMON#JEo}cqK7EFP(f8t`^1{t&{0m%1+`BsCV`=JdKY>EdNC&vbKDjO zhY{;g(J^Y(6?4+3sg%+U2ae*!2vwW;x|^;&EW>ev;$-i-zSe~*G%}hFZhwnA(80A@ z+`$fRqQ%|O!A-WfLp-)LdcbIqi9{nF-o$OrY=kZB;*n8r-8{10tee?lVGrx>;TaB( zXgssM(Ck_44SUx5=zby?L>6?{q_VU)z?uiq$`pqfKg{?M#*ebK=zblH@7K|&1yOAM z80&O!F`~n9&yTd`gVy5Fc$QfnPye>@P+>gk_c&}?@z@??JQ~~M9piZ{9?x+e4^upz zQP%0;Vmyz>;~66v4wvy+J{Ob|IVpC`9Qw*}tZY68Hn=`Q{(ggkPEJ5hO~?kxJ!V98 z4vTzibU2(O8J`t&EJVl8mkIkS={HX;82K4u9wnxY1O~?l1!c;O3Oh)9juDxluoPlW z11KcFi8=?eDJux!x^2#&WRW!ST12RB&V~fSb55je&Z#727b*o&d}K;0*lw^V@k!Ku z6S8*t#bi4-%a9btn?}2cjxDJq5uAx-2e=5NhL95Fw-s$B9>Sk|2>*o2keyahXJQLI zxrIIx(jZ2Io%n-J=gRzX`^SA$<@(Dx{~W6<4K(aK;Rwj+DlAo5Wc?wB-!= zn4xTQ_6dxNo&CQ73ANkWEmY0Py@zHqOuG3q-(6hLbhQ6OS28QvX%@70NO_f3~-n{9fid(b;6J6P~b;r4}{YL!d_`8l${ zu|_-K=x?nb8ZlR1Q-}zHS?FqpV??Pgr@J4ziIlm8-YPe&9^*ePtnNdIw00q`8vMve z7(YT3*0*}_VJo-Q^QMK1=Y$-xBYdI<+76!(>TKS_TW8kZq!ctpQ+tx8h99Q(@Qu_~ zpUqAxH_z|k@_CiY=PNZ)FMGh#`TUFZ0^a{si0Xoar#}khWz9#uDXuoiH_#7!k}UY- z$fAnH&?Yj!TnkYCMg@KE$Mf^GQYr94wZA3i>Pp1T`vI-7;({l5)kW*CwVpawOcxcv z@>mKbe_oO|Q0~mR>5G@oUb-g#1*D`uOvvvO_+q|dSIn2!-7NQuidQU@b!HwjVtOqc)NMu z*bE;SF6(?`y=(oKGs!O8=+}FrFTed{_Z+!zIB=vJJLz3%ddfBXr~9Aakz~iX{v&D8 zsbuRP4bmcdM~fcAO$gvtylTyjN zWBcqOw%;CR2ka4c&>n@{7(2uc-&X8#c7z?p^)x%i9>VqUOA0&APTW@52`6z&x6d$Z zK}kQnO=FnWM8q!?9p<=xR#+_SJA|fHVW6GG)m-FR=A?Czng(;Z%nP}!`*fCPi;nMb z(K+Mf3dL4Re758__++S<^;d9>%r5wxOso?!1M3&b$Q-Z`cUUz?y@tJ&YCi8e=^mlZ z2=$y$&kFSkp-u~JdJ?~>_q22K?`coqcXs|geFTU;0!$w{J1?|n7KJv0-_=E-PXp1X zf$7s%7q>0k>$V245xp`y_sl|O=E~5S4P)k6!1(3axeJr?^A|1+of+LQuS^b&k8VU4 z7Oze}IW#sjHoDP0J#%?xe)7`Qxr>)SF?02)*`YIhA8hdsG8nruJM;9_*||j$O+s{W zes*DTavtb0iBDa)y!hnR%QIx1_?NHFU7{|jo0#n8BKiR{c4cyU=EAd!L!;=RlRD@H z$YEADKGdf9K8>Gu1fYiRrn03B&{f@PVEdJuXfG(Qs&jb8da+Of$lkyZYb^h&!cRi? zy{VBWuU@$@vQjQOBVM`c7e?~jc@9sBIpZT$&*2%G1S4MkxfL95& z(`ymT6A9PDvPR&blYU%Z{J*eJ(UVD`$_; z3nj=E+;UFn3s)vZC|_{hQdaaV&R>|D$xJ_aVftg4>B+g7%NG{XF%hB3nx~i|B83t| zobt_6 zX&RJks}4@#mIGx(&0N(bm!1aETLDlsOO2}dX@(X>+tT8?g|>yZcF)iy1$<4_?uHCd zwYVDJ!S(2e253>xk^wDh?2~w0O|?i_2A@S=IU4?*PZ*8HT6`0$w1(9 zlJioXvl+$}W3;8#c5FWhnGPxQTGMu`T+quA$aP9NaUWaguXGm@T~gw|0zVM{>d&L} zJ@C50(|GqT9_hUSUXSFBH$8w9bgA#sE8wRje`(j)O8zcu#(A=L4f4HG{x>k0pMQ59`$qjP|5oIVH;`@Q|0|X8d zI7Hwufg=QHhv!`cutsVj?0<=BzJ_7v&1_%riklDfiXgO`kRmq=#xWK{?;ewZQ z%bp{`&-s;gx3DAc%+HhlsA6(znl0oY~V7Z@v9QTo@-1ch|!tmh4@~5}%tbF|F4=0w*!W&=Se&_9pwkn4zHd!iAY^IgR^gAdH zn8`x;g_*@7cS6e)+M{zKIbANhtbD!nbUo>eLkIU0LXEmoW}?^g%ashPB6=b2Wpa2R zuH%W=mF$ffp1n?r+oMq7l)orGSPBxbuC_cz+9y#m-Lz`qRh<&aO=C;B8Ic%LK#4^q zhBQZFv6_hz&8S7FphHTCU|U1+w`vY3t0AO->Uo9d>6`*`5h;h_2Z`L}=JwcnhOYfCPoLT3^)B>G&n zfSI(6l@`pj+Y@}1bSb3}5kv&n&0v)WTz`VQVC=}gzGDEnf*%k+_qrXIGaNK8Gq%Eu zq!2jpy4`Wj;p>@tx1_cGEZCgpI}!)*s58VXja5V+YOqYUly%oPX#O_qDVi*@wBkQU zdSUOms^e@pnAsJlM1oGH>gParC|~f>asE7Y@~Z@1AngkJ` z5ivLG`N6spsS0r% zD#|iIS;(nAm34ptMrEDqqpXvXhQx6EA~8BPQGGMOsJK&o6nByrmRLBzEQwhG7Liya zz@iePLk#jICb3w6#U&O8me@?zRFpp|BR>pj>+qOx+Wwqfk@QJl3VI#7f#JkZnaQ58`Q2ADh{Lq+3pr9j$;C9 zkMyD&w$Lw21)lWoqVC*7pR~{~eIUC%(uV;lo7!VxP|6OS)DAp42om*P)%|&g31sngRh7L zS=QKSqd7jh{T-4#F0CLz8z1FQQQNAQnMmtS5E`b80Bo6CP)>3kRTr{F!}FOZOqn6_ zC6dG;EfWcu?J?vhGEp>T&d77bkAfBW!6~uYrmp!4u@cw8Yo?bHZRVay;==fj(HP-@ zB@rP+W}4|iS(1uAWOtdi5_0qb^sxuf$3>FtG)m0TJ^0ujd|dQX+`eDO4`7TvNMjsF za%8Hd?>&UEy%OX6XK5Vu`Gr~j^VACSvg=}t7P^O`S;tEQyvT5}II+Ak`7EC!k&dOh z2zF3lJ!ClBwV&i6aKCJB8&Hp9=Whf@!n9J5ue=x5!kaAtAckwf63FJ(Y5W zjuM^b9eUrP{eu@6U!_>#7))ca6@_o23qvBx*X6wX^iTVYSomqOIjNf+KA|bp8*BBs7IIbiabs(?t3w zDS#GE%8=?c@JK7pqAB7al2?^v+uEA(5y&$9q(kUhUEKY3LX>J6c;@10cESvOl;V%c-&VBp74 zRW?pP=o!<(mM2ETXK^^6b-iMK8*kP%Nt zvE}q9Kl6>Bocd~z1k3X`ip;zUfY*SATfqA#I;25tM87*nU8DSKB=tuP#?A>Bs_JW5 zS(j%~Z=>|yr8dT2C$+D(sg3QH8|Pmqxj$}`L&c1H=2Q-0H(4p<{a4$TbF^`2=J999 zjLZZjkm+D|W3@C!GLBLAs0>iFkk+X3)X@7@II8c#_wu_!9ol~ycoEQtTsC=E(~}=+ zM)wCe5tG?%A1F~!k{@Vh>V559EusAwCtsZ#M?doUwk_ch9F$A=(`fwvE@A#c`vPW0 zV>y`rx8=b93OBOn`S8C6t%3)_{|1eX#~(CIa+7(`I#`Elx|{zUnU}LSDaD)9_glpI z697@?H0Ce=d*b{hbh!pw4G7po} z*35ya|yXCCUzeGZt1Y*Q8 z@D|kPe@Xbi5coF&%_rW7NSuxY`YDCDfre!m_l&5a8QA{i*TeyP^ZTw5rvrDx#QVv+ z+DE~Ank98VG`?&6B=m$#L9{y5)MzKY>5NZWdiPBCl=LF~GxFGqW6mKjYWyF88-({Z z@L>#qhn>MGJ7f>B!}c)rM%WQ{^sZu$vVL|9_p|IcJAwOCR~2@Wow}>AQ*P>xZl7b;tde=G zN^_XeL@dY`T;_U#BP`|wE|D2k7{DFe%{lHcH=~R0aR?VnJfCyC=N;}8+`#3ceZtM< z3(cJ5WHG4A$xy)wmT-?v&IX)JtPwE*>sQFgG^hx7*=mk@jrvQg3k#l`=@ROMP%jAe zyilJJ>bTIxFXFm%U%N1KUwa1E`I-Cr5D0w;lsEk!% zsurHLssXIWuTM_DFq@sYK5%Z`n0OH|IW;+b`QpsX<*NhdhS$yO7Y9a%*W2x>ctRDjUFOl{eLCykotPF9JBXF~u5NcvIo0(f0nOp=&p; zUmjX272F}ev>N1x7P$Ko-V$?1hgN-;XUPeM{MviVyZ+GC5~H{H+{D@Jx$N1&@|uVh zOC0Y%zwUmT#zcct)I%!I;GUxy-~J_lL~zNW{~0Y+wW8is_?e2jiAB1lZfH!eXv{bd zYE!KcN3ZA`&7#b9QivS9{V#kiwxL@FvCTHjJ&l#OU5!i*J--wuqx zteAJSiURFu5UE7(sIv;B5lFutL}Ab9Bbn9EtXM4iinc@Bg7&{l?MSHIXc^nxa@oRC zSH7TCVhb9^+T3ewMSE4{mzw*nm|w-yR;;fof2eQ7ciX}<#m_52BrrE)g!d_%ab|9* zL1Ie_pCm4A{^?$$3+>11y_l`N>>TmW>yu%QGl^+hTT?TB^`tkMcrV;?+m#lY$URnchGR5W-W zQM#n`^Weaf^gxyViuK0~ti36z6OlR*=%~!ZidjNCtN?1=r;y!OwPG1ET6-@C+NRzt z-_oS~dB~%XztJpP!HP%Qry!%1_`Sf`G@IpH8aR=ioJ+MfG#0En{?k?s*i?(}LS9=z z2U@L4jc-CDA=lVbVGo9U7Pa^WBuOcmX?hw%K8afVBBUuP_3G_tOlr}CBrPSc)+C!z z+%ZR6YGvE@HR!ZSowpjcoAtt8`k~h@^~57=p}*n-Xmm)8{|U!|_*Z)$#UDY|37N(_ zcgU#SJCJor*=S<~&_Y}4yZCj;yQO@7$J|Q!4r|7Qd!+trJ7&67Uy=G(px-O? zcfN0_-`e9PrFE2Krvgt|&=9fqO^e9QlDArLtCUTyn*)d_1DOav2#OyfaG1am0!In- z6QCWQcMy>KaVKG21iA^(_RM<;>?5$BKpz2_9sHk`GnQPH{28bVEDRCN`#G=VyCV8h zP+s%$^CFewCErKnns>d@tp@4Lmu|X3XZf3%D8E4MtrCJQg%&HM#!_c>v-AX zYC*(uCB()cUn+{Ox;z_jx5%=7!Py?n)c7PIUai?Mhf0f!|dOiGxNFx>>(fVba^La)Z9HXZjg zH(qP$CTe!_nwtcM_HGLI&b$gv8W`HUZMefeIPJjDpWA^uEP>Mrj9PW!UTf72j9T^J zE?co}+3wNOSi0}&tmj^g_1@F$Uep}>P;=}@&C%x`UX?Dgv>7HbndnS9FJ#p7S zE%YQCKrQo_d)htYo@Rp>;}9E$B-6whVWZ$Y-o!b}o&x6yb`BxnX_4NUzVavWhB>9J z)ra^A?Ofr?;#v9o_SoWA{(ydeGd3s8T**Twp`%I><|20~WtsO@T@hJxJzARPt)+b6 z3M04X6f;qgoS)Bv?ecl2$RxE|#sZa81Z!+$coQL$ocVDE!!JF z+r|HQ@3r@*t8a}7!-sJU&|ckxSG_m)0;>?dQ3X8xVms~>(a zR(*hJ=ae)GeSURq5eRgD1ixlUPnWo1} zC6ASE6`z+GsC>bA#0U!3r4kc8eo!iB*(yRAf?zg>cjURgNL+VrPjKfJHLMP!8#%7( zHDHUs-u;cM@2$Tvb_M&z=fM#Byi#dp-pQ?G@e+{bHmu0vDeL76`GEfhsdl64$~xpG zh~zBnK%`1VZ;cvf%TAulpo_ejrf^|*1WuHBDR6T%Sbqc2$fpfQF(@fCOu+5G`^LxX z2X;F6U{h00`5&ijJY&M6(5o=zU_~;w$`Ss9EakTiifVO{-j>1ALO#e*(zyLg0-?lS z)_uPZq@paN2Bo5GS=-Wn6-CsBRf#U^lv-{YTgpaEQb-CV6_*qeA4w%DCWfCdoVv8H)R^j-CEUnTnlNB?o`zfI%P zAXC)!{#~xzqMXsg&3pwZ!Z4;=;P`UKc zk&(O~q}cHk%pHAoY;z0U1u;^LGh)a`8I;*28XcF9q6!WQ~v-Qh@j zc5t_!p;ubiC&xf`yW|-5OWp2W7W$-apL~{{K1Dvm0ciFt_Xa(i`)KB{J0MkgMW<6F zc35g2K`Wgdm3Y6zk4gNvv{sv$BjL;(ueBgbT0bFmnz$HIf7tWM`tzVQQkmLYdQ6UI zf6I929IMvvY1lNvv7M24ZETsQ@tg_Ab6So^QgS?xOPwYz#*+!h^90Ed`ktJYAx>_A zd=Sfky99ohzyd&Z6hNjP=Z33{xCD^!$V$XOSy7Z30w+CQARxC!kF;W80x~jOM@NUN z-zBA!BsY<}j1KeX2)Amr7c!nxL`G>-gjnvev^U>EDS|}G@B`)xQzlP*o-}cC$wW#f zLJa$~%%Mz~PVpSc;}C`8;FLd9scXJOqSP(O8kv_Q8);dZq%gj1G(mJ|NyLbdNm!;& z)@h;_`AN2=t{UEhKe7jZRHVsHz3v*`MUU*FN5wvh*N^IW55dS@EkFo|i8!3f)H~spBLar~9shW4!AZU*yxI(l%dH!M;iF$``3% ztxTT`vSoZZaK-*xZhmnopsIGKV#ciPid4|@uaT)(T|;!Xw%WbFb(KqHI$v`ePw0P# z_7n1R1tn^&P~8RfwAvZ|9a4Ln|FmAU8yB6pC8@P6UwcuO_M+fS;@%>OtRLUCXFXy_ zEt^vWqm!G{H-H4n9c2T@YuFaFKo1OLQsx~KnUsNJa2`boFPk{b?>UrMFR8YQ@mBQh*mEQG~>Qy#r0kEZn=iT=A}QTrFz35 z-yzAj0YpvG@JREfgChw3gNAIo)d>F%wR*cD*=~jHWrzJy{$0}i>sHNC{v(o*XY%(6 z>~g+Fv~4`!r^x#*L0M6oyibjMY(<;AuSPe#FQie*{tVuyX`jIB%)KLNqr2(Mlfuf% zFE-h%h-b5f5?l3L;K^+ErBw%CiV8#}kY~^r>MSqiakzovAIXi+zk+dMbK*{(cC;+< zMehzWTQ2!HD8*Up;YahcrG*9G4aCv9SXT8lZZ_{{G4Hu0m&u4yi!Jql(Fh6h^>X>5 z>+h}Uy(H)?CW~0GkYD8gNU!3b2)suiO3z?TTppjiesOA+{|oVcLf}IHJ3(;)Wi2II zO@wnB2S%KnC0OIXMHX~KLt!p>B`4rp)ckb<2@)Cjj27^}CHlt%{*6Gx7bHf}h*Q11 zPlW?em2DZuLnCfzC?;|Ci$4=5w2i-yj3k}d8YVt)Jt7`dU=N=CmQB%V)H5S!SK_-U_FWR@l`{LYS~x3Nf29DE#?9oh52$B z3sVl3PsvemjG|QFDI#Lqfy0+yBlu6jthp@hY;xOba^7zd@B0LPNZ?%p6rg0hk)fo? zmt}bQXOdP4d_W*WV3GhuJo&mFqUt7=@Xwv1b)-Jz)c1xIyB-Q=$YiLrldf^oK SoUrS!l@qZ{Yr^`xV*D@X5t5q# literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_6.cpython-39.pyc b/__pycache__/GodStraJD3_6.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e689f10b7e5c16ead9f233f7edfe4db20c9b711 GIT binary patch literal 11066 zcmds7dvKfCRsUZ4^huWO_z@@0)4NGF(e66&W3zpEoyd}GtC1xm$w^iN2wC4}M~NQZ z`^m}Lb*2p2mIWGE3KZH+i?iJ+EDU9M4D6H{9s^Tg3Im;Cm~w|d`l8frX?e6v*QFbN z=YF4Fw$pa_6KQn5d(J)Q+;bo2-gD2Vd}pU$!Y44DSbEE^O41hyY5n+sT*S}*A6b%2 z$&^b{SryEOIq2J^@y@o(#zhgw`^n$fqP26vOnt=Wxdo<4rBwO z>@9VcyRu!PY?Qjo`?5W5xwqV>ppA0Bfcvxkg6=B~ln-PNxI725gRsG0I#fQKJuFMt zB(vkAGGIW!P64|F>=tmJfIR~CepEL5?itx5*3mms_Lz0dI%4(@O38Y8e@l6wrF@{J ze6XcF*it^!Qauqi zbsWz_*~hGto2RW)FH7c-_1MeOht>PCEWIQ@SHW+|Izy7?BR7jVmjtlB>P=^}Szqf`2b;x|wJn@2*4Vov-Q+PgZK4zZA^NAVBJYxo5kj$VJxT9vD zG>xWKH(L9Ii#8 zb0Mx>3IVEAH0ENF`D84SS=Z-493J6n8e~r<6`KpWw9$2AYATe7q=!dQ8%j0MF|K7u zNnkdU%cP=-NIaH^as?C}Y|*Qk2ImM@GUHsmQg1O83r8ol3}hd#52isxG6eP=ZaC! z62u?l8dy^T&eyd;t!QExeG`RgG+>C9+VqX0hpE_AuFfUr>LMcz)pJ}6MXsi}61mz? zGvXM7&ck+$X*hysvp*QYx#<{s9R`3fzN(=k`m823_NMn~$Z zM<-PpL{BIJv$+zAa5Xt~*&%9)P$tFoOe&O^iK3lMYEmO+<4QO_PZNL+U!HMGnP=zS z(p)qmN|RnNuXkW-=VwFdWC}~8G0$_!NR(4mBs?EY#gdU$KJ)=KX)FbdZlgHD{SH5r zMJJyjL;T@Ld@hukO~xa<7v+WdbSx5`3C)j%Q^_l7-UZq?EvYo-;8K+D10GH;Br?%d zD3b|=FA2(cIupVOCDO@-%&gD}ZlVdD;dl~tGjpNT!c;Q5AnIphmo9~%F93bf#LPl` z8XUX>*nBLS3fGa@*v#ye(B&xaK@Akf;}{t<98D#J>bYbp0p3XRolbMqopIQKJ_yyB zPpBJJUTNIjgSEMfjO2)M9~RqcpSzH!EPX-$mLKX9G!|MQ7Z;vDvsVd zgF?p4q+k}7Z6Ydkc|j5d>d;gw8oCs^5_(p&I(}s~mWie@!suCAG}W+|hCCge4`DFT z4KdEbtfXTWH55x+j;1ov$ZTkCF6s)$$L-FzEth!3XEYV2J!ic#Tr_RqN;kPsMv6*NxA!h3mbkmg3Kw!0mXUW*)715We zXxv#oWJM$unn@<66YhtL|zXpNpcANLmoFw4&2k3T?!264MomlVth|Dg`Gdo)pkeOoGz^XU&L?ccP71 z_$Ex63nfH#dLC;6cqTb7tXxRMerO>&o{C`_1OwdmQhGLoeKa8$(zD4V^{Hhf#Y_p^ z&N7FuiYaMvDrdMc9qF`~B6o+kJD@vQ??AmWN^$6X#~F%Ixi)%hl3q*=i;2 z2Pt1GmWs8tR&w_|4qIyo2XMt+s zApbl<3&mV|O=7sdQty$prGR?4F3dpO?gAU zDX}vf@+Jc8`|@qYR5uh;n*g>cZxBV@P;Z0Ft=Bd*(=#aD_SBRO?T&m~-;mK|^}Ylx zmoW_TAi8Txx4j#>sohZ6^EIvJ+3?&^HY9L+Yx;)wj+~ZItE2W~H81pOgH&fUIX4W` zdsk`WHo*OL!L7U8O-bKQ$?FC}b?H6IhHpuMt*x#4Lk*Pev<~eGK3=nfobPUa?rQYHm@avqkHI-315+xW`_BOLOgdmDXg*s&MUw zwPthgvIPgNF|O3|V*PNnSOH(LR4uR`$Z~zDSSnTWyiY`}x$rE`;anI8Ks=fbc5ojp z)+LIixvyB?S$L0V&N6dNQfI}w)q-lvRs|via}|;{M3F2*KyI&=YP?%en!VzFL4Zo@ zdERYb%d?fHLg8Bu_&AyE2;+WGSjB!;Ww;+o_BGT`qiiSv)j-)mS$UwT zf`WQQR^HS#P?dljsN<^tyBcVI(7IfjUppl5fZX50WoYbEXsbXQSp7pmV#6Ch3qRd} zKMrCo-q7G*z%~@}n+*l)k1$nW>I5iQOKMFPD;6GTYVf|e;L!z-4j$R`z-O*O4n9EA zgA(a|NvW)$j=b;1nzE_3*56k|{XFWusDHJ!?go4wvQMFoeDO0iZPU|Qe_sJbZ>LN- zGGq(UvS9u->*hlKUj;`b6DW(*kfomfEg-4E6n@ez9$AMSYt#?IP*B z3I6?p|NU(vy_+AI81PSne?aiJ&$r;;-QtwA+e*^2ft9kPV9z>`;QCDwVz8gZ;$Qa+ z41umk9hKfe#S)WdgrK;DZD{MBrrt_XvELz()vt zPt8F?cqRK2aB?^X_ZIC!scKu?`&?~htyEm(ods64ZR}i&R;hZciRKrpH!ZH3#hXDd z`%{uPs@QC4YhiyzXir_Rx#bzm%9nDL)iU=Ls@NMT`r&=7611=ffB znxnH{U?*H`zraqq*noAwJY_1@L1fxP|hJISI7G9(&w))WVokLEmav;kx}HH zWoA*BwOCrUxV~nUXgl%Tx>l@NTq~^QD?u+0EH36iw%B66VhU_^1)GY%bnFXAULkU$ zC;JVu;5PvTVQd8V3j@-(ie~K^*HOY{BdBhBu)mItY&%J`;nCsIk!{LdwB7fI&x6tL zru^oH&u?pytAK&VO>+4B2-jCwb%`Rzm15-vdSn-aD)+*;0&bDqa0}y&()pmym8mJN z#?TWDV2tbYjtVtp7ukQoblB`G=ZlrxN~sDR_Ua-P>~$Rp23XD2#nm%!PHza9W3q#wj)zFe14rKq$RCDn${@=<76F%&$IWF9dxcOf8%rS|3YHx z^$W}IyMOQ6l}EmEVcApsV7gV~!ihJG>6ll{lRcvp#y_q`7Uehpr_w;2t zmHm`Jsa8v7^;TtG+!w^@jFH!oNWWG!`F^`rUCEiNICyYmfl{@4V=-U2k;5{04+;VfJV!2pj8nO1{ zdYsFXZ{Wx^UERF1S}CoOaBd}EWa4zh1&2m^Y3EVdfpuE5oW#NejtzSNCP^Aa@0tg& z{@vETvwpDEr_Z(s?ix&5BVsV=4z-Ny7kdo_r|k|@?zEz(SfQ*4hU=y!Y_>m5w%~`G z`TDW8c6YG`RX3o(=;!G=MK_?@DytM!l6OC33Ed`yS=+Dy@xN@!i2nh_jRA8mFz5at zn{UrU*7Z}rGkUBg?g|-9c-a? zHmHY0dpe}h0uD0A(j#ruy0u3Im3rFBdras(ChR*hC<(icgVVU~s~MYq8V$4495Nrd zt5S}{d{l6rfRt*U6y;N*{Fo@87TTQNo^X46+L0hkXg?!(TBxw-l-qK!VL7DD$KC!s zK@#xbCll=7uoT!g2z-UWR|$NLz}E@T`epx4;6Dja@Xdagz?TU89D#o&@DhMHP|lCA zpC`h<2!az6YY1>FV&fKB1okar_6WhUamS7y=0CyF|b~fIxhpZ+)n3-`Jz6whe-E%gyuxkeYNy!cvv4%h#3L8lEbi zxSCzp@Wj>qx(826mdP9XhK5U<_m1a>mD>i=e=4rM2E7pmm*GnLTxq{6-SGoS2VCh+ zSGvoU?*0L!Rrb%6u)Myn)-`wq*IKPnF2gDnLe#z7iBc8b7xhR=N!)1N{H>=Dq(_{9*!1APY#0wDnMu0ozw8`Q4+!cW z?%O3Qn*QtH_0{V*FdN@P@#w>J!b971*73T|(VZraJxtTs4o!8uu4#--z_7i&m_0)_ zJC-yCgAeRI47{_=Vi&D9v!@F;Pf;N%e0ZIF_(w56@L|f*e=i^2<*N_j@6o)qHHCIa(%7I+&xz#-0jg$%NK%_(eKFYCb z5qDc$ppaO!oxe0W??*au`@xIV%5sis`9K?*TdCSe5F*ETbZ>gDy0m0lHGaHNEiOq8 zHCMEA7~;Y;Bpy2GQk}!TyHs3e6cF&iW!%9ik!T}dP`j3EzEWW$H0kFFyp6zF0#6Zm zD**S^*RstZ0 zrU|^00HrvQP+-(*ZH;vj4gcSh7$pYrFR)^v#!eGHMIcR}xh05_*cbi@+@D6J{UQp6 zrajR7nxY|NDZV=Lmd)=QT7dGHnn%#St2woJ8dxIu87}=x+PCzvmT-qEAAW$i37*^Gi8eq;|pyx_Cg5;7}{LPlhI5CrFKXOO#Uc(1&8 zHor*C6p*B;ml#FZ{6tgTO++D8cGk_CpKa_5Hd6kg$_cjpEfu~c`U?Ou54PB6xJMYv z{#ICle-@b4G7AvWiHtz5v1k#*W8M3jct>*q+w8|^8Bju><*7vbHp>Cvg`&Ia+3!&; z?Yr!CD$#3l=hi@3XfZK+ts`zsIIccPPGm0u>=W+-9T-3?a8SG%45<16eOUM4tLr{J ur2n||IsM(zu&#-y-ihrAB1LZuU=eDDs*Bj;h=-E+?oTy5M#Olxr2Q`*s?9$D literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_6_53.cpython-39.pyc b/__pycache__/GodStraJD3_6_53.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e8d3b57f609cd9a899303cbf01168dc6f8e42e7f GIT binary patch literal 13611 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

y3-$ z{s{7h-MniwrOmWTfJ5$(?Y|&R?5T(U=A!-A!0c}j_%?yxAf{`W^JN)?2cN9v@e`p9=p@CVh%0}b0Ek|*3` z{M5TG{(hXcJ%)B1a;CoD!FE};Z(>DzxDzV{+1Ai2?SGAgTe&`JF^cxO;EPDffpU#v z`yL~fugz2b`@uns zNOe@ihyXdNq&gI10`xG7F{ut>Oe|?IhG;Nj6vb5?pNCPTNp%ouVxFHdzlQ}F3wT(N zv7m>A7^9df+0w&UkB5aB3nTiBnMIIIy>}x`6)_oAkWIbU`;bBe`Zbg-sBD+w_b61N zOTo;-GE|OB>G3Gg?ooO@O5{8&5q9G~Au=sm!^0nA(d!nmj29YA~^+rTdB zP@6j3MfJC-2fC=iHg%+n+S8^UWZMQ&0;~~-Jo`p>QCl@T%o=dZ1(s21Lu}bmmL1-s z;TX#v;~L_=4A<;9BoAC4aSq%)NG&Xa;xRGynojY4ae^gJB9|^6=kybtev;FtSex5k zC%yJM<>nxawLir&T~t`~gje#@?fM|M824)V9(?s)NFlc5!n=pWGxyX(%CLvC-e)~2 ztPvmuxJwE+OHFuEh}J`jV)os-fV0$TPYP=aNCEDW0?txrs3w?YCR04EVCTfEBMu;Q zG~NEU7-3l3Rg1Ik%A1_O{Vxfy&;Jy$X%{v-$qRM+e=@<1Ga2N8d2(`R37Q*hnz?u~ za_5V`CG-J-e*++WI7!0x=D$)p;6~G?ChcD))K801uzB%M3-Ol`T%!GA!+}@#@mA6P zbCSeyl#spHKH`lFpfaMpL8AePCwYVLu)88_S+?{`S`3{7#tcD}t1d}yP-K_>}T=g6V)>oi||1eukHJ6r982N0SI4`Qz3 zU*M=Z()wwf0vyIeCk$cCT$sNQt{cH@G@_nze?!}k@0}bTV#%&qfKL7Vcz720EUM)w zw-t+yLMTztJFC~46I_#V{bE<;_wySOokXvMSqWAK8r%j&VcLr_bZmuUKhZK*G&#e}L99?*k}*1Dd-0 z-E%Ztba)v1{UkyQ-PW@C%dUfuLxWYZWIu+NQeF)A%jXc2 z_`Z!#z-C3mSptqS>E}iu7`_wm^KmKBwMWqR$(tv&|8C&06iad~f`xeGyP6UCj%JRi z{h;)N5^^cN$U8VoxUapfjcH%SlK$E4lmBpk-R1S@{ts=aVtDwRo8J)r7;;mlejvjmMA&RHuBXnYT?9$o7TRNxq znmGL^P1AccweXgv>G9y6F=M|(mb&G)n?Q~{yz;Vlmz^HkQf_|*U1aZw=*;h&m50P9 zX-F(W7(;??3Vg32v9IetLjK_Qn9n;Ur^cU?fdXHQC=_IS;U2m zDq%fX{L!uLf?Y2o((c6{?Wd@%dJzG{WwY9v8^avgg%&pI4V*{fNb}gk>4p00YQu8m zV{JCaTU~0Q+$f+mOY1nW=%L_#0S;}oyk^q?l%s11=hC@l1ItKfokNdqkk=M4%q|nC z5}<9hy-uJ4fY1oU`1+h}5%D^K8Ub3)$Ur<1!*%e*ti4LK8wBbEwh4ScfsYf|B5;eq z4-)t&fsYZmO`u8O2MD}MpqBtmu6CGG_-_ft4iTVOG0s73vdP)92ME=1FH8DXEA^sd zPm$0x0g9C(gi&QtkkdhuV~ zeA_terN3iDT2W#19pmf9|1saYZ=s3c2Y&h^=-~FdKOG^HgLKg)91ZA$w+27L_Kbgk zgU2?lHe|4UBh99Gf;`a1PopUsn8U4F36XN*9qaJCD{EV)H(LKw(BSUC-;P^2AH-4k z#@1;Zv)g=I^XNlzEa3%Si<>IE5+Lc z`FKa?O)LxQ_G#~A|8(1HG;k?_S?*pT)Wry{(oISnax{4QmOi%F{zJ9`7c@n^Y9l0w z!G-gPLc5=%V_>KRJ9tm$)>6a%N$Lu}O||(nrRE7N5J(dE5P{1C76~j7ND)XASSIjc z0vQ4{57<8efa_A;AhZ7+X(m5ze~D7RMu6@f@$!g2@5B0`44ij;$^{y;jqeBO=bKK$ zfcrVRu Xy2*=&qdvO2;5~Z47l;QwrWpSp7Y%5j literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_6_53_1.cpython-39.pyc b/__pycache__/GodStraJD3_6_53_1.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..89b36c1a9b667cc2757950d0d05d1b1e83087e97 GIT binary patch literal 13100 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 literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_6_53_2.cpython-39.pyc b/__pycache__/GodStraJD3_6_53_2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..729b7e03488542d6aeceaf51433e62eee7c505f7 GIT binary patch literal 8704 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 UJBRIp8t);Puuc1#{mY8}e@dFvKL7v# literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_7_1.cpython-39.pyc b/__pycache__/GodStraJD3_7_1.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ce8741250c291d9567683b206cc146787423b19d GIT binary patch literal 9303 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 g6#tJ$+DfZK^1nUOnmvGRp~h($ literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_7_3.cpython-39.pyc b/__pycache__/GodStraJD3_7_3.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..476a5c15f37ecc4e5b31a04447ec196d55d0f38a GIT binary patch literal 10595 zcmb7KYmgk*Rqprn%+Ag}wGTa3Z&@RIpNg=P$a?L{wj62gND48Hsit>s@9gZ(OSgL@ z?=lLhNP&XFi3#SBScKi+N@0Gus8AS_9~i<9OhNI(RY2AK5wMd0%fk@j5Rf0@JGXmg zXI77d+3M5ho_o%@kG}WZbIx6_voodOmwse+{;5DwzD0%BpMk;&JVCdrC`@5$Q7Ng8 zT2dTErLtDkO1h&<}fDK+( zoI`Ag4dZ=3+sd}#{lKFN+s<}eSJ)0eb4_l3{9a*Qj%4#AR7D_(zi=iiM=7l~L zv#KzFdw81{xySshA^N9LnXm9d&MQ9Yaj)cuJ{R3Hey&hzrKHc4!cc{v7TWYl zJg07I$Ijo>9>#O@{7rov1$`VPef;Qop?%__&}Q&FaZ%{gDCpBD>C;bKT(>d$bq!!8 zb?(gUCoZ@%=SGk4ZA387ogAH-STSav0;OBBT)6ne^kbuwqmvUW>9c2MPoF%0{`8}x zMXqPM!%ENxS+ z0>7ZVq|V}_=%qp#z@jr>RfWIYr&B@jD+4Zl}9#ZS-W%Q)3u44DBsaG|o*EMDwMQK&76GgAJ~GWH2zFzgl65mrqva2Cqk>9xTaoEP-~&~t6>8A zjA5#?H#qAyOFXN!aoga2TXI`5_l9J0v*eKW`tP%DO_OHwvQB~p?AmHsoLD_HP&bwi=tTM@`&$gwcYT&`B6E1g28#&deP3O?G0tD(MX+>Lille{zFO@R0HyYVe4 zl5acsuv@Xq2C!$WyK%2+s7>6x_7rK;%6|=Q(Vm^TH^1aw1$VR?`Mukg+#K8;vZr5Y z>xUs{M=PI4eLCLTo9D-nv!a#9z|$#t{&tg&sQ($%XHc*4wwYM9!QK2C_%>0SYWbVs z?~?o<+5Amzmyvx?%fAbmZYi_a+K1cuu{Li=Y15dQA54HRxIC~}MAK#Ue z#;KMMfU{3>{>$cmx9X)Hmv4f%AG|uBy2CeZ_F+w~{zi{lwJaGY&}#W>(6dGAS-Hd4 z{!#S6nS`E2=oyfDzI|^!_wMZ}<=!Kww(3{~^BS!8;H z`9s#0pP*t-LvZ61u(|IQ-Eyr2Kh=kI_VPFcaupmLVWCnMeN9d`DC{8;uCm+Z{vve6-Q-?qcN3f z1=Z1+?!X6fOlCUpikt*XI5xALBulbX0oIRoNSKzeQ^JgdT`iPu;5|2N)(if=f{Ic< zFmMCg0&GBH4UME_@P@WYchnlB+0YGx4c{=?)*G4wZ^!AtlSZF9@nrCHq1=t92Tw1` zeR%i#Ti7;cQCz}C*v@B7X8@F4Y&R%-V#=UDgtEr=qP*`}!x;w0ZnnRLW9uf4ZIKRt zJ3GMclUjF3j)N?VGU*uERNg7ehnn@fVvXBl{@wl_c0YUII($xNufLCtvN3TX2>UVA9BQFF$Q}aa{uauI*+)QmfPEDE`>4pY z`{{fX-=CiaSigmz(2kTI$sd-#KRcfP(iiFX7soFOGgm3XWzrEX2{X@q^3CRoHD6fE zei0jPC9(IR2mKE{I03}V9rmX@VdR#*a@H2_AzvuyrOUCa|^X zEldO>Q_<_l#7zsO$+1aU391b}R|+g#5ayMILg-VU%4`yS3JSug7RpNqXKd_O9=gOH zVMitqQ6|t{&WFor$H?I(c0{CEAqc&4&UevX))d;QQ^J^rYN4MG3XXZ2G;+lKGKMXA zg|b^MR-h}W%@GphUmf}EzkdAHmyh$kV9LK(eK_@Zzx{j1g+5nX&g#;JP~ZwbmihA@ zIgN01y__F8#$zONiA1X3InEEuX85P@;y+7Z65yYHrvK$f-&}d|xJXrbCG>M-hSnKs z)+Y$O4scs6NRFIz;>dQ=re->}7Tl>tWf4x-q9&m(p|J?Zj9E7{yE$PN5Trn~u?96VP;+au!88SK~OHLYHEbjS{6nM5a*(P4k7YEdWSE z7b*Q1p5Px5rqhZ+&@I=@n)WmT;H!2$v8Yp6uxhL+SCg_t4n37pvP4cKl{)Gs0&cU8 z@O{x7QWh-)0Okoena#4bgfpm~VsMP=NrL1?@fBtx95>-)x5`?ZWK(yPeT8*M*>sz1 zO3H3-=PlL=US%BROme(81BWWPqa&9R0HOB-;W!Bk{(l zaIjHpB*6xybP9L~>7l0n+a**f5wc+^(E*9yVO#5_q3FsIbXA^%v{E)GwHNnM>X3Zf zq)ZwPJ%TE>6@Bbr+vSV`vK{@H)mX+@QcjUP^;+VX{UjTS#?Y!4y@58iU25(G{&aKn zcO7y2cZ4>YC-f57&Imh&S(TcfrsN=IJ_F4!LUTs;a2L3C(+U9fQ)~}7qM8(=n!QvL z_ckfN4`kUssizAPZ-E;j3wlBA?r-64pKXgBklzOq>^@2i!Nwh&(?O&Vos@c0rqR?=am;=&lZ6JryTG1v32t#@s%n~D#HFTd6%ayYX4A=qPv&%Gv6 zmudwVM3|5;A6fhGxJbsQ)#OC}y_dfG#<{sCkMkPD3f&plMx@u=+=5rm`;B&vi*%$W zPlwduSkZMH8yjSvr*`Fe(*K{w<+0XmXKI31P`myV0NmuuFt9jV*UwWi)hN1C{L>(C zI=4hBSg7E*rEX`tkw2`80px02uk00@&U(NHoUho7h3ZWJau~P@bo(aag$Q%hNr0gT}0ZK~YzBZ8`{VW6tVv zj0d;Gqs2#gy0FH}IXWU6Kk5q|ane0{9lAq#z#-qEGL`aLiyAOEOwZCAS?8Ey#uTeb zNyHSpNlC^OWM?7U0TYaD%`he5<$D{wJ7rxbj!2zl)>JwuLG5axb~mX#EmY*aW8U5t zs@bIWwNMjHYQLnSCuog$P1zdCvM*a&YDuQOc0i5`*>oBS8;r*`)JAR0({LNLF;`pL zsEzsBCR@ePgg()z60%P_B(EK}HWIhCQ}W{Y0DUO#^)7HG7gJ$!wWF@Ck@LEn?Ri$G zTLHFLa_)na&h|_EfW+^U_(7>H`Y!w8?{YAbppu-EtmJ8-qU8f|%ZFOz(Hr?)8*_O- z?k66Idf89C9YrtGbv5in{u3}RWn?K6m%3yL-(GTd%Tjk->X9X^09n^7OY+Ny-I%#P z(mAZiRXaw#!XY+3D=m0*SdC+eOLM<2b1`&wt&?9Y1F9nv{4-QPF9}gH#UN8GkA&#R zqq9Z^Ae;^>{uKgWCH}_21SOYCgbgQ_k^kt$-3?SxnZ_6l6+=V`Q7y6XQke+6qX<}1( za#MMVU!b8G(fLGKF8&Sba9TMt)*gGi6o)y2**!*J1f9Wt~T87}=+yXr8Tz$vjS z@qS#;Z&N68fv<|hKhep^MfeiD{D>P6hC{{;@@R4K^gB*hQjtZDeFL`52paB1vDW+UT7o~UEdhM9ER?v=4{6nbcL z1hk&&5IoW5guv#g&j@vS^Zu}p7QNCO^N!6x!S^7ltQ@}EBBqvQB6#d5E+moYIR+$> z=6?lA{^tONf7XGWr6PIY{0#!H6Ob12FM;LH%R~~a6&YFnWdhQQMFGL{$2qxvA|c!J z^3jl|Txr&%0p-7i`ju|vv#LdJd5ml;uUrXays^^PN=7!XRw@VUBM`vC6>MOf{=#pzQn^}Hlj8CDVh#En2x)f@eTIb?5MC^d0FndBIUZJ3ab@;;At1p zoQF&L5|uh|=u=V@3$%btYMedeFQXlttK2Kli5z9%2HJ{lwG!YO26s9;?o4+p^Yd6f zVrR2jW(y;#TL@gN;oJh!*d3I-b+KCJ3wi$g)Nmujq}X1@i2RR0=C2U=0|H+o@O1)z zNZ?ffu_X>15iMimG$j+LbNXmi1%*7qVYiXJ=5LcIStI@*1ink)zW{_ib9(yBxszuv z@GlVUmkIm|fzK151SYc9oc3n8%zuq&?-0$LFIK#e{|OQPlmKP$5JwksA^#4Q-zD%K zfsNHKG1>zBCjtH*kBlpA!?U(m!qMz2|#BDSMI~lgUg@D^4Vv|8vACWD{GO zthV(7+=1a_@$&%P@%*J{Dh8jy0XS09UQD zDsnGVEfvZ6S~-U-Q!$XnQJAu)e3p7?!F;kxiD#&gY*^|*UXk)7w!bvj{NJgk(t6X! zZ8z(a{4J{ehubQq__wIy5P`=D(6yobE_Yfw!af@9?qf8&!9xJu^8ZkV;{QfTTWNJj Y{$EO3vj?y)G`TifVZt`;8T;e^2fuCYYybcN literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_7_4.cpython-39.pyc b/__pycache__/GodStraJD3_7_4.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..389f180737babac7c06a180bde088a12242186ae GIT binary patch literal 12558 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 O9o7g#rfyD~FZ>@rK=m5{ literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_7_5.cpython-39.pyc b/__pycache__/GodStraJD3_7_5.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5fabea424689b617f8560703721b4481a46f901c GIT binary patch literal 12895 zcmc&*d2k$AdGBjxdQOc-_hI=y#$HP!$-)cRmTk%2HQ4fE8w#e_bVl=9qmky2U-#IS zGODCvL#PxB8$$jFc!@*^Rfbfmk}AlCRI&tekSao@@<*uZkWC0lKqR4(uw355`F*c@ zPT7Y4qN)D+z3+YRd*3_10D@!?vML#L0_pw-dKkG;zVDa=p)|o!U z66qxCN*`w3=_9Ns{RHbx9|eAt^`*yHKO49srBiH>4dFe`hS?6hC!Uhn2-|r_VmrG*A|UwQgMQIzGyPDU}qeiWo(nON!igauh{u=DN{gN13rVd?;6iAGpRY< zGa$^Bc|MydJd@#>qG_AliO-tZe6bl5xlpp3%FmX|1v68sV~BMzV=v+zyl~a##B_Cz zaxqK-HgrA$NlBJ@6wOCnLD45AEN1k=$Q;vMXkxx4EjHAq) z!E^S$a_Y)`w@5Hl$6dpWgCjJvSt+ zBYjo6C(mKV)MCB_khz5sP}ssfi62JY`)9|VzI^H2*kZY8j#=f3ogZ7^=5>rJGbhF> zmdOp8#xcts=e%i+Jym8j>TjE!G>#inqbsXUuvF%l!Tg#zOEQsgk~}Q)C-BbFfYkoN zNciyx(Z4_&QLf52Bz~wWZ(s{<%IgYKs|wRjAh#h`38Plkbx?WbT2*7d0cqW5D^=~b zyzZ~c=(M^ify=uv5b^*z%%t@|)z7qLh2OF@+gJ77R;m)H1Gc{!xGi6mQ0hnNr|kgv zX#-TIHz=z*3p}T^Q0t)no}l)7)Qy_K_L@sNHl_4Fr5apNAZv50!DZ!nnV)TLx9a;i zdRx^$F1?_xhuV3ePoWc%?YDg!K}ruu8zJV~lwy;E&Yp*eAJOsTeK*-0Q-@;Cp#fJ5mro|FS9Kbs4ZF*TRwnf#PL}xg}m)( zOJ!P<1+(O6%jT-(1ahXGv2E@sc1EloN6nW&moJpFj(YXd8OOhnFBD1{r|a64b91xC z%+u#)-fqmCnVY?M?rJjZ1ZlA@&=KGS^CgCp#x8TGQ#5BXqk)Q+%$rSFEoYWcg`iQQ z+PX-RWH>jhO2KwI1g6m|CnOMHY2I==ti=ppX)qLf%LPZ!Wv?>My=F{$^#9hzyPnv${T(SSmf$?HT1d$TnM!2A^`?T-z@tmg5;vvvHE?tH?Cj&|1@#7~-QCC^-?pIU zpbiU3Khh$HCPqgoT|s%o+uQBqqlr;bN~55O3Yx!f1 z>(Qj2k08f)64*sxH-S9__7Wh|g?AI^BG5yimp~tZegXpo1_=xi7$!hA(BnBLgW`nZ zr$Gtzu$W)49i_D51oBq4P_|4baNS;6E#&8&XqJ~P3y1lXYO{Eei} z&k!5E4E;l=Ek8@y&RWHW7SOqwDHx?n5k{ara63CXJILL~1y6TjM&CL=stYW79 zWbdkz8>A0Jdmlt|76*`&h!&G$3Z2d|oanpo-iK!w&ifI0BZt%xb?`Ol1-&Vyji@89Df-Bdlm|+bzZb1$sp;lFn&Q)VtQgLDQ%cKB zN-IoeN?uN@OijZEO8b~E4XY>}V1cyG^mLE~StyUwkA(${2pAPGCSXSsCXRIHU7aOB z-<6k<>qZLHNcAAqD^fL%#HH`9(#G3u4YgVSU5yRg^|8UbN*dNqI*ca*nWA`Ncsh`e zUjB*&enRb@n}{rH9QO zOc!Y-J<^ok*_7S|F8kR5a5?C;w0j$7&o<27I_8j9Yu`4^{%x28kZ+hJ*IW-`eZ)Ou%MU(8<9WDJ9^Q~-( zz3C3D^YjUGiXCT95;c1>R>oV{TS0M>onR;7te!Hb&C}*IJ9SrO)9f@#&NN}pu(QCN zZNkj3Szu<^Ih=&&omi_!!^f~w_#D921N=G4RPlUnQv7{+`r9A-P5S%m>1&QJTQ0!3 zRN+@Tz8p8nik&Z1Ovk@!7H~AJ1@=C9+&p$Xg+wMhX-;GuExVd2C3PnMVi$Y*^@VS|1_M+pvxtO<2lBvYT zA(NGNw3U2m8IE7n_0EhN8E&9Xb9O2Pw>*HBvnI@J-Ahc?Q&Uca z>{&;fK_iZO1#S4wWi#C7H!xTf6ewo$C1a&f1~02JPYKI)3Q{zrBN=%rd>`rE(I z{d-S+bM2!q<-Ei+pCsxj0{Z|wf=^95p%q@X%`6@8TMQ*zB%4=!XWG#$Odh64Tgl|P z6Do5C7ua51!L(+IL*#807`VMmg(3X3 zjU)TEEWb@0>>I$z#%vw0m3z9sN0}v zO2r%Hx8;86Hezen7g651DXn)3x=t9O5f&Lj6k|P64Z*0DAKshOQZ)gS7WxeRI*&2v zpfO_YY2MX zI=SX!`Gtj(2wf=*))9m%mKpZ_jSNm6{v@^ScV%*XD;&-rgB zKD%sk{vjc?=n^e&eh7Kd75?!?37l(_pP;-7iD+;v8CkYy?Bc{$@`>K?PgDI64X}Z3VZ&)ugWn^xMnm#2 zyKEuPi|z~=VngPz)vWWVBE>aX{9u#Ch3_0v$nqjPEsnP?Ctz_6KJcSF0Xs|wbs*%D#o<6$!(E4ItOO(Sf>EU1Y;@;AvnLZ(*YF$RROgnxY5ji*Uthl zM-cv5@(E1nt}my$A(+?7JM z<%BO~Zq4${O{#bvM*kl|7N@UJE-%k#vdaea0Kzs6YB2>yAz#ef{6!+|rYRGX2s@m4 zjGSn>R9L0ThB!N;SJ5_Xck8K2hO;f<92`UKq))WRzkoFVV*j88 z9gQQ~pbj|$O^6mznA6`>M6@J;sB}-}pCyhI^=yjFgd6nM3$#A=pj^@@-lF*w+3)?7 z`&tk<{QR==s`mZ0!M3??Vk5Sk*gWVeZ7lx#1ZW>L^n_3%v6|LE?S;JE5}%2HoWf;7 zNgToZK7IFnyPpv=o zzQa4Awy6f|6SYG~e+;$z>-@zCwA7GAjgPSbQ6r2RFSEg_Pm@$>8NAA$M{TL3(dA9^ zC_aS8%WO#Sh#*RH46P1A=5X^oc=feq=`8W09*DGU0)L;iF@{eQj;2i%s zY@Dq(pvbpg1@Mf*J1@ERAer`jiNH+)*!0p`#4eW&oTNr-G&RnD1*ol#cKk%{#gX^@ z$2(s9#7iyYlOFm*FFB#>6{u5~_Ll9nX-_*r&#WF#={+VfhiyD3bfbLF= z>|&;rGi%(ZorueZG@LQXDVk8@ER-Hu+q@(yd#oo?oWc!T?5NY^ats zCin+{;AHPQA#1VBZ6Rqg&VP$gy<{U~aCf)5MyGlskWPA7#MJpoq6=8pE2g;y6IhrF zWE2<`J4^R{2;q0SN14$W2`BEM>OBPulN=Sx>1v^NJKV;CkWR#-bA6(w92^?b4mdoG zT0iUc>s_>x5%=tGJ&gHZ(s2A0f!`%?hI%I(PFR>3uC8>tSGaj2U#?h2GZww{uv4Cz z+@@Eu6{w%0LGHxoNUzW|N=i5E2>imma_oVk#NJSp&>NbnA?EXtyc~iBg1Z2e5}c3` z?MGxtgFW)3!1%!{^gyBKfui=l?)MQLur6;kChvKmyzcjFo!|jZdjz);l>60f8mvj+ zZB`5I5n;8^Elv)bj2}S4?em}4Q$n?h`;N|h34Rf7X<whE&sYfAI3MDq~KC3dk2${)rWwE3n1?%cLdgDUT!}vUm zzm5rb7`=`OdKg^1p>7yQA_DOPbiXRzL-dY{vMAK6%3_-`DOq4Uny~RYwzCO~D@~6! z(S-HYv0Y8rKpopHu#g0;5v@T_cw3haYe&M22^aRQizM(rRea$8HvFdNZ z*2Zg~1zQ`l!4~Y0$9GuB10@1!slP!X?TDZaxpE-53$NFNaCgCc!MaC2vDpEqNN+!~YjiO<1&iz-#$% zQ+-GyX1q4aM{p_qgezq?NqZFeAl`0kfavXz+uI>YP>c$0qiwS`)`V@$RtkN_WsTi| zD>Oha2M-*%xR>h`Ijkl@nGiXwB9ZG7IjkO$>lQh&YU~8s?;)N8lGq*N)IQYY#GFtX z!e!v&*h)etZi(nVX%~J6Kt!6RQv6p5krxOz94XlfqglzD$)01l~tj3U*DTQv4ESboaIbROFgE0W$khE%Xp*@(SH`Q;f{Jhqneu z2at^-;Z`K!{f434yk7u!BJjh#oYJfG%H4SA#yf&L6lk>veqS%1VR-3#h^|v;Q!2y# zN^fABNB$5YTC|8CveZ5DA3{93#-@lHpt^($@L8Dh&kp0{LHuk<;R)M!TUyuf8HsEM zY~3cGJXELl!zuIOV-;-!?$)M^s1!61;%4e?xewQFRe33D$C%2rI}$!#A-#l8SvESV zs@Ru^*SYC#5x2u7whsnAE?@}HT+N2`vMjKmMj2vm!mX*DLR2n^a`#k%*7EYCscJ1D zy{rhTnBamu>*%g=j@K!rrTCKKwbuzsX{poA={rz|KZr0WSWVhJ$@UG9iI`&BVs z-Ai-{KZ`NwZXE;24adTj2Cf%r?4M;E+#Wx=hi;D>< zJoh(WrN3`XyVyyC|2$}u!;X)WW9B4?wst{`#JrJmPw;4S0WN7Mo8q5AWpNJ+wF3kc zC4xO=p5kGc(Od^`3u>3oDO@e!`iJgR5I@HEP~|Kq=aXLsaCJwjrlO!b#mSvAY8NvP zk#54}Uz2#k6>P#~HPOy$;&H4dTUkxItR`JnlkKb~>#Pt|{48~$t}6I@s52pQ`^4qC z)s!A@UpI;EtD=k*WPK7mq(lAie-MSM{}7|Quj`>$?E4zNgo4MTbibjfvDXzX_L}00 zy{h=T?<;}CPZYiT$4V&nLnZ8g1$XE%+_Q^1&?WGm+UQ$o{X`p$y`m|Pqz%S?q=fuG zfQCA@w)>~9@fIZn=LJEcCI^eQWW&LNGt@Se!p{=m2&%z}uBlpbHr-A16B;f&*t(Ba ziVgC)=;;a1YBt{fr z;yhVDZj83KC2OpdEzCPEQ+7NYZ4{Hbw zXV1-CxOC>?RsJO^{WAhzCh$c96h3z%VjgQYEIti~)xJ?h0U5Q_m!b0NYG51!BwgxW0U|*MV?g$0z-#v4R zD>yy%{3u4t!c~`vYjF2EZAYWb`!ITSbfxx1yTt>j@2(%QIA~p@gZRp5Wpxx^F!3<4 zAASf6T-TX=0e9a#LL~kbcmWy9n-_5#rb978QNXY0Y}BW3+#w;4QKguT53%P*GxH=^ z78miglqEq}CAuV(z zD;Ig3GQpbCv-oM``82H}(tI5M#UQzgFo=}9nQFx29RE*&eYCA$g5$atdxltDBtTy| ziTQlk(sc9_?(ubs%zNuJKpYMPxe@&W>f#exKtC`d9Z~dNT5n9^gJ~hy)9Yx+Fq~6w%*oyS!~O;okRqzkAJpg=&SfQ5y4AI^7f z_w>AMhd&urpT76pbIyJAz31F>?o~D(k4X5%PS4K&$A={8+mxyQX~-PMV?7~D5|fx* zkcx6fE=n0mro2*6ifTp`X|!mXfc+F34E{+ zFD5bxkq#Bwipfk;r1e63rX8}wh4x}9lk#vKnGWFeLT9EE??|Dm*q!P2%6f{unLaPQ zt=O-iZN&irw`T?fK3W(o?#S%$Xm(~Efs9yTS8;b{w=6A4EdFP5W)Dkb9%XHry)2n| zjJ0R>u~a6_Ix_oNXXXIw${b|fnIYhZSx;t!^|HQOQf8EGWBquKu>rOn@9{Gd8)Q3f zNoO)E~=&KFH)7VNB}v#f1WHZ42)#T7eWE@cZyYrtpm4qV|` zW~Mc#a~gyTWuDJv3r}TvwrJWWcak$^E?=z2#LkuMy7H6da>2}&d<=;$X6;41!{;vB zoP@5;Qf>wno+i1-#4XcaT`>yfg@t@+0f-w8?kY#|9JzE?9YIDNK~5bxa>-HNe#KE{@Las&sME-()5xjQ7q4vT z7@92wU_El--0a&g8#5P%Ci!+k1TLHy8XsNPW}XHnB}gt`xj21xXl!U~bUk+d-0Z0n zmoA+;Gc-B6o|>LHKXd8CnTxZh&%a~l;#bzhP^3pc1&LR3uP`>|iByQ@$*r zG>FoV+9Al(`lw8=QC4&odQNGe)f;#9?*Eqw?oJ%^ErSwjv5}sGk*7{n*%gXaI zKUrUICGa8iwxWMX`mnkYX_kdPMUF^z&<<>dDcvV+Mp)pEY)9`XUQZ%I=BFOiI!}Js zuO-l0OJj@wuR0mVI31m($$#FFha7oyi>4(P;8CPrkevwWgcXzL%WO*pYKvyYRsbL! za{|^%A#Xd{QkiCD!7Mr2vbknCp#{^<+BSC-J1b_7qvlJX%NNQyN4@s)SMRg`KYN&Y0yk3{p z7R(Y@2pT2g)IBIoDT$m<$#5#SOYO+?!! z@-exy$^|N2(hBHk9d6p{ruHD( zJBpy40c{AhUw9DRvLxsRK!??eRo0C)6MPW$9R;PK2bZ2CX-esP=9) ztC2svZb8jK9ThEoUqd@IF*-`=63S!V+HM{nO^k|C8U{^V(0r{)hTHxG$`dG8c*96+ z>Y(<&2EL73r(F6b=-UMSlg;0xl}EHeF8vjFl7eTcz74JP;tR>8b`YD(8g0e$U{-C+t^>V4jrMsZ*1g*-)@AFOlHrx@jzuKaDDa+b% z)LQx^-YKEzlt2}Ng-zma=L|_?>t=2!&`e&dU(&Nt_o(syaMgrGwTGel?$sy zbBlJ)>wzI?*P(Qf4?S~)7v4#rgFqL7ZUQ|7dI|Iq*hZkAzyJZ- z1wCAHIxI94KMqQmhsFH7?I@)cCzQ8xg|cNjp{w@FS|LB@#B;oCSy1M4W}$q&md(zU zubGa@^4HQjKS5&jGVBj&TYi$V?N!E_70|hvEf}TMBDS$6v~zX=Mj%&);h=r2)8SJZ zHaAPmu!`Ac&EDaY8)OeeeeXtf76*`&n3j+e3TfvAH2O}w_u$zHeLpA<-c#-cwFofo z(~b8wJoMg+XTSFbHF3zhGqQDLfrWcbZ){rA)Wk!}`gIA@_ytd@hjbZNc;j%=S8(ZDv1vgzaLx z!L#Q%Ei+(lXSzr$nZdgBj=J}tX6_A&cBu01W7M_Vv^ff-c(FCm^w(4Ku~7@laEKvMK+#X!C%1n2oSe@r|DlZ5n1{$kX?GQ&av) zk>BN)zggrJjHgR~#5`&qGpDM5Z2WeJO|V0^w9Ijo92Iz?IAI>`lgyKB5;OgAb{OT; z<_vp+z3CP_h|DSTD0`BTVf)&x&nMZYjX5%5V^78F;NJt4msEJ%wBW?*BA4) zNo^{zF|^6bJK9RVv-6=<%TU|@5!aPuz0xk+O=d|VWMn<9F$s)!Xr)sOe?UVuwQm|85RzMW~_>!11CG^ z#)^bX#(y1o`Z{dI>4v9gWDzP_TSvQmuZ{l!Ncd6W3t3lJO>^CZSEpHKVO~h%&r$h% z2)viT^8|h$zzHs2&+-M!wfwT-B&q|F!{jZPt!8`Gvdcxc??$#*UM<-x@Lw(AB5B=n zVsL#=^O_sZwK8F|A-WkUbB1tRu4@(KNoW6K$0_>z1Tkpj!i^4y}+hA^9fy=Mu#Mg-Cj9RZpFf)J;P0n;LTFX7%%E_uWufx<6NC)9jdB7j9I+KnwNZG&u5Ymugod8aFMv3o zTQ<43l0~0rPIJ-)qA&cT#2*$VSj8qkLU|QUqS3KzH06(omevTrP2j@>K0@G635W%~ zNvS^uaANiSc61s^>LqDvx4$kq!w-i72!wr!9OEZ(UAdGrsL!5alUs(RENKTn@*d;YEjmJ6$K>Ks1Vwa4_h|s#wxzN4Rk}9CKgcu78-VU-5_Ns^tE(HW8 zayzh~VzMGWmzYNIoRzPao)U35v0K2j4I=cuSY}QaG~kNCR`Eq)PYhR4T2Ayr_Qnj) zUMI%W@CaW-Tb!Okxx74=%`F=+zliD8sKrMx3i)E*=AR?dPWou#Gm>Wa1Ud0?sjx=O zhOk%h%cvWUopok4%h{Ij91npz9T4^LmyqV4C-6A}{{)Z@I(oGg{NseBa;L3k5~0n@ zD^6b>qM<3w>8&dwT55n`dO>!lDFwyr!gkRbz4axUA-hm6X%vjp0*V~;{*=2~7&!a| zW#v`v<@JFU%TXstEIF}w&{?rAK0~$omaS&Gz%-w?8=`hGkWvs;7>5}s523IO&=%24CyQ+YDypmhuARasKt+g zY9G}Cim$UYDBO~WDA_OScUvD8BMx46K*(u>oa3NI6ca6?nh(}di;?qN9%Mt}>w|+0 z(^(O8<;bkqn{K@UttL9xkvDx7oEt}RajWKS(fe9X-_#}&&3%VU3l$(xu>T14Y2XQ z1fbJ4Be$3>EtuRUSuV+MPdPEa03KAe2U88M6kN4BDmO<*cf#$ObfPs0lhsZ@uG>bk zs>VdM#lpreQT@WF>4fJ({*~YM@lj3@u&t*FsUtMU4Y+$-mna*lW{q+Fdq8kH0pT%Rq7e-c!a~kKS(``iam^L zhMmq8ZeGimS1qF+i$;3bNzVc|Z*HPh9|hk1x5;FYg^`p_Y*O$Sg=2G1Q4()xs)jgY zV_KtgCtwMV15yePVKf~PNe!EweWEmotxx2hLeD)#?S4HNAfBKnPf-){u6xSs!JyU- zd62qGNFGFaP`!_FtZQ=1mIz&Nw?yQJE?}|py-2vd{<5DEcKZObPJ6xtKMOYmur(K3 z4ePvuQNSZf^7^h8!`woiR=wykc1c-sOkEERrq=&aQ^*KU##UZ_h?gf*np4isKbVQY^T7YC8&*P1EMzMMO(V+N{Oejv|IEGhxDQ+ zFy!z&-BIbOFY(8!w*gxnuf7Isbpkby$+&)x5usAFUBzygJGUa3SI#Xo2sfx`vPsgCPf(^r4l_>V zIz$dLP2@U7PRuYng|>E)%sxr1qzS4Y#`(~!uq48J;bT~GoXpNvC#A?H^IHHS);KxJ z-$jVLK)B~d8kq}Wnp{&Z?C1nhks;>)O5p26Up2au2w37Q2$qOKmu?DzA0rwHbB>Mj zWnwB2cqd^g+&Mlv$}dnxcTd$oMLx0Fhl_nUgZl!i5oi1 zi6c~sHv(27${G*>SH4K)5fpj*0$v7!uK_HipLy>~Ki>!ATO^vG8${**PUT-CKsyA! zO5iF$x|_clFGNKzpX20XB1BLw6mUh#QQ=@aDx$BBit9^G8^R9MZUZ(szg{j0hYL5a za1{##C!XfQkhjgb+%WFcUdxFi{zVtOTu)pa={embs^#o$^a1#>2jIt@1j+GF&S{&woH=9=IWNb{dkhr@o8n=NO@N_mBDXPC80tczqj=txb%IbO>98-PXh{z5p# zLwLZ;Dc!J_op|VuMhvHZu&?)mfo?nl2p;UJ(zOd)OyxKg><-;0Sa6@FUcS|=_9&@SNNj=<*FmUTan*`jJAmILETbz5xL* zgH>9eY}tAGYnoy~mJn`T1+$N6w7>+cTiK40Iqf(GyDq|!7R=a>3|MFaa;i` z=Q#P%{2~C38X;uNa4jh`w#dO4xmIbkY6fAStL^cQ|D2>tTRTg1yE5*#n7ZO})o_mJ z#kgDTc(qz#0mqx`0#`#_<3@8`6ZfgBhI8whaO;|AsB5CRE^sx}HBna=;<6&H?i)7# z4jQ=#Ik@8TidB~$Yo4E}*17jF%v9?WkRk2sg`bB&rvHa9=UrWov?YG3;ilnU_>Y}$ zz{h)C(GssIfyApyu=B1G>i!=^@BF0_NxY&&6F*mC!IyDQr44sk1YeT)nt592gG8T5 zysRk?qz@;4;norS8Eot1`tDz0T{W0a6+}Yse zJkCJ8khKVjV6VlqdD^?VVejsStbqex%mW-xZNER>D9_L1lDxCSFBSoM7i;7#TB*53 zTpEbd0lDFx2l4M>;QYu6g~3EWz?%phBrrkXNdo%; zT#YhL+gmp|A$I6PRDOuSn+ZHZV3@!pfsa#FyC^k6;Bf+J0%HW|-%1>P=G64L3n$KB z=KoB@UnKA)0$(8Tc><3SG0i|HFkdKVZH|9dkvPQ@a4Q3cHT)kasbj35LhUvF6)OHQ zfu9hFQz_20ayIWFNyi9G5fJmU1`ZJQvjE?~WBomVu4(tQ2tqsv{D{9m_4LAf|5A(V z_Y@sZ437rv8(P>adrkY6_Osw&kJIIMp%P4pZLyLR7n5U(n`?zhY=D#48siU(o6;$Q zB1N?*Z^RyrAfOa`9ATv%y8Il=>uT!h)Ne@%(UbHc_rQ@%GR`I8XOtG)kqi6(v%o1T z<8=A?VT_^$9ctoghCI}^nuqxqEy<9^`E+A!`O@+gTP zxQ_^&q?&vl_d$4!NP;WyhO(CTy~JUq4vPtU2mh+Q#!vsBf)?@wF(qvL3&q@Uc8(gB z!{wA}%JtcH*VwK>P?-;VHzJ39pUA?UPEq7~XOX&s@ce2ihuC$&5_Z-Jh)(gRL~CL6 zS-HrQl%b?Mn3J#K}^90D565sQF xQ`XUsxQg%?IXKpFfFv9#a;JI))Wzj@z#uRp9aHpf%u7Yg%?7Az`i%az{{_BtUC;mk literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_7_5_10.cpython-39.pyc b/__pycache__/GodStraJD3_7_5_10.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3304f17a68ccf21463ad614f700dbd3dd075d1f9 GIT binary patch literal 16163 zcmc(GX>eTGb>7>0Z!{W>#twq}f*BHQ0K{+>kRS<=LrNq#LsH{$NAh#C?*q_4FW~J5 zatJrduHwi`l8KZ=<;WK80=<<-sdB2k+dn*2wtmPiCn=|5+4bT$a^%FpSi5T^hngAs zJLkQA=!Jwvu5!7%-g)<&d+xdC?&qF+U#_Dgs^AyDFu(8vpHq}C5z_cGfSkcoeMMCi zp$N68l+>(RQnHFlxK`9kdRFJKUNlO9Y=FZ?F<1&^LmUni!=*?zQi^7yrC2srif7|Y z4;DL0iEM(yp<-t#nN4yyT~5yVi+fA^vim%q{n-PM*HJuJI+Q)6Dpq>) zuyy2tnjKwKMB?Yv>{FsM`?N@AkBYABF_FrSiSF!ikz07BEkmfIlKcmWKLM4hLfHJ<6=b?^10$mIhiY2wk4h9oRu$> z8Y%HhWxFB%T%}U9a%G=FqDwh@3Gc|I>$W7JcjpP4LxvYfE|89@Wv{K8#meGhp}Yt} zZ^@F2mRTq-R3wrYZd>MRt{@Q;l$Kb_Q=(9HX>DPlXpMF|>YSsVcGPJ{J>{sgjy8K1 z&$)-%>1z+QQ+TGYJ=Bi_(T@Yuk56B7w2$0yv^hLiZ#eoa5PcSyK6~}XRv4AGr2(u* zuUwk{$aQn>%CRXqLWIDTv&SYf>&D!xpma0K^&3}bFCH5|HlA6JU%oVd{_M4D=U+TF zm03^C&Rw3ncJ{@q^A|3E|J>CNT{1U6sA!yi>JB=poagZ@<8dQxB9@xhLjD2 zuPbk=^Jq)GR44=FUPBFNV&P3iK8>^w&mX^d^~(9c1EQK|HMe@6s4nYwB%99;vCD7`+eF4Nd4ZO&HU_Hq{zY^qRf_E-&7w86q&CYy@nr zX53RZf;AQ8)*mR4@(Sug9YC3dvJt8Ug|VW^*KETM)B^XkngZ^S9jt}!sn-?61`+#9 zb_nu}0g4N6bJoHl^oG{N9R~MTnLFrlZ>Nm3rd$qV>?$AEYLNvEx;Ew-S?Q%_~WiW&Y-qwDaUg{9FR< zxioA1-SQNultXOxePH54xbG9uV&CYS}ar8nNe1&2q@95XBoOOZ=g<`RsbGmO_ zJ3l{X&R#q}`(bnT?EKv2^VdgXPK5gF0?iLjq)--^hU|)Tx;Q&aU}mV3I?C3a2Gm%z z%1A=cEK}+*2T3zb9o4m>?Q}9_yHrk;2@q+$=5$tv8$labO`}m$)6n|}(;3sJK|xQ_ zZI%0LsHO@7WA8HagqSA;9#sS|Vr-ZD9;Q4#sh3y5N8@nQ);EnO@jlR) zcMiNE@P7VDd@Bm`4TBG(6{D;dVSmZOWp z?a{D2M`FSitUol{N}A)Gu6n}lRUj;_T+uAAm0;mon4NQrSOoGFtP*76I^8~}X-lgt z%xWpuI?W-ToZlyqT~z`oTHHve35{mw1ZMO-cpt#C2lM@iI`T++6f~lsxKA(M zgLvqD2+vdA8{Cva-KR$N5qRC>s)?Y*>V|5fKs50%kuEV;E=H!7#zFlVP%f z(#7GF54-Qjklw{9P5QnjdO$%KST915$J}mC#M1|!<~-dvlG1|h&Zh$*`oc!ojQ zEB1l1-%GWtjWW_k+0Ak&)d4Tn9&0c5ZwG%)6^B4M{Dz+0XYCh9#He_RaU*-cm-4ii z=3pD;P#fj2PdVzPI?_fNZKFJeyobawG3Mp{bPIm81wUr(x1L@e6T^LqIPRx94(ic1 zY6jF}ZPan-FeEalk#SGYi8jh)8|6eBWvY$xOdI878|B$H%5!a$=i4YRv{9y+GHjg^ z6JnD4&}r1vus9*6fFFLt$j-FjXBZ## zv94M#Syu)W>t!)>KP=9Ov#%T3Yu0sfPRzcJJyrIGbw$jH^TaJKVBEeaE`sAD;*$6f z{12~K-)FsQeV_R7eO+7@R}l044U~Ct6_jiP1x}G$_%DBc@<#m z5q^#~Rl2Y^$-h6C`J12qDf<24%nc`yuN1K>(BTts0*lh3?Z?gHn&lX)h4P9MylWM) zvaE;pKYP+TaWaEIEt}GZ^Tg8gQSP)Ca zcqZc*cu!$LBg_e47e3*>rZNwMz{bbMIdUSC4Pmt^o8uxI<^>NS?Ayc0;!Z)>OHQ<& znF(>U=8U5N)dF}r+}*c%J10#@Z$zgu@X3UrQ{KWhD(nR&{m_&X7lo>wE9Wf}+KdJq z?c6!Xn1y&pzg8_cf%EyCwB=pYDS3oSxkA}oEmk0_x^|NSRd>@jPBl+Ezr|apqJQ@D ztN&-l(QmHZ9o2bZCEpJ=cfwl8(M}4xzg*s0bQ|P%-l_*?{^p0$zw+W=t-t zqMp)JGqHLB8WA`lM0T6&P9xVH`4gvTi5azW}up^*jAoQg)ITzE$V-0ade+8T5-K%*fWdozWI&+cID;| z&d4be>0&R=EGXajy2P$IX@a8}YBt!-jK=?xh(r)uKgp%9w_tbvn9(=*KmJr|dK-uVGzAo&1 z;vzl-@7YGG7R63VeSB%oD76&!Q81!Vt_!GxPO1Zu+=xIQwHB$T5?zpzs>MWitxKdK zyJtHeXwac5wFuR*ow~<$SBqdS>z%(*xQ_{ohZ2UFQH19b!v&2g*WiGtS~;0taysF@ zGkKC=2VIrN8<=x&&tdznxDH#4=F;lc{{vwwnLuZAN7Q*fDT0wy_>DpiE#1a9Xr zYs%*+@1U!b6Ihk-*htEZoE~(GcQr)onJ8fFVDL4KN&{S<+pCh!>o_X+$AfD>G~laq^8C*D|eCrp(|1*MtH zEwW4Fp%mcK$p1v?qg25riiH7ZPzw30L^h~O9=91-D994F3vi*DJRm=Vp;o1lt|SeW z|JVp5*_$Y|@6pa5cxD##)3Vp{21>qiAKiIbJjd8U$=i25p zrXDhjr%E#unf^k*uPwqAI%>>2iCH+{MdeQUC3e{H8j6k>g!_J}BAlM8U0F578X5_6 zotejSSG1~J_)%5>L~MI%to{jA0MA4Qd9jQ~>$}>E(7?}~hr7XOz-{2)r5vb5 zw`V+_4iokH52+Ulc9VOHib>u^p6))4((@DqM>5{p1MQ=5?{0)^p=F(%UYiD{ga~8g zt|BbT7`aFYi`4>17g!F)Ftyd-qFxU5W4Z$^vK(9qV0vhw2_vo0bb;vr?&?~EAh`_? z;wh5i?40<%*+r3~+U#4wU5%U{og&k)&mU!}WSgz$Juwq?97%>D=k1MvWnL zl53yRZ>Q-7y=ys$QmLHwZINaTQgC>kfsQ@MJtlg&1prwWs{g(f744zpJMu2zk0 z1gVfD0xY!~;h*%SHngjk;(L?&lb+Nx98u^6VgTB;_Ll9Ea_t~~kjnzdW%26KtzoKB zF4d4*j&rCh=nv^)koE6D_!Z<7;+%%LR(q+And^|)MfKhk+vmnYKI#T_-Cu75k|ai0 z>Hxx@Lb^Vdik23;y`JvXXG4pH#U5^Dq!4@Qpa%2LzIkb4pl-d7C2;FIv<@BuD$4}c z31I$E*5h`iVq%gqGh>-?Hqx3%6HVCU;%|?9{BK|W?SC`l*>(0?PW09qRvj4ZRojVp zhU9og{t)HxO8|6=W#*T1Yjmnza`g+>SEFB7SkR?HmER`0IE!PRhoA^O;y4<TO`}o#rzu9(vnH)eU;WlYAQubW1+`Cwny!K zq-lxwH7)wSp&ROvHazi2Q=>2u4EPTXEv33Rr3varVi^1s7gmFi7iC^}1@+$df`J{W zZmxPl-S-^O9}6UAtyV4?`jH)WtfCfu2rLXZKuqZg6I$KrHYEa2q+x7sZO ziB$Q6h~jp5Q**SjjB8TR@weMJM-g(O(_~iWE2Y)bbYT5=pegGo+s%~Ls|D4uX&g<$ zj6RKkFM%ek`tJj3>1gA^)#q)bZ0R=g%b*EJl3zjKA5#ese`ja$R$ct4=FUZ3b>oWN z9c*>$JYHbqW0zWWy4otERqby=TN84tQ3Mp6OD160rUL8xKS0Hf>YfuHHfWA!Liw{) zcIr{}ADMUl3Fa5j6>Rjcnu&DIt#%_9+pMLTB%; z9PE)dcpW4GzXoB2`H^8|8XI*+5Vku2?Y#)~kwNq51zymt-j(ZYajR7Hd z*sZZ4mt%sZO|-Q9WCL}(#ZRE#a0+a9;%pw!!{B8BhMviBU5s^kSc)<9Pmb$m4E>U^ zG-KQ!?G*CwA(;aTkGU!2uPpa2_hJ5i24>(%@hoo9(2X4`iQ$bFexuF}9xZ@=z0qhf zc(l~1_0eJo(xxAqH{23f9)M){u-^z^s&CBig@3aAUU+PN)Bj}oP5b%L2L04L8}mpz?qVSk=j|A<)Y zmhn`^3FAZ*_9<*GF9exKSR6U7GqOU7RtfwoqLMp%B9oExgoNF*WpI&C#|e?Q1@Uaz zlVjlyPFBg8Q?+3x2H1%%mx&V>)r8XlUpL-xGO#Z_z;1Q9Lh4blcN^HW$FRWL}u1#)~ zaLW-4XJ3_MwSdb_jeP=@vH3@aNZjcqQEg|}nJ3W4pFp2*5+uhz(ayBs z<1P4vd<7c1ra+3eCA8>oZ%Je)#Zo=NN%QLzK1YBICnw^U%851D2rbru)4f_*rQ1;C z(DtrnRf}XTP{S!WH;1j@{bASE{~?^4!)@l_S}$zWG#04rD@Q4i_E zaIEbk-Zb0S6pwTG-dI{?iCP+#w=26LbntO5w#Vq&WQzo$au^R=yMGMJgziI;7adSv z#!ad@xHdlaM;p3r+*39T@*3k#U&wY{NOi6t+)^~F8JkhMF$E7Z>=!$RyH{!-Oxl`? z^{)fgvH|PapqZH5O7M>BJpaP6;fB-fgU2l>a!la_pXkE1ix4JP38>^$!EGhr3ga=x zDZUYp`#7yb^_&o1(Kr<>AV>i&I$b9WFgPVVPGz}sS@ZJi0w+0f>bRRfa&>umMf5SO zoGC9|H>d0J)3ua_(}9K+MOjYa)FO@32e$;!-7h!7%!xO1b~kbM_?*ge8aZ*jL0-=? z+<|!af(DtmvCy}uEojrqtrWL!AGdG6-@dPHz?~@in_CPuGEgt27+fLi^nrQ+G4n=hIeX;7wJ!F$;g*q(9=AQxYh;1JMLCYi-3ihX zI&L=IY1a@z4=w0?%u&OPcm}9E3<@uLuz1wh9QAJN3K*U)=1Mn3?)39IIB9Rc{!IJU z*k5`mbH0bx=yY%e$oD9(ZxQ%K0#^yJ2UUkNLVlZ2HW1lf`wq~>YwRM0Wkhx#8%wS) zlWnI@fVO?8K2?(UKyX5=0i7>XU;?L;B zB3%$T75%N5(ZfzalE2kSk-Yj1JOpl<8PAE?7?F{`M#AWZ8%#u`fIW+Ipo+}M9x7!D zz>TP1>&xK$99J^vHUkbxanrn#mvja$Um_W7qh;#mBv`Z(nd-@Ak%-Y}RPf)o8L_hPpX+%j{G-@E#HSz^} z8sU~|v&AX1)SOXrI`hTNYD0Lub4#b<_5ZbS`tP-9;#*qm&^ua3;_tM?k#B09N4}vY z(_h!RQeV?j!LQ)fW>5b>V$0BAlfoz4BE^?5O!xlrXzyr=1f=vb4qxs!v@SpVHLW}S z6)m0mvepxP8y0VWe}Cfb9VAB*-`1kOjyr_1lD9aqw?`JoM$?dLzkx9hRM_QI&SYT zS{rnGAHhz4o+00&7NuEPdF+9?-vzc)%#VK537h=!hiN)d)5H~rwW5V^+{EEX4j-N1 z>J5$ou&JZ_Os1?9aQ1+Mahxbsc_)P3o6HqxH)s;w>1{&I)k+n;6E{0{J)Uk>78dYD zo3q=Gu9SE5M~UqBh1`iTvlkMXRO^^Zwh%9Q*f@iiaz z@b!mo?>wd9uxnEODVXJ76ZoG5{*u7|0&sdfZk%U9vwCjsy!;=Di}pbBNZUj_-aIc) z$Z@d97YNJ}m?RLQv=bDHPzaZ06iFv>@)-iB2^=GElE4gsg9J_xm?rR9N`|i`6*)!V z6oKOeo+EG;Aj`A$iNUa0b8$&z+yWbmi>j>&^hG!@B`y zl8b|{n9#|c;B%^a6!(Vy2HtwNar;TQSgEYs%;i^{7@8jUMv8?}0m~JY=6?|Q3j+V0 zz@HHKGXj53;I9b$ZvtN;@DynmSSVI6r036oT3ok6pG@!EdC5m*M{lKtN5*ON9u=_K0K$nyra*%HE7BM05SvMviWa)mXLS`uUjE2#{F ALI3~& literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_7_5_2.cpython-39.pyc b/__pycache__/GodStraJD3_7_5_2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..75dcabffd3038949752e8e33e39f1d5f2c7aef0f GIT binary patch literal 13070 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 Knm(n!^?w0HErc=v literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_7_5_5.cpython-39.pyc b/__pycache__/GodStraJD3_7_5_5.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..deb604390e37502bc452c33486873c594c83ee66 GIT binary patch literal 13805 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 literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_7_5_7.cpython-39.pyc b/__pycache__/GodStraJD3_7_5_7.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..50618da19bcf48fd6023023969f0b3f8ab74b770 GIT binary patch literal 13792 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 KX_(XI)Bg*bw{S}U literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_7_5_8.cpython-39.pyc b/__pycache__/GodStraJD3_7_5_8.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1c14c9f57029ff45ebb241fbd3aac6fb77057cf GIT binary patch literal 12850 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 CrON>T literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_7_5_9_1.cpython-39.pyc b/__pycache__/GodStraJD3_7_5_9_1.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..60bcace089f9688905a603aabea8dcee25224f35 GIT binary patch literal 14033 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 literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_7_5_9_2.cpython-39.pyc b/__pycache__/GodStraJD3_7_5_9_2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c875118f9cbdfbcefd562d00220ed4706d9e20a GIT binary patch literal 16981 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 literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_7_6.cpython-39.pyc b/__pycache__/GodStraJD3_7_6.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e38da0fcc1ef2bf2aec469bf68ecc0cb81abce8f GIT binary patch literal 12606 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 literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_7_6_2.cpython-39.pyc b/__pycache__/GodStraJD3_7_6_2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c760b99eca51153136baf77a577704cdc04e5b94 GIT binary patch literal 12670 zcma)D3v66jdA_fix%2dRJQ>@u<7D&NI2${DY<2@A>%?|mkT^>cDi}epCUdUsvFG8O zxs$~1SbZd`qL!tUQc5AE4yBcXS}Ks*QmS@a=!2?=N>v5YZHr2QE{0XLylM84e*b^( zjK`0Jj&=Y0pa1;lKaYFPfByeJ$GLbsqTm-hJvIH}Zz{^yDbe~fkT`*-dQ?>up$N68 zl+>(RQnHFlX|1T0^sLUfUNlO9Y=Cj27%YXdA;tqmvlPyTOOb4(6wO9Uv22X#!D76W z$R-#M728V5Y?5)a*q&{N>~OKYl**=jT1U16G_#n_rtyvxx0N#4jGxz8>dJQe`1Vqd zhPIV@8ScpTGCf-CEA7ng^m%q=AA^ioad&A?c8{veC?ftbHM>_Nvin3^cE3nw9~bS} z10t2}7aiGyBAtCgY|9=Jnd|`QgQ7EgSagZ*+e&swY!^Lv4~t&01MiVDis%zNZ!2P_ zoxG)IM}>J=>EE?VW7@B|VW&{Cgo@5x|DNf@eZH6>_`&2 zI7O*RRCt=?A`!3JPHn*|R%T`jExMV|rWAfsg&kY>OW6m~zgwt%Ez*F4?P{-eja#!i1w|cjk z>QR<~Usmp^Q}|+fsZa*U-NXoJV)~vU52Eb-$-`$aUO07lwoTjMLwVt+~vPK3M7Ts{UBJm9iOZFtSikhdWy{bHf zcbwo9Lj<~& z<$$Brja%w+u&$!p`dtN5eg;FKcB9KeSq{~M!kE|OO~-Ho^}sEyu7Eq_1nZ$&>SYDF zLFE2{6M{UWoAS&x&blc=&uiqAYa|eCywUXhDCFe{mOXWGO9-h|F*4A3X^V<7V zIo?`tJ@66qwr+kz`5k>ZvQZZL6gj3iK_{>hCcIl&iHN{m)rsEK{GLQu=AS;Sb-w&? zvzEZ-TGqGtr`0Ji#{JPLn*8Tob--1JR%u#F2K#{BQ{4#Zgaum`Dq>X!X_aQhY5<@= z<_4+@#e(A+a}}DEMZ4@8^Y&uZ4b9k2&T*uxIXRv^u3jjEuTZSyUH$TflWuUjP%M^n zZpW2Nr=}*YiL<9B-eOIhoSHm;>T-Y74bya;CMDp83uOUa<5Z;E&ehq%S|i2F_KlXL zF=LlegrHTX(k7$SGH8x!t?0OIOj+xd8({)O+Be;{>TFIftO?}R<$)*AWxp^qU%pnP zCY=SbIt!rarW#T4Xof~_Q;X>)a8ol??SX+@P(asI?e(AmDpJj~9(m0GEdpA?=ZP5G zIUQ5ejZ)w;4S61QQ?4y*$@_Y@|RbgQ5ooAj9^Mt^oiU3xOgK}5_D_R{hmSi+=s^finc>#R14p$s~#dsL+ zU5$As!5aea7aqnpuP|RP_^?{B$}(6p!H03*)sP!{c3hG;hsLKANAoSHSJ9 zM)~NvnOlN8$}N5Wx^@^mI$HS>@?-wm-Z(x6kBU|v1W%lKzPv$(*Zu_Z6Uf)(x{+8h z!QK2C_%>dhYWb_+Z)5&9Zu};j%Wxai@+VLx$z|qR+pw9RzmQs90)IR6f1s5gcxc`H zP|MGPGsT>L^-xagr&`_*&JO1M_QrO%%B2>U?}IlDUR{oC@lBg;xXZJ@(V|u<^Tr9( zTK+WTY-2e~TYT;Rj~v*fkTVB48J6>nN6UG1YsZyG_nhjgV-`$n(B7R>Zt!}gSS#79 zWH~Pd2B2LB`h&6$DY=utE&`7c*iB#$0Ww}>nm`AEZ3Hp|Itg?U=q9k8Ko5al0%Qd} zT5^AwHIzI7PHYdeg=xpt$_s9&P|X)BRoe|+a~2khg{y8nFDun5l=)S=Sh=y5%w4Tq zw_RNnuJ@brB#ALA*nddd%5h4zHwv!p0;aTcMXOvZ!Hl(`opUqT1o9PZ9AwA39ZgQl zk#<>F)lzPwX76aSTeKf=eeXeaRSBSIF(aWSG}6uqX!Kop@5QqV`o2%?d!RiC8WB*u zCxiEPJoMg==b--vH+5;^v--%lDRCVi*KJf`W z)ek|>+e%gyN>&r9&VP?Z3EFuMHKM`dZV;Ei0yZ@Y{ZV$5HBlUuxGTp2s%v>_*-F;-GlKuX~T5 zKIEtOvWz}R9q^^@W6Ex{e^4Ce_U~uP&KAn!kTN8O#RxchpEt4xz%eSG1jiA-O#ddz z!6s$E=Xqii$u$J(;3n!Yr0)>B?Gf>`ILiGP zWoZY+8<3_}Ke-`&gww;#{HHjrp;sRN)AmvPy#aqm?Kj$QvX6Ev_A&8B%;z_WW4Dd$ zar+r@Ts(6denNK4J}Sn<3E~zfF-OP61UOEJNpT8}^GSQ$p0LNo={veOBhDgcvW0R^ zycv{JEtI#2^Prp-7od};+~h{@N*>0ll~Vw#5AbufBc;Q(s3 zH*9$|R~DSAEkJK_DhP!)JTwG_Om8;;CvwDl9T~bGL~3|&m@`2&A?HRxIJ0ix#%#f{ zsZC`uj5bvZuCY)k&%+Hhp=G6G5xa*S8A50vg!=L}41Lo_Mw{3XHzo>ICs)qf7V7H{ zxZ3!*YfM12t6zfTz$ua_YZxL5Oq6nkvb9jGKvuPOm5{1yR51p~3<-NWN2Wa7k6hkn zcf}vWdt;LPmKT36{mC<5Te>~guk(&Tk%_AO1?0$IBrrta2*8V<>3i-wZ~e?i$K)td zGhXVwV=wl<^VdH2!vimm&9I#G%VVpjk-sVkyq&-$V)ttKz}U>ST>WZo zadm_kzChqCad}_&&tGUDV~uhz$i2w-+xqb_H?klrj-971Yn57vk7_LB3et^Kq<{ z+OC8sYGE0Td~lfpZng;ZDDweGx=!ULm3O^oIZiU+2*>9l2n{6a31Py)ZmYL}5=8_A z5OI&B6RSt+$y;bsJz7sbtt>}Di8=9=1UTC6s>q2UEC4Bx5bjn0;aCflUE^uNl56?5 z)Gp-~VuROak>7q-S#D>(c9_sH5$iz|W;s=lz$90XF!%!fZsVjv$P!l|}G2xA3 z0VYvM;A6@fJlU$9llfV<4PhV)wow^Y4FWw?p88btpX1}pIRQn*?xSsrSXD>sfWA!v8=_)G6@gld^CV0t=*{JbsY zuXC%pOEkUZ0i?Mr@}p}x2o2cs7^QWzhz6(Xpe3Xordfh~KNb580v{lNK(Zn~NZ_{s z+~E9;oSdn;vDU6#|4Vr4-=a$a_>iLIZ4}TUc-gm5_f(WP^reE30z4 zAWPmCvUou{SZY;LCrZ*#`41Lll0E;3Ms_1vnMu4&H3fTd`-33zQ?MdUa3{PHguP;f zf`}?C?cKQ94DzV1uXI!Fku<>sp6W9IWI_rZh8&!CTI+xsL!F^9hcJK$-U*5jj5ma@ z<^oKK+zHI+UI-~Mf%vYd+$cZGp(5VuFmr>54$oGEyRGU}7A#T2SA}-7^4Ow^cGZnu z$laWjxf@jQG@O<{LtEU=Vx=;FHJ6{au!|t+Sx^4+#I z1#MA>-0l{_x~2%Xt0jwgsR3N*o+>{{5-D!m5?P9_@mD`VGlZNq#h`eR5zy42|EJwI z!l2nt<3@Fnurzm1XsR*YiNg5<-?4~igS!2zDWke61rlE|gE zk{Wu5)R*Y61X2-QqEKR&&Z3;PY?z^{%WJQ#@ z6U_Z#)Ek>Oz!}Ge#v2XrHsDDffyT(RAp(QiA^*c7#pRP+{!1v|(Wq&QGAft(l1Ouz zc9hX%T5PKa3`NHnzfY~agz`$+pkpi4oY}Hwl0xM=SxSnf+~j_Cpq~b|y)OPv1eLio zjUo4mHtFtpWt=2apBTp&HtPXKJlt2VD(VRd2D7Ahg zw5PM#9vWlRg<7|J*f_ok*ZZ;vaJ^lqSC?los!8ra5BI2>S^z#>c8Ol_dASkcgzBVR$n2jfh=HI0$7SUm5K$uYz++#4YP^6jx@3^H7c*8j2pRD!)6RuqU!jDsx#(> zeS>~@Xa+p|lQ;14jtW+Q(czJmpUstLYb06}FWGLw(|0g{c%WF>yQ%d=3#uKP3K?2tdssCmPsgG-=jhnL$i#M^ zxHgV%?TMljcYVDyzHZDH;kkW1tto50!gtf_ zGN*HS$F9j%0r2%&1dI$lLI5%w0%*%bAFaj*bUQZ?cp&H-(}vl~*~l2#lrutmJNuc9 zE;lDawLj+VuIo2D`S&zw{(-bFH~w(tCfl>eCiS35ZSbI z=WE#DOBQ*jpQD}o(~z!cY1nG`i$-wHlX{?Onb$QvfpZ=Mwq0h6bmH}(PG?6EH3^H4 zu%H?NUxdpAQLsT_PVjYc{#rT28y{%&Jka#aYrz1?$fya(NI*u0YJDvjG};lJg!~NE z!!`DSC#Y{>Nwjoqll_Q}diJ9MWCn^z?g!$H&EGaLHgfpnti4c%kBSq2xH4>)RfjZ; z6&^)Vmv(QmVm7h&>wYvCCS{IRw)_x^=3$OBf7BE*R3`9LzX;I|PSWctd^%OAbo@1^ z0fVLcJiT!^?Nb6iCD^2de2UqmgnbGQaZxr3^^ZfHZaS^!cMrYeoCg)YqKm{`mCPMb z+ghl}CbhkVisNygH`PK7G^rge)KHU}W-3~O+K4y6wIR)I+18RvW!C3rxL-JQ=bn&x zWBUE;Y|UwoRo6OdW4yZ8Q5$2noomJxh;~uW!rZQ2=8bqYBJAgVgFfc%8IK`g)hyVDNf-F zbE<<=__CZzbBe#HlfuYtBbnU_&)flO6*l_8Dc%;@wUWb_;*z${RX)X`-Cq7G07vnT z49Q<6!YmWKa~Fg5FW!MU@a65^6!hXei#2YRlM=Jvy zxRhjRxDjOe+Fq7P{!IeU5hxN^0O-%iH{pdi;^lLa+*w2_z;N>I@)DJ9yXuvO@j>mWBFeV7K-Zl-r=JhF+zg4@ zbP_f65c=>#=p$}|5}8+o;2ax5?2qCcjTj4mX>;ad+xis4UQ31&R{- z_b95PqCt%o59s~m!JiThNo%cAR z;#@2f+9DRQMfIC>b~6_7FW6P+0s=)F0rfdtT|m6>i_35=h2|Kzyr6dC;IppIA<8Dm zr3}_}tP;{-+C1rbTmtfFxVR92>5PjB4GM5N%;gek+}-QJoj#ujbmYS2VCRapqA!_o zp9+27qynGUSU&0dMk`Ee1yd7{2xrzyi#Cy*(WbRy%C!{tFT(wc`u%gcf9;R#U(D~{ z|ED+T$4XFm`yTblYthC&LeHSj9nC&_>9sNV>Aj6_=xt(CZ@_M9A!xk@E zUd=Nvy_G`lpBj_*F&X8L2>dRADFUS4O>wd9W}pHyCd zn;Veih`1@rYMclIxoQo2OFrJ3fwK)nOmTmyl9v}rH0L&sl!ra7G2Bo?u!DKc4qJ@_ zjV-Df^<*@v_LB{fqn^mojUq>zA~&sY`3~ychL};Rxwv>N=xF;nTH0CgD&v`C;=2aS z&?L;k-LGjz;#Dn>enm6WKhYxTA8E0~543pjdpLwnX4(?pC(hk471L~PdfbUuv{3qg zwQ%~!S|sr!EgJj*xQ{ID*<`G3ZpXScPBz*)dy!_vn!O0ijw51Cb+)meYdVZrKw6S>Mzs$O%wNR6f#p{SgbBC={)5236ZkrTZxHxz0xuF+CGc$m-yyIA-&gJ?U=rvffUC^7 zfV|J{1ZY=c#p6XoJIL}ROq_|zDFQVxroi@9hs&Y_Yv4nU~Mrm_5^Xi3xN7v z{RDt%7!Qnyp&2?y=~W}ph{F2spBQoTfo9@~;W0pc-3a@6uNq%7eh?fFdi&-^sHNZq z`a3W1l$KPJS}MR;2vDa7H1gV*7dSZJ9X^OX1s|`IZWYGLrk=W#`gtYJ?e2flJ1T(& zpyL;qEae$*oWjk^z%oFY+nVV?j9C@uCpOMyy)&8}YkB@{;l;s)#x?J%457R?TX>>G zkj#d0@n&Ih5SMynl*IRLAp&PWwnR|CmK05Pg9|XMa#jE9@DYm%0}Bcards=2dAj(G z7Rm$_N;tTie04B)l^T}E$#4Vn%)dQr#jZn8MGpE`jRw~&A6%B=EYHx3v}A;*Yvnwm zKsYCEqsju@DM?lmPEXO=N=YUu5vF-Yw~?yy1k!Sp<`T~F{B5I=J0$Vn1B%4I6Ku#B zmhF_$wkcyorYPeO2|Z7Mt^o0u-D;}3<}pu!Jre|5^#ni?_8D1eUH;utx?gIlu^=d* V0@KV4X0pb!c^xzjbJBe4{{fOALxunV literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_8.cpython-39.pyc b/__pycache__/GodStraJD3_8.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d29aa4b80852f7df93c12ec20fbd1465ca2d12aa GIT binary patch literal 14637 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 Kxc_sC{(k_iJaaAp literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD3_9.cpython-39.pyc b/__pycache__/GodStraJD3_9.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ece2f35edcadf909a51e63ce4ab4d023bea33ae3 GIT binary patch literal 15062 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 literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD4.cpython-39.pyc b/__pycache__/GodStraJD4.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c0d7b6eb0e298993f5180695f9941026f9778356 GIT binary patch literal 14525 zcmdU03v3+6dEVFUJ$QUm4^onKde{;jQ=%w2epIG(ygQK>9q*XCqhudaT#wu(dD1=T z-KAu)CnTtpG-+$Vb=#y#oK%X_Gy>8VO@r15&?H6Cv}sy2DEdeTMeD>Z5H)Gqpg<8b zveoaO-P_ya!&aU}kJx|bpZVvXng5@E=AVC-rOwWffM57bdf`j&7KB#_@&1B9PUA7Z zEee7rXktaEiUm=mv{aF*azU;t1%=~s#aC4eDo-mFe>G4D@U*WItcD69o>nUz)o`KH zNq1GdB|#LbJskEHdU(FS(pT*-^gA>Ig>7gnP}yGHQP?30OM(`BQ3MQe*ui0#!%hyn zIPB)I=S5NLeIZcTsSjQg3PbvkzEkTP6f(_pzb8H5NpJI{w|mk%Jn5aD^q`$y6ttm_ ziG^Kw@4|bxw!5%L+f%qt->VN@6Vd)&^dh1U#f6`Kps-Khh4)ZlzaF`9K;QRaLAy`i z|6$=H@=ZwhIq~T%p10`-siwB)qV$|-yjLI6_6|_r59+(z+>h$Jc0qxLrK|7=euE~YNzz=J~z=y9%g<(z23BwQG zrkOA-GDRy{rD2H;#7dUFSU1Y^rOHgnC{=Y!H&{25^znW)$CP+tI?9x@Q9zlJf$3y?CX-C(SJi1`j*K!n zhiqRyo1Bh1dB;`*6BE&NJU4O-rO~X5jxi-qOu|$7Vm_Nl$5Y94f=S3xL6&$j?^2F3 zDSw>F=bJ5Nld;73Vd{|_o6azKA~UlZoQ}rgXOd5jjID-p`Alpon@f(2uKJ_dOeT${ z{WGcATrob)%$!20qolWI4xmc^}Xp<^QBTEx~k|`j~ayZlE2DZG35sZx|&Y*z+ zZ>f#nF^n*qe3Hr2ndxSgQJ3{JQ=;)FvrLLV>9QHMJA=Wa?~rLMj(2N3kl^&0Bt{(r zKxKfLOsmSVR&tE_&L>l;EJm73I-JIspT=@*w8{F|xJ*LyMdRo;lcI4ZXC}_snMyjE z&oVWijix6PXeXZ?SBN-pK9-sx1z^DECY@CNsTn6Vorv?)xF5u;9Z>DeR5X{#Vrsa0 zp3cM*jEds1nM5|3iF@fV29)G56(C(VIm$vdJ(wlNpP-I}V)4{;G(VL|#aSQHvopD5 zJTV!a8H;5z=X0zJdBL~#xrlTo1@_zM?WwI&N?5>&7MtSIGjN$lb%c^CMOeds=$IE zPNvfd41yXyj?VLn-9V!8bJ27xF_FrklnY@Zh0)qVq0UTZ(Jf5dbb|BpBa0_!Koi+S z^lbEe^eNuz@$*y3d?E)4V`Mqrl-pktc`h*%g)lJ;E@$4YjNL72D49N&$mSFAsp#}{ z!WL-aT%v8%q?BAf89ST93UNLeo8mn3(X+GJBo`CJkw$qmb-bljhY8mbNn#3}m`*Yp z{8R>ZJ2{h_Ok~nrTW7(WR6>HlY>7=#XEW#dSSAuFd-_l>;@RkACVeLL6qf}_)zvx) zCO?&(MMuGjnt_nxiR?rspU+J5X%wXujwCt}op_4%gXBqPHXff&ppkfvXJTq4&R{CE zWyUg4S2RU6bLUaWDWP~`Ko3`fJpg;o@PT)tjVahBx-=b4^WxkL<^=G3W`_52Hl2L? zY~pw}2{mv6nC;ozR21uIniJ%vG8r0^N0MAqoVPvAVXIsv9;33w4Rz#lTt$wEC!Qj6 zfDw`EzHD4#pSf-X5zV;NLSF=Zl=$EI`+hTHF!7O^wIBoj0F;lNsV=8|qyO3RU)wW5`E zbFF{6Ue#;XN;Q(KY1lWdx?!&Mo~u_@s=7C88?s7Pxl*>Sc(c1_u-WR7yiux|3w5Kq z))TujUumn^KU1=>4c8)BeZIa}D_iAyZLL3NftzWS=ObrUYV(w58b1dq7}p3~CjfU( zFy2Gp=Lx)*!21A(1Gjy6-Bt;N33L+}Bk%|TjX(`xxSxq}CXO@lBom)tB9`}96wkz( zG@f0Pp1^Z5yCxq6A|C}NA3cekXf}`C2u~)DJqCyz1185Z`P%_v3_nv~;>c|Y?|DL+ zKT6Z?5W{0e02=taA>0&p(p$XgV!MQ^NauuGVj511TrJlCO3%R4khFzcg7Mp^yEc9l zW}G;>1oa#>>nm3I=z^g?4QEBu$BwR;x=|#j=csABFHq&t$+||)%-iB8jx1kc!5Y+2 zs+3ptyVM`*m>}*Jjo-z4p8DSWbs#~a$t8o>n#ef72) z$r(wA^ZbLYdgt&Dx~=#&w$k3?H)~UajE6hYG+R@~Z$Y0-95p_Hw?XsL_#^-(SjZ|@ z^<~{C*R|W^_S~jTLY-!0MXxdCqJG6>{zVxm*KX zxl*4ux=@#?3*}0sR$~3!g)PRWuqhW~*bP#N+;9gA(o|g_$C?Gp&1Hu5^5%4{*vjgx z>6bmIvZ&Wkg)NQ*m z4h!=uSlsFcj){V~gc3mth#?V=q(~v815#KHARRzjx~s@M2j!9|y{;r8$NIOO$j+_d(MPihx!7$Grqf#0zAdq%P6;P zXlNr>PVqTVbZ{#>;k2Oev52*gqa@5rvYyc>d?aG+Jt*zur4_dx^+~K%P}0Rqp0i7C z`0<8}ZiBfY@s&wzeA zr*GFcr{CD(gs`!f#HWFovLIp2+L31JCGKzBrclqSZv?03kzv*N4PeF%0yhbKjKGTo zK2G3Q2>cp>UnTH<0v{mo>jXYX;6nsHOyC6qA0hBj0&mE9IKZva_#7x141@X0=6t1Y z>dgPNwS1*go@1T!M%^^Aa?RzN&Rf@HhDhtlnu{M$`#QNRxqGjkct!P$D>ld$nm%KT*)w0p}gYS231(8j$x#i{xoM z=2tLYT_}i}P>?iHgQKFs6?s7^$eLPEG=IUT1q!MbEcmrhA%JvH59u9RhZfd4pOp(? zj&(U$C&#)Stcznk4%W@FUI**pSf7LS0_)TI9ju>Y0}eL8v26~vP2Z0GZP#~bJCL*U zSswy_&746eXVA?Va&m@vYrC}FywAHhw#UJCbL>6`+rzQF4t5{MA`Z5fWBVK|0<2FP zaIk$G+wWleId;Io4!Hd}h#czkK{w}qC+B`Q=K&|@0sWAESU;p4dO_x4JEbt}$$3zF z%XOvjpeN@cZ3H=Q(T-q2ILbmTS9bAz(VzLZuk3&2)S|ic%m2|h^{1y6pME0r4=*nN z(JAiO3@gU(VJ0y7ttU<}<@nfJA7jcRV~;+zc<0u4{$_ga3!i7wiKYDFasK<0Q;Q$^ zApO2{YE{MQbz}^&y1A7r@WTC?zEC2E0!~nAUPm!v9wTpErE{F;s6Fy9g}tyoQT!;t zgG-b}XXn;A6m0EvhOa$*Jt9YJarIxj)sOz{w@wYqETGvjNz?cjlo&5l@zrnq+}ise z`}(QHFZcY}XJ2~utEX1!2<<(djvRYdFltPlZ*fPwdY?+-+t-T;xFBNVd?z7e?PV8D}V80x4-pG zblh3h5LhjdPe*Q;wPMuf$(L;Y$ep1lhR6IafF)cIE#ZdL5O0dMl|C!e5rUk2Y$m>w z*km6IGq~QeEG=p@Cg3Hjtb`jpeX*{{aV^H8_VRDf!$QxRwS0 z(=9?vwTM-^s7IM$-C4%8UcjNgeLQ9}+cPMTm}O0m!2IlyVOF>Lyj)+lUGfN~+EQJESQa8R zUDtH2)rDxqtUqKA)~1RacIxRZpWhr_*HJU#n0n+=sj{L+>I;#jD-bk>S(-!WVwo-u zOjn+e_=;(Z2eNS5St?!9xtQk4i;+3(z6)iGG-tvrILD@TsUk$l8FMLfstx1Du^q~F z2ifLH+O%Vo`4Vx5z=Du zDv$#r7SyuII;|_qbVE~Irq~hNd08(nE?MMFwG$5e_*%EX*Bs*&;uUmBSl`BK8$6^< zX2TE+(mVoQLU22dZ1|J9#4hgrY=;L#UXLno$X8eIYi&tKHeuVg08%v1J;UKz5zEju z0Sg|M!3)w=1#cN|_`eqvym3Cd;KQ3D5@JJbDDaH^*L?4gt_Bcek#V*N(2X0Y0#5Cq zQ#<6;?zpGgVW)PdQ@hKl-F*+WW!&zm6c@VCgJq7+j-p0qM|xus$EnhHn$lQ4aZ1Fw z!s^umTJSm+_G{7v?m`!EUgb+-N7@cbd>b+{=>E?5FM!2wz5S*8-u=x#K4pBFvLLed z7!{Nz(38VsQWUNMZl{47w}5cH-;$X43=^-cp9F!El~Q$1D~(?QgZ~D#u-XGF?q_m-(87$}Ulo%69RSRQL<>7Y%P$JTKf+k(qjI~NCSav4MT2eBQJ^l;F46)%qw zhTMY(Y`IMsY}=+nz|!N9fsHj=GX9>JQ^eB-JTBAcS{PO2C!h(k+Xavb5>7A(qR<~s zAI-n-1^hfvL-~@MD@xw{CM~^rz8a8TRbc<2yOI=oT~fl|lT>L9=Z?_op3Ust?akKa z{xW*_|KHq=f3l4oW{fkHZN{478nMqd;*9JbX5I%*<2G%%Xb#yba zlf%2EZGIg*hx-f(wGrp++C|+3txe47Po9F|f|I9y@5WnN7{J-!)HO zmD^{7?6Uw}z&Oy+>Ar>g|Kyk6iLd9aW zuB}vbq{GGH(<>zesH=o^;KGHz2Pqo$G7FeieHqamlLg5+wS&~1@W*h~Ra-1lDcjzL z7MJTLqBIEf?AbiOSYKE$b&KtFi|e%t_5hw+ESp6LcYaCN3_3Ki!Nt1912`t!CTpok zy?`@?0KMTO4U_+1bP$lFAu=$_o>~ z=SzYyNMH|veFSzA*i9fpV2Hqd1ojiyMPM(10|Y4Wg0F4L^OkW3Ndwi zTE>lk1o%r7nslrTD9);UM@3RWT!{av5~4^dR%{o_p0u5h2p0a3M)b)G!O^YdP;Qk& zu`tkr_c|VO-{HQ;c~9rj$+jPf@iZRuT>z~pQ$f}hdsW0IQ$C~-ak`Gns)CBNs`+`^ zk2H6HkRL#LJH?!?i@<`wc4(bi7so=ty0so&rvvF;t&gX}NcU?4Jl&~n!&!1C!=0)9 zsnV2Q!3;@}m$Lef)Zci5EuZD<#vUbLKKWW5V&IhJ#*YzP>^vIPomocd}*# zm$s4miqYcYw81krTyMI^`iCGA`(^?V{vbR-eouiLL3|%(=h^9tkRiISti8`|gO0;G zc}@86iXZGQqjNam;#5WFrAH!*hOXD>+=?R%9hlH{qkh?~|2RLQMPT_0kFU>kZ<;(M zc8sEx!4#NI&EPI51!iuEi25v*7pz;N?dG&5Gj~UNt;_Wy?^Ds~(@V74k#{5nyTl=! z9#-$)L^($`)Z1F1mH&|F{)nJVr~2s~3LmHGUtGF&?LalkE!)*Uhe49qYWI7p)~snszOHk+js)(vmdv zx+D)_FIhdhnYP}5)7EOog&?)Z@;Gu>9#T)A>FxvbX!@_Q-(B^VU~o9 zM@igUYUDGd7P>e#ei7g{?F?IMso&Hu>9uz4{2gg0f@+QpHMOG*-I07l@WhAKB>xa@ z4OS0rrmamlZmlIcW7;ybrSQhfl`HM<)4JSxKOrr-?S(uVMB33{$Lc*VxsU$eS$6w8 zGEUK?`%S3p4JH$h7qw5OZ_s2q_~w%-F!bH6Ce$WeHxERpXeNfoJPpv)0mZhF##VR( zp$X^!VII1;r%kXS?w~7Vr?iPvn1pYPpSjo7_|XXV@*mx{=PuOtF9#=mxir3YG!EWIgK%D(-W}Z0yWg02^YxW)d$CQ#*|Pwr%`4At z8SWU z;O^?kW)j?z*Y_d9mPM~Wws@_5y$HQ!>%DAm*?K>~HUR(X_hA6uqtJR2z`XnM8US~_ z`3AkE|7~lOEP(XsJtR&1fEwM57yci|zU_BjUB)oY!UqYw1%UaBMXf$hp&nep;j3c% zp3ZoJ2*wGVCJ-eMCy*d8Nq|0?F_HvYr@mGQ;}H~mnzpJ3V0-})0Vy%miKh!sAD(_m z4C5UhaPqq)(V#lR53->9y{-WU0^8%_M@;9CeS~kp^iN7A4+inbO(BSnftIfv!G}f$ zowwQUO)BF1OWjz&hYE&DBk@)vWp#%-qWbWsszEiXzDxL;`c7d)RrqC~ oeO1V>3lXf`j?k47kX0V!-0h(g+2L@>DN3=Z-^*L=1r|toDuS5NewwF`) zs$uou4IzJzdPqHt=e_C?^&UKrP6_J0>d_m5dQ|gY_vDWOKc)@?AHFW-hgBsj4BvN$ zR>H8zWVL7(hb1-;Et=X=O)o7J%dERDj4@9Z zWN$8$n2Ff56YKuT$w(@e9XWy8NX9|OnVcgbq3K*9mx-rh$wVs7Bv2Hv#UIN#oMTMN zon)SijUF?JXnf)rjmQ(7Ni)x6dUic96N$#oB_10YUk_$;>F9JOn;02e_eC=4bP8Sj zW|MQ-LTrY4W@phPla4ZFE}2Vbqs^vc4Xy;uEJgeYCWAG@;cP=1#PTjiFgIQ}hYtL_ zrxtl9FvCpZG3J>`&or8hIihEn9Em-aVN&cdN6eTt7)%~xhfbq0Je%`@24~JCFzYA) z8UxIxn@vtMi{s3DF_BDWFw<BH=9kw z;!}~?@n|M}G0VC@J4s6_3mcq|vmW5l^jsIyp36;hncyZG zml;i_Q8zUc$;?fr^K-m@I&uDd1oA?V7f(&iCC`C_bpV@9#52(bGM$*3z8JX>XT7L_ zz+@7dLC5h-ii@5}XHwverQcz7N7FfpabONybS{#eJDfCa#u zNTuSK1a*88gXayqfka{#BB^M6GMPp#SHffxv$d2$gPF=;SXj2HIG5!Ei5F->lbLwr zeB@%}ao+36i_?i*JPQqDW?9~qGhR}8Ha;7HGBFLVXFja7H7x2Vk-8Ai(NidrH zbQ*p;HJg}sj~l_7fVF1$#5>W)G<*|7nu(-%b#@kO0(dSx%SSnvO1yI} zelnAQ88`#n_I!3af_*f_8M4#qG|kDSNp2}F+gj%ERc;cOQ(5YUIkH)9B3s3ikCQvV ziOBS^Naj48XD${^Q|F{Xbe74{I^nwK3mV7-MKP?K=#fk&K2^Y!l5v~>IcWHTyCR!R z&tfCWWzVsI!_Jgw8qJbSo{Z;kC|!W#_B*u&oJ?@Z_-t<2ztNq(qUq)0N;qq-swK_X z=%1-ow5qvU2`8#5&P}tX8ymeBYUS06<|gd`sc4qUC3DS9?w-YAtA%rVv1%;V^vXt0 zbZw#B(z1WHXyO>IhBMkiZK+x^OSS4of7XOF!z?X?&#hJ$h-T=&04?a(3EUuna8J&F;g+zQp5iSB+ap{2QwUl-l~NU;_#`3?NnN}x=)a4$8xzOj z#_{9JFwb$LwrZA+FY4M82v$^W{P?P&=>-aUjvH3^0#hEJs;Lyryd(C&=*k)kRAG){ zxwNj`rSZ_f1aYsZ|301zH1@_{2MVN0KJ3p@6fM@pD}sKkF5W~8eM`J1sh+x|%BO+d z6zfFs)IHb0W!KAfS@jMI*Sw}wm#>T0l)8usdu|Dkasf&Z2QgVyxaO-Xs(e|}pEPCD zTlZd<>H@fZrc(D^7qbFt71Vyx^g*6HNOk@uXWg&*o|0O){owu%=T>a)X3N0lmY4j9 z(uMa*^}wQpvATN=@aT2Y-EZBCcvq_XDc-#n+$;-o3Z52B#q{0`PS&Qe+mE#ENGT0 z+KQ%^YU&*dd+yM>(0_?4L(FTez`vP%sYWZZtW}wOSz9xhZ%Koucm zRIV-PU1-ac#ZtLkEwX+d!WN>_IFt)foCe8wcDREDXsIqzV9f%h#x}!xd3TyxXp%as z+Eo`SFKJaYAy}wVYd6nkX2{s1YQ;7!>i7WGRTUUkZ8rDEm>C?#docLA@kOuPZXBQb-Ina8K|x z8MGj1T{bN!4{e09#iBi79Oa|@V+GHQ888hB ze@o)^v#9r>{snj4W%xYWoXSC3#oCzk~BX)1v9E`~v4c2mYO$ zzt!HHe`}AE!q!oeoH|y@qJ%wbSBfcDc)W3kQa$V55!{|fh86ucf$291+#>J^0?!k8 zfxxd3_zeQTPT+$CK1ATd1U^FGqXa%i;8_A6C-5ABHYQOPfL-BkelIJC>6EQan%_@{WW#qqKtW8-s5L2w6o%b;YH2RJ1pDtEB~QcLg; zU|S?E2YUKmm*t>G78Ox>T~fMU!N%FK1(zH3_VX2~%PxHvl6UD}!(0|B+ue! zdguJK*k!R(+M^*B&>dSjoe_l}od7m20`%w;PL9IjWP(y0xQ=WW? zV_i1Z$+2!5>*83CjdgRZ*T#A{)@NhA!1~mF8|&xTfQ=1sY=@2Q&~{>cJGEWvE>Lzq zquMd;sCx8S4^P|4`C%93KJ{%k zGfPiA8vKXnSN`M- z4{U~I{SUAbnCI=MPBHo9_}d?5@`K|KJ-qbl?eG5G%={NV&!kh!xuui*?@!Mxee@&r z_m^kZ72IA&#*wR=UtL2K?o+kJA_Wu(f{F_os*&>;dHXuu<6Kwmkq0R4h5d>0M}D4M zA`;!5oA*$Nwe}gl_wfCQ0_`8Q%jig-fC-+?49#Ez$DQr#y6ppdcTIiT5-P zSrx|z%06E9^Kt-X1OXP$Al;%6GBmM0gm-Wr7nNiT3cM~vA<`w!mY(UlV)f98*8Hl0 zR$aVojiTGtbC=z7C-0eKTzU`ZaZw?o+wQrS>addgil-651;h4V!aQn(mufXNY}Ue! zs8b6U%jK}M-y0}$3Wc;`D^fSY%bE_psv0(G;o1tSaIFiQ%UamJ{;iEVolLHps3{pv z6?&?M&EjP(Tv=UM4s*Ha-##-)>>Bv1OK4Rz!?z%97MhIOeSRLKQ%Epx6d^TxFu#&ss$ z?ETIF{KvYyNldv4wIbe-4O2Ck7oi8jE}B zY=f7iYcif5JQ4q1lJUg-=#m#t%1DTHr7j~f_FebBOSUt!g`eKIfy-~V4%n@O zcI%F})H-Ch?zCHX*{!?ZLTeA+?kSWPy3~Vhj_!`4N_R(kViU)$(tDQ5*gkPf#J$4o zRsCw<1~&HV(j?x6F5@^mcISY7x%ySTVFh*f0;;7S!<38 zMic0l#m}HDTpiNR0@ZH=;dZ|*G4V+zu5Dff{?p}RWnL{#%tOF`gIic{$&^_ejaI-u zWX0N}r;*kumW>G%td{yeqt$R9lgr3hvM%Cu_O~(S<=Y6`N#rfz{TQ4P^u{f~MjIjj z7!PlmQiOxTd0Qo)*hb`uR*^Q^5P4#gl<{q(jJHa$(T0?9q<@gxAx#el^jGk3IbqPb zc)*uil)-kiRRXpimkunf(Ubo7B%Csy7T|fAKF7nTA-@5QlHV?ZO^|SdK@x@j;r7w^ z_kF*QCu)c;IaE<{(;K|>^QO4a^jXW@>_6G{WU0OldW}pULkxKjdkpC!unV{wNt>m zt#5tXl)-kkR|D&88L^Evx@qg4{f>L`rrbZ5 z$v^Y31^kl|rf=k*Tl{bvep&WPugX$Ll3s&PdfT7D%^{?Q?`Qr3PZSplELbR1YU*lP zLpf9^Jh56tg1SOj2VS_)_aFtmR$_j`tgRrsW3T`Pr&f}>6Y&^cbyb%NRLgd@poNv1 zfh-LYJ^R|z3$?{XLo?X{r@B@x;|$=`LdhsVxeLphs?(*34KCGGp1?5>Hkr!>8U=zW zBMbbi>9?QESjXKV*Opsi$$rjg6qaI zuM97gi-tk>!WDdWgzH2V7qjp}ZIxJXLs_xjp&DVlWDVC=^`;cN4^G(fI?}m@-w%~o zZxR3zzaett?-lR{58rcHG^>0WI>hkhjSo0|(DhZLCO5qmM9OUuS(@e2 zqIp}i!jk6VL^=w zDH~-{wo``}h>jsprn(TBLRtTiqqR3N1>M{$HJAo>`D*y&Puej0!{qvoUHCm!|3hd- ze}pPdw{8sFHhZcy?8~}VFqg4tTCMbHvQkgeO488ll4l5e|N8NE)_Ma;+uQA{KNY(< zHdS0UMY+Y^K35&eEp@o^Icovsmb!qe4rQvtDhcWjk-E3F$Y;nb^rBb)Wq>=hlW%XO zenY#WRa>p|MY2v9&1@fPSVtauRq_rYavs`{d_&0cuODq^tu_+3w-Vh6Egf1~c;oHL z(e@AMT~527l9inPf-Vc9Y+JBn{Vi{~5B=YHcI!IQ&(Na#Etu;K78B3!v@WKv(qcOD z=8MTc^u6sCR2!-769)>qiQ;FR1!&lSa?vQ`D7=YO0&IY^483TlL$EIHqF26lZ3CyY z20s`-^Hz7`hr&3^e|*QHeTve-_ehwZf7s?JUuk__HIKxtGqOiEpP`#K2HK!+OV;uE zGi~2^eW!>X}73dQ}*O_*A{r+~^-%iB#R(KD@S_{y&f@_O$Z!epU z_aEaShV9CypL>hTwq3L~$k&_|O1ElmKWUDyUn`<^M+0})N7`v{TUkGV3Y#9i`P$<4 z_RT7E*KLln-F2Je0LuY<>py@4xUWK+T>$IuC+h$_^yUZjw(+-RQStz?r~8sL`a@c@ zohza)n63=P=yZb{Us&G3CJ;Cx@J zLx8~cIrykyU$RH}5lsK7$l%Evo@vPk@bS*d+9*B@(&@g<_O@w=?7({%NTW_}&%hT{yvlmx7Iakv z7U}cqTCHsGTp{!F!Rmj*M?r5LYZaZ|SFujKhAud{DCVIIR-lP@H1kr1eu~sj-$&^W zP>FJ{`bhwMpJ^wv^iNYQJSn-? WOCLnpKabz*kNMv%czfP0Wd9dR?;f-O literal 0 HcmV?d00001 diff --git a/__pycache__/GodStraJD_P.cpython-39.pyc b/__pycache__/GodStraJD_P.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ae19d9e13dc2ecfaf6156f1dcc45d48aa4cf81d GIT binary patch literal 10131 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 D3UQTX literal 0 HcmV?d00001 diff --git a/__pycache__/Heracles.cpython-39.pyc b/__pycache__/Heracles.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ec7de166ec5be423aad8f05495e1ac1fe821b172 GIT binary patch literal 2735 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 literal 0 HcmV?d00001 diff --git a/__pycache__/HourBasedStrategy.cpython-39.pyc b/__pycache__/HourBasedStrategy.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..616b5e35e0b4f33ce2de895adcecb511f3e2909f GIT binary patch literal 1613 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 z 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 O9uZJib(c~Sk^ceWq|sgg literal 0 HcmV?d00001 diff --git a/__pycache__/Ishimoku_4.cpython-39.pyc b/__pycache__/Ishimoku_4.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..344488f39f974a66cf7d2f52d5ccb32033a5bf78 GIT binary patch literal 11289 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 z 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 avn~t+FfIO425*f(D?oD#hDE1oLH`^5txWy^ literal 0 HcmV?d00001 diff --git a/__pycache__/MyStrategy.cpython-39.pyc b/__pycache__/MyStrategy.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7f00ee9e723785e7c7f4ed057e538763094cb73d GIT binary patch literal 8230 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 literal 0 HcmV?d00001 diff --git a/__pycache__/NotAnotherSMAOffsetStrategyX1.cpython-39.pyc b/__pycache__/NotAnotherSMAOffsetStrategyX1.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..129d761c99bf2c7adc5a14ac8cb24d14846fc702 GIT binary patch literal 6483 zcmbtYYiu0Xb)NS=xLi`yiXtUZq9t3>$`)Ue6FXM4lJ&5aNR(_Uwli@u9qyf7t~j%^ zx-%<<%CblxIc-rGae<~qTc(hJAb|b^Ns$(5|47KMz6<0>e_Ws_+WrXGqz=-ety9ZZ zzjJ4nT+)hKv@@79=YIFzb6@A4bMAHf`|}E(!r9r{2W3V1Crb1_1`;RmMgIuG6sG!0 zpxUZRyyk0xZtH5Xx63_TU0dj(2dswEk{`O$R9+7;`9}RZcJA$3|&S00lD;TrK zg5CD+RNkIoi#?w5PuP3eASKody~qt(zURcD(`d#XzvlX4 zpo=mm@Oa%r!7PK|xDj|FN0fPf>_VD(nY+v@n_?)6!V@ zaO1``&uO_0E(T9~)kfg@mt5)w+7yLz&3HXkIz@vBd867>X4S+XSNDNbbsBH_Wbmg2 zKZP$k2GUV(D|eJ-Jyx%19lfJ6^=)n0NK=|jnH{5Jjwl_SX`{+ch3O?_+3HxBN@Mn} zD)KbImd6_*6J`}-@>UrHmvv!O!`70}Vpr&AE}Sf9L=Lm!)@T%V-V0pE_nP&1!R`Y; zlO}}5YQg|8E$NJHfoF}{U4`$4)c<;A^5u&c&P*ni}vgPF`?8J1_v8poyK>Don0~6OH5@iuV{76-pO9Dcd>K!82gmHn~m9f z@S$b<3E+F#r|o_CKgrJ9Pq7R3e&7e#tbLGOWS7`04K<~&u4oO_E_;W(r|~_5_YmI0 z-UORl$+64qGq((T(wp+8y(#wEiq2kVZvdI`4zbUo6qSF5*((jK@IHAt@Cc=In8;e>Nc70Mb3PCNo2k2p|jAaFNl1Xb7n+_ct5-@ z@*qwqHE$?lSLGxJpxE2z5$mN2hGg*rztaO@Xzk0v;WO5^SyhkO zp^!xMlT$?~;2;)&9`#XehkI@mHbtS@=G<$>4wa!$C2sR(HHWcupMEgp` zFTVZT&37wjFJq@^tR;r)UhKH2xpV`owiYWSA(RXogwgQBYPnzTq#B=t7=D?^XNbH; zS(T1!T7 zNaaFep-s6bIXNRfN<v&#&(>DhYWvv2*t`r8c^vZS4^cRxmKD9A?vP(e9nyxhl0Ky3)o)BcUXvanllMVf z+3MK>Qq$LLgR~;WuBAvUV4`gnbtHW@7zz$B(YECb{@N9FnepH?3FwVcP_#J{g& zcJeR_$2*c6QMSX3G!FsI0%e^XaX2#cb#fyL%t)S6JCjmIC$n6Ltxln1-qhxlyXtH? z!>7=;FfpHPk57Z=o!nr!w&>b_gwZjkQI^oDF*F#`C4=|$f~ zZgHHSapdl(SFknce8u!d;;=W#a7v%(CmZA#f595VY6#6)Dgls@f04K1&ZKh(71hnimeKr`|WG;{oYEn~f> zJ=FGU@Ac;Akw5-Gv#g&ZFaJOJm5(;OLA}TvX7R0gkUEa{9sM>b4ct8_lxfjeOX-hP zvP7w*r%06u3db@j59LDr9LbT&w=Q?RmCbUYs!-mf{4bRCqH+f}PaU2AHOoU4&DbL4 zyanW@#`;M`Q$eA+g&mQyLV4k_h51^5MLuuv4smB|GwFQ$=TuiDmaSXcX`q6PPi8TI6 zNWv-1a$C3zVu?CRJ*AjGQOsW@j-rE)`l39d4c4H*H`?6cb(gf>{s8(OJqJmOYQUTo zwQ;B^sUfLp@qH~o`qZ|2U4w zJ5CU?Hm=efQE;5AZP!n8ww#;2eCE}&Cr_PmUONNFdvR9eX^*w&{wqpD%x)rRH0f^4 z;b8;*I=xim12j=|GE1hBVz>1}Rx+1zR?d-7h_ndglpf`cL?8TV>{aD# zBUI;Z!*4X}(szjA4N0dJMp%_=o)~*H-aWkTS<4Qa{!)rMjfjH&>Vn6_R$Oj5o7{>R zAW+V58gdWHMB?rpl7yQ^Iy!Wb0R$0jzlR!>>9UQDd1G)(WQ@0LEsTT*M*KNI2oh}vxehg%qfBs#yrkoe1Rs` zl4-<9F~33Sw}|{2k#~sf-bg4m=TCr)rE~%pr8^hYLI;`UxT5IEdBMbgRP zoZ<*BgWp`O-K@r#*+^OkVYP6B<3`C87h~6N%ul%UBoBAo*)Au8VPQ7g0o~FzTVmgO z+t*-%LOyXdZY@#e^;i~OS7wP!nkVw>nR1QoBDTgtlWwCYa6jww4E2+2ET_v;v2DE- zoaQ53xO+_utq*zj(0X2X^fL706sU5!jpf!Jq{5~3cGkOK>$Nx*xnu$m2Jnb#juRp6 nd6Zg-?6JfW9j6(MNbw5lAS_)$DkC{m&%QnF>Oe<+ET^Q(F#+mfRercz6A)2WlmaPRDLsM($M zomnbmmqi1~Pl=GEK=Z34WL^rr1VvF4?Mo{3AqdjurRdXrDS|v0izYVG{P{^bM&d+wck&-w1T*Bu$jD|m|M7aNa0q$ocjVfe{HIDqJv1Mky#qcb2qWmDNwHndpX)Os3IruRV9PdURcgWoFD48$ci;gdb z@!575FMIsbb7wC$8j%-o&!*E|_ZQ|nYn(i79B)(vQPiwDrWQeqsx0Z3pllj1lpg!1 zZ)#_@ncK~OXF2wK-OTc9akU+PqgF%s}1p zP~GA{J29jkffktJ<=Etqg%Y&mY%jD^L)uX`ZI7{i_BcxSvl-YwFl4C=EwBgL*&*!& zv~yB>ct~5pxR0<0Q1`(h?H*PH${hu|Jp@cE+S7RVv19gr=w|SLfK}~-&>dnA+q2%B zcNp&x{O0g`zD6YaFW! z4;1PNp`F6}p$`gu;l#=2*S|*3zx_KuhVuT$LHJ%G4}e@o7-pE)aJxY)%&6nmJ&_GN zaA1Gk<$e#%mqGyW0x8?N}JJONkMRR>yBUjgaH( z3xp*O_uMFKi(=@vm)ZFKQe4 zjY2QKqSJ=7QS23&(p9apx4h2`KU z`NjTSJlHjpfku9_?FPbH2B17~B6W)YuK}Q;ICay@J}eBN^85&@|FxD6W-ZD5K~QS*8h$aYLM)b+>TD>&xg zX{)LyqsStaaA}|Av`D+apOOgL7U=7u@4;BY@iuWHRXY3BGpu&F;_;=ibk}Da=RP)hS4(!eW9H~DrB%} z$xjdQD4kL^OIuWDAM~<)LN`aZcFOh-mCX#bIgs=lo10@u!c=6JNzZ1_OnW|vmS)=X zA;|;V(Zihl2Kr(%{f5J`k0aB{$~b!fbEbF^Vl`oR+Q6d*X`m|5V4Htpar}{udwEwW&RrwxL=tye%6z?KS=?!Io zeYqh^F+u{=CzR#1H&-?OBGojam_W@(9e0g}qR%r_HWM+ITTt{;fv z(h?V(98 zVRA}|IH4D(aFb9kGB3rQwZLBz6U$wIU@Khp+fBz^^|*`5a(Z;6rE)$*mOkD=9w@TZ zvE=mUFI^C$OJNY8A>fwA775*5!uLnZej^6ju66vn8#o>A18p-rsVfW`V%6f8X)u%{ z@xKHS`l=h)CMEzIT5e$XULcw1gq>~xFb9k?SdT*<{SBH&wCgB(QQb$cF{zf3C6)Dx zUIvGMq|8NiN}W-s^hwDVp`X&$PrWDF+^54`z(N^j7Ow;rfsLDCOIRVNM#jkWD?4Fi zBCL^V612eSl@)zOBgj8PHE0_ksL`$%QqD-(gq)OTVJnxGA*j)UK*Acbmf5yO$y`%w z0KOO?;C+oGyWMs$lH4C-B(R3>U?uv7p;3*#&r!W|e)50dd;pf@e2h;kn*~r?KDBg@tBB(H{pxl1G>Lbdr{5uqd$cnGYH-6jz8Ws=Fx zLZ33eZH)yDKqeJ!04VT12smY2{F6io!TBbL+b*Hg>{7InQO@ul2U=AcQAJWC=a z!!k{k2`&Fy543yo1jp#cdNG^_HamdU@N6%MPVwD&Sfr%;-IOZ%lZVf~Eye;>)(JHKxi zdeR0vK+iWJG?fh`R-5FT@?M8fR7PYX>G#FZ1T)FY%`^glr!155iKpiV(Lv}d(RtHg z`7NL|^^x$bP*L^{_g5Um(Ea-dLL+XH{@=CFrx4XD?hyE|f!gYd6=&!o?m9HrG&V{| zEu(8}F$tqjV;Yi>kzquc%up=JnDaXe`~nS?_4Dt4i+_1bFye&HQ~*gq^NMuI1vOiA$;&7$6Z# z0@$Z9n8oU2BI`J9+$=hd$UDfsST`WP=r}KR-5@O)zp!}u-1FzpK7G!4;T+Pfi;JSr z^4oN6>hRF#M`4T8;)*IoTBTc2;kS^N9 z${mwa+2LWsk8!mZtcmh2O>zx6g(GpuJ(>;2Z^&~%(w;w0gP%hCx)(*#fYbJ_ZeMrO zwV})7ZYNbZb$Lsm<93!?)Y5DOp&N6Pcw;FH1O5rHTpkAuz*P97-4ksb4^2xg^1#BsjV64PIw|a20YyndLeqc0 xbrIRqN%TEQa}j+6giZ=YjZO=6(O$%rd)CYr(`il??F}oD-?lP?Wz6JrzXRT>%lrTU literal 0 HcmV?d00001 diff --git a/__pycache__/Pierrick_20211201.cpython-39.pyc b/__pycache__/Pierrick_20211201.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ef46e8026e941ba5a7261b3d4ede7bbf76ce0734 GIT binary patch literal 2459 zcmZuyNpD<55PthCo*A#1*jb(JFhbal5dtA6c9b|IhfEeHIlN2r=5^1whA^BYT1BRx!!E6 znqmBb!v5xfumnSX0)!gWj11q*XUn%?wjwKbe8-`iy&qw++0y2P^_~N;`;vP zg0KWb{tATUzWLSz@DseVsLf5^p^ooT*Y~LB7ihsR0$-v<_aM zHcE#U3|fH>T3Xw3{4qKVePqk>$GJgA=@>WZ_?89S1aPDD2+%snM}eQ@CP-5t9RvFb zdYqnkX81=~ogHO$dUDIAr|9Wtjz76iGfmHcW@?}2EIkLBWAr@4EoB|s?Aypt$HzQ$?(1?*R z?IudQ5s$gjUL1%urW%g6Tip%qNESu9C{>z8sg$_4&yh4C?9*^HNIFd09The#QH9e5 zl#8L=FpW~7-St(jSi{neVsW5t83^sPqAt^3DsU$*#X(4Q8L8x*B#5*vBnQGMuHL$G z^_sTtt=t5m6bC#Z;D`c`x-Dd6Xu|hiH{@|#g(0ypa$h-J;%!^??tz(jA-Y{v{x`d~p8#F=fEiAk*SPttxfYglbTr1U)i78n>KZ(j7^ie3kL9B&KGjNnDZszO%g-oN-#(&Z10DmZ2jZdV`jN$S>;!jYyE5WtkTn? z2g9(t?~N#6B=YJ-Z0JXUzP_2=6d!^X&|?~=96baEODrJCbvZedEQF=Lmz!WG^S@QW zdf}MhnRhrrd&bSiaa|-NfmcU}E)x=`vQ6=J48v2?>%URe)w`A|Wh8si5%6;CV5r#|HxV1FwYu?1fAZAq>MmBx&=G zI0q+*{FmgwV^ZP+lrJHm&r09$t8>{B0HiosEbYsA1E6KE%0a64~TN&+pcw!gsX9gI* z0YI&oXa^w^ShKpO0)U&DpoIn@fYB~z0a%~gJKzaE7cn!yCphLcl)!_`m&AK`UU_dJ d_wa22(Zz*+QC`N#$O@36Sp*2GI2Ci!{vQ)msz3k$ literal 0 HcmV?d00001 diff --git a/__pycache__/PremiereStrategie.cpython-39.pyc b/__pycache__/PremiereStrategie.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..98a02f3d3dae9e75cb57c7012f35be5daa39d162 GIT binary patch literal 9571 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 literal 0 HcmV?d00001 diff --git a/__pycache__/RalliV1.cpython-39.pyc b/__pycache__/RalliV1.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3fefae5008e981d14320ff0af702f186ae8f8015 GIT binary patch literal 7039 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 OZ<=L^dD}FLh5rV5EBbr@ literal 0 HcmV?d00001 diff --git a/__pycache__/RalliV1_disable56.cpython-39.pyc b/__pycache__/RalliV1_disable56.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f6678facabafcf039e4b3af7bb6bc70d8f37636 GIT binary patch literal 6729 zcmcgxTZ|l8d9GVmSNHVv+}g7<_Sl|Xudy4}GoINyIBxI7h81|ctgR&}mXvDxRQJ@l zs;hmfdb~5Vk;oc}1gwQ95djSHN(jj>fOtT>Vey2b@B&gEjv^tHhp3gn3ZW7(<2=bw2Te6%B3SrS6$;u^r6n%3+IULgzRSCx>oUq5U z_DOpZ?Pf3)R_sc+$KE6H`Cu|cOE_&$%eoaj5YE^$vRnve!@c(2aG$*|oU`Y`{r3Lw zfPEn2cQBl==TR=Q5msWOtjxyPIGbRT*Dd=&HpMDz51YQO+Yhn*Y?kf4s@jJ>sIYx( z?yAD(JoPo*ewgLXE7b!%?0`~LMd5|>33n5(xmneO@w{J8gz*tSPS9j>k9F!E5k2oF z?iuce-U_HH+>70?9e7R>Ieshg_=X#Zu|AeLp~ssZ2rUN2i645RK#+Ms;(}Z50(Y5L z&56l4iP}LF$4-Msp%b;emY9~E!L||c&`tae&uP0p7vs-+bw6~27hReL#uTL&TFG{; z@*FK9;(mRIrdP%yo5suKW8eLncB)^v)%*@$`{FNdKX$A6sC?e6HDCYX{~mqi^$+5IjvQv?zULq#m;JHQ&`^R$XWB7YogdMIm;qX{zg-#Gm}Oybl3{M_e)|9Z1l)kWprp<^!y#0ZVs=RP`A z0jnHM{18B02k1X}6b!u2;f-l+UFC-IhH^zu)OD?^cU7id)29fyN^P+&`cN=tE z*s9QV0{qPV8}q##oF~5%#@!@g+A1{Fj#;=Ev@8;647t0w$%O{@A%Av;)#SsFqsB zucGp0;OcmHJVbK}Z~PysqIim}GR4Nm*w_xHGlS)rc^yWAchINNJa zpcQHk{`-eGO#*v>9RzlM2s;JrgA)7D5VnGOA7T%K_V5sP&j9zaL#9kb`z0*q4$Mrp5oh%K_?Y{^$MxO`pnRr_J@ zu=gIkNANq0-%+p19=%>*kFm$E8uok9_kEJ$sH8Z;p15wZC)rbId&E2DEqKS+)7N$O ze)a)i7Kbn=*k1v2d2a|($jGC%}-Xo^}(NSeWTXAee+j8IlFqJ z*8ITF&RzRb>mO?zLIFh(>T#hxhWDu_g}!`zDYGO${Q;jxd+$X6UI7p~Y{ms>N1k~N zw-Y2H7q{KIC#8fhlxW4&-^X^06tkwIh7TTp5lA9(KEOBG-^F!+&crn z_Y&9#a3F7;y&uPBz<~Fx@cfwBVV@Q;AVBK=2R_ zTm*JE@ZXq2OC$IuyaUn3i+13NYrZa?#)`EPq~M76hxX z(z}ZXzAl|y>^EC*kva@NpTdyBT*Im#KMuEn@6z$(XD&dbG}ac=O)qg=5N~dIOd7Y; zfJz6N77ag$>eW#xG7V14;2$AymcR;ua|EOaew0eINMT;Q?DA&Jt5i`PF%r0Ok~ubM zYg4d9&6D-0)$n=f$dhoqSN&uY_?Wz@VpPf*xk8m4oy?eW^hdM zK+F9@YxAh#C;U8yshc!d_K-;)oEZt`t~1MDvAX&6%x)O)Z-s7-X*i>2yLyr%vwcm4 zN7L3#oYS)u0WtDt`4xTv+%heHvDW)MYB;rg=(jqF7x#t!2R*6vdHy$O=Qe?l6Ln&x0b9{#rk5UMKN0T5;yY214tWAzR~ zrO@feOMegA)R$=$wXESJw5^Y%yRS3-V3V(>siHsJEmm{pqdO0v~h-5upLo+1EyzIAFbeVq))gGB2kt3CV znjc1)|22UL!cx5BEIkIVK&>K2)u&}#!e2&vj}Amw6ficRuRZBfWHme<^8i2L7s;TI;|-hS7%KYC7(Q$K9qU^z&z*k>d!9I6H`6g+)wdNq_15 z3sobPU9uSbBF#fboPV!qPPLD8vyX>~IFX%$_T z^sU3YMfN`2cEq!^A9=h|K*u4vO~kTY{RWPQkp)LoOF{{&3;~5zrZ4REy{#7!;Q=Q_ zai|RIAo2O#J-}OE*DByBDJ)Qs&}&(bf~@CIPupccxPw~>^yXl8%6h)f?G5Cgf1wpv z{V}ojbOnV!#R|X;>u001_P^k2cArP_&#?c-pFvJW{`h%*LFXk_?yBphWR!CMtB{@7 zG&a$IA(kd;a)k#_kkrP+RN5f$Ab}2n7J+2~WMPE45d|G2-Q?kT2+*T&lQ#hB8bJS{ zGQH%SV+){*@t#HXrn(}H!c9%6uL^bZUJGHJ2;6X$xhHY2qWl^im8~bi=BWyTLispF-AGR3koT$L732FNmamSL?7fMrBWX=|O6 zpS!us#uX91FhB95R($V5CI`l8Fvo9$NBm6y1uodoX4ILxnpV7{>BYA-WB#_5EC0V{ zmVc||i@(vV`M0z?M)?n*Dir?@lwiN-}HGEvk~VpVmTXeKylbKw6_hlq-uBZ z%iS+r+r6k=2A57-8r*+JVc6hGGf8Halq5B?FsYf@OY6BEM!9c9Q!|UOndLKTGDp&k z?C8DIgx|{x<`)L}_waf)GfDg>tO&KqB_?I+nc9pF)TX>5eRZlfQbnZhkV5dkBOvud zs?w`e{loQxUnP3F&fuRJlw_5HZT?vR+~_Hsg1W6I-$gTIp7TzsL3vj-&;Nl~lKbbM zBS1-EssawsLr-geW#}P#bkqYbJa?ueb$GOyTgREuZg_R4ZUpn*5bEln` zPa{4#w<1Omr_-gN!y_NjOLm*c$1rD6BqeRrZDE`miZm6=Fmj71pV`i^(|wXVc7+-~ zMSzlKnIfkqWRMRd3FWRIP(($;is>Cyrya$xAse39e}8=c4)5TgIcf!)8P@S*N@we9 z9us4@?juhTA@)NCMzO!%j%{yKp#5ZD&hfyDAh(LlABB;HaXd8+)~ zHZ4QfAgiYKuFx=$zM5*WPnY)55vS_Zf?xkjobQ+>FdAVs^fK=lcMa1*$+%O3MaTGe zOCyu}CO%u4#)>R~ZAxU*cyE-E({+qi>v<8+tGiHpE~!CfVOxUskTC7A<=T&ok(Rcxy*4sDOU0hbx^g)A$; z4Eo_ZLIBysw`eNG?S}O5%86HBYtg;>0xnz}(p5eVAS&DAY)?VxjU?e;BBFmGAmbJ~`}+xnuui15<0S24`~*Nr k9f$WTX(e1(%-|iD^*OaY}TbZ7R%>(4lj8`72!RlQ&d?9z%Bs;Etyk2WH4ATgp^39U9eV|$ZbuQR(& zad1uz$5uk(0_q{AA%TtG$7_+Mg#y+2=WyR$rW2Ftk~BA-O>ZYGLYC;eH|tbgDm6tqFoQl#+1-Z&Ioy%#+I-F>_+t=W>(MJbj4rcTO0F`=Mm>_~Z1iGu1Ml?%@X%O8?5W_M_ z!b%d;O2JIS%#frU8c99XtSm{x$UMbXj$}#hK(Quho=(y{nRu#_JefSutilMVK&F5* zHNq*9bHFK*Y52;wxUo*%(Ef;{D@O?QYy9-1c%J`OYrJ^(yFb5p@#J>n+v2bHe&74y zHpd?aOWX(?woT)b#?{SEmunIAeQri^*!IIH;>Bak4qG<;#%(&S2IXoac1xHEC$lsF zSG?=^Q2`Ni9%l4DfS$6Y9HM8+J`!d+P!QIKcgFpz(IYjI(89;vm#P9 z#6?8_4Y=y-%s%Qttkm)Mct?Xnh~w+c=L&chm#=V5IFLciDH6QAS-HQqx>#w30j)$~ zC-y3JMt2|p2)$bAM3mVCJY9+UA>q-e(l=na-Q}rS--}{54BEGDz=eOoRQ5g??4J&r zs66Y3uH#3y069KZnk?ZxbFp^6^5w=tjhS$PVBD?ES8BjHVcc1{w^jk}kWpJ(9c3i$ z-l>h#^Y_Q;^@TBdacyIq1|3FUu)J2gGsak1u6{PkNUVU8BPSBo#W58V)d%Bmc(A%Y zCjVgl^KrUnjnEvEHlK7mjL}wXKWut26;>;$G6L&d7k-TRX8zAh@Pef9cwOW+Hv<|wg5(CHafh{B5xB>X0Z0O-4CG$`5rG_~dQeXY z+E)d&g?gCa^GXjg|1sXzV!WmHpaKy6F^1KcRbs7&dK$>;Ad?{GV|^bph*M;C4jPvkKrDiDxCAllX+h^Aeu~T-3Tkl}&)QtN;MAR4hCh zdjXXneIz~+vL+=sZOXim@X7gwm1|$!U#=}~tgS38FRpLYKtiwAJtzihQ1`eNwrPte znvN|I-G%A^_5-?)X-%)ul=~7<{qP|-U>DX>l1&R03Jn^|`YKbd?*JS->=JA;p%i;p zh^ebf%RI3Yx4XW#$x}`0ZF?;zYI-Gu%?PQB0=z9m4TugquJiOrl&~ve`=kKKAmLN( zu-);Y=tHe1p6kRRi&jA{64xlmz#5DqE@(xypy#v{%Jc``yKr0%&R#GUD?)z}FEUm> z0fc}rx0DziV(=bB2vN5+>0b?SeH+gSuNr_SwzWBhm~}kxUb4zW(6P$^j)d>5;u-JQ z*PAtN?1p{^I+&=vvJ^vXgWX+L0S3>y4C*%}wzC=T(&y?a+nu&h;lx7F1?(8IlC$@kT9|Oa*_Pw(DA<-0`2s(jvIHzta~wZW53I1U?XHy=x2L%TiAaf zn+_R)-WGS;G~!bO0`eW=--e$Zm&&@yM0e#HWJJua30r?Ez)b;$2_!LDYQ>*V7G_ii zV5-_{P1jTn$)OG~4I?@JF_N&pGIaA|;Zh;P#Mx4w>#a@zofWs*+-N&3;zS&`iB&jh z%H=^P?lRH+`(;6DnTs8$2W4kd2)Hpfk1_Tk2=dFr8+IXfLslMCkn%9&A!FoLKS5H% e4XNFmKAD@d{n)rAA~+H;U?LMzA){u{4E_&0jmnV# literal 0 HcmV?d00001 diff --git a/__pycache__/SMAOG.cpython-39.pyc b/__pycache__/SMAOG.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..59b4eddff89265ac4bdfc62e369e9fae7dd422ce GIT binary patch literal 3851 zcmb7GO>7)V74GW4>FN3LcsxmL=g*r>vYGv{9WMbEk{##A&Ss@J%O*moh19gCYQ{b8 znVwYlWN}z=N%puR0xpYku*E5tT;YI(1QKE;kPruuiqmSvVT1$%vA{;V4)0aBXC`(c zLAUz#t5>h8Ue){Fd*u}hwgOLnd8N_WuP7g5WAxFXu?S!ET_9AUBv9IlqqK=b2)5Ng zZEKD$c`Y#7rejK84>E1bu_SK*pLMcr+p%Ta407$flb3uZD71@Cktj@Wjlox<)_v6( zzpT(KweKp_X8AjsQ>N;5rJC!54N6rN7WHBl`)ww&NGS_qZ>36vaf5r50aLjM3eAxF zbuW0!<6fJ^jKjdvC06&_BQlY{(us!}L}agAk2wzBfN8Rqyx3dD;V@Rae)YW2mad+! zqZOl%1&u}cqBWu@OmPTR9F-DBqbk*?P7P|(4D8Evblerq(fobQF_=jUw8(TiHl@%K z9j9eF@xJL~Qn^7ZAm2BTTP#cW(*vMC`GmeL^U|=U%Ic+m+32ab?4ZKWR}i>Mk-~8S$KB_faKZ}1bssF0h z{KxOFu9m*>VXbP4iB&IRZfDi4dmS3E$X)Gih#U|%=666e-9!E z&Lax}?lmy*gaxtdVN;l##Xa7sYdfaEA9+ks7vYO$VDP(23zF27(jrt{SMQPNP^^MX zYk}-t66?SlEpmt4L8;jy6_8TxtRl5CBU;u#E1PQRXHnOfQZ{XBZjsI07Pd1}%4GUK zh^yyYWZf9(SG6WGt1Gq@)ae z*;F!))b2H5Ehg6shW;`?B|22-tU}Ir^URXTxUG{B1u1*NE9(Edj zQy90rpoebv+!(Bj5(v)WatL(^Qi({3Wr5z$g}vCHYjAcGY64{obG?Xh z7r{6eB~`*_(VWaev)v6b(G9!30E&m}cPNyeIONe6VNOXDQYI(KNp+g2k9A@`R)^fS zst=@}sgG4tePm_IpkF4pr_$N?&b%iMOjt_B!JB*HfdBJvPV01IruZlP5K4hp5jcVQqYrjju!83P?F zl>p)?#6+tEvI53Bmeq9~{$Tk|YY{mcO5XT7RO~^Y2AWB`41E*p`l3QB4K_{R0@4Qu0WfccL9fj?Huymx z)p7o1Hh_)t^P+>i=+s!wzpNS__?avp8=3R1vqDKPwoQoV97lg#;cUX@b`g% zY&w$5`FyNyYNI)PYgZ1Z;O$lhyb3uQD0+41e?Y;eMfEMRj(w1|skN@i{0n>{+3CNK zU?B@Sw!}hifQ9@h7G4A!`4SQd1BnWBZ{mDB&cwGfz*Vy&z8yMA`WB0FQf@B;2c>(> zZ^Tda+AzbUw*hdQ3uB^VK+tV7IrU%Q2(?0Na7ATX-BwNek!q>`21n%fWRu}6E7cRi za@`J;T-O!0>q3F(1<2=J_h!!v(w^}vD>s(jUOs*;;;+#2Nr@%k2iUjhrTF4 zGWM|mxY#d_4w_*n*x0FSaC_s-tFJLC_74#xuJ-5T&*|~X&N(7ZD?sZdvTMv7k zm}lk5KpBboB|MZjki3ax7|D{uaN$^vOYlXgW)WRg$>4otW+nf*X-tyjohH+yg0HF* zoq+!CS1p&yQ*zeo1Xpnd4TvblUf{3JdaGFe>annPIGK0EzCo#+g}NX4QOr7Z#_@vY zl!tw3?cCrJ0umMo6>(^<$}C(i7;nH;mSbuVW*4sOUL;RPUiXt+CzGYTl04;bgmpFn W^Xr&tBfJ!pNM643Z^G`kP5r;H{@=U+ literal 0 HcmV?d00001 diff --git a/__pycache__/SMAOffsetProtectOptV1Mod2.cpython-39.pyc b/__pycache__/SMAOffsetProtectOptV1Mod2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..395ec1acc59a56b486cda3c991c990d6d510383d GIT binary patch literal 4941 zcmb7ITWnlM89ui?d+~j}c5KH^+$2r2z1T^bBB5HbLoRJl?Sv+zaA4JUH!zUX@bzCZgKim~;Bt{eEWvYg55sIAmzL77i;o;taRP^G+V)b}$+i zoI*I}j466L$cGpS$DMJ7XM%m1nec#fpv~`KIO5Er zpJRQjpAE1Z&n)IaAp3|@du=m?CC$SC zB@>|YrO~m_&Z5N&01fS3?LF<5nds|A!))kGf6KUKwJ}3ssfN`^O=u018I#(6jhO}Q zmff(SVQXbampPJqjSD|wGF5`8d6R*0(Uevxs%=Ok@uYcv`Ajh_vrv{-CgI8)4?Q>F z)k?A^`_MnxK@3)w76>O4JSlDTnP1+~#8cqZN5We|iFhni=z&M14#DKN_!WBtxJ+CI=GfKTFgiXC$Hv%}6b=re4_IlvF{ zS$1SWV~?|=>*$JZR_l5_RN;$JkF2uBm5|v+cw!e zI}XYdJ(LsdBq+yvD9^G}pcL72FtE?b$vxHAG@y#gM)T;2%ulu#E6@Mw(p%rJes8hz zX5sx8{<68%=_HFqOX|m^aRTjGv{T0`Scvb>zY_cL)4U8IMggP=U%Lv!O*3Bh>Omq? zam_1nnTcvJa(|PHNt$Mnw^e@sJNo{%SfT9-?|-x?#xYp=;ScEhcu`CNYn~+VTcV@k zJM}@kGy8!U1K!58rFQ4ok|1Ai9w2Z5KOb0My>CJS-miKJd@ zhZDCH)vJljhMr$_5$|yHQWQi&+L7?8DpJ~TIEp!G{L%$VFyh^hVnnCKvp~M3t!wY< zhz$BSwKAdtGqy4f{hW4NzhyKGsK#8mhXE;r0t?A8R)Roid2R5P-q49v+4!z;-MZ3p zV#MdM-r!3CL`4NeHe>F%v?v-QvXGq1%IyXDsLU+{$Qq?6tew6A{GVvr=Hi|PGS|BY zv5e;yf(WrYK8;R?6Ya@ra->ujf>#sw=9-^y8ZDaQi?nM>EtMk=aBH3HBIc;$39_S@ z8l`^C$mw}w^Qq5pI*28MwBu0#E0H`qA1P)xXp>nrAklS5mg$q!H<*E--BF8p5i^R0 z`e`M_mTXP2i~*Ui@I<9M6)4q^AVX_kS9}S5afATH+aBpp(ZXIK_*I5Xm_#Dp#LPGY zpc%HFyK9@9r}n=0-fVCLwJ{xmDubjhs`q3s5(4G5vuH7yKuZwkwAZGQ!Hjj}FJviX zFo*`(>>ce##)5V|gIU%(Z1*9v%-vaYNeZjAbsH^xn=+xbpn)f>9$~%3(_YXTI&43^ zB4)6=awWKw3%#{`%2kP@IY@&hl6NuN3M?t*gyInB;xGY@SXErq9mYYI;xC)qUi z9*X17#M9o3hxj>5H}v&%lA#>D1+KRZmMg;;l=}Ks#6jFaTq8i4UVIfm+Bc%0j?$3g zVw1oDK@((G z2_h2f4O%Tm;yExWW~J@bP|mWpiNQMRT;(8@LR8{VIYyd@dV>6B=K-6_l_J4dWGTB3>szW*|I(N3#X7 zO7m6VEbaCbN(oGq0e(RpWd;8ybM1i|PBc_$V=?%X6jpq|CT(6$QWR%AD(l5v$`(=gNN9yo{+qz$NN|#Y>zAo| zYp)kC(={#t$O$YdalCKU)>VG9^S~5KG;HGWEQegzt5%}~Z(TS##NS6HB5KBJ*{{e^ zI$Ws^0fb5l^jcQMjI@wa5^C33+1;ue`CmS^=UYN9uDhrt{*Q=!@ih@|Vjq%{c7I$+rl8qXY>Gqh+bq7V3 zPBVzd>ROfl*D!}?ZXnWhPAakly;bE{cNaXqV-&2simUZu%*l7hI=2`7v~qxe@(pq% zLI1jth3-DxBS^EHBqE_nbpk3&lAIr=beUObIreE1Y)sSo^$qwSW{BiA7N~mr;AVy&ksbLC<)J z37o=}e~?2~%7se~`2+bMeT#DPAIM7~o2vG#?F7b}^4HZ}Rb9`aolet$r@gZ~dJX7T zT&zAeEVkh-1rTO1Qy8fknkHf*NNR;vYKOL7Tf#}*(AC%$Uh0Rw#*S#D&9GJDcG@9^ zX{23EPla96xNR_(c`pp+ar0X%Tw~6j(er0u(de0~ad)qhwBq~I9#QtqB(9YGMN(F< ztUX+?J=P5Ew&a;wk6hcAK4jnHI9NSGN~j4&%PLz~&5!<^9N9{0J&-0v*r zG5>`fHdZ(d)&x#-h0|hf;Ivo=ZrfGPUQ9(nd!}JDgZin)Ng0&&?E}C=T5=XpnNKnn zRAU|_jME?=1^YbX;AE2R2Q(0Qg}frKcvb;ZfZ$|W1{8!+dcXrXHw_AzkCLhk@+{yq z9tU8C@#ezz6COw&=V{8(R341!ArEL32u{l?xO5>XsH8%0F&6~w6&RR;?i38#|d9?WOhHerB_EeR=~VZFXAYO^x5t_zYkZ%IECv4Bb%nIN2XVnKv;D z1(!+AlvBtgh9K~iM!2n<81lfOIDw&|vPAwwdAJ5J!{A6LLqjsXq*N172N!IAR!7 z#3QU1qH}C*83(wsp)#BUdmFK?h)CB$;4BvMvMGrldjo3CRP(E+4zUNCO-L{9Fx}5bN>t2LctsHTJ-BxQ2Lg5IdwF2 z%#O%1Hq1F9*NyA&saV9t-<5sPA_qv;kzg=pJBV~du*`(Ki0jXgU^T5cyetkpH~07& z94S%QXpld^W2HR) zD$hkJ^{{da=wn*W@7Jv;ba|f+Q6GGw{Sze5qwM*m!9%F1T=wg^0A^1*Qn~Z3dD%9X d8~WC7b5{9R=Hb1HS1#K?+UA<>-nMzp`WN5YUUL8d literal 0 HcmV?d00001 diff --git a/__pycache__/SecondeStrategie.cpython-39.pyc b/__pycache__/SecondeStrategie.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..585bc21f7773958201af1e512dfe3a010f4e996c GIT binary patch literal 9826 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 CWK3-U literal 0 HcmV?d00001 diff --git a/__pycache__/Solipsis5.cpython-39.pyc b/__pycache__/Solipsis5.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d064548786f552ed67c39205fb2b3fea18e2fca9 GIT binary patch literal 15191 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 HFPr^;OoSed literal 0 HcmV?d00001 diff --git a/__pycache__/StJD01.cpython-39.pyc b/__pycache__/StJD01.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cf1746e952e2a9be5cad1414fa8c062eb2fc6d0d GIT binary patch literal 8881 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 literal 0 HcmV?d00001 diff --git a/__pycache__/Strategy001.cpython-39.pyc b/__pycache__/Strategy001.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5ec92691ca26a9ec06c09d004eab9e30bd1bb2a4 GIT binary patch literal 3562 zcmc&%&5s*N74I*9jy>PWP9!#z1`sh!Y$vO57)3kzPFBi>MVY-ci(2KbYP&bxT~2lN z3?9OXaD)>V&o3??E*>;)LCq5&%wl$8rM7R|$UBLXy1tuwzGvEQQc0_R)$m?Y zOY45!@Rg*IHvN?u-%8t#Wn1a0p=N${iWrv z@pZnz*V)ELF56^VFFk*ANwUo@fMjb)a*@3Ol5MsFr@W+B=BMlTubuq9NubOn1Vlza z3pEn*);@X2B~2K4EV6{=rfV3hQ89q7ky7J)zt@`zx}ivWk>t<8bc`>!tNGNHG0&Su z|B*N*N{}MwBvuDT^fnn!V7=@u(p@O_QW|I7@q}E>%l*x_H~i7|?;ig7-RqW^}$T?n}gx6eu?GupyO%hJALiE1@(Gg+x=P3AO5JF8|j0Y!Y>XEbtQ?@Sm|m? z9*^RT6UtbuVv*5gkHlFdWJ*RpTi-NcbKpvV`Zb#E4G_c8{r4<%A+KD;%6qveRJ;K8^&Vhf z1`P5?ZxI~5c?<%^0rh&LH02Qw@Z|YTgp~utS~+=hdGx=G?6|st(?^(|bUo#YViU6$ z+BtM_l378VVl|-s02pO`Xuq(T&77Y(@ax{3OT3w+Qc9{bNev{<$LrAhjhDJ1ej-^6VKFg)*q-ScO<;3ktT05Pcv!%t1ChQ* z1Z-X&#}GR><#C)On0G-iJg=hkOx=XWQUI0Ay3eDTnD}!E}YE z^m_m7I`kO+M*oZl18wrUkNR-Yvvv#k4{C3@&U| zjhq=yYbU3rx9&g^V+fdYu!QtBJC8}51g;E{$3sZy1kh-ZF-2?{h~m?%8vad zRL^r#nG#FD0}j;4EE9^PNV9UfGrV);SCCvr@^vJ;NZ@^Cox0jhL@2)ryl#gQt}@O7 zIuJ+v)P2=&uefY;e#8Jy7bu8Lh8ZHi2*heRpL@=xz50dYtp3CCoWD6=IQ5guuk~E1 z{y#bn+=Y$;$5l(mfg@S1bX@)9x20kJJICQQ|9|qF{1(`8>NJ9!@tkp_ao;-1zKx`d ztm5`PxOSB6V7rH;k7Qwa!(lp;m~q-ra~x5(y_RE}@9))y=fY>hH~;m@ zzFNJ#xwW}&v^yLAX8B<5&aR1xbYMPRg?)kHB<41W*RyU!aQ7%7l3e273tb(96=*)) z^{S$fle=_)v%ue#`ka&Bz_DLjECY8a6S6zY;@w3m*41g(G*L7c9cHhuGl8z(oWkR6 S+-2SZ(z4evfx9hx$NeW60_rvZ literal 0 HcmV?d00001 diff --git a/__pycache__/Strategy001_custom_sell.cpython-39.pyc b/__pycache__/Strategy001_custom_sell.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8a00116e1f451ffe26bc62f00d4316cffbba1c0f GIT binary patch literal 4339 zcmc&%Npl=Y6|SvUwHC{=EQ809p&`IM$ZFXR51?_3ye}~xF(A#Q8K@{#W_OoWOFOGt zqJ@0I9OuRb!a>-?T+D&U?Ct^;D`3D1m`CfK+bz9iXL~x)w>UHMJe7U^$y`{YA z>8gRJc4xEo-buswf;y9rgU&U)X$Zm%ZiYr=HcgY-R%k_b(~g{`qx*JPh>FdkZaZNq zDmTlzT?i{twK+9x*P?05FpX$N)7j>XY1}nher}z>-BNoLX|JKt2|297h?rHuO z?tV_49^H9u?V{`V(kzMGRD|JysxmyIQFd%t4|=_br$k~G=4!v}RwPez6 zhXQNGdR{xoI=vmt>ttCsU2il7j8#908Z9XvLF-(MTNl!S83K`3wfwzgpJfT_rGf?7 zhGxCbx_xXOUuLUg!A9f-@oKlvE~NRvUw7{IuNMA%_wNT!Hrk(``^}d>`TP$XL(XR# z?ce{7o);T+M_KQ#DeE%cOKZwp8;<BI0JH5Vg zNQtTp9dyGaP1VAvHi9@4a?cAH^t%(p7)j!k6u2Xp@A)0U$wCp!I)aT( zvYPL(-4W39e_6^xnGv^7vpJr~z7_aca_v%yO2XXlr1Z{KTtymf1Pd4(-siI(dtugoWz$>;p? zjqRKH{EJ^N@bi4R7ZiF3EiH+-XlU})2aS6_{_u9AlSHDCCcP{egZxqhejr_rFw_`3 zN@L`x$M6nsP$zP=?g^K)kX|K-Ss=#LLH$jV{~=YkRfV=~QS?*gNMs&$DL9h;eVY~> zK7?O-HJB-+XU0SG(Bvk!KDF>~UmbCrA*YaYibGBb9P62VXg)U|+lMwUpD`d=xoV(Y z)$J*gdFFsydrqrPW6Xrp%y^$nI?Z*?QW-GxI6xFp;;=IrK2A0zmBZ83J)qXpO$z^!IMB(Qvt za8(eIcWLeD5=M0U%Gwd9b`kuGYir6$x*}Gkj_2w|!AD>Oj?mYwqZyiP%QUDLlm%Sb zw?DkDN{_N`KMZzMwIhOEM66(w2;$Pgi`Ou7hS)U2-@Yu=oB zX<0M>vK;Fl)=R5=@Xps-F<Z6CgODI)+fH%Dca%eo%$qN6d-aoZI zu^yKmmk&!1QOBP_x_NFqt{hf4{Fytuj=XwU;05~U#Vwegz9YeI%&DXJeg}yK zb(-HnoM$OgJM$%KA{lstPz;ClC|@8dQmxCWWNV8USqm|l63qlO^7V5kMFP+hT#2%Y z!h6s{@a|8B`OPF2xnSLr@8YtmLh8~f{pckg`CTxN?tn=0>E#C~jMhWkQ z)ULViqn;NIM@p`Xq7&??D2Ri|3tgE6@&p}S-*g(-3qlywrTShizS4EONs2OkPpG$E z&v%nnD;1eqf`WZi(D*h8`)(}u-NDUMH47kUy}N_JuI98y3=Kll)e<5;if1~Q8?TwS4`AI-7s=tzuW8yWL;ab$QG4g|x7? zD&o-4s-(m!$vai0g{mk^k}#D8J);Y7c(rsjR|lwhpH7*QVru3b)e?5iT(bTRkaFKY literal 0 HcmV?d00001 diff --git a/__pycache__/Strategy002.cpython-39.pyc b/__pycache__/Strategy002.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..63c0f9f4c99eb1ab41fe2d1c328bbf38d09a7a12 GIT binary patch literal 3716 zcmc&%OLH5?5#AS`1i*(ViHghe6!Q{N6v0TbD`6?7BvKNcl4M7uFPo&c7MKCB;_ePK zGaw9_Iz>4qr(BY9x#X%`m_u%N`9t>{6Clz$mqIPU0wr|@=JF%0vzMFWyXU1-vOY(l+ z&|X|fihj}1xww>+{kbt+Nve)zTS?9I^L}m2U+@?1Su{@z&n#MC_Rn2^k>>WSdhrx2 zTXkEP-r7?lRID?sJKEccT1tEOBdK62FBt8&7%tigRd`o~347Q=x!IP3!An@T<}it${?1 z&eMfwmOsxH*dkk?iyydjiJp7r`AajLWqKYs=Vmw;=tbZx(-k=7B|SGe-NweXqd%4j zvT+>&liXt>j48R#(>Ro7suQVhzX?-arFwF+(HJq-TRdsB1$zRnqijlD zl_Ot9OqPxOE*}uZNnbJ&sV&2Llk|pQFTGCIr-F?njMDYqkX)77{wCY&yubX1d!HUX z+viqUM*iN?Uo_5~Y(9U)Eu5D<0V?6VxKWpdpENfyVtC>8I;TuQw>p#q);?7dbuOgpS)Lxd*{Y5ca~lbFvP z3mk5Tp8(UG6J5Q-+EL0#NNJ=Zo`&%niPAO~NvNVjMtWf+8d0j4I1FO~K5a%R%{e^C+nGo{VoIF{s%>&U?k<~RTHVO8M8IgPh%#*M3)T-PWM8g zGKIWtEDgGmVlm)=gpv%vIa8B&xgZG#x$G!BT_e4ig|L$rY`z~Wql5BRKVhjdN6HLr z3a0uZ-6V)Ig68@lD?ytP8hM9Y!6Tu!M$V~c;wFQ`1VlE;NE7+VmDM}@yN!4EcJ^1- z$m;9KYJKg@dX$-MS5|NDZ)fYj``-nAmZ>|znt|EU7Uv-7ru%%WvHSLeJB=<+SVQu@ zil&KutpT|&0t)q`F$G6sf0-iEh(oMi=;xM;O2#b@f3aSfr2P;GWj(i#?T_rI?y*blOV+VX?HkB-kP9Q{hDBZc zdSinVM#^_2S@s@k!)Y<*0?r~n_o)=IPEkYeE{nCA!JI>Lv zu`_4v6vuWdW>gxFsy9Ffz^NAgbQVsxD3wIALo8E-M(s8ekX~f}7UT(yTA|{?AP1q+ z0f40>tJ|Y;gh}uY6DG&f79$V2f;7T}Mv*3igzU{s0|MKk-vMKMl$>clX)=LHJ2jZ4 z#*p_7occ0KJL}{=Q-Cx8H9`Os#6x2w6C`KWWBLO_kZF=+ieY+~9=#wljsDV8{0ZbuSK1hD3G85nR=<_B;PJ^&~vDxs+I0Ta5?YzBsEf;H{RQ0UV3 z&i&n64<6ikR4;1>(lWQV|MvE-b|3A%rE}m<$bOf-*VBb3syB?Irk-!|I0g@bJyaug zRSkO(+&JiosKvCqck7Y(7VaW1a5Lf}b}P`qSrl(|#VbgcGBs16it{*guE%@*7_R&v zi-r6VOi0vbmCJU;t+@;K759RnYR=JXvjBXF^cjpH-VkR7W4D2zfTPEV3B-_lfRqPR z?f{+*K<5n_3YV+{&y0&OS2}QRTD05-^cft?9i3#^a2v`j#e_VQ_E3Pwr!s||z@BVHnBN_nn~5nigpGl$TQV7W?DS%CuycmT!u577@QjF zYd9p>eFHk3YrzG}OaM6DHxzchZCim!w(E0Ny;wX@-aaJd+$EQAm;n$N;iIQo6& zhW{;0`0V(<#_5}ILh(K9UW2Z4-L$`K33#ffqtK+I3BZ zP_oyhw(eQE|SW6H()xX7=nD{{tIWA8&3cL literal 0 HcmV?d00001 diff --git a/__pycache__/Strategy003.cpython-39.pyc b/__pycache__/Strategy003.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..55596cd3e0ef1cf3c8e6b412ee06b6eaacf1a820 GIT binary patch literal 4085 zcmc&%&5s*N74I*%?e@oboJlfaH%l>G;$jpf7zv_gr*B^{bLaiguSe)X#A zy{cF5{ob=zt2r7x)$RMiZ%%01AFwn3SkT#mHxtlMjp~sW>m6N3+K7zU?3l6Du~grT z3bEa>6>UYuxYQ{rx)7CPr&F2G)wpJ8x)#?}yU?jm_>E5EmPTz_e4^1J)4y+a7HMHe zYn6^bv)0mO`Oc2uo?yLE%aGQM&==CW8)gEAY|iM=XE^DGC%kR$#q7S1>*k*VbhhBl zegqBEIy%)l2Gu(zHK<7~TA=nPa28tZ6lkeq({iUsjZTR=oifl4t#m5zR)Mb3D*S4+ zOzW_sP8Vq7iPl+Q4YtS{bnz3DHtC5cR;M|~S)wO_b7GEjik=3}5?zKDJdT z9sbH8@Ju-bSc>5dML*?R8{`(_UPQ^=G>N=S4fR6NA9i7=FT^0*Xt&3VH9w8p0cQ`v zbd=4g^V!&zAeoZJx9zyZwWX6o2Hu2H`}-0 zyLYqQPh-~3(xC`vj(xEWzRz6>_|cvPM|&EB;N#%+axpjMK6CNp**DOw3~02<;o14w zUzpi4Wf{c>2v4#UGvQ$u(F^VDnX13!ix(MHZ$oN$^s$DVLbl* zlP1r?DN1i1T?DT@LVrI(P?b&)W}wc6IY&#tfZS{!z@gFu2tGkGfbsNzaarwlT}5?4 z^AWl>gr_6In86{!luC=m-sK~#Mq~lV3+w80dHskY%T}y)X=WZjI)@xHi@jD=8W2F{ zz3n^F-r0Tc`t5CLez0>#7T{(eT21!RKo%d0!6*v5a-o|>5!?mr7B`W}nivhhO(J)| zL!U|W=Dn-Z+`0MzH21EyY>wyQr?FW^jSi$5zwe@L_*rC>bAdC6;U{tA#2_6EBPdqf z{C=}yYA&%?-TBppKvM2#}M3MsI%p@0z& z6e2*pn|o8r+OxKhE~v=0&uDv43_-sLvq}@X474+$D{3CZ`JN?oL>pRDocbB_?Vt{yrG21+Ro_p>!NIVpS=U9kBN}5o`AoUT(`Wok*n=0I^)0>%AHIf73!ATD^9D9b)B;eD0L&=$`PYG#HJ?NM!l>(Y z(|z{be8t;d^{D35MlP7g6##S;OY#gAYD>nSEyK~BmxfXQr(rt(Ff8YVVb}HMOXG8+ zT>qQlIDa*&#$Sw=M&t0U*Sc3O_6y#X=b^Vj)x39sPe8uev*3%lrGQ>Q?fmA8hrh@z z`@h{h)^`8bt8c>z`890ROVbj&(_{=os=clO`uS4UFo=PoYG;0bk%hq4!vkHQgmIY-GE(*!z7FW z%6J;`MbIhBh^^yNqXs2qh{)$+u`Ev=>$-z9gPM1r$+NHLyJ-+)OvvQ{Pkksyq0o*- zZo&>+A5atuLb(8yldCpA4qMq!9he`4LQR*;5YJGF;`ZDyP(D1FTS++uLn4e)nx4|wp`Swxnu XjJ@Su+-iolsp<`tZ>svTUNZj$?*evJ literal 0 HcmV?d00001 diff --git a/__pycache__/Strategy004.cpython-39.pyc b/__pycache__/Strategy004.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dbbbd94ff9f82b8c241e923f0a61ad4a17c74c64 GIT binary patch literal 3986 zcmc&%&2Jn@74NV4{_uG0c;gkx7KVikvOOkCR>BIg9Xn1UWwQ(MN@xJJ+Fdm>9rwp< zb+x@V;Y7HxoVc)xP$F?L2gGHi<;0CYqBtQgSg;Zw5(sO-yjMNbGY(ncMz^}=y;t>K zRlWMX_iCJ4t*pUQ-M-)5nANo3A~X3|z^ua;??6K}s{2}?cXSEAUwRhoOCwTdTTS!?OCboYVaj$pmR zmLaVz&lS?T=fwiL@|@AcWjJWd5zaPu0(Rd;y~!sB%sPDW51?UMN2glHpnAuo1~sWg zb2R@1)=CSV94&V8wA3k3qf?~iP6_BTt#m5zRe`S2D!esXqIJ-y(<$0`taYYXgH5vr zoql4{CY^a~b(#~BS$Y8^GZT_I`VvTH=|$M(ysV6Nx3co3qYugio{@xrNC})o^dr8$ zN^Udm_>|m>Lf?s1SI-muWEZ;nLJZ>7b~_a;yHU{Ya`q0mjpDtz}8$^%ZHn2H({csjm!se(_6bTnn~`3O`)ikvZQBJR$Re!}pi~ zJ##lXlvd1qUlwB#4g4sM6?toHL$sJK`jWKrh=$6fTm1&m35Z3yV*Dp={8|O#&7QwWVx=RnA1h!&QP& zMsT_WbYrlllv3}IE4U@}R_dHqrmhg|1fa4?QcL6;R~ByV+-bl4U~6Y#kt|#b7Fvtv z#-q;Ub7kS?&Sp0Li~n8WTba3I%o&K5E}wxzSIy~K`_7wh+-moufVJZ&5#AWsSKEMn zZd1q~?Qw9lM==N(2h_{y%$57h#+}F45mp8?TIJ}{P{63bCp{D}fN&Q~il)AnMHhh9A6ic#&ncaf;`13_&TLe`hV{rA+Ni=f$dAfKP3aUeox0+S89#+K9+q$& zwDBIUa~em`=DhZ_{0ylXG63^nq8p;O%aa~V7Nx)rlVF!|1pIiy z3|5CxqCzo=y|A}T?lA#T3JC$j2MqX!YLZNmoEwj!0|HLQ0Lu)+@>Ey<5Ed5=1O{l= z>m^X~AkQq42o}!|JcwL0dEogzIdEd3SO{|lfeuL`q-680w;_!?JvL4R(sby%C!YZr z$wKT$2f#_wb-fdWnap*;4o!ddPB2xT)={*#G~jICi;_6yX~v&iMZFT5N_8`G@3c9R zPN-+Hu?YyyvhV^1oAn|pEfzS}WPU&LArYNc0_KEEDbuP*1A@fd*!sFOH#hIf{DYl0 zH}7n7qShYwy{_P|0L?##&8sLiZ@sZ0Ei}@~ak#c<{3UEIVqXplhTHF`6)SR_9{2?u zFf)h-i4PYIJ3GbrE_B2M8m(N{8)n_C>i9N*9#c)DVH~|OnF3y5>0Fd!#OL8tQT{#< z6yp0K#)dJkQFG4%$^t64XWc|8L^3Vx!6gRhBG9Ehq{=C^p2{o0Rh7J!QbRe6Psa}R z>x!S6*Y+%BuK}Z{hcv_}ok5z;c8y__UN}0=uzC{;2*q$Y7nD$1hMrR;i$J+1vDbrQ zN+1?hT%_@uCBe+#DpWQnAX&%iL`;wBK~|2kaEB-meKn1XxCt|J{L|n|{xUY7#pYAc zoGA7ZP~MZXvopScGKCF{3;uazFC%pY8-k5O)gn+m_~7~7n#{Ry>tMb2KKkJ4-+uM; z8+;1H$GSBA$UQcngWB`JYL47vgJ*03S&fFE9>0#ZUpD?=nPt6vW|-xVjl7~xjk5av zt5K`}#i-Z+Y)qB^WHicuG@8ao#+fm9boqs7%glc=*uDl+J_k00#}I5lA=DJ?6gbk9 z4-guHr<8#SK~l*;Qa$=G@E- zY`i+lxpS;Nh+?<_?=yMn#d169c4H>w#Q~38xSYbZ+ds5Jc3`_ueBgR0r{ErCtHr0m zFB^)1@m)`-;qoG61-O^u@@%iG!eKbJ5^)N*Fabp{=5w%ep2y`|Q1}JEhWuBsS;r>3 z)}<6CZ(e|=3SYbeP0_HbhOXX^^CioKSBH1>Yh`_fVy*dV^Rm)z)jG}W57Y9nP#U*JG*EF{;n2B6kkFExYieK XLKd&52YdtPiSZ9OmKrEDX4U)`gf~-# literal 0 HcmV?d00001 diff --git a/__pycache__/Strategy005.cpython-39.pyc b/__pycache__/Strategy005.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..93a03cd552afa4884872cbccb5e8113fd0d28b5d GIT binary patch literal 4902 zcmc&%No*X)742;n4rfLrMN^byNo_i|H8vN?OW-h!D2fs#1rlY8w1G?>{hCW%j1koWt5FmjONI+n~KICS^x#g5|&XrS;6A|PT1O{Zs&imCfJ*1>8w{)X_ z|N2$`*V6AV?N%x!4Oe+(wfXsRP5TQq4qg@-7xBai2-BGEYk^+Zb>a>1M%@U^x~bZx zZw0w}PVttX4+`~y;&Xm67^;_g{BTedk63S9rdtdFqV znpP|BV`ivzNBj!*Xlac>IT#6CNt_5GnvJ5v@TZs5W8bT^*k%p3v9SvWM+Mc zS@jb5VOFk}@l?Q9Sp|2M4YMO3TJ-nDR!Ej!TL_1$7au9)D&7L z*|Y2$ceVP{{1iXUPqF7dG}-g)g}YY$%mK++R)fSoAbF9!1j#dO8eeQi4)?#*xpS}W zep<3|^(8h$O5k>rb|fy&+gG`8eP-W?Lf?&5SIbM<-3@fLlcW>R&&{QR*+vx1H3fec zFkpPZ&BQ4#JRX;n{{orsuNPat`~_VfFV=F>I{)gc=cMsE9(uog zPU`3Ozs4nv*MgOGne)BCOJqK9#THL$mNYlIJJO1|@5^GGL>)hhV>#aEoG5hodyTdm zw)p&EeJE-BjL2zH zJqq%0M8w`q;5OJy;Dz0oc@HbGD`uKr+~(qT?A1(hf(D-?@-)bPjtI&2e-47h4`K06 z(~T1*M-&TPXs$*gaAkocm_t^GxfA)_fG@p$b)N#t{&!OYqqK2{rp(mJN~D%_`8v*o z29T9ho(Qki;-Wzt>R-guvy3Mu>n#l!)ZITps6IWR?=o+CA-7$|yu^z_*Pph%ut^I| zygS_PxSp8vLWGbzu5SZo8(xT>D2xe58{BTVjW%a?Q$&HCw7K2?Dzn)P>unCW{Rcy? z;C2{=GYY)!hR>(%Zs_wEQ?&VxP2C+=B$>gesb<=4dkOcwII-Q>-o|kZrd)}H9YoN} zwi1QYcE{%~R?@)Y0S0A*MGc&On5Zpf6U+;qbVWFC-yrKWT^G7BtSP1RbnLUVB|5G2 zaB7*la%wk#%DkOgvcG?J>dN}HxwqCXuTM?eQ?CV6wdq6SN$22ocIwjlayI__e^>ZH zX6|E33FJ9VF^7+?$iv0CYqxG*nQKP@pNpez;thQL`>xU({JpzFo z&5rU8cWCEvonS?=KpWmYb#U}!BWtD{BJ)kkGO~z_~0_`8c zB-(v_Pk*4lZ|<2)Kd$ZROkdEz8;Um;XoNu{&||8W*=tz~8p}ay9Y^|{)W)^gwjhw3m zmkjMJVW%;jVDknS3YB4l+pAFm;E@ZE$_597I|sVqvrW-$VX`QNbJz_wxFDDhCd|OJ z6LpmXc4IGW&DuA30^dX!Ag6%=e@9J{DcXm|lfytD>;bGY!&si`YVTlih{xoXO|R8O z9fRmHZAVzV*!JMBWOCc{eR~@LRW;$<9PW+K5HfrDjkl1`+!i0WzqBZP%M#JpYayrs zbQH@S=1CJlvzM8EwHW|D@cRogpH>5+hNBfrL>7pAhX_GH ze4B_WaEesmG-N?Uz7N+F1VoublJ0WApSz)WfyO>ZzOAaZ7#`;I&P+5{7 z2(h0+FD7Me)cA)5)YxAeM)fPhtp3xms$Uwp>K8`7`p_sEpBrBpmEF@%#8PJN5R|AM z$>UK_`UFf%Lxk5hwN1qLEkoSiGw&OTd7mIfk)8mdKQJEXI$}S?du9T1))wM;CdsLB z`AvPxQqt5~9@c)`(>VaO19~ao3YnfMK4K*RyO<2&SEYOxGk##ON(Qv*?kCxY{NI2^ z)!P3HX*LcwMUM(4R1u2Ti9AE(0+E|U=7~@gY^bCThA!giS;G@kpxlGAT~vkSs)|DR z<<#Gk`p%;vskq>~!3J|L{1!@o$D{3@e0ogm!AMN$LMvDZrzi9=eFE2h-#B9$sW(TzZ rGK=aG-y@CEphK+CLg+}IbXKyskfQVgEj6Z7E9)bwm?-O$deQtBS&&I& literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyJD.cpython-39.pyc b/__pycache__/StrategyJD.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..da6ebee108650d97503eb81c0028a28c5a1828dd GIT binary patch literal 3736 zcmZu!OKe>_hiPZ>1Kp-S&i243A<5%z*o&Wr=`=8hU zI1|)r6%DO=b+x&CTGReS#KC5PxPYGj2!Lr!kF`YeG@WoG);&Xwre|R^V>@v?CnfEK9Y`^dKz8n<6RniCH2+leoVdZOUg@EbSR!B1)Y%Yik==lX*)41m8O!JIy5uY>EgDu?j%6N5H$><4j}b~{xpAF>F%I?*n0Nl@4vsk{{HT)KU|T6g0#o- zA`nHV?T10iV(y1oC&k=xlthJel0e+#MZ*#Uq-*6oj-{IySv$`1oWAX0ewO-)HmuVKl!bV-^Q zFW-=*z7D=9Cqb0@IGh5f)LADa59yY68{yR7M$aiJwX`+uiN5-TBA<@8Dc%HEf&VPe zzw_SpEAty!!sqj>Q$+Jk!EYgm89y`M$+_^6DuVgE7uFHa=lA_?uDvCPqqLccBq*ZW z+;0bw$UmiN$s;xW-pm2z|5L>N%4EDrX$0rRi-1T+9bX?rBOzC2nllzaZ7!!tGFaTs9Qe7-PNrS_p#ZZWkBgz__Er! zRomm#`<%fg4f5JlA3nC(&}aH_qK2XCY?cb=u}k*Y2peT%56w@^ZIc~S`p8niClo%Z z@F|5)E1Os?tc*`-lQd56Q_A{rrE`D_oyjRp$!17~{4u*KO}H39a2K(INaNizIA3^3 zh3W#rAj+7up|iNq{S73D(&!w z)0o$;oo>Oyoa?Ab$-&8>;JdeOwXDjxc#B2FU*%bQJ?X08(h%@bpcC>F@)E-8U|4 zAGhI0Iy0uoTTMiWwrOn|H*N6XH+>#z?JjT1m z?H|zA?j1@~yLUws7lNm3ZYj=i@A)#2-j}`NQ&Jj1tKmvps6taE4(etQ2PixC?En=; z+`tU*QAqY2M4CvE(u7%2g0x}3=u1?5dqyf@2A?USEX`ls$jF`#>@D?kSj%SsG-KKr zGp6;*Gt0G{`sbEWHJxY1j^Q}^z1gGuXwSwWH!@-BP>eOdASBx4=C34B@g{ac+N?n{QRt$ujrgR2+czT$s$ z1tI<3s-Tq%dUqg_arWdvyP1GuLvRueAWhM07F9J8Q^cvEc12WReR@jB@k6BF&T`zv zZ7yez=KEQ*ne#%P+!JSMyw&IWQSJ*Kgd3d6GUd%)Et_nMEaZ8PCks?ley9KN$>ktQ zs;>?sk6LLE%OOQTzZn%uT~0!f2~O+xqo%KRs&vIM+Kn0E=``s3L7HX-$}YSJXE%@u zhO~x!#&bE^C$Ju*Cm;1W3V3;?&8Q{bqT`}mA}wUELVQegOduh!_dMufl<`Cv zU;;h=ESbG0wTi`3)tXhVye$SjT#^;Xf=m&oPwrcK5h(^ya@lJz1$0htTdX{HbluU$+~ zwvwmzE!;+S$;4d0iTK@yDt`$`P literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyJD_1.cpython-39.pyc b/__pycache__/StrategyJD_1.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2fd63ee83e5fb16650b45c1b63efadbb3ccdc40c GIT binary patch literal 4340 zcmai1U2Ggz6`uc{UGJ`c;%sckaoVOWY)fq?scF)hCTWwjDT@1(QdJ|+FrIsNJ@)*p z@60B#amqu2!~=OiLP$kHN*?=y1mc0W@(8ay-X{=G7DyEWApxQ0J9pRiI!(o_=G=46 zJ@?!*_nyBEYPE`nr+Rs*_42Hy{h5ft#{zL4Z~hYirZGL%63x?e!i`w>3^kgbh0%=d z#POV@IwucXBu1qJ1cK@nUgc@U1^tQ!(mzH+S)P~F}LX{W)*1#up4-qJOVYo5+D z&-fNj$uqgd3}!N&SPG7us6aQ-YD=fjEC@!vm@SN-rz^r#G=L~@ta}|cJ!X*O|WVF zj@>i7NjAfdNBSOoLg7y;_>_Vt6nq-+nR_MnY)?}0dh2?!UjgT<_e{(-)!cJ{&)+lH z*T8!L`%LkpY?jUOX+F*7@7e4mJ9WqMj94bwF!SU9=L|aw z&Qk-Nm)R@eoM4N%?{CQJ?tQ#<(LcFu;QZS^`&d2a+92@!_FVg~@BU`y2Y>nHIcY9; zH{cL1>p4U&ON_UIZd^z^?*t*=#?@)t697#^)G&xTF{%Kv(h*s~!y?Mk{uaOgwEeyD zU%c_B%{#luuRoRge1G3Rp4;BR^M3o`Cx83H)#Z0~&G_*-X)Gj{w`WM{cwPje=yv=t zNLkGNFzco;CXSM*kWLbaHC{9=F+{Rf&f{3Rd69MEEYE3e5A(Cs=QqQZAZ>GLwu=zP z(+V*~%j__VGa>C8D^bCv-4>ju(prwYTsj-jnB9<8xDlkAr4!-8nKNfkH%qd*yzFCw zi)EOlsBWjzr<^w7ROQAmi2wy$7*OYUVBZN7cPD%OMMxv zC?`Rb`Z$~dr_^00B$xL4B4AvQXEbeTb%F?Iff#07R>Q6kJS}`m6#Ep#x2*5Z;nrJG z5jt4E`%spoT=-4A`Fj9c+NyS6-_kJ}4~%acw@SC%t`Xi?gdW%{ZfYDIAt%2K}G<$Mz^k{3djaZ-KN_Vs|gq1vZ?+NoJl{?-y_ zV1+nK;AH}@5Lg6|&f1MYv~yYB-%|JGP#nO*o;QD@iK~$O_xZUu-@bZjZY4|jT%L7{ zXs#vry1Ii?bKRT^W%V4aqRblcd~PtPXFD5mDD1-76J=Qrl{qC_%^26~#PXQQX=u{={$pb#=Tc^^z`#Pe(gU~?(-(;{wsb`qM6D9v zoYbh4+{FxY4scOW6xUT;nje%cAK>oFR=_n;W~#T1i`sW)ZkfGR_pL2c?OTI;n&h;L z+WJSZ$LZTsSK3OD9`5ftvd0$; zk|vS~KyVXT5~0PnPvIs|<|`CC2!|+R(uT~!{PyoKgNz)d$aUZaHy4lsF@O}qnA=w6 z;v2xkb3{^-Kvkh!xy^|CEHT_fas1QU6y1V^khq*1QOYO}=LJK?QkhpgP4Y01$r`dB zM$`wDj%}5T#WOTdNwOcd;(1b5l?EKmym|#6clE-D(iW;JR5~G|QdJ0XQ7L}!+7)SD zyLtgOu0*Xumez~TMjS26;pJY^MOe~yxvUi%ohS@qzat_vL3S@v$h8F}KdGm(ytn4j z2s+fsI^7s??c)=nrN~77F;?YhFg0zYuGjU3-Z1E`>(k}>$e1~%PYpHnD*eE#?-)Cd z-Pm!aH=j5VZ;w&@<$?7hY*tXt)mIIs!(|kv9KNF*-aw`{mBVYR)~c~)!(rfJeI7cu zU3`t)%|-w^YgSvit7uR!w;Loa1l_7rlvtTn?!bX>8x&e?qlNI*we=saHM#82=bwFcPWhErM$h7t+xVf%JCLASLBr=e0ari^i8O0PV$T+6Bd$1^U&Ba97o?g{(nC9fE!V+K2SF3`UcL}d?Xwm zwQ@fIQ~_6~d6>*pms$BTr`{J{YCbJp-%nA~eP335Kgn1Zt)efhzQ5iL;{MF&m8JJD zy?6P->zDiwE+M+#UJ_)x_&x!uGU6iwmk7K8Acv65=x-6atVkRsP7M_}qK3<-)>1YO zkbWo2aS_+KJob3LpS4;!FXZvP=`4*m`dmNCeZhlpg)?!1_L@Lr74kes^9rGq-{?0D zxg17IpenteM{V>qazq_QuMtz?asm^X;AFWUwS2n&o~$@Ry3Gg;)3A@OD9s8KZ#WN* zYbX)D&8ePrCM6ZWQDj% z^lbun2vDmt0H}WuWdO1$Uje9?mN{mP7$f>v#roWGEWQ7;EzBs~tXhVyeh-~8#?Kws z(FcD2vTE**VJXgkE%>tYx%E%$AI>pXH+HHgTyv~Fc3AD&Jfd1&X{X%;4U~~~q|-q< z4{~XAm^`-Efz9T)F8V_|+u8klocA2ZqAyU6)B9+&K`XM{>ZW0Vf6uuJT{-<2ML2yf zTKGF9s7;ggB0yc44VG!A5WV>>Ce~>B#J+~>$TXRl?N_4N{fZ%--jtx`PgeFP)VB_t ky1E3_y19$JIC+&mBd2OxHC)}*$5B^Yy^52r>J!%g0L-MmApigX literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyJD_2.cpython-39.pyc b/__pycache__/StrategyJD_2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5a801db6eafa070c1b1973e7ece21a5798113fdf GIT binary patch literal 4344 zcmai1ON<;>6|H}Nr>Cc9+@2YK6X(}JX2#b>r%aedma zs%?+y*IEOEL2*Na`M_R18nohhC>8_zl)3s2VksUj(6PMgl zTz1RK#){0i;#NlXRksQ|JF3NXx2|+fG!{49#;ATgZn_gAz70G z*?c2SBkm`=n)>A=-;vNk<$|C4m&hK1BRCrb#8oa|%>}ydZ9r(pT=cjXCf!uTejeWB zUf&NzKy%pplp&nMm#yfU#x+-GnrnOyd+D0oVg@sr&Mam#=f3UQ++iiGtOU7o-*QWk ztE|TABe@K@#>QA?k`H>O1tf;-69USw&AM`W)!<4@&HX!cysq zc7^SW(7g1(L~TdaeFgNZ4-ED-=w8M+vuuvd-_zVf{4hVl53>d6PqM{(mV0!M<`jDc znqzx3OYG~=9A~fM#81om_KCcG!CTxiu+P1p{zN@zdl2ya{%r5B-~HXuAO7VxXQjD1 z*nmrvS>Y0yEHU2o2T?BVtnUYW3rD1FO@nj{(Lf=_i74$NRm~7#M3zCMzAzf(PiMCt zGt23$HD=rO$|tzg4X?vzdhOQ%o2cB)g7_0?4m60Ne7nj#Bj8Qt6pS-u95GwibIv)i&9hshxGSS(BZNHG}t^sD@pR~Nwe z!70AzcMMrYW06KtnDk_0m-o;bJsEGjAr%SxTg*E3))?M(-;zVm@E4lQ$qs3tJHW3q=)7o);IMb z!nbRDWZbaONSDURwU6N|(wJM27KOif9ma}$dT#z(?_9Y!zm~>)K1&CAINufg1^|Td z#rZ+Tg$J1O=d%J*LY~d<_2s$#hO90}VU`DJ+&}XRbo+OF+MUIHR!_oDHn$w5fgfdO zz-%|B39`PN2nIs%B=?keP%ZYB9IY#DntLWEzR=z7dc+x8*+r0_M1y|$Ho+HKn^X&;)SQEJM@LQ6of>T7IA0}xi4@nyIY8=D!? zhc;_I(hrd|0b6Iilsga0)E{fHNj7!g+%$(KJD}{_Xlt^AiU%ZvpHcj*>Jzg?FYOs^ zn#$Q-N%ek6+3eB6W_m_bb%#->tyk8oLxUZ`{s7YN8=IC|nT6f7m|7XuX|dyv4A5uL z!lM0oSYyvBixZfAeF$6hG=?6YW6$5WDFWd6FRVxtu?gbe2Iv5yxM<3OpauXJCV&p;{M$=F6ciA}%c$F;ECditSR*LfM2AR|wB=Z-y;EDNq6Qt@ z*|`-ajKD0*Sq2Ct_!2LYJ*LIv7$5{CKvrRztpH8DMD>I;m|cK45OYw7ZxTWBLy%9r ztk43Kvu=@VN(<+mJl)8x{VgQ zk^@QW*_+nT5^YK%^U~0~s~-fa5B2qOUZI2wmxFcJK+KQzjIObaMD)flZ?u)?H&z7s zVBtI8CXVh1aSf#Re&gle{_K-a&H~!9&^x(ScyJX-ho`*Q3+P_ieu$?}-7%CU4l(<4 z5I@#e)Oo?r_1jY4c=GVd%aI?iGXKm`0HipFmUm7*efMeP9FWiU-U>zABF!i%3%}Q? zN?WLuqD~s*M}FiZ!LV=o$Q{HQ#z0V^W{-Q3Nb4jVsO<>A12fBoG;s|Hg7Po=D5YOT z_thYU`{iMpWKS-#wc|q6wOYhDmYW?%mzptaOc}F!74Fyg)T&rcU=EP;@Rt^FS))7= ziFeWsdn%U^l*dR57?_JbZQKut+$FM;$rYSRf9Rs3--B!wq-t7btJJibdb47EZaJ1d zdTk3eiZ|<)p{w^Zr-Jfxr|jr^-hWwR<;RAlH2<+2l%HGwwEp3|S+2C!TCWQluQMaa zlcb#tVkCt|(w9yjcZZ)zqtE1#ovd;Wai{)H=la|Kx>?~NYA){2T73Q zS2$B~Di1%UBWyDlUHlmd0w~$YeOzL5{wj?WAoJel)NijmvD?EAwaB-FOIu4_* IPwRE-zi>FUZ~y=R literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyJD_3.cpython-39.pyc b/__pycache__/StrategyJD_3.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d535e9ce395fbd345b36a7e2afef45fbd72b6bed GIT binary patch literal 4949 zcmai2O>i7X6`ue7)oQhpWl5Ih-#FetB-xJ1pJLgG?8Kq0gxG}Ckkr&@yLUD6%+9K3 z*0vPsf}IP*P@#$gDMQ&+9LR+#C=T3`;siH}W1pZnsh}!Q6ciOq%=dbCwURL~Tl40< z*RNl{?tVYr>kka%H9W-&i}g>Q)wDklvGcJ&oW+~m1z;M}LoL!=O()z4b=Oc|)3xw5 zLpyR@C(5{)DC=gGj1`(u&dqh@^KKq;c36mtZc*`^a3CtVrEdFRRCb5DcqJM(Fite0 z;2w8G@iXCQwAbCMYcFdo%W}6gmgA+HraQ)r%UU(R1uM0xF0;%}c^X7~v1-cl0L+=8F_7f2q2BRFf<2+Li(oC>IJtbrJfxM*-M zXx3v9`Dt*Cdo4c@HL_vnlL2uSZ<6Yo#x+-GnrnO&Yw4QYVg@sr&Mam#=eF(I++i8a zECV`s+j29Y^Q^#%T{;W8zy?^UOXrmQAmqzRJ`asUtOAY0;E=`w8)16_-7T_FcYy76 zOKi*?WaDlb_z;_LD{P-T415INJ$Ogiq`Q}o@o~0)PGeK}9bo&|!8?{a!4Bbf_>ST3 zV@KH0K;MRsDV*T3I~IGq!{5<9e3gH3}s1Fe&MKbvJI_!OUF zC-2zo6npBHQtALfVH(~y3KJ&TqjJ2=m==fOF;gEPlo0Ov7w27WOw zi@kk&b-_EeWnecNAAVmw^9>MqKACU)=_|jOdhd@votNfPdkv10WgW*!WQOs&-wso0 zCoR9mx3H1g))+w55G8!XAOXcaNloJf7=bwgMF5#^i8$r8G>Dtsv48z>Ghs{AIwYRbaY{BijFPUMTLJgr(n8DJ$m3}rS+<5n0a3H9w@UflHfjoPx`Y;b8d(i&z(J;VVT z!mh<(ETnyXIY_y5)}SQ5UbSR#X~_e|#S(^ARJ+ycQce^!PtMNDeCO-U&YqB2$X^dw zx-6}wu#JHbRaj)xXE$UPa_z)pk<7G0g{)_eJo7Yv>gib^e(e-L=~oSzhYAsgVbE;I zQXluw8=8z4UzFy#g|}p;D}x@n$Pbzx=9^;T?Il7IX z5bNXGUF)8PN)x;t*ceY$cwuKj4i-P7foh?N=LkGcfc#uKSFih`k;q(sNZpf#(1(Q`2mMGB zmmvAs*_l^fyL5hLIga>D61US}rY`uZ+Jlob?Su--!wL; zx|oZ*I#Wm5^2^kD1fYTL?-Vz&=}fB|9~i3^I_c6lv8?6{Z#1T7JBvqapqOG2n9R?G zL6X+usCDK;(0|3Nt)JXAg@>z5GChY4^uy!~kX~oR?L6rl>UpY)S!%AD;uUJK$=MS* z^dIUv)+oEkB=IJ|_wXjl%Zx{mw_%^twXdwSWgtD&wW6}k8SZjM zlwB3Xw$1?WX4$|!eIHSKAe&psr@6ZY(#J;GUN&~y+_W|9+(AQ%76h5i&{R*E_ zIx#P3nHbl`=sVS?l=cHkW(OBCljE9dJBT)IrMNP%VX#A3vBT`hZDZ3`GqW*co2i*$ zo;Evr&j5T3J#1RZyB0g9Bp!$LC1f|sUxUzaoKJfM3hNvn7|9v zYKkP-qNIz89Qh|^1qGmV6g~?NR0t|Pu|)v@(dS$bnk>GKB$hH10ad%iG3p0%nH)et z!539j6*OB_Gx0dJQ{CxiLh%G?R2lGfrM82g1HMIWE-hGgYRe|H*s3A#sj}*wzJlr7 z^?HCjT9yWa!@P763w7z-ThbP)^5*;EVuAXbZ(P18&C8e0VUo*1J(Zc&w6zunOLB0j zQw5PH$=E~=q-(7J>A-7=pvF5{ph8uE;=Nx1L=Jid8C!K!LQoMazDA?zO_|$PD+@a^ z#6!734!7b~J49l}*_UC|G#1GZp(3Gjpp{E{Ngu_pYycW1eKJ=nSImk&Q5e;W^aHQ- zzp(Wlf<)?j`AO#=t_fre2x=5Uo+{n6^E3XsyGa>eI1soJA>fB8gG`K{Mipa{d69= zGYLGD4sv`rV0+C|PVLpG?(`nQ=~Xo-3uFVF2_T!S8*1knR0XFv2CTwDN4tP_8L_&Xeo zNSY(1io!PG-{|^2s+*wgt=bPeDjmy}a+$Bia6mdyCl61svh6;kT1}vUsU`=ZQZpuu z5o1En!}Us^Svkuo{nIjvrgPtTU^vdP^#lKBDR%iGMLjfk3K|^8M?T5 z<@_5L&b@Trd-FU}%WI2*Y!}}mK+z<=N#K0~w+Wml@G^ibV3X(yjnj7^4iaYoC$oSK z;vU^1BFP;^9hIxI!#&LGX}Nc_N3q1WFeYo`>6hjmrXEC2-wp)`LdJW9@nv`>A*hTGi=E z&`C#HNRX-c1~ES%@LdA^<6r;4Ma7J6lt~>RZ(3%>DjQ{;ewCc{x#d`T_h(yZRk&HS z3|;;1J2`wmce0MIe)p|h_JLt3{9l%X@8{Ovt$#S1S0Jeti_qRR7v#;lI3i;%RN z?Fh$equG*93pK@0q|st>|Mt0jI>BbqUmw%0-XAGR$AQS{I}X&DGX4x%mbrSnSxfOh zOQOP9PCh~r)}D(xF4Tf@mn@||ircimL?hL3W9wleq2?of4cCy5Vlmwp>jc&rnr|`+P&Bf7Rv$r4%j!jJMo}Nr Hi`KsZ5n^I+ literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyJD_4.cpython-39.pyc b/__pycache__/StrategyJD_4.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..67f9733d1225e2985a67b34b232c5505b2116b59 GIT binary patch literal 5808 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 AVE_OC literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyJD_5.cpython-39.pyc b/__pycache__/StrategyJD_5.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cb80c95f0ad52bc25ca95a5e9090268b23012d7f GIT binary patch literal 4996 zcmai2OKcoj6|H}N-EM#4*oou(Gt)DJ?IaWC=ZTX{l9_}H2xi^S%wK&&iSfRNA#!h~~Qb-V2lph|t~-uLdk z?{|N%{egj^hNpaWq0xC+)BZ@r?#BXg0l#DmfN4w*wMcU{op2-6T|<3M*TUBf?Z|PR zDCg#)yqi}%R%k{Ax6tJ;x<&BWVJRxRW!2_{15w4Tbo&RRsyo!hhocb#<3yth?sG>~ zdoCP{_PhIa?QMc|>3cPaHbjO)-L#q|HF-xtc%RKW_o(2(LsF|{Qnb(8J58v~J zAMun6S-lj;A@^JTmh!b$xaCjitj%B#@M90pO5nic4$sxQ+N-vgY3uy%RRu3;yw1ja1XNM z>_njNz>^9mcr_JPGcK~p>2;7umPmG*jcL?GRJts?}VweleS;yg1DNW{gj^H%y*GT^RTKs z%d9F9Rj`O5QgnvE1OY~1jzAee7TY3Dc|8r{R!`aI%}+*u{`Mc%@AZ&hd@l9bCb;qZ zet!E2o}10bkN*1G>x=KrH~;+pFQ>lqr%&glaW1;LJp~>)nxwu+J8iG-w^+!%dfaJY z-eC|0sdOS=+~R4?5`)BRB|HpeK1t(t7$*tIW|$YZJic0A@>@+V&1PD!8A?JNAu+oi zhp~|Mouwe<(piI&_)g7|<;6u07#E8eT2Y;LyGuDy&^j|aD~s9Jo1Hx^^WeV|uyjdU zi(v-?!K1K;rq5Po9(jjB0p$(SZ|7jcNPgrq`Wnk_AISZTwj8JFdHM+n|Z zJqjFsiu_$xdVM&^Mv&GW$oC$KpcE&+j$iTtz=pQ0ZRs02zQ#l2BV#kSncv7Qn@pe3 zwycNRW?`eijMPTIvz)uD@7E?sCcm7!rHKV*?(VsPNDB+L7Q_K)#ujl=#~kGl9VpHq zNxg1Utso87mSatxdVb3f*VZ}n5C{DRg)v#6Rb{9ZgmGQwl9dkU>o|-g6|#Vx>Z-PH z`*cR-F744swW2bihIkFL5U&$>g8-ShcoRT6x9<3&naDzaNZpgA(8mlj1ATF9`_#yxFAzpUNWH;oO-Eat)< zVG^Vxzd^#I08NO0T;9Z?Gp%8KYOGig(xq{FNv#>yXiU#$n@4+~8pXT7WN|JGlC&O2 z?em|3{u_SU`k6gTcsj!*({ngLKTOU8=?TMcFF;fd^(sT}#aN&E-!sA(Wd3kwp88|~lr+}kz)Tk~@`E0zaeVU)0<;v)XtvZXz(J=wC7 zEyMqmhVkzAAbkf^UrF^j6PoJ7SM^n(&18eDde^wDeLS^kW}|Ic8>X875PaAq&z{p( zeuZ^8-E|GCS>(Vy(w+V2PRqV-%a|H>RE-O`m1lzwbv5dK@D`SfX<@5G`q(&|U!lx8|Sm8&MPV5Ipp6ssWNS{*Lk1C#Bt>B&NuJ;)FwB_>hz=pw& zW2aBBllP5HTdmB-ifyJ=hIQKP)I$UCIY`)a0$Ub)UUAeg`wF5W#l1mjc!9lm-zJxV ztGu)zO+<14e-&8=agB=+SPFcoNP!EnD2SP~!EDN`>M%r7a4W96)};7fD&=M_VOF@jUfY8tVpD@gf?snDD+* zo8gy$Zsx;th=JjjXzv~x2l(tZbyx3^7P%BOQkh#x+iPL4C8XhlTGA6y4DU5UA(pkPyk&Z@nK3T z6h8V1MHJ8>XtFg&#TOL-#D~PLw`5^Qtt{;dI8TKdIns{Xoe*&zWi-R6X)KbTK}AAG zqg5+vcYtCzQUBRZzx|kVAp2X*U82eev7=zf>&wF*0OQS24ySC z24(F~4k)c@k=KebhKfJUWwD}xn6;cw3)#(P!@6s*A}ckGhB1em(8`Zi#+0v=7X+E1 z_=o`6y;uZ5z)b=V`G(BfEuxzBl<9hP#MzyPaQdmlMOkwLbG`ubk-pH&RQjsa*PcG8 z{9Ncqi_AZN2uW1jgyj0^XYVhKoT83myR%d#o7JK@#o;$=MR6N?P(DHe)wCb_O)lO> zlUS$r0kwwAZM~K(5lL&LIMFBk)vkpjB??L|HT!9Eq*7fem&JMvGovy*d3u4vI|ibr zM+?9eRZFPU){Fz@0li|u(7v!rmQ(qsWt2_lvGK%k9Ao|P|JjH=c1E5}b%?Svsv+o1 z#XTU|{%6U$?@Uk6v+psg0#rMFk|wH-x%l6#zR$c+J0r0%e za3sf3qt|(o;A#V=7QfT2KNC5K>QWVrUJ^7>BFn1UmaL{%>~b6(vEVdwFKBpV3mLCC zMC&jE)ZT+03hY*#;?564!p4y1XV0fTC&Ts`@a(B)!80>r2bA z^zLh0CSIbl9jaii6iP8)f{Po&Xi^3YBxKb_#P=%145cJF_eBr_W_`plHFCF8%7 zC0S^6TJ;ov%OuK)<>WIIVeh$U(5;*zm#m~d6479l25O0Zx$l+NAq)mC&3Ca9i=2+)xy~!9r*~sg8eH1w> Oua|KcWqn*PTmJ@B^Jl04 literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyJD_5_2.cpython-39.pyc b/__pycache__/StrategyJD_5_2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..41f2f352137d1982539bba5e6676ec7251c8ef12 GIT binary patch literal 5014 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 L7-fA-FI)cuS$A%9 literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyJD_5_3.cpython-39.pyc b/__pycache__/StrategyJD_5_3.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..173232d939f5a83eb5dad9da16d52372c8f52e65 GIT binary patch literal 6608 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 F{1=`w%838~ literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyJD_5_4.cpython-39.pyc b/__pycache__/StrategyJD_5_4.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7e57fd5b012b4c55ed0c27320ff31dfdff5f6bbe GIT binary patch literal 8073 zcmb_hU2Gi5ai0JE<#I``D3X$>zx&aief*d2Y}2BCDCuNd^xa7gE}OedhTF5tS?wQt zW+jm!0~=D1#E6hH;FlZ}M>60C<-te{Kb&D3XFCpH2YE6O1n7q#KTkyR;2=m4IG0nt z>Yd#k?(%YZ@D8S?s=BJWx@Wqoy4M*RN-FrJudJ1BE-T7E6A}FxAkO3V9|16hsa2(> z6cm+kt*RC@`P2&ro_f`+Sp};WE5vH?LR`uiRlSxdBtrRQAqhFNnyRG>Y00yyL$ypH z6Sfc6vW1ZlAFYjP=%<#G@K7Nq`LXJF?Qr3+s$5lAoF(onEa7JE>4gcV-B9w$J$R|) zRS{=S;09jJUCZktyXqFbnp1t%;ZDsBTrRQ~8;z>#)O(!twR+H{U?8#T1kM$b2VrrS zwTgr#uH6VYOqaJnjMrRVc5Sa-YVev9c(+}<>3Fp5a)6G-`5qzRSGIo3fdPj zmj&H5n8tKwFq2sijDp!mTYYq_kB++u7Q;NKECCD22WBBTz)1~o(gU0!*p0DN*N+V7 z6ic(A(63?8X_mp=_A?7Q!-jFU=@D4eSeA{j(a?|40nXR}CpW-31WpWN8DV2AC&x2B zz&Sj?nHbLRvTi|2zGj zghUMUmI`O;6zDW&?r2D#mhF0Xrk{ZYjU9{3~ zJYT~7GW%HJlkTV7PqU9NE9^yFGwcO6`_L#XvpHPz54FN)*aBPh)Gl0-IKi_I4fcsP zKhpk^#9s#d@(n8tD&v|NsGuCdR8b0xz09Q!;tSJ`#Mmo<^;tpCrh+Gp*hJq_!6PbDxx zzyuKbdTT4Mi5PQBPOBOS({DOOmy=Zaoj<1E&sIX@$qM3AoVDYV&$AHWBV=fjzyg6` z0-qzm0LrhwJN>)=^4c#}_7F>y^55Rwdu~4diWa|L)~@VLLq_EMz~MowX&0S3tGafv z(W+y9s$R_tgjI9+EjP#;oQBM421MKs8qI3M_er&l*^RpG-Ysr8^|CAUa!|}`5e2i@ zs5ZDT?`(L1E37R{M&nN25b5=G8<@-2(X*sl&1OhhHLt!fHz$(qr#&|}FXE8DSdAX z;Wo5kNxOJa=oePs5V24O8i|_Y)oqM7z`$GUg!sa|$sOi$`q1*GFet+a6G@1#}-3St(k`?QodRURNDJi?d z%xKFErT+I_YiTL*H_#gjt!4IE8;)4ZO4+{-t%WVH_IB6W2uZxH@@d!`4egEXvzLq5 zI}}R48Cne6VDTLE$3y+Y`}8Lw`jdV7VcQL5$J|NZGT@bkSxBXnTMF=42fs*EyhoiU zkH5weq{oubOR|)ly(!o?Wb3zCdZ6`4yEW`ZsT{3X)RUC|CpI)7f2=E?f%OC%o>Xp` zkk)vXWpALkL$DiJejZ)Y*ciU4X@fLikQFIa@}n#4n%0`jn<+)&xCo~p+%m8&5o0Z z21LlkBXmFDB7vnD4n7?sHcf&U-fwL|q$73~LaWlyf=G6aEQq0~m4$vYi~GzhM$Ifn z%q;FVvtMf|sNJ|XI27H^AcbkFr?_owTb=9WkmTSz4}`Rcw2qyFas z6fLJ_)Qpx@gzPp zVUdb}&W|!Ug0Dg|Kbyc~i?rJ-Oma=oKSJR4l?XLtZ?>vqD}X!i$Bxr0j((M|(*{`^J|M zuVCkp!|qx7Fcj4pXwAr8D733=Xi}L7pGW99EZe|^RwDEu3u{T*bKKKbmA6jp>TO>h zrFL{V4`V@kN2f%-jP$gb!3+(BGn9j7rZOC49%YHc4q@kW_<_D_>=%E@>UA4I$Kf4aVod;`0EJViQRLdPr|5JQ3Nu z_!57Oq>q!dNr|eC2Xay!bt0%X8j}skQ&hC`F#=Z!TnE^ru)tFQSp3!H+R>ypn+Nq8WGZm*DNjrvYOS5hKi^QeesJfwys}z1NjL<=~(}D(o07;luCQ;l-qjL690ZdWd3}Ko46q8N5cR zoS-ljDJ68~-9{hSt}vc}T~0&JTQa7}ki)-By{ULN-7Z8b66E^An;2_0nyo6b6poJ= zJO~z)z@&%o^m*9pIemb?=>z;7`bd8*>F>MncWA)h44q2$`-`Y~bMsy4ai;4r zFzNBvBOa5FJ&%VE@EE1~zzk>iX&p4f(qn8bD)2cw;Pc3VJ|o(}?~3$#r0X@X0k6r= zp4X!Xcs+W6*JJy%KHO`KpMck6177Do#Ooivt!Y7}}Pc)(473x<1nvO45o!UBc&W)nVwXNYiBD6wRqRWtUSL;dIJ{leZ186d<{wnes$C zXQqe@cYmTsAZ@o(2I^W)^P~)KF-(--giDG#g={e;^GfEwjkuv@o@Q0+ zsi9dKwRGElX(W1W@qsq0tC##g+qO@BTMxaiB-ZpO#_&$Lj5THVdKPK=Nf!`#sPM}Dj zOkjh+Edo^lS(edXW;np~_!;61VNdPRS*A?~v0{85X*U}_KFZs!ICe1KZj?&C8;IlG z=0?4`6>@FQx4G*SH(Vy`Dlvg$m!j+YIO>H^-MteY6Zm2nn|!%*w|%dS6CaV4FR*<~ zBgMr88XDXs&uy<{(^_cD@@Hurx`)H7VH@W~^+td{?XZyWic<}aGgcebpzDjVkU-gv z>deN!dqtcL(P=CWtm`GO+>R4wsoHP?zD1MvB?7ds6b7offPahVpAz^P0U3OIKyGlU zNT!jQ=$L%bJX<+SBxl@%ZSAqvtsJou8EF z(ZZ~^YB*BS>P=xaQBXU+(3(sf>z>NY_;}&;cj-*C^XIGIUKO;++Y6^XZTJIqLnKPA zdNII%vwk}OA3eYj-k-}$^oL3uCPXH1P}$Eo>(o;b2j?A(zeCNZdlqgZ+cx-2xEGj- z)ZJFQiGPzcl97f}J?-v5I*7gDR8b9 z!ZlwlY4WR=419IptXn0ko+u^i$x>3v7`|Rll~S>Mx|D{T>1XQMQdaUTf25u(<>K9Cv>imJP3=stX(E7 zb@h74p}M*SVzTb?s%v|VN|V=}(7WZ@Eyv?!vZ4Q%0C53t@Ks$=T&1KkrKEiYV_DK& zgK11>1~Zv;-zb>_v^78{2I!=lVhN0c%2LpfzHgS&L!8VICp*L$f!+kmbnVE2⁡S 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 literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyJD_5_6.cpython-39.pyc b/__pycache__/StrategyJD_5_6.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f5c047feedc530341f3d2f8d120e6c8bc7ed78db GIT binary patch literal 7055 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 literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyJD_5_8.cpython-39.pyc b/__pycache__/StrategyJD_5_8.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..49ff4c69a01833cdd0cbc8822a77232e53880237 GIT binary patch literal 6579 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 Ezb0(^ng9R* literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyJD_5_9.cpython-39.pyc b/__pycache__/StrategyJD_5_9.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..69aa16176b2b53e2730b94f9ce47d2e0681003e0 GIT binary patch literal 6568 zcmbVQU2Gi3k)GfE<8rxNQY7_jS@Pc|Hc63^%4#I)L{owSHyaYk~08^M6C@sZORKm4D zwKQ4kmVr_a%vR3IwenWJRj>+D#t8IQ(JE&0C94EEGZ<-=t+J%$g3(sRs$~4JR@EBM z@LFp^LqDxa2@hD4lAaHyTGQ6Fs=TSN0xRBCSkbH8(XAP#U03R*9!9CuRZ(D0;w65| zTdC`!ddYMBmJ__|aHr)Z9v9V%Q51MixJxNt4U<6)3W}GU#JNK9sN}fEIxb}htwSFNM$q;-rvV?D*5wT`pr ztfzsWV9#48*$dV)z@J6=9KPpS-Fm^RdtYE*SW?&*@qdxMz+Sp_FL|?U znVt9Myg7E^zR51KH|`qNJiEj$-&L$r-h#L2EwC&1b@nEE3!KwKl&kD*P|gfd-eF$` z zvlaH1&>ATClDdYpd@rH-G!ppC0@B-+XUbXlGhiddDCmCga56NvCbQPRIh!cB4*+ zl?eQnpNL$`;Tv93H#i+^et;Sj;v{MZQ5;j#6tkny_HMgtPT2H>-b~!OHq^j$qafnK zytU>hp2%%t*`ixp`I9ft&wH;d5aKwe78ZpL*CUM8pyLU%$vrO=hP&wiw*7EJn5%)~ zZqzHHyt-;bfyY-dU|H+5+gUBw^23Gsc^da^Doxbw5QTHw4&kc(@Qi(m0dZ~sdTir0wssycL zBMQb`o7O5uOp3sV>+y zZdM5v?vjDP)`}?yXOAyKfhc6W#Q}CkjIG)I7B@P+yCFRpKTqPNH9NZLaT}I8XmCoD zcd7P*Fy(RJuakUbh=X%bpAi~si>aoU*iPsKn_C{Uk%Bl4${<9MEI36CKZsnBk8gB5 zZ_5*-QQHei(z82>D-7mr#^8Ex6f*dd{&0tp1wo?DAr-dr4Y5`V72y&KXYEzEUdm5#R-;3w=hSb^irWkQM2m&p+4?lv^_#%L!O{x|8Yh|^n7PJ}F(4J9^az!nuB`t*o z&~w_MPjiN$9Rhz!tvXMN*KF_9apZ4yd`ZrvnW-$-`d97gJ);L>C{Fx8Y-%($K!A-7wHd z71}8#SM{MLwAuM|%W0J*kte`JX({mI#En|*bCkRP0iUvUdf!C%XV~%V5>Am5#OHwY zn}5y`UP}ja3;m{;93YL2Zvy<9kS&1EsebJ*tN%@3J(2@jO)=gNY%(#R$<=kfK^&7} zV=TrWTa5j~{yRwj3STU5c3KHWSHpkn_-CyudTLQm1zeIGGIP54Bpn{QWKI1ah1xtm zXsKTDV4~vl5AH- zyHTVY6!pd7`{NVrp_HNgIu>?!oQ zy9(1!T!rJ=D(uP~AIPb2Ay2RHufo)N{{sMjQldDDVCF}Rp9P7(LVz-M{wjgj0FY)P z%cQ6la=JqB<3!A7Ay7v9)Dd+-$ElNX=xZcMj!YQH02cWup{7g+-bcS?qu~buze!RR z0FZL&FoZ$rvt^*aMG4O!ydfLzkrDR8V09^Qn(ETuxBS&BI znow%H2y)q#Ax^l^5n)ruP@~*}T)y`n&NW8c##H2^AP&=YZq{9Tk{)9JtuvK zJl6c%B=HXDmyc|@=1 z6xphXQZ;=Ncun$%pUTwKLmJU1wXGBTT+kCb+T&{|$P9eVcTh>sHXxGqjm+zyrsMq5 zxi@7>3V(E1nN}uK3Gj9_oIQO#J@=?@kY|F1wB=!H3$L`UvK+PBFq+1;-qra(vV2!v zHPT8wh1-Hv!-lXxg}1Y%BGgzGSc zsa;bOB}!s_y1K{v=TIAL{9uH~Fq?P^8Wbc$C)64B*e6Eb(6mQYHTTHSx1RpYIqtC> zidd8r$tXo;MEk}66-c_cX;9rA?fwz&o&lNB%0+yUhA8(?{qvT*E51@cD+;z9;s#*b zqGa2xh;{IoV2iSC-{?3&#u>l5^8V%bu3UKIvi=R(!3sAZz0d0!;#I1l9@o0HQe9Sy9fmSZ1gE0}}ab0)GQw(F=z?&ut>C4hWR| z`ysuc+C1`k9Skvw1lSM!uxZnKhnU(^wcAmQjlb!Mqfe&WQKJ!iiFj(jj>2FwquPFK zbI)#RFOKn|0-?~mmE8kkF@}s>PQ;G=ChlFLDwjWf6p-R#28@V%G;-T- z*ko_1EPsw>`CXdKn2lRo7$tbag|T9TMk01+=YUwJu06q6l(Le3a2^L56Z_JVTPR+?m}AFMy$l6loJcB&BW2c zb!ryzO}?bhw)^k$F_-pD$2C0y=@Q~?eN7Y_ozPA2Y#&Q^BaS>l5yGj*8+fhc);VT)aBZDo*EMxo1aD`>#{{d0B{c`{S literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick.cpython-39.pyc b/__pycache__/StrategyPierrick.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f6430349c89a3b1ac6b6d8382c7fea848f12c77c GIT binary patch literal 2533 zcmaJDxpEvubf)Ln*;A)w$){|YU=ztjVJN^L`H-*!uZ?XSOwu(w-MboVE_e4@$R!BC zgaqUYpde93$aj!Hbwm}B3MWKRwy}6`c6A$is$ajO`_1cj%>?y&#eh*;YIRnJ4C8ks z_759~1(-4hzzk-_Mq>Dei8zT(pA@U*+pt=(l{mgr$n4lnN`7gu&r8aFWq?o6Ov;t%m*KEkS(4OW8(xU8{Z`=e|Kp5YDRkFgOp8kqw)wn5lv3OE7qI7lX8 zPQjURc7mOJYWT2DPOus9(FHxc?#svDmxY&8Wgxq4MQSAc^=ISHbaf?*0Q?6nb*p$YHb8HnS&22)~gcMWi( z+1kWJ!>e_~G;j;>{QBID+qV|yma~M<$*iZMxsKqEAmbQ+bFL@3ppcotob2ad#O2)K zV9e%gdL&9anMeW^J?1nIA|W5(%P~v_d2;^9kt5rimM-IqI+*p^OSlS<)UM#FC(_VD zO@o0~!qhMB_cvwC^lh9ZkpPPvvAWojCoxpZ3 zXSKv7;6gK*hNWmG;vK}^MNsexa42rrrTP|s0sCa}kt>lGwh30c{}-#hgRSOC?NORS z)lsS|lqMPLL3gCOM(Lwo5Dz3HH(U1??=D@rwn!f=-o1D8c1u^2D2)=xJds6W7z}`1 z6hVa9NwHt*i323fGZ|rj(5GL|r&*^XxzcBM#aSA!4Y)LtRPZ2N=1iCIJK1j(lex%3 zE+zD3*qZXyL3fvW2znPS?kS^g8pL|IAYgwdQpI*X34%;;w4X*DilOW86;-^A6~Xsm zh|(ZUGXu9=h8zdZQF9{>OV literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick2.cpython-39.pyc b/__pycache__/StrategyPierrick2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..52933b61da624635a40f2461176f4fd0ca20d1be GIT binary patch literal 3836 zcmaJE%WfRUaUMO7eUe1*OZJhF zM7Co~+z2aC)v1;;wW#hiO1v43X^N^u;}T9djvIdEn*VL!^;dUd_3ev-MpcrAHqVh`Z3TZvZgG zQK{l+RE5=OR1Z{#P+XDWnACJEYB@HwoeHfuRa&LBKrLbYg+?0#T=JR{jsYBpo(X8J z7n)OJbvjAgtic-e+zW$t==tZwY0@cr;kn|Bv2ixR#_7cuI-RC7(9;^>%+gE1nH=F< zrtbl#P2Y!IxgsV`R`?`foCp5aVqb$533Z{b!nc16kI+8cc({I3K7YQm{@btd`Rkph z!tj$Y;lkM22y)idM15_|4U-+l*SwfYsz}q4vY~G$p!tR%Yhl5J4t@X;%i~EB7pbU7 zgeE&NaJBpQg>er!NK_&(h}|?ya$#r18X}o6o^p>eVN-BV9)PzDK~gUOW58q3QTGeZ zS)9Arc}k%-Mf0RDyEI%Q$FmNuuJ4-A4AtUpFCsS z1@EC#T`A;JmigepEa-oZs%$v~$gV9knu^Xk%Uv(_!reVaU2u`t!-x_UbjAQBL74c$ z%C-x}_L#7Cyf9?o*KsKthA>;`#XbX1!`X_m?wBAM3wwMT7chfh7QrP1mjMWKYscg3 znW&u3r@EpRdRgxJV0y?zJLT+IkQA9a%AFW;Z=DQ;gI&N*5I+fGVU?!5RQOL|qVJaG zfBNa;rTL8{V)I#2)|xT3c=6ONiQ0t5c@hI@YwA*MyL!saB7PzBhYT+lVF~9ena2j_ng@89H+v4~4oS z*U?q^2f&i}0Ne&rOayT+;gOdI&zOr5m3@uU8URJxyLxu=Gvm8DzloY`$f+J~1?Jqm z;IXfxlJdbI!zOK;0`<8Jvm*#Ks@?`_Qv(c==~wtALyl2$%pu1rIrflKDLK{K3dq(V za8wMBsq#CG--hgKK%nZhd8A?}19xnMJ3i!2jBt%1wP8D29h`nCou!@oe&I$g7n357hkyX+44@U;tQL z0NndhSO8q0xdq)&pv;{hhQb92DyNXIWCi5SQk04YZcS(qg8FBx%R*m$d{0> z7kXAiLVvux0^r`~!rIQ$-7r`aV{3x}nDrrVW}=brrh)H;ZVDF!CZ={+u)dK)W`Ap7 z*BE3JjDCIx*H;;i5UsOPZ>OHgG)apPDzppx2@~cC&wdALvY!D^E;bu2RlfENzLLT(OsA^Km?k#ztlHSOO- zZ$drM^v?e@Q~Qs0tX21}eb2rOm7LiY+#0zpc;W}!LQj^c50==~q;;}_hn&MWo+H3= zIsmftocbSY;=pG37f>>ce8tjU5f$wGe*c|Hu5`}{+jV2Oc(|^px^9%vB1F9Iy4!^p zmOT^8D^Hg`dwB1YCHEIg5G9|kh#CYp{usDC3E<9|CvfM?GX5j50)l~i0mhGue9?J> zbkigYuyTsock|t(*UMNgE)TntINU9{Zjia0dHx2YqK4O8*H7YJfRUQR#kLnFUd}(l zJ^L5|7IZ-%P;#CiT_D&&aJsJ&!_DP*dMBY}9|EumF~~8g5{*EZ$k#Mg^EI*TV~t4c z75UC=*|6dDU)rSgMXM<_bSFinSHymvBw@z!QXq`Dh~PS-#i=k;$QNEFw3Le3;iYH} ztd~DgbLq*CR>twv3|27nj)6f8e`7t*XIy6KX6`{^n)B9BlMgr46O6B+1y@fsJcDDN x@VPRT&7I~Eh$h*^1C*$qcF1MDYsZ9rdvIbt#*NM}J=Zl`wPmKMle#*k{~v=jDE$Bc literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick22.cpython-39.pyc b/__pycache__/StrategyPierrick22.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4f32b80e91386bc9abe73c0e040e417c4ba37bc9 GIT binary patch literal 2885 zcmaJ@OK%*<5uW$X?hcpalA=k;mTXHgum(Z~LXwjZ>P5?C`pvkCH$|Lt61KK0 z5m$V$pV0it=0doNF8=|-_1Qw}W1JJGvzVQlp~IZeWp3y(FRZaz=!35_KWwmi*aRP7 zoI!7~MmWpo_&jS~F<5{HTGo2%gl#s9XYQ#LF0gslPR$8jcxti5F|T!JG+hFH12fC$ zD_FD0&a!h)jPML!;&1RJ_U2QYy~WNyal+*jnhWf0XjV>W-eK=TbCz9%UoNTk)B|@@ zE<~E_UcP)_;ggm5lK}>_f36=KqTL^S_xyiVvWWQS z$xd7hxUvUT(y_!0b{8#rg`1RFDU`dnlUBTAsi4=3vT~1$9$u-X(Qr7Ce9Y~otlCjd zFB@Sq!5Nm+Vo{dGXsGI%VdY){t#$pna<4%{LOqYuBErEcoN&}5MkXfSKW)TQzkx0Z z#RCJ5G`r71^r!X31!y(-c6056JGVF1cFLTu$#PVsYkk2V0CS9AUK>d+A_h3F$uSC3 zF4tbaj@9A5noo;02r>Z;l>8_9^}QznHQ(_*Zea;dVR z1BpKuD{!GW3!;3Dw^Akti=-pp0&Cu0S^$GUO~oXFg}^Mxfc9W&1|Y_WaMB2OHN3?c zns*aK4p$9W0P+bhDzQX+2Fk*f*6#l5JL@* zqjkr@Al@N^g<&}yWyrY^z8yPNrI4RsLcR}T)LUlDMz^Rp%m7czThe1dxE`X~!Pjj$%27u}i0DsP4 z{;qq~xQ-$LyE;}hcAcvRYxWTm8ZbfkfbxX+DG`0`bVY-ElsiBAa((dI#lPPA*Q39! ziyxDEEd6&~c@N8MlyjliQuYH-?5s?bD|ga<_1u2N<*z!pedGn?+Nct|V86pIl5l9W ztgjuLJbUTTdTF_h7nW~*V;x$-qaXc{KaX~L&7I^@?M_P3V|TKt1MIHYfEGzyCq%O7 znBo=&u|?!4O}>f@st@Cn{~2~t|47uV7mkUOdOv*g$4k4NbIOmR0@*H#R3nP=l8sQA zA{9i@gHfDKX6CoL_c!isUc0do{d(ix_7(!6i3m<}fLxTRn8gtQdl9D;2ocpPwe&qP z8kRDp%&guzT_2VGzT}m zFQ{71q00|I8jj;RFVW4(<2p7TlI*~-O#OW4)iJ*Ge9!b=IyL{$ay0kC`POUuPW%1# zyxz4ltK4Fgqf%PMPKiJ#N-#p4|wBBDyMI!Te(XPU+kx_U~i(~8FNgg)YF9#Q_4F)DvSu8{$VZ~A6n K1x{ct+y4ia(kCwf literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick23.cpython-39.pyc b/__pycache__/StrategyPierrick23.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4b8fbf9badfb832442a8c249e04d63e7f4e40202 GIT binary patch literal 2940 zcmaJ@NpBp-74Cg{W|~8CNKvFVC)Q$&52T_X8z6*QEXoYbku2It&8csF*~_T9 zM>G}r6#1A0xdccc1j(?v1kv0uY*HTZxDc{qz+ZwZ&{a9l*cYb5~Ev9d39cK@#Xx)x3>u-f+xF*7s6S2No zim>E^-H7Bz9~;CqjN)GaT zAUTb326N`wS$6KR=AYmT{3KssuRSr@x7fEITmIq^&g<+vI7>%37uZE`PO(ey&Kt5l z@x$Gi3lT@#SH8Pv;NXKl|B0S|tX9ag)xjs{KK}2!pZsNl{BE^lN&T`kzJFPo-?>8I zhnM&E@w_+q>e=W2-00n19enyO&HBTtG`^Rv?VW>RIbW2aC`ZE}3Nx1QAj(HsDeWXq z<5D_lD7JYC%Vux1E3JYjiFAuHA0~NG(A+T=?w8p^stSiX~i zYuvaY?d#wWQBT7-3$U>gI~?^0DIy)~PaeW)+{7p-><1cru6LgSsH648>)I zcQ$UVY~?9mDe_Snuk;1Kk053I%F3wVB4CK$m0}#WF)vnLy}9M#uAGgtelF6mj34k| z7{;Rb87)tDsu>S295d;a2^~|`X|X;fi*!?7hD1t7@Ny)w$Ru0UL)k(@ozcK)OYN!t zNdGGa)2|X`RFt78v!YBzSrug|%C0C|QBFlUimDOyk@?7EwIvO3^%;cm9=3qDgu^HkQi=^voULe#nJ>1ApB6AfeXc10O_iD%Zq4lo_OS2 z#G11cXNW;WP00!*3k9<{N&2wCBoox^y?zyJOY_TbCwy0M~zx;Jb7%9Wtlck%yGh)W)4e=N zVx*%8Xb**)DtCu*6ehtCWrcTaaURQxO9b9f8&e0B^@Dkb$sjI}!rU+)juI5<0B47# zG8e@!ASm7h(CRI{WnvgK8oGzKCdyhuyV*wID$G$BG@c?rRSEza>WgL~tOyE^#w{NHZ>`{CbL#bwAU>a$hpJjjz#%7vOs)enJU zCV3?7Vk_>K&&-!x{<4GH2VOv}4NAc?_A#s!rvS8;@r7lQXD{pgeEdLP7&lHj|KjAG=#kfV@ia8dX-%G8W{}pBBshL&mPYY@_xVIrF`u` zoM*{y#SP*j5Il^wIFt1@uEOaglfijs?J)-Y4nXiowJAsS(wNfML@qF zmr7kOLXZni`w!xNK-V|c6;pH?Ge&Qo4uUYt@)A8I&kDGunG1$IM}qT0&Q%2VgG3F` zd?GHJ)D6+vvwl1f7wHPA0Z9vaT#DO7ZxHAbIJ{hiQSJ-smK4ziX*4X$F?97lcj}g2 zy|!iICCc=yO8%8o2lm2o9o>0h)!coYzcjL;bcU!{ zVIhqn8}IYXLGN8Ia1->OxC~El(GMdoD3WEX3{m!$!yYL__yw3?;zuMte`w(W@+ue0 tRiT8PNijnLE0MTCGa82iwZ-H7BHgRw*!(d$sPF(>-PJwAvpjv#{68e{Kkonl literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick3.cpython-39.pyc b/__pycache__/StrategyPierrick3.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d89e89ca1c47b426542e04c36f51431873152612 GIT binary patch literal 3164 zcmaJ@NpBp-74Cg{c5+CKIHDxSS{%%URALwi5+c+_Dg?~YLfJ?fF&aHxHNzHrQ*{q% zD&kc$ria^!&ma`7QK`*-A6rv%A`NDkQ!hAoi!s;0R}6x@w^^|r2hU2iL|Uau&4 zYRkPrWkyl{CWy0-0pd1BwhllPqK1m2%2{)C%vz{LhGVE>ndz9|>7f;soKnHFqq0*e z@M=`k6jh1p5;mN=sytALLClv5F=^@7y3-`;n$oonu$$8Bs=R#P%e`ghMN|+gYdQ0B zI^6b!{On_bxQ&th9e_$39qkj?3oA9E2dZNb1J;?2MJ%U8N{$V@Ol+q@%1#w{4Ral% zK`Ks@PS8nGT~J63FSw+!XE-g=#5=L4Ia6ekv;y@6PDy+k@WP%!+GG9+Z<1UDKC`Eh z4#p*XKTT%I<(G#Gfit?vCy!6# zQ9v0B{EZI}H2ghRZw)cw`OD(L5uT^R*I)ko&#V1Mi^G3D{_5&){_(p-u6-0OA6$hF zpU!fR<)hT~y_kg5^^;MYb2AL0Am>)(u??Dc4X*b`+uX=#7;-zylQc}SOnf`W+$47C z=l;4E4=LA&x!=`T9jmj3*uwOaFk#%>S`Tu{%^{;S=0-mpQ7l~R_uVkrqO6boB{fRZ zg0iy6`@yYST+AO~wxPQfkbIr%jFPU#OCC#-c$D(8L`bp~gR9-W%gsCB2%;Q$LF~fb z9EOkj0?B+8W*t8Q7vmmACgOdl;CR*EmjLpQ2kaWS5`42X|FegyOY`eVMCY?)ln3(z zMxP@p3H{;xD5K0J2-x{-9H#-z=Fd54F5TvnK|DxU`PA~(1l(cBXxw;0VwEcSJ=RPl)95JUdqkMOU#vajx_uP}(ZASkV%G)d_N zrAx{vC__?aL79@W3d)jHNl?4`j!sG)1#6cU6yUbRZK+`w`ei{qD?_J3s$XDtQj?s@ z8BYC#(Un1N0G8wAwyJL?(tF@jxv`-$4G#~58^TF7z?-#9)R~byNrWpR{*#z zV>HS90|FAkRmp>hBG!Y1aEr=NXDGr-@IUy7b$}gA12Ew`M6$IN#E5XjWKJ?5W;(*b3Ld1Gw{Zq}G#%4}08})5EoUTwhwf!}T?Q((^ps4ud{#_LDFSPz{(XtjTyi z-%bPH3*8izigrzQ9R{!u1o*x*Q63I2pL}-G`T*G~qq5OsqJXg<3-BSp@QEn% zkN>s!booF^M3hLE7m}|QhrgQs(}Tah_`@Q%Hj{7^QCY9MCO5P7V36}NN;SiXGOop` zsNIyIw160v2}9DXeVyYXiR<&(=y>LDsYaD8anqB!?t7`w%jGL-RC1OEF_aFy{Ai0mhaqKaz9;qvbKU$tfJZk5t54~0c*kn zq#N@Bk$|pf(Y*Zz>842r_iR#r<=uQY84NO-^Y>1~NgQq$TsO#EMm>L>5?&S+Y}|ro zQkM8M%WxHdYD~9^3q{78=!>rW&ny_mUdSgT0sTRcOLaa2LBgo8-wg(?@Z?yRiKFCt zfGbJUb-g%Fax~2(&TuT%gb~yi6c(ECse*t#C=f2Xt511VbbH)r;z2NEH?a~E9hMuY zA36J2(4vAcM*!*lQy?Oe82~MeOz2j0!!!;J%RrpU*EDpz0@rJXrpouVRmOa1*_JBb z*GAbs(hT5-#<#}T*0im+e$wj5mAez%j7L$r%^qWUZlx%c9?~r(g_*b?tnSL^f~a9%}BK*B1&t?m5R8TM**wrz>nC;Sx4iRB5bpTuD&{q=-Eb jUsO&7($aSw4!0M^0s2#MN}0$TTeVSZZLMb1)EVRd&3$tK literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick31.cpython-39.pyc b/__pycache__/StrategyPierrick31.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e60018e30bbfe24668be728c35ce91d80ea93cc4 GIT binary patch literal 3360 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 literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick4.cpython-39.pyc b/__pycache__/StrategyPierrick4.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9b114a8aa7ca9de1ae1308cfff7d160b1308e105 GIT binary patch literal 3013 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 literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick411.cpython-39.pyc b/__pycache__/StrategyPierrick411.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e79992b6c8ec6533c468376e620e97988d4138c0 GIT binary patch literal 3998 zcma)9OKcoT8SdBg^o+-^vE$froR{<1$?n?Di#U>rlWZPDU~h z1C8Z*=_9i`$n*uR;;dsO+I+>3eV4f(G`#SJC%gtvxsZLA;yC19)ZtWgS-9+_-c`~7 zVGGV$K4FFH3#sr@USIWTyzRvSaRD`%(lw2%{Z@_lvG=OUEoLy2>C9p|WX&% zu#z1CJo?CB$H037^Xz67cKm@>9p)o^45jR{f}S%@xE{V;fJ%*Tx_jYOzE)p zohCBRc+G2tsmvu!&*#6dsq&}U`Y(S$<12vTV`Jh8&liX{r9Yb87k zrIVy_GmMjj`nEAQj$D4vU-F_lmu5ZnD~9SJhNxT4kHc8V+}))hd*W%TiRU*~}d!Rq$!d>IvD0|z|tjAW@r_b`!XQzO8{waRagEtk? zS;S!&M0MG-g}dmDk&H{1qX=pVub8@b27|$)&i#CzH6929q_xZ^K6!KRMY-xNsR9c`|8x zZNQVs9n5T^xhngEs1}Qcmj-vZ+w=mFgfu@Hpr)@K*{RoOdSJ%|nyyB111HE+k4TCi zX-h=DNrPpDHb&;BcKIXhH`N~N_w^^JOrIgj$S6ZmW=5HcvNFn2R4$`(in24xR#ZNt z@``dY${}jGfVm2+_z*L(F2yNsfq*r{c0V+~Vcs{{@Cc1GGNS?CqwrCM?^XC1aGmXI>x^i_M2&4xqeSmlI@`G5 zjgDwab^sxg_PT8ZwZ;*WKki`%pXiT!pU^0W)F_A4*ax!S<7y{3RW?4NAxO5*>o92| za2=T!Z{iv^y(AD7P~teTHeD!?CfotQyC;r9u+9Kr%f&Qka5!ubGih_>ui_BNe@4B@ zbK$X~8-zFlP`~x{78QLso-mLNn?cn}95 zqct$u4Hrd=Af{qQP6Z5ckmNZZ@ro;l#+rN{rQ#U)qL;uFfwu^}4N&Qm2FyBt<4f-^ zxR>WPk?Ud-Lg~z1pKtRwvFp+V58&d5(!99<^2*#rX)XZdZ>P=GFj$nmi|r@^kENw0 zvO8UE2EG@%O*{r%tw350JkyA@XJ{qU1kTZng-+kC$RIvPl7VL2Y=wBRT^tKWOJkA5 z5G2%Db9(e16IJgqPzyay*(vK~eF$IK9@0znkz~WzuyaEjwsl+|)Q5)Fj_ni;c9`iu zJNyTj9R{Vt(6nW3Szmz*Geeo3IijtQtW8-;!&ue_!L2K25jPpMm(A2_=Q;Pyj|}G2 z5GIsC7UtXc_C-vgZoX|(6j6N@&45rLoazEkBS-E~m4SpJR+4R29{~1%x4sQepyf(g~UC1;+Tiay7w`4Gl9GQsH z1=i68t|BAaQKY~txeG{vJ7(d`i?3omg3Ll%ag#?XWGD-%e|_)=*M7V9@vO>4ke6^$ z$|XxdEv=ZZdE4t}yX9h(`LwTDqJKiElU6YPVc~%HY#7F#mxkH%!mtiJH*Dim9C6`) zIMUX5^Ob$laoq^ds_V+4>o#K6LL%nMlIz}Xd0{5$zdrxom77;DzH`O>;0glGyYqrf zTr3ixs4Ke=i7ER>(h`W>#OX$A5K#W&Ql=({ULoCPoWP{+a5=U!-;Havgr{9V7Mt_pC<`2La0XkZkAUjvKJYPtG1m4^~eilpCX`N3sR*n zhardsr}eu*%~k%Pbj1Me#te{|_PVYYMRAH`2F`#}>WT$JU`0gbi5$oXtOrriMb_hU z*+qF665FU2)WumkE=o|Og`k;=TSQa#EtUy%vadEqfmV>w??O$!22eCDvuu=$)=SH_ z^z6%7;40iKS%$8@=XL?@OWU#aZQrL>x3ghbiu1y<(Z00)Zhc~pIVOI3`qf;OT{0K7 z8c5uXs449xo(wOMMw7|0P69ZQ;8N&cZKApPua~s#f!s9PATqusS*W!lKgC~QqGF5O zyTcebST1V#rw}%J%bwK3vo+x@(j-1|*-cE$k$8M-ggc0Pv6#pnx{0laNZM_Y_zLwX kZVA*5cRFV2%(N~4Jl#%G25@vo@54jo=p~$MNguZU4WbfF%K!iX literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick4111.cpython-39.pyc b/__pycache__/StrategyPierrick4111.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ad947104c1f434186fecea6c990866fd7ffed094 GIT binary patch literal 6078 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 EPm~jBM*si- literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick4113.cpython-39.pyc b/__pycache__/StrategyPierrick4113.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6759cd3243b0d9852316c8d70836893c1e720084 GIT binary patch literal 4986 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 literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick4117.cpython-39.pyc b/__pycache__/StrategyPierrick4117.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7448e8cec6492acfad54796b4506e4793431ed7f GIT binary patch literal 5002 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 PQK?&M9*de+$Bcgg##4-d literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick411_02.cpython-39.pyc b/__pycache__/StrategyPierrick411_02.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0c15a2a0b8fd95cc8a6c24cbc32a6f7bc0dd81db GIT binary patch literal 4141 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 literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick4120.cpython-39.pyc b/__pycache__/StrategyPierrick4120.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c2324d7aee3bc555995ffe9670b120f70f123e57 GIT binary patch literal 4645 zcmb7IO>7&-72ZECmlQ=w6fMiL{Qna(Ka#B2X#%LG?buEX)UsnGMX(6ESaFurO3Phl zc4=F#eTob85)=h`Y2ky7^x{iT1)82(^wev6DKMuNy;Nw?Hpmas!ax1q?2@7sA#InK znfEjE=FQA|Z{E-y9xf{QN|zR!AHAd~eWSz8+}pyq%Y@8d$aknh_M*MY||zxnQVWvP&6%xLvkKGI+E-rYWk@uE=`auBgfz z3Nu;$p2G58=>y%aGIdF*SsTzqS*&Sd^n%y$+iq~pX=wBsmMHfitjCcNEj&w|3H@Y~CFv+4VWJ<0arxBtFo?_menL0|3Hha`Mh z)<g4>~hK6%!{p82;tG9sQ z`*po_{p0=L`|DdD*M+{^U9aiFVyP*_BG0_0+YJ(ti#u+^%Z5P6cdI{p<4?DLUeAD^ z);DmvJ=vesTebIo@SD$1{Ikx9yfIHz1(i_eG8uoYXNvstS8ul-Kl2{ok8?wN28c{cdK7rr%6JfSpzUC!0gOiyFBldzoSaA|{ zf+&t@YzjG1=yh=PcV+|3m~@kFl0Juei-a?tgJxekfZ zO<^?F-LPhf((c?*d-3c}<~7q<7h`_djCk8k{2QLraeW?lNC7fDMZJA|$6;R? zg7p??!Y0KTY>bzO&N!E)b?=m?kkj!*q!m z8D>ZuTm@GA5HqnMNhuCcN`1<3)~CFc!bYkJ`j2j- zj!9~zPaV%jR~M-Fu02#w>`^AOb}zNNWp8Lsd<4Z*C%J-EtY8b3Rx-3&N`@W`(`wix z+ry?l)bHwdbhcMYpT5YPFRd+MaOla`1{Izerv*cX3(gQB@S(0g8xxARtd(dT}gNEwdyF$Od1oYySb3$K2 zm483!tOx$G7+Frk7s3Z=8H?d$z2i6B!0F&dLZFRT{AQA>X0(xLAdH_PWxqkyt2A+; zFJd!V@zcZ^??jz$fRw|*MnPy2@i+z}K7&fJ%4%6hQOg=ip=?#Gids=8@T(Zp)anyz ziGIX?rad!r6VFUzL9MD2yKkS^F-+|+;g(30$GlWzBszh!hPA4&YNU12!u1}!MjWGu zvD_Ns4l{fB9c5Kr)mXl&NV+909dg!`mT^~4bc!_h;Flk0cM-j^8HNUC06Z`Quq`uC z&##*yhZ#z8h8$)v8Dhh#amTo0o+qu#QXAA*)iCDK&SumUPkW>*-oi_FARR~gVH_K2 zA|gu<9bM!XuyNA%rIAa+Z599UQRkb*U(|(pBMQ20&;JWdnO{WDhP+Pz&_^(pC6t)l z=MJQv1(3qul9O;RWHSpv1XGUB z07zx0WNxCm0cf&=l!gEB|p;j%w z-H%2Z5xgYvqcC>h_$NiFPXKOjW6#^egY5Zj`iV;475dxpJIo!^ek+wJbyS^H%a4s} zS?j}QxvYI=Xr@|yrfE}8HGS%dR@6S%o@wRV&;5^EzQx-ps`b6?`#>0O{8h3>P1-2D zO?m;a2k?@rfUiCbpnXCyHUuH$1U#XSx$Lo3WRO1ATTB2J0Es@neU3Zezt`qCj_inrTjUc+~LX z7_S9%4ZWM$ntXn|>8K2EFAeY{#m z9LEjAC_#Emp6H07h%;P|xEH-xjAsb+!KLOPkH#fAM5#R*t@mC;;8zj!aotN-s z!b7Ses$Sk&(LKq@M|Ppa6ljXNp;t7z2~ih~r-o^$*)L~+CtJmWIE``BJIKKHdc27@DmO7&loJ;z$c(#8 zlQi&1>OuTEv5#y@xPf3E@tG`Xnc2)HaP3GIe~(5KHyLt;J8ibG7E*^fLuVAzgV$0m Pgu@c*685#EP8$CKn`-1L literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick4121.cpython-39.pyc b/__pycache__/StrategyPierrick4121.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a56dea2b8b0132f938ff42e9d1293334ff4b55b1 GIT binary patch literal 4695 zcma)AO>7&-72ba?e?-YLtw^?P%kfXl{7AAA*GX$cwqrYS8!JZS6af)nyV@C2D=l~F z*`;i$GSDIhdJ39DFD-PifnIXyy|<#L9&+e;PAz%}bPiesL1OiL!zD$#Lfa+w?R#(L zz4?28Lw{tXq~WRDTxy=3*R+2SG5lB{uHa1{0Wgi}k=D@F*Jzmd8j;nu8+O}iI6AeP zkn2* zM{w2+2rJ%N&V--w)@DHZh94Ki6};(9UDLQ)Z^QTqYj2p`Vg@sr&Mam#=QFGvoC0%M zk(F|e!^*4@>Wu;$!Ro7xA{%X#*jS^?4mB!lyfFe^75FH=V|WiStd$?;HCDTT7>x;blpPCoz-a}KD|kY|lS6O@_^F)&dl5asA8P4IkdD(kCR)#^ zwzGieb`16sc;_+B3w)B**~@&2Pq9~aZ1yU9?UB_u%4XTzBdu|aPxIq^n$4s41-5{e z6GNQW*&E=T9O7JLKLBTjUBaooDQo@xz7ujG!eIT43kw%^4eWF4cfVE7VhaSGKP`UM z<9x9w&6VzE-IOlt9d#-Tj5qymlu0}7_yHFc(>zY_) zF5u^}rEBq-XM2m!x6N&1&7RcO99CSn#pkR9SvQxp#P3;o-4b6ODEsA*Yz4AkvJuE? zaFObgAnVHv*OvcZLiQz3&8MX++G`Qu_Z|1vrhdt2tczM7l9riFKI z4mDg{g-T-pN@a0>6y1~M$WOD}*<`IF_(qt(EQh#X zXyV6^|MtDP_dd9NV{SER^SLzXX5m~@@O#RR7UsGsc7znqrM*BN@^tQ?7&F`1l;xlc zPi=eTz8$De$t^XbrdRcUfnZ{t(gPW453s)pN+#OEX?~$E zsWsGfaUCp$_W&8NJQT*wM6~@Z+~6L$hsv)hMMF*BI(Ojs{~=O01x{FlgY(R%ouKrV zbwwPQq$hu9MmFBw^e{rE+~x^fhv}DzGPZR^8HzG<%2bqr)2)LIc^AwzzKkyu%AO28^PXJo*s=A6+1XDsI&8gTl0#xHBoaTrdlv)Yt4nSYPb z_lW9^`B6LaX>VjT&o#{R87|GYyjID|k4I?ElzZ9aXXZn5+hkKp`%#Q!vSSLLR`_v+ zpBS3K(R>CcdJ;sbnVeL8hPW7gdaBnpgM3dr(_4emP@~xcJElFZvQtm=$D>b(^P)_Sl0}+has&YoUnFUj7j2akRj5?i zE7vFx54Bx#0YP#03W#r~nLv9>VEe#+%Y=x1sI| zNO2oPFS;92e<1bE=g-Z(9Qo}P=D&?gkM=csYg=#q_l<^GdKu~>HhUSl*N@V-f%J5U zQ5uds40V{Du7=G_&{Cv{o^|_qYpPLl4zmyi>3$U_j~v8Zcz)jFgM%N;=PQhqQk|?# zkLgE^Nqu_HF>2pfrK)3mXTg???~Ogf*?Q^!T;DTxL{9ol>ZJ1;+FtXy#XUdYegB}u z-rK+$6!sEn>3>=GzR*(rlyp5WMj_;RvgCQ~gmrNj@MOjF?sferZ#i^p>F$jWZ(e=p zhWFDO2n8Q32{I8xCY;~EjS-DNZX{6`byXP!G~H5BT!&&xbw<4sQl0J5?LkgFLwcPg z#p!KudHi6$mo%Fx&*aHLa}r0JIoAtQPjEk2Y1pP*CB@8^YADo0U3 zsd~su!xqXxIi|KFuOq3uoJ2z+IL+J(n-n_pzTyb!HbdN#Mm-dhagw2wN#Yb6R!#&% z6h!#rsXUw$$QTG=9%`NdJ3==FT(jb4*pg^!8 zJ}6UXu{wnB*5+)d|0hMNEJmpO)LSOXz*SjncHSIVWE>ca!s_2u}zX2bj40iwk literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick41211.cpython-39.pyc b/__pycache__/StrategyPierrick41211.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac18245c62dc14c0bdd32bf3ddd6e522986c61e7 GIT binary patch literal 5030 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 literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick41216.cpython-39.pyc b/__pycache__/StrategyPierrick41216.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..47b16cf46424ec9e1cc39654dae5cab118228fdd GIT binary patch literal 5915 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 P6tdX}+A2mxoi+X&PKCxt literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick4122.cpython-39.pyc b/__pycache__/StrategyPierrick4122.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bf82b810936d1de7f4c76af03891b930553d053f GIT binary patch literal 5258 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 F^?w`S+HC*; literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick413.cpython-39.pyc b/__pycache__/StrategyPierrick413.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f22d6f81b2f2e697334b572725f15f83459e26b4 GIT binary patch literal 4992 zcmb_gOK==V8J^e9&ORlrWLfe%cKp~pBtPQB24mTd?O+Nkj-5biDlm+;dsidP&a8W8 zZOg1qHgLf_Zb11U7dhYy#eqY(Q8{rS7cNwFae(4LTu_h{2}ME@@%_E4U0JwzR54rq z&;RPbAM^iz|Gj>xl-KYS&(GH1J)mj7Cu03$fjEVhd=r3aOb<0zS8u~L@is!MnQ=2s z+qIinH`{bvM<+cqG@3a#r*J*YyLs@eu+S{JMa9d6rDoYJ_vAy(VRxj5SDK@SrfbbH z1vj~4y7q#`Y?i&Pu`Dm>m{eU(u@01I$R9ty*&UEUyJkKfL4%zsXZBN5I-p9I`8xHgE^}x7)IU$%LD+(`c8##yg)6D>Q{Gsr(O~Nz2gE6~x|`Sp zws+Fq419uZaVObUcMI^XcyB}7&MNK>zLW1_+fHa~JAOOZR<`r5{hmR|KuY&s&+^^sP!0O#Bd!nnUc-?+o-6z3$>aNM2zH6{4*xkdX*}>bI`#9gr z_wl{#5M+nhk=vHLe;wx;b`+cg>p0J{=fJ75V{n?|a&+yBUJAGnL2dDwLq{GM_>hes zd|y2?4G?&KKGV4T#*VN3@ufFrq&e4Ks+!VaT}Mk~mhrmZ4pW&)T7HecTs1@qeMA|c z*B{7N$A0|6?{5BdriZ*f^M|^6{yfuo>zDMrGt>B7(RlJ3<3F1ba3k%(VFC)Tlyu+Xwq|$EsVv(m+OB67av=SbM(n->|6~;+IeY=9ZTs zX$Da{@mN!4TcJX#hBVHck>=^MuL8|A{UGwN#uSTf&k>T;XxrBw!l^xvmQW6CXjioR z`s{rTS%G$`E}jEdfe&Ap{_Kkv&rQ$AO+KB(?KGIK3w{-egYm=D?Su=DA-hZ`-Bc3r zWcm?4IMrH`BSBP;Mbl4%Yus!3fk+Y>pS)AkZ|;3muYc)*Ey&SubxLN~2%h>xQc_FX zBC45WtoP8uGH7a*Db|~6D|$!2hsN|1L>WEGP?XuDOhs8e%2HIOM`aXc_b6LY*&dZu zl+&Xeq84%(3;FO4Mq&lU$pb6)IHf*kO{d)F4E1z|ALg);F%1?f8@Qv2JJ#oJ>h&H6 zr?5~=3o9j>7e?O9ChnNaX2)cclVoYj2@UvGg(K?$->&c-z;(8>t23!h5Vd1KZ6$h_ z(pkp^Z`-7%WRH;yIcfK-IE0U|=Bk011|<#>Yt4@XX~Lrc{2Ssal8yqv6Z2`%?ffHSO@C#y4C?SNz5D4)kK;yM?e7qhTOqr9vYZhjYp|?nrxP( z0YA)M`urg~6O0 znv26QKu!`KO(T(|bg328{LpJ5wR5DkV=yj0P2dC#lk0aF#IHC`lF?S&YKJ)MJ**oh z(pV(dAV{78(44YfHqrF5ftD*f6{n)pZydjhJ+2q&N3vC8)y|Bs+7mbTKPp}yG0H)Pt%#hYpl&{yLZn7v%=6CT`^&6xWwT59>?b4%)9-&c`d`!`m9$h%4 zu}yWPLlwH?v+CFuv-o1-EI{M+)@Sm+{^2__FP~RVFYRk_*lzORZRFjWO61U@hj40F z&=M*pJ1}_)q<9vD^3MB))Ne?AX``z;CqlnD$NZCTL-Vg_+RbDC*4I$Ic&XqKn>rE3 zh>qkWkgkbtig+A0{@2-wub|V0sfikR?+~967$HD8DuAY2$ezQ7ZBz`)aP)D*(z}q}TOciiW9g6r=8dOx8yvf2yk}Wu zeQeb*%AXi!`D4Q>e`I8hzZnJNePh)q-Q4%zMZ-gh7x6hryr)1gR6z@rAi7D!q$C0g z?-f)>Dm4LDsR{3`xZW;ikQ_4oI`OR;RW+l6PQes)fU3FiOZKbF zji(b;=-quZa5^Lv^wu2X0}E%*u2myre25)z{=?ZburXVGLOPxop;q)fnfJVA%-Xml zc(UkuSKEHrlZ;%Lefiww^QWIb=Y8QE4&xVR1tl-hBtTmy76`-yA_55kLEtKZ76B?Z zMVkN}tb!^}aUDPwP~6Z3-V<>kwiBm>S~;MT-J=_b9Dj)PT5$rmzQ*PDNAtb7UQc)` zclO0`6fX6+UXXZ#`?Yz5evay=ZlyfY5^;?u32r)2jrjH6eJPPcDB(S|Wl7M8{7?=n z0{ZnJRqApAf>>~xzZcX!a`mpRn55O10d8DFp65qVoT9pqqXau#hy_F8f{KMFam~)wtEoG846%ttBy!!KK|oedZ$_x0u}CzaC8`_(Jse%2aFZzni4%SjwlA!zefD zt{XR_d6}!Xqgsmpcam;K-S!A;*l#ZCxR?t%S!Fr(Q7umSb2MBHcfK`DTq5zFfrV>0 z0b()Lt1qVpr63M+B@!<6$PWZ+v5(q)>73{W(@9!}3RDMaZ32a&qZhHcMSa5hCnLH@{_p?m`FH() z{q5E36$MZ2>PGj~aYgx~0Q(;U;37))LlB}6HB=l`)|#WE)6j5&|ZF>i+Q8wB-AHPEVAo9ZN9`hoaQ^v8Lh^nJmV*KXv@%V}@h7o+cgY=Da>*-H>AE#PRkU=7$nBYL1Z1~D9yn2tp(r$kDQ z4Zcilr$Wk36?_eK9i>4k&KMo16Qp`UAvL^^lE!nxX_7I#^hCB&4pN^y8eHVGsp5w<3>i z(Y$SNz0=?3Mn=Ps+gYBZVUlH{?|^fY*rkvBO)u_IuJ>}kt+5G=&YEHh(@(;Lar5D3 zkW+5<7^N{cI$@u3Ya1gb54quQd-3DaN&npW^Y4DWUE;M)$AyBj4wfZRKTQi{Ws!Gw zd6|p)9O^Rax0jdQch8U5b6jUxz-r{JTg8=rzmD|OjdA(iBdYfIn! zV10FIGl}R@mh|&rsmthnoN+?mUg~F*xde`|lnqXOK(nO-KJ8+<%_oAmo3O~sg9p@2 zy?|wrm|uK!MSXngpkDvg112bo>AJ%2UL(~fy&#ZQAWb5@K)OUmfeeYv0+|w71+pY66{sYUT_9Vadu7a3CY6sdlW>+WAy*fF5sGn@c+6x>b=gq161QE(BXn{5QG|t&cF=m5nIFP05-@0|fy|TW? ztg*J}WUsGn4CH&<8rLBMxpar?AKd_4UBATj8z80odAc129X{4c!Z1K^Vy;+5#_RcZ z8u(u5rU;$1ZLqIlaJDSSSuss{*kRA0* z5^Yv_fvaEoDEN8hsqw^kp?;!VQNB~Vt35TJT2HK}`u!6G{&`P$6nXbw0U!A4ctO*&S*@2h3T_75Jfd$+HuXQYo8l1 zeREecv@f(>&3=6Pe;Rf;)N6JX@lV)R+Epf7h<5jcg$8!gVW$GUKopdAhHSuZDHlgN zW1kNENLN^U&=z(_-$7gOGD@JHN4m9T!uqA5_3aII8w(p)|J#BS9w=R{_r2E7ul?ci zPgdl8i@VnmnmZtE{S8yUY1+|o7I*u=w!cHiOvDDIs{PZzVV&F6w7FNB-uO~8j=j`u z?O(8O_5U2)(Rdr}d2YLIj2p*wdBt_3g!GYWxV+}N_xoO0XeQP+ZmxcG_0s#R?(J20 z@&_Bdinu27N|z-8o5YIXxy%cMFS{c7;M1=`H%+oYT#tO=V7Z%gyBW>-(V;qt!|g)q z2ARvK=WkNN%OZ3RF3FjcB|gnEBspjr(}zW>l<_g#-mctd7W85-ceK*Kub3Ow_ z!l+ok8+2W9=mu?BL+nNmkZg^)t{2Bij*C5sGnl2CFoIBp7)3KaSpcjDA;(1q;8R`| zISF#LxEu7?5=LU;qUHu7QqFD(>z0 zI$pu+HA7S7`_d|-er4H~y6^qmsM~NjDf!a)xAB=ZYwOM1%_%uUdzzbZKSJuF#VNN^ zgkCS>T1xoBFa=u7@F_&LznJd*J7i1~YZR`?L=IBy4T;YtFL(Q~pX1jeV@~ literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick4151.cpython-39.pyc b/__pycache__/StrategyPierrick4151.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..522260eca575c00855ce464d9d7f536273434f13 GIT binary patch literal 3581 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 literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick5.cpython-39.pyc b/__pycache__/StrategyPierrick5.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0023c818f4a4c1b9c18faef4faf85269888fffe GIT binary patch literal 3665 zcmaJ^No*ZS8SbuLUcc4$i{tpk*`3VfvBV})fRVyDo0G9boJ^d+=n-hzUG<(j?&YPr zpFIxoi6BlR#F3E?iI~e=5E7T1=L8a5xQtXM5GRYUiFFoizN**Wk}%b(zyANPzxID? zuUai>D3zeCNT2)=Urcskxk2Gqr!uO5V0M+NTroE2UwArREidVeMyUM+Y zNn&O$<6g!(E57XCT^tY>P}6?_Ftx`Pxr6;;mxLOD-ZH6)-B~T0+N}aDv>f0?>a82H6lTpVw#wAGox(X13~d0N>ymY3-v!v>xc&aG%1572L1jh=KJ9#~)7#axS{M4i(VEaGH5M>vdhTt$lVa|s= zCd>}^R)oERd6Q*f`YT@CEQredybGG~dF)A1y>2(B>@@PeG&w0`dk$@}-@31TMWJUW zC*9Y+x`Un*4klX;Xtp2>&S;Z}g2$62?sY{`Av9Tz!6lb23F{&_k|;)A5W6^JhST=u zC6f9&&azb`F6(8~RGLjk!<*={?*pi7YV*_JD)902#2YuRPfsi)5t~SpUKUKWIlB+# zrtIuQFJ;`N(EEwBukHa$Cti5jGu;(26vXX>M_v{zF}LdlJpGnjUB0=dKNx>;%nR~O zLloswZE0e{iCE@IQrMhjJs$gpoGLFCT3OWzpW>{U_E>+YKS8DXc}bC+B8oC{%21S< zQ>LPD=i+l`S>%WxZ(kfwsowh1}p5jNr?cDv*2md^M_b*didYs@NP$qsz@_+MdS)NVp z81XK9KG%8oC$ju%r~B;W`Y^_Esh6-)dpU?t;=h zH?aZ!1qr?gAS`w9qPRV7$D#1gNr8Rcq}vM-AYI%7X37$teg_@tc>v9+=`{nD$hM?c z@YTw7r>@ua5qy0(VvMMsij>+Uo3=HwX^%cQ^`a|z$y$57MG;FDP?fiE@W52sv`$Gw zdyE`Gc@5xWn$-Kp`V;Mn_U+1DvTCi`5A9Xs{)vn{mOj8-MAWAC6Jr&AL3#v?cGPI# zqyKO8ck|KuA~Fj#+M)0h_soaz9dZwVrG~bMqybpDfk?k-DZC0?Uo;iIOD>nHEy8mt z`2k>s3)&?)gH!3x)0ZE_y!zg`E@^bA4X>!IuRhCVpy1OIybjQLulu#qhd=%6)a|Rv zaQK(OTvswucavfX1`^huYAX2YLX@eN0{}QW4N~E?yHb}wzh&Z+X zkYefIQF7fVp*>_< zuBf=~{hk-*Jwr3Ix2JDiy?A-r{l@gIxf!^XGQ4sS!BOxe-~-qJTnG08=}cUie~FPj zq}xqWJmV4*hhNTjlXg30nK-)Loy6fv&UJ&-<;?RJ7!^h7>HDd|XqP8GOH<@|5RKV# zo-L+g0Qrln&Y1?C*bBv=A|T%mG9@lX(UEW__wNR6SH5IlmdmRYMu2>Cz;(SiPBJ7n zNu1(Z$_c0Nm2i?Q75j1m`+>7_kp=lolx0?ibTw`V9sYUD#EG2FgfGnaHzmy^XiKo8 z$qJMHlgq5Fj+)*AC>f?@ZkV}DyKwy;jI&Gg%_5sy5QnzY!80iyMgH=f!KErev4Y>A&Q*w=B0p-taJiF;|}K8iVbijs*B6>iqcMp+Ud)# bNI2*FYWljot5jN#qdSNg4yl+GeboFPLOS;t literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick51.cpython-39.pyc b/__pycache__/StrategyPierrick51.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..26cec3770b6410edcf0ce62cc6ca5782cd79f1a3 GIT binary patch literal 3671 zcmaJ^No*ZS8SbuLUcc4$i{tpk*|*7Ki9L}b7%7ai*})QVGI7H4$k4Q3)qC!^w^aAD z$00ru#A$>$f_zBCT;_t1xa7XSFQwjKB^HtwPRo z!eXnGCUdSXd zGna8MW8Iau?B87+5SLKXe*rMH#}>JZ{bHAd8ou5#sfpcLEt}e{0xh&0;6>`RO0?K2 z1FxX1qSk1sHNXbh5G`NSXayg*w6U03#;2LS|qeHar>)UXj!iN>yui%J+2LMOc z3bfIeRJ^{_LEvL+gdRdYj2VaN5qk8o*4ocT*a0>|kF6Q>I6d*$Y>n>XoTR6~Y3$;h zrf0x8NMFOPofY-1`?=*a&i(e{h1b^!E=1@PT{I}an_S;SxzqjR{lES8#{8|x?(?^v z9r@lLe>f?~g>ZTuI-rT+H1l}YOWd{>(SW(_xEE!@3jEN|gdKW(k!4L&81uaqVWuny zgp+1*62xgL$M!Kdj$HOmd%=skOc>p)-6Xt<*?CPaVYTBR=E7QD@G~Z?E@vzf=6uj& z!t8QyMc6BtH(nNId&P^I1yPxwcR@2gk3A`>mn1o5r=j=y#DtLT1+>L}>%Q>?g`S_7 zaNqdi4tha2m~7dn*@7@QqfH_T9*^Uwmx!W5XuKSOORiiI)@5)cQ4Bpla&gEEr|r#4 zByH>LU%q)`YJ4FM*?1cFGJm|o*#l@d zW#`9xDdR4M;*Y0&efL>9{_@kFOIE~?A9Z3LdYQk(+{E*F`c1jKd~{8JcIoFC@GRtL{Uag8HzG< z%2bqFkkqR2$e@LWhPewDHQ3lu zQ_E!r_HyXpuv}&61#Vq&_ib~B^U?eFa_ESXJMaRxKj-KR+=h}p2yXd)B`ZIv%5%^$ zdWar=*H|?lne>P{F`W*isP$qsz@_+MdS)Nbs znDH))zR>;gk7W7PPWSnV^Js49*l_+Byf~Ai!y#p&RG)#=1{oxmAiB9wHsT=jBswu%=TGZ*o-j11GxNk zIF8#lZ_WxwH8@ua5qy0(VvMMsij>+Uo3=HwX^%cU^9o<`ipBQ0iz3!6pelFa;DxC)YMqjX z_5^u?avQ+MG^zMc^rzZ2?d{4vvTCi`kL*?B!HJAKkxsx|MC7LSQ)3m5LAnHtcGPI# zqyKO8xAW2ZBC-oL+M#e1_svIe9&!(WrG~bM!~s~jfoQ*IDZC0?Uo;iIOD>nHEy8sv z`2k>sOWGAVgH!3x)0ZE_y!zg`u4r_q1GlKmuRhCVtl(1;ya~{KFZojGgP;6$^3HVM za^!=pD;cT0N)vOLpG^vTDGque>)w{J`>kh_-R}(l^!o1~zSlP`6=$24u+jy;lZhfi zEJqDfL83&)T*eWSgSg!+zNBDFo{YhIBJ@R}FNtD1h8|^p90_~D>!pZH+d-_aarJE_ zKsw-NoJI8K*l>CcK+{dA)gZC;I?3DpXFf$;-kx?xOvNV!~5 zaoq$fiHXCn=DTsHld?=4-R_Q~U?u0ee(G}OwHFu_Md|GO*}`bT<2FlEBzq8z*m9mS zreXj|jH}L>`rXJ2#GoP|-|;ggE=JK2b0+ui`W;t3WnY%dyA+0xq;tS^y(o$^Bsg)D z;#$fvr*N0>lPnecasvB-zjKiZwV5c()C@Um)bYFgvzUn!Ih_f2nDMVkx+8&1h<81% zQUvKjxlG;asOfEhl3`lrhH0D7Wc695fluN_#U#4=KCz2vH*Ck&cYPn4HD{BU;B1&5 zn}4?(j#0l`Kc(hx4hk#kg~b#ZxKQQGNHJAKs^ b3Fl&8P2ZGvl}ht*bO%AhAr-TtkDC7jU$gke literal 0 HcmV?d00001 diff --git a/__pycache__/StrategyPierrick52.cpython-39.pyc b/__pycache__/StrategyPierrick52.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d52a9f92a3bb9d00db5b047499f110ad4fc77d9 GIT binary patch literal 3671 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 literal 0 HcmV?d00001 diff --git a/__pycache__/Swing-High-To-Sky.cpython-39.pyc b/__pycache__/Swing-High-To-Sky.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0668d7249a0c3f6ba15fdfa856da74e7e7865930 GIT binary patch literal 2929 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 literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus.cpython-39.pyc b/__pycache__/Zeus.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d258f5f459648fd0e769d1efda81f04d996a630 GIT binary patch literal 4530 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 P#sL+EdOQ-Jh+p_`(zA{F literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_2.cpython-39.pyc b/__pycache__/Zeus_2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4a14c4cad574c125876a2976eb7f0ebdd083b79 GIT binary patch literal 8091 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 literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_2_1.cpython-39.pyc b/__pycache__/Zeus_2_1.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..657a256c8cb759a3410964670f3f3224a8d5837d GIT binary patch literal 8151 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 AD*ylh literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_2_2.cpython-39.pyc b/__pycache__/Zeus_2_2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..47698b04cd41c1a113ebc48cdbbc008b460edf1a GIT binary patch literal 8371 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 literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_3.cpython-39.pyc b/__pycache__/Zeus_3.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3fa1f7c4cf02d35e20630d1f92092bcd1d433ff2 GIT binary patch literal 8159 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 Z??fl*1akTTh7~woOJaEFmz9Ur{{o|TUjhIC literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_5.cpython-39.pyc b/__pycache__/Zeus_5.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c6c2038f698d21951e19d03e8dd13f287e6fc9d6 GIT binary patch literal 22255 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 literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_5_1.cpython-39.pyc b/__pycache__/Zeus_5_1.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..03ec6647c1bcac2e8b7c61bd2c55aaafd538d602 GIT binary patch literal 21883 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ăb8O$mmT&J;^a|;5Q)` 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 ZC+4Q&s$9GaLmwVt4KN&!#3$nC{|^wHmz4kj literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_5_2.cpython-39.pyc b/__pycache__/Zeus_5_2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b5181708f1cdfaeb6f46ff357c0fa815e8045ff9 GIT binary patch literal 22232 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∈e%G2x{;Zm+YR7DWK3d_E7MO3FhjYgy+$$c};kAX!FDse}7Ynn~++cPg zeWOPeX1_ID3Pi7pQ}4jWYO~p0i9M}sgayH=`Ig$6uf{9bG}yhs4%hXUt-jVp*lE#z zwCJ{){;(Zsc7NKz{fTJ(@tt1$F5>8vitvKUy=WKbLR{SK^}^jAWnjJsTDAKdHdd&2 zuNSxbBTBWo^0M5Eq37S@KLLjg$SGLVEk0BS<3Y~+np61%V!GHB6IZDgPem3Q2uqO3)+k~6-6}yoMzYo}*yQ_x`;1$8D@+BY(myxOUvW9xJE4zIjedu~ zArd7;fObdnUJ4BWWY6=J6ljcgKk^9Se~Us-0odXVr|suRTK)=IRH4EjW-cPxcOr5@~{Ks?&0kNN7s>NxK^5Bo9l2r*Wo&@!&Fh;ykcM3SjUlC zt|KM2#iiP~*uTsZE_fR1@(ZsLk=$)t2yUIK^tO@*&shIQZbKK@GR6ue}34 z$?*5Vqpce!gwFO0Pi((#Mx*hr`Ca_%^CCw;j)o!nHXKKjrJeyYQTqA;Q1|rC(P9^5k3;s>AFE8K4yfPLG-wQT?WiM3 z*=1`x+>ZJj^#~vnEY$@acEzBh|Jl?X$a7}tNOlikC41=bbX(x?vvT! zfIH0;_}CwHT6afTB^-Fhil+GkHtDn#Pp1ndv6MFuPNd*^aM>jd_ZoCZ%q8UJ zQ`Yg#FTP3WL^%;tl0VUnT1}V+=|WgqmU6g7h6_G$QYf=Ki{4V_QzBnR?e+CMO;Q$U zucg_mg+24jFqHve-^{T+TRxA>Ruf%POS6lARxZ%&6t?Llz0KhTM)kmLJV~ZT`(+5* zjXVuXSt4K(xK3b^z!HIF0yzTo-j!Dg6aZ>xzEo{=*q+emiMUMQ8iC6Mt`PVT0iLa~ zb+@N%ISU-#>b9<7a7_6lBmT%J9I%+oo$AQcBeRr0?#<|PzVNjDlA-sD6kaj>B5{kc zBTg}P#3_bfGCZSlF$U%5Tc_;ZVF%gUOJ~y+V{IFpjo>uLC`U(|@)ZK#Ch&a%uM_wI zfL`w;ME+I!2G8rp_8elTs}a5GJ4F6%YzWmidZh4Ey3USooFIQlMcgFtJp!*1c#Xgs zfo~D`4uQWR@FN0m5cn~HzXi|}-09I-aCdS}zD@YI2;lZYK;lb?0U0K>xD{|+a%w8z zN2aS4fDcSV1?JLq6~G4;0}@|TgvTKP+|j~~6#{RP6h9$wivZ0fGD;vupou`7z+Y49 zHwk=$z*hVl9%`zW6QKsqkd*oh|a#&|m%Sr^jV+TrPa3%3w*E5kh8cVEE7Z$Uc^k6 zDLcK4Z&gN|uS;4{Sh2A%hW)y&%E-RRR+dT1y}gR}WOM`RIU|osNw!d*LT|KUvrEMp z_%7zl_ToCk;_|v1*R|=Ja)X&^%8oU3)D78&`W@ARu0W)c9CSW6H0W8%ac9*|vhDJ5 ze*zZZqEwJ=Br`rDXP)8Pc=9P4MPFc~yV5S(UpR_Ro}4&+_V}3#yylj9BBUkwZLIdr z%gAMtsx#uJZj;Sfal6^zph|BUPw-*>7A1csl0UX*e}PFrUhZb=jeqQm?KQKf%K sfL1J3shf8*r2DwMq@|7?vSUsv?(aprFbTkktsW-SXfPUzPDCI6KU{_0pa1{> literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_5_3.cpython-39.pyc b/__pycache__/Zeus_5_3.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..27e5d7dcaa443788a824278246be20454c0f660b GIT binary patch literal 22463 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 YO7Vj?;V!IQl~=GA`on?nWccF$29@XKy#N3J literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_8_1.cpython-39.pyc b/__pycache__/Zeus_8_1.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d3dc695f1c66476debb513c97878d4b7ae4d740 GIT binary patch literal 21187 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 literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_8_3_1.cpython-39.pyc b/__pycache__/Zeus_8_3_1.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e28e0d508aeb663d419b195dc532d4ff2aa7d894 GIT binary patch literal 21922 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 literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_8_3_1_1.cpython-39.pyc b/__pycache__/Zeus_8_3_1_1.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc3e0ad3390519b6bef7b212d30cc3f6379c9e4f GIT binary patch literal 20830 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 literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_8_3_2.cpython-39.pyc b/__pycache__/Zeus_8_3_2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..af4bece084be131bcca91307ffedfcc6fc1296ac GIT binary patch literal 19465 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 literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_8_3_2_1.cpython-39.pyc b/__pycache__/Zeus_8_3_2_1.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..48fff65bd9da1d1ce6c2e6710d9c02bd9e8dba38 GIT binary patch literal 17081 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 literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_8_3_2_B_1.cpython-39.pyc b/__pycache__/Zeus_8_3_2_B_1.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dbe53ffda751111d071bf6ad3f726975e50ac771 GIT binary patch literal 20310 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 E4LFPLHUIzs literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_8_3_2_B_3.cpython-39.pyc b/__pycache__/Zeus_8_3_2_B_3.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..239f509a930c15247c72151c7794c18f863f5c25 GIT binary patch literal 19607 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 z%0A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 literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_8_3_2_B_4.cpython-39.pyc b/__pycache__/Zeus_8_3_2_B_4.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3fca938947fa12b504f681a78927a6ff1f7238ab GIT binary patch literal 19441 zcmd6Pdw3kzb>GbF>=TQ{`$-UdisDithzCJZ5=9UM_!2>o0w_zGSY9vo4uA#r0o`5j z0WU1ulGDGaD2bcaveJUFlSXNi=Ha@wn_6k&q;i@xi5sWMd~NF5{k|4)K1GgVe~B!M z{hc$9on3;mX-R#LrC6i?1olH!0x%bRuvPztUG# zMG=Znb4p${)VyLSDuuP2mJb*K#sfJ$A2foD>$y-qY=jvP<|6s15zWVpSiZq%$j6O1 z(?hw&e8NaD9?mu8n~i41Be|A*tI?WoGupf~NY8Q6T)WYZd}F!xe23BD&^nDy(4x66 zql=&2MmL@fxt@Hl(aSvX+%}Tu@b?*gh;Pho&-WYs4sD0Alhf|X?>6>qk!ElH9t~yA z?_+qcv5$EYx&8U1kyI74YvX`<-!0X+e?bvVKcgB4MYAy=T8u-BifH|`Y7B}t(Jnef zr|1&hqDS|(I4nl-d{`V2NAWx%j)@2G96PIs z2gO6rDdHis`PqPRQbcE!OXcE9u2?Fi=4COTDz2CXt21xP1v8Z`%oionWUrd3m2_6h2udNN z1i}E*Bx(}co*|_WXe^oK)sZGNf zu+-z0`mm)=SlYxmey85lPRzWiJ&fP+nKuK65C|MXC~)ZbjHNwx#nLA6yL810Odt@L zKqxSA>B=T4wy6P}AjLK@iWGS-VF3a`3hpDUkw6DPjw0ftKXvv)N_PCnJ!-c34^HdZ z&;8ok<#6AB$ z5%|!>CY$4D2Oe5_OvxU9^II+1Z@=21X4fYUT;^O~HNX34T*>~$nG50Ur?>sQ&3@nH z6Bjfk`>FW@my;c9(aB3^Pak@0cJj(vIeugpB*5Xav)?x2w;F3z}Vu1q?Zao$NCIeo>^ zB04!f>!Rl`U2$@c%wBQi#4etnerz^1dGX+=+<_`v3tk*QI6SzfPd))kJ5ge@S1wJQ zJ2-T3XmBk)b$As`Bm-7>(ZtA; zB^I4C=ksMNT3Re#%NB%r9fLTyBD00EJP4^t)d-1s>i#b)@-PT*o;q~y(#6w<7K?fF zP^q|D&K{bV=F=E%!W=%dS~6vd#`mF;Jpi+2>5yTrmQu%3BdOuksnp?tl^a%cg>y)W z`5dWF{aR7w5x_gjNd1n>xD!YP-woxK(oIkGmKWNltOK7_URI}}ejuMM0Hm*@@-!$< zYTIBGm=!J1n9pU`%oPMnl!2o5sq!H_Gpq_x@M*v=gdb=BMOwJjih5O%`zz`OmaJRq zI;y&&3H>-i8)}6pfl6Q4wZu& zQNnwajhG1DQp*juG^Zpnmif86Y3Im~dbtGe%%xW2ACxB#f`)0DOs6dMprsDV{b*I` z&7m)=R*crE6;o!5LM9QpNxecI07%BIU}+_nEnE6hk@{86EXV;wSm6b;oGzE86)dAS z&2j&+0vI@!p3N0AR$%txxD}eu=5mF!)plj(^z>wE;@s(p3#p0m>B*_nv&jZ4N@InG zmleh6!wOg~N~?u4GzHctm(*A=ula)df>|)FD8W<#gVTyI3f*WeDy`9%pFgDaut8xU>kMXc0B0;-~2vJtJB?5CI+mUAwIZm;$<{ zYHx>hP_?)kcf*0$_jS->pd}nyOz-7zTy6JpMRfT%^2*TYb3e2KdK2DXVs)d*t_OW_ zSziy85vpi3ZYmo3AB6%O3LFOoeJM~@xyObps?ag|rkE$pJYn#tB8V|lMmmgv4XuJc zOEO%X@^~RoSOFi6#Eo)bL%$pEEsc36!5aqet9RpDR+z63d>F47XPp=|p}TS4(hwWI zd+aHarWO7(q}ii(@}2padjs6|c$Dv)H*-sHH?XEZQKN^>wWAeg5Fd9&cYXcnTq{~( z06dM%^CxvOZ2c36Pas~CH8rsj0k_v0Xd63EweaWQZ({xj>)YhcG^~SK_yweC<}^!w z9q#1k7E%jq;BR66Px$$Phim$WTDT0(R_6TDT{)?oYGEHZ+nDn&>h<=^C5;Pjg0~&K z0Xe)yn|eCj;@24@Y^3O`+5 zxq)TDYRpKSJusQinYrS%YA`)lylPqjk-eIX$SD#N!O;Y#mZFpwDcIsBtgZqPX{K|j z!fGCeD-*MGdI5_-rifL7PFGf&$C)ZivmjEXe7b&SZ}Zqwv>tGNpGS73p8!zQxSmiG z8qLlL%;-Du+>75%%=gV|{I+Jpx3$}$P)zH;9m3qrQ0>I3(2E~E_u;n_s{v+u2d8jM z-L1v~EJg4A9@dIzT}Zd%7=BNS^?ygZtu@I7Xq6$={2w}Yoy4y+gLT?e3{@zG=5BpO z@Fm>{h>)R+un`myY+$2CSi}tMlZ>cn5b-SLG115{!LW&8Gs6~!tqj|Iu$}P^hMf$F zzw4z&$goUJ8mnaBY7{Gh$}Lvx*Tn8%3B7@HaIh;lrDJ zc(V_0@!_pLyv>KV`|u7Q-s!`;e0aCnBXq}r&@cAf3>v+6P_~(U;34Az>8cysIlNCq zopAphDRy|2d%;6ecQR$aNQwgvWf!E}C+-&q9Vxr(!h4+XfD^yhleycGaF4GX`+WGl zK779qPx|l!KKwo(e!mYt=)(tm_#q!Yc!yR)cTk48zN+;%!r^ND9lj&Qs7G<@?+8=; z^>?(czK+#}A8^8M{XOW(bnEXSa2$fcC@KcIU5`8AAt(GWQotw#gU~M2_$d17k(*)T z1gJ;RV~#O3>`=!X>H{^@lc2(AB#zfm$3fjG9!3nwbtRlKC&USJ(mYLHG4@hOoD}2F z>Bbpz!aOTZVdPARNrcad)8Y&aQs>P_%nRlt;_OQSaZa2^%#@Gvh`0dCMIU8KTm)s> zJnQCxT&9uBC2<+_OX5+iHZxYD_RzX8Hu`}to{xWHY~jr3-uV6>U;m@Ah0phY{9BKH z{!3$gj!5eA1m+_nFnZ*up${J$I&A1ij~pG{yp0bGI-_|$Ol^~ zEIu~}>((_>&ZP@>XmtgP5{Ck%v^2ty&Z)yBp5*}{!F2GJrK}MGK#){EY|hVTtl)w) z%|bGWwCU?9q&J#YGUZfekxG^tA{l-HE9obA%8sUxE~y!T8DtZ_Y|4yTpl~#w&K6QI z@s+JeX?2be7&}U+P3ROB=6ssYLpa5yGv>mF+JEWn|6KdT7$5Wgw;g(J3`P-JQOVv| zk{?DR$!7pI2MNR}_#}YCT^?I_>*a5M_2S$I$K*JX?a<}1%`?P4N8kwpmjU>wX~kA# z5%wEeq}gh>@Zu-w^OaW|bw1-FpMB*_GH5ICQA$l((7pi2IM^#JJy%Q%C-;xIYP>ME zfUc?C_m6&b%+c^OV@?KN7;`fC?AXFzK1x0ISH>hAs*)jeGZ;K^b|u4&70;|n>=?=^ zIxkj3WJ9$QZX60Rk8>~%nB`Sjs5zXG*^!RSv>hr*fQquDEWw0 z@r>aa#IvCiti+Zvcq)u_D_zQZyl?>22&keGB@EV{hDx+sX@^09LfdREy%JuJ!$cA% zgUPHSk0RS-809)QJ7op0rfK{HS1zAP4dF36#Serq-Y7IINoR}~>L~N_c?9H-5qN>X zj}xFBkri6LmX-@8BLJy}PSOmWgfTwM_^=TzPij?7YZ{;#63Gh84RAls$*5qM$jIKbq}|fq5Bm@r zA-oizGxLUiOF>gmh)%l{iirRk1JWrDkpeN?%RW3wp~^6y;Mn z?Nzajb!cTBz6u>0IR8FwneEgvj@}K_{$86qA+8nl4${rvek}W|qMv2AL3Th6c(Qj? zTm6TW4QXO0%Wh}cFG6;LHLdMY)pB!PA-7$e_im?LPOXAYBLQXL^6hc)lhpeH@IGePiv!4= z`bMX3Om+Fj6!7;jrtaf0rOOp@zhACzOu6#hF-zfhKc-yye<%*t$ghqmS02g#!E=Bu zFDQ440d9qEv_epx;hL-;Q=p4O%+}cLxavPtNe=5FqiVER@gzUh=YuH*r?Iuai*SL&1RN)V~7nbZ^}#9r7*VE zXxba)1K9fx4Lg~i99v7wrOL%(s+be0!GXac-p)kB2UCvj!-HN04deL1 zOrXWR>H(v?iMr-3GO30uNLgmp{7N~Ph~aAOS?KHp^y?W6f#N1lVy zsKlpNO%pr#MshzOMl-cq&X6wzE@s0eldN(jtCJ2bscOqQbtV5X$!M*ML{h8G-gP1g z_aUx4tJNcV&1-cziJhpsMtH4bCEOHtC&!hMt((`CU4{&fie|24HG0`{2TAyY=p8j- zbcCwvUah(nP;EgcGpFoTy1(phBT}FC%I<4sr|jrVe1566e|y11MTM zjwuOE55(2xa6;8pT??tP+nSoVt!ug(g8dg3V%!1hS~qB#mUvs!VsC4>^$Y1bEWmf6 zkqq#KkdAD>AsmB{6XX&-!Sp`0lj$+!l3+V@95!f8?|ds1qI<}@Qf(~)39?H=H|_hO zu-+EKT_ed3(G6w5E{ClT{AD3N$z};5g`pk19;j>nZF{Qj>uOnRN7@#jiy7EepuvsW^E-2;ANL zxc0P;MDWBI65P*T)+}vc(C+4k!ESdx_RNSK&t*&HOfkQ5;mH8u2Emn|plm7+a%TP-GF_0flJT?$4?{4CM+ZB&f{=Y;n zxB+skGHf8E!XVf%56}=qp_QKoT@J2-R-BLHAto3$J>xhVY z)P5he!K3c*Q9C{AP9JrbN8Qaj#+3QrS?OKKt1N8Qv2D|2Ygg&Kb!g>Qy;3!?YNIxmHRML2f&*Ub{(H!${` z`M8~eLM;6W=J8RX&68Wu9&@xYI1bXl$0Oe9(;ZxcKY|w6g&4#;bbD4pc$*^jvmsp- z@At_d&V)e!=Q1-N(m%x{BY-fexz;2DzJDN8fXMyE43E^!L@&A?}}i##>KixrL_mjlpOs|c}- zW#&jARRpOboC>R4xx1p4d*F`-=!AMXgcXP5ws8pS4Tt(T)aQh@a|o*r$MtiFSDP}d zuNyl^W{<)xm;;Z!dgic(xie|LhU(7K1*XzGO}s9($ohkV6&XDSu`A5C6WG(vJS@SZ zEbUs_ZODCNf!6t`xaw$HOTAV@ zwKa{3>rCBRf=hLIZ?G=ZFM>P=N5z_tLgkE#5BVsx7m11r=RkKOu72wt#g*)?ZM4I& z%Rpx{EZG}Amv_opUo&NsnrmEpbw$$*WchS)9NoXtHg+;~3 z+}iqkYEv3l@3LP~t@`>J?Wv-ox~GZ?+Ecyr*r?XhN4;8tuXyrVxce_=7Z%~ET)ehP z@3L%?pW7L{XcHz6|KhbF7a4|6UADBE&dEo>VYS&#W7*7NHeXy`P0gj@#};JY07>`X zn@t4T36PguvO#_m4@;ND%n-w2h9e9Q%m0B$`E>xp$#=v>;2cpXWfwSCBVp&d40DFa z&d;--1TM@y4}r~BNzNF7V_*uzmP z!-H(C?`T}~_5KQk2GwcdfLmYN)1Zs#QR>*$!#AtMTl0CR?P?6f} zMxU)Be1!^P$OzK|{muy42uyxNpvE;KjH22%$t}_fq{~wNAu!ih;1_{!hA9iY9(R2N zFk8SFr2;U;X*1U!$8JF<1oL@yB&O^;fRQV(<^<>I#RmB`h?RdrvV(ba>XGCo2vcj^M1OEc_#P|el{0S>C zGke|&U=Ydk#B30ALnbTv9R>1#4YIQoSWp8`QEbzkEd?(nQ5S-Zx_o1WUNeN@Gn;|; zEqVvu(-@!Omp9m>*Xo>Gy^*@Z^>*+s^r5@Zhphz3@f>nt|E>uS)rE)U2T9?;_>~#k z_R~r?3uNV~-YW3hhLV~y8MAH0WTBB3fDu5oT58r(3r5r{v=u04=ZrvkSy&A|OHrMT z2$uNb3cp}hRk!pNvKhUf%)WsT%&Q@Kp@Y6}#v;CuOQq*`LWwwROP$`gS#4H3H76YC zRQrJU+4d*+y1)wMePQ19f2(me2J>wnOt$oEhLM?kWp=8~x7!1|A(dV)iNj$T2IJeX zk!mm$2l~~0+HP>R!v)!f?SVd|rSI+_IV_WNb6nj`5T+=mFSG_$z267*lW>Y%-G?qs^{fJ%LU1x>cq9JN5Zsg%ZBbhC3SJ66x-#Ek08@4u_ z?E*Lr!{W9Mn_ER)qLT>T|AH5gj(uEUnfB`B@55odn_{1`XkTY@;LQ;i-y)9QjiCI!>)|>SiRE=JIMR9 z|7&Bs@Ncrgm6m8sy8H3KVnUj{z!rrL}^$0=i*=6D`~D zDk}HuW_UcPa(eCSNRtp473(dP81;HU^!R1v1^6$Dj!H{AdOO324@Mb}F>GKMC%6z< zZ!1(hIk2d(oIe%a%wtP?w{i}PFsHK9zROgVHn*(n?UnY&Q3pCKjW4J-rc`Aef6lPh zS#GX$&>(N zZ;f#j+?m~|zZpxm@Zv826&URsFj~#oML_-%VRXW=+R@)|hnu1u7A|s&lHS#plNA52 z3DA|U{1xKdhWlH`uh`uVr)ccZRk-amK>Nk{?1B|NeLZ9He=x`qq)c|&Q^6LUS>hil zFqaVUZk*-sQI@|Cz~`=%Fd7`?kXy-U%usctiu06t$-Z(XNAcCNS&}9xs;+pRFK~C5 zPiOF!Q%N2`CYY-{&3G1NKv!;7M_Mewdls_~T$EEQ#S*{e)JP-tLBuP8PEFTvxk&?t zzB)r(=(gkX7`p)tSIAgtk6;&ZL_3UEn?hLsBWkz0wzqEL*)pT);BJw|YrT02HK3&AIG&QlEPP;`Kbx@H@Gg9+&V83RZ&njc5$p%TB`aBc z6>s+`tJf`O%2q0mPMb0d>A9R~HIQq2%4t!Z?gOomEke>=ocuQgK1<-=5%_fi{~o}o z`dZj-BINI4bU6MHuI~st!HgJH?-BAX;?t2NE*M7c%? ze4fA;2t)~dk-)DJ_*DX*Bk*qljF{tDg6+N0P{oJ+ff1w47vpwz`I{uz@O_VrSAg4F2LUZzZC*X zZXa-^2odwTN>6LD6)r3|1-;F6kR0dQ zl^Uu6Ll9?5EGe|STT$=NFeGi(tqw0d0DDCVL)L^Xft$kGUX649E#t<(iu?aJCH@B* z-yhP@2sfg5_jBdO0R7zn?%NfsZwnFl2Le-KiZx}NlC<8*!mmE?Y0?U<;9Q(8Ih}#u z{s=pNZ-du@()dFGfg^HDRR%@Uazrm2Si7rquLLfVS~PPgveeLDP%Wzk%=tyUUDHAd zgIo^zBZMdT?-K^nbCfazD~^lV=72rbJg!2Uq8xDEtR3(U*Cp67Im$lu$NUj635a4| zwo*>`zl8D>>n}%1tB_q7E9$;@Te9EhjZB`NIDc_`YSz$soW*E7v`~shT3uc4LD^0b zt#?EWNo-maiNA@eOED}vDdHfM;eR`+njQE(SMFQBG3O0DD{|akd`{5BTKX^mT&4K0 z6}lPr@p;yMp9H-=NT*x+#Z|=PCEZ9T#-XJR;ZU!@R;@>{^~)G1S1oX GC;l(!*R^#3 literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_8_3_2_B_4_2.cpython-39.pyc b/__pycache__/Zeus_8_3_2_B_4_2.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c7d88024cb1fd71fc154f0dbb83ff7f6281d0dac GIT binary patch literal 29727 zcmeHweRN#Mb>Dk$-+nI^i^UQkNQhj1h$1Kn1i%kbG$~LLNl7*Z(IPEbYsu>cW&td) zAK<(PidZjX$&@T92@^VT;@Fl_!0~CrG;!lJC$-ZwYLlF_J*VwSa+*`;ozv5NH7XOw zaS}VWX+`Ys-g$5L?E(-TJIP-G4(840ojWskX70UnXHe+sifQ;+{`7CogbiK$V+#0x zOaxBg&x+}qCN!a!wThn8D_Ty|DQuLDN+1_tf1qqug1KNNlnYhDxo{0VTW*^hx4n`zfMaC`yE}6`-1uF&UB&*D-NoBi_1qqm zij?oD+>yHjsZkMoQOoTWago4xNOXxLzWW~1M7K!2sEJfDb~%u{Q<&$pp`Pp1OGAbe zwM&(vD3RKpWtl3t6p;oHe79t{cVREqy9Hu}Y%^&iI@%JG9tj%z2&DI;B9$|=pFt3=Gbs7!y zV{pPT=Zj00V=h=1%BN9zc(#b1lNN(67pE(>6SZb*&z7p9cnKXIT#%?+_5v#E4~c0) z`kE%UBI3r${SQC(=&Aj)wMucnRa>-6`=@2`0y;?)$M!E;MVZGK74}=I+e$@if3CP_ z<&WeKZ{Gr zR&5#nbJ{h1;+o+EDy1r%!li4vV*q_ZTsedAIngSOV7XK;-j1L}6=?dPE_dTQ#i&pW zenR-8$;;J050B874Vx=o4%Ck}0xL$tSVc#}7ieg!x;zM<2_*GZW7%w&PXZ-7D2#^o z8BGLU4_wwisz0e!51N`CTB_^Xa!}XmgLYV$jo@dr*9{u9C$*Pr zgIPcI{SSbB$*w2P6tY%vil{JqM}6duZ1s-frIKYAvkL_)D+*OBTP|j&OH;F@qRfh7 zcB)pLF3l`rHlWCG{m3aPi;FT_u1RDsX3GWSpRyNaF)J1~ESEi+9f~*!1#6)oK`&0? zL57S;M=dT22>Q&X@t!d{)l|)Wj4)5qizcnIfDEibyUbqPeh$FYli55&B(=3+h)|3z;^)Ou43}C zmg_EQxm2-7M8p=c^@@>8`~1B=f5zwEk>~h09*Ob`RjM?o%-OVw#i#_5FH|BOg*(-*` zJ{QWKj_@6B_)a%{Zxg16>5#7-`+WX8eg3C8szk4{`>c1mvO5EFw@%ry5$N2m2T^;>(tRsBf4SW4}UlXR+f8)q;m)I#{ z;%?BOnLFWzhu!e~D6t!~v0n^h3?ld)xf0GjfY?znCJumRwupmn?1P9sguI72*2{a+ zjlHKW_7q}|hXtJXCym?h*PE$1tCci~C+QbMJ188y6=K_nzVz z{631`GsTJGW5qKATJfy7|FwvCKs<=__agrBW{xvRJBhSY;xy9EaTzMhzv7|S!s21^ z2vW`$&xm&eo{E3ZYaww)Jc{`Di3#x-*rX?lPZr-_d{Uf!Eg;@29!JVk;+!~-l$>~< zcmm%K_;Nfc-j5tl`(mCFImA2z%-lMPkBSeVmZ!xti2oq)dQyB4?aqr2A)H63@R}|r z#S}sxT7#PqMZ_1HrKZIU!jl}H1(cGQLwL%CGB3&q2~iPM;8}EIYhnSh({AX3kO<8n zWT9SLEFv^3E{bPSq9iVXm05C9oy^2g;ySTH*J<09hsa0U`E}a4uSdwC{rh@?oFq9Z za?<2v;LMC4`rxlTlK7+XnbW`gH-Gm(Fa6H=%on%4^ylyW;;)TM0y|_n`iK)abod^} z96K_4&@t~he9xilH}P}cF%DI(W1ZKo?}g(8r)t;=>)~BT4o}^CV9W_VJ6p1gPH3rE zF4vw#yeJ&Hw{UnU=!C$XjE#&qK`CdDumv7Ra`;CFfmmt`pmMeG56e$k|WM zI63FxIHraE0E%HzoGvVuZ6|0g6sC$ZAI^OKp+BsDVq6m3H&y7xapIJP;@T})K#H6s zNF(H9+^E{pT^K4CfIU9**0sO--A5-sG|mljLubdYpQeIb`u*fPOCYF2oY;b_fw?BG zhI?b?)1RWB|2jStR8u- zV}1{LHf~Hu=AnqZ8~L4BO+wJh+e-^r2?~|kV$}wN3t7(TA}JQaWFFGwR1qBUBG~Dw zou_CgO8vi-w+l0}1~5~!ka<7d@O0ttJ@~Ue2FJ$WL-Y$@*84S*9~#=60cixn;GDT? zJZ&sTmt&3S9Hfu|Z6&m-Eyp2mApNqjoDk-6mmRr`GWn<-Ly7oGVii!9lXllivY|t2 z2{yXmh8hXD;YJv4q!EG}Z6qPF#98|30)fRQWCPep8Zswz|I}>3w$9g+*#a$pdApXM zTwKcD2xpnoK3)%IA3)gADN2;Ah1&6)oJ-^^ky9t|7>9!FkE@iG?nbZy;%~z0?uT%$Yt@}xUt**Bn3$7|u;`jM z9-vh-IpKt!z!(%QMb}|$$h5~YbKq$8_&MxmS?<-W{0RC*J`blJ&yp%BPZRz4jZl`q z^+*;Q&2-5=UQa(*s7@8jSzlo%#_K?yMqwz$ge*Tw@Gg_{A~`RSLla)UOwKFh{17=; z$a#UBSIPMSa()nw6K!JQ#If#8e#Ss=#6GNDLUOjkZ9r8P}(B4LNjAb&xrADVZ3b^5&Y3NdL!y^dR2+GrL9FMLkZvD^YH!) z)FW5OStaMQ;DoJam zukWM2zHe`X{29tg-40c3*`26h-d+84XV-Q&yH@G1Gt{j_nOa{&k6~|!=_!33o~xr+ zo31s0()34kXAQ%lE+Ia0StqUXi&&9Q5^DgJCp65>@I4yEaM!ef^H|h#1ddp^8324H zv5c7_v}xnx#syOHLq9tzxOLzxjo}eBzFU!9HF*#|Cw7ds^r>29;l5=w^luQS)eoD`Otpr5ZDv?AI@Xa|ak_3%TgRT$`J#DdRH@3Ge zHsi)_YK!$C^l@y+h3n<*Z;RdRLKtAI`nd(0+!o-=E!fgl!h^n*OKjy5eQpVSxx}C^ z4Q=B7z?ai*^VQUuwvB7T7J<_-)eGC3FA%p8?%cc0v5_)Y`8XjxwhTyX)6(d zlQ}Pk3UR(YYw}@ieEAUKe0QwLmu}045a-*wCf`t7K7=^mzBT#oY|Dob=ex_7Z=j<` z@8+}}ps0u#W{TR+F}r**BOJ5W7c8OC9jRQ85R7a8%48?)|7sQ-{SpOshu_ ztIx$SOX4{G(V^;u((y`QIf%{XT|6$w04XSrv;RK!kF);-*PzDknA@%sssx3&?e}|m zNI*l)4=@g0E)E`DKiC#~(xr`4T=o>^)oGr64>iLNbNG=a?4)@2s=m^VH9yn{t)#?G z%ytNUZk_M(js-T>v zD#bY;N1fVS?_5T$P^9p@0+AMLMNb03TgiTuhzbvOlwn5&!EWU=m7jA0w!H~# zSrj$&0Wy@RSAy7}{PjJl>QkjwlH&bs{R4Vv_D@ti1zfo%m9L|JD6iK)QIVs($afgA zu||wqveJ)!kGuZO@I#IQybmB}0^g_k8^?T%GxpCQ97hUb#JyWJNK8@jL4qot;uh!F zh2NK+=kyP{ln@mkYU_a~y&ez+jKC!F@@fos%I6pCpW+b{1E`m3N1h_Z&kcxaK&Nq; zVcMEKrZKdV55gS9K`ul;UyaBk+}~T~wu&%#-o*hoMy-=G2E{$ry4z+mY&;{(ZK2Sp!P3Qf2`a36xzgTVi>sZZlur) zm96N1?g_LS&~$;Gu(yk9qlXwvs*786``jLJ#-;HZD7?3!`lgrq6Y7}lVuA8{EvFh* z?r1=bqk4SRAc>jq;<_$yZ!0XJzeNgkD*|j7e0DRu$l;64@UtAg)C@0ixZVtZn8OW( zlXH6kX_-^r1IX0gp+;9*d!OS}qU(LUVts_eA3>hNJmK*?`r*!%yU54A*>tgjr&sm) zz{=gL$T2s(idKpjxbzEv=9b{qnqyb?ix;pOjfjtm%i=}!U8a!{F9Bvoe2iC9{%*xs z0RN15nIO%Ll5Yk)k9a}6B3{KhP1N%;;E#y{!{&S(b3pt6X8l2K#SfyiwsMGieiha{ z4INDE4^1exbr~F0E>vLEm;4D1Tx8{eW3p*UXtu!L;1q05iZH_{uqPLtEx3AxnaBC~ z;cMeggp=|kW2|F`l6nfdq}f7srg;6o05TZo7^w?svV5J!zJ1C3(@WCTax0{u<$v{?V z>LXlZ-Wr2oMD@X!OEiHZ#43NN1A$}SI>fm~kxTg~{~ zoeW@V&qBd%3n&ixEJi*`n8BVzI9)iO!57**KA;Km>5QD9^ca#{JaUP`#iA_0PzW0E zqSNgU{IqqaSlky|?9(j|_Mlzq@AoCjcN$pkPb+Z>eW~ z8qRru2~NYHqAWj;V5veah{5D{lDSnFIXu z=i|8$h%p>IPERg4QDB-c7cUmePJ$cmt^+wj7W7($0SPo&`I3O}`73A< zMvHpM3z^h^Xj>e57}@MQ5QW=!Sj9Ant%dY?l?BkkqJZhl6b>q28b$>Tn5P9`9(J8H zcDYnrru^ZNp~Fh=9=>2NER{=>PHeVVnlDug)@(`s2gb+7anPg_c=VA8IQPHb37mWM zeyka@rD+>ym**aFx+iO8Ix)go2~92w$0JJ%WWDF&XcL&oqmP_C``9BB=N;3A{vHjg zp*^f?7SI^gr#MhS{$AAk00sq$=f~(w3O1ATLz|R7iWPnT z4%Mn{EJ71J+8mKwFZDB;sCFc7n!EJyMx*17i^lkb+Jrl?PDr!ePltNAue~3f*zCdEV544dwSaN}H7{VJvskmsI?npG zHQKPYIoo!oOiFvwfidED!2>=HmZZxDOe_Np1F}LNX|_m}KX1rX!(0yN+J(IrQVldl zZbJdAsDuR15*P=x4pEp|lRT^GJeSuSrt(g*jQ~AfPA*bIl7OjU6xE2E7}5H`dJWA;{v2sTF5=h(jzK%0X$skQ@<-Ar|8jBwBn zqEyIViq6k?Xrb@pRA{9XlvO^7bJM~|gT1kP{LC`rG7KA$Hv*nI#@u}zoOU9k%@dbR zazb)mKS5GJH&R3M8v7wHA*N?QbHizH<5AJe+&KL6#t983kDF$UX#-MTp9>%QI^^06 zjmyA1=?_(XTfF)hat96^2d$l3Zg4)xF}HF3+nV)LsqYQ9?Je9yc0;Uyf!N{Jy&i`D zaQN)>;j@eJ*^Rnycl&t{{CBjC!QN&cs4*F8hWAlk^fG_vNmHXRzY{qyq7U#WA@(lh zyW4HsF!%I+9!K~)#&N{OZ3BqS8axV8w6X>!@*xLCbd>9%++(1(-~-6-qnZP5ZPfmQ zEQcO~@9;b@2ZVdzKjQioKA^cK2B1Mj!yMPyGpZke7w4f;REGz-Sqqm{l&yM%DSPAG zKHTrUWl!A#C$4)<&$ac>QMV47qwnI`s@l~)pZv2{wWV#AzGIKM^V`?1V{V z0o%X!P`G1V^g(;@7SK+1K%?-+&`!0WVb&;GMBnq6`}a|zb>JWN^|FcFWqx|Ij9+ts`8ZvUyL+QWC>UC*ea z=`0^QXO(D|t*6~sGOvIc2DtMqq&t{IES9q#`|ZkdS^0OU3S0d_e4IE5U$8Zrf(XOH z6>C%8{Jl%p_l!oqHZFe`nclqd+}y9)KlaKS@=FNGFOu_zpU19z zvihUruIHn$Cgj$*^(iAgsCn|9$oZ*p`9A>BGo5^c#Wt42<)2V>e?Shi?j%gJn#aw1 z;tly9DN3nOR4rZ9s-^%hLtvR)lgF<$G3ll}Bc9R(vaj3RH{@>voNFri6K}}hp^!@Z zsW-f4a0N}hj~ClZAUpI%ZoQvq7Ug%@o9sK?pyQsPS!+M4+3v8S}OgNdBE+X5KP_p&Q0cqu2Ob(@5!Qq@|IT zLRu1OvF{k6$;TS)lhwq$@Kx3S0gmLGSDPXnhU;D)}9a;f(IiMefa%K}H$?j+jnrDa^47~hm} zgZh{1_40(i1Grrq#HS3vRM=cFfzvi~tQi{%G;#YjU(Cvna&W z)LA%K2lhVmROcB*F0Cn&Ctz-GqM5t8&Mn9iG{mK@Njg@|ef~(|w7S0q2 zL*7pYo^Je+y4z|1cgUh73PEfksgw;Am35X$1`jJi2tzPB^qDKdK8uVHMPOc&56}%f zDvP5y*veki<$1o77o>xpm9Plm=qZW(IE;c>V|Sx#J}578j!+|Z5tctV%nM;-3(OnK zsVeS#;XsLx`j&g_2m~ast2n@eRTyxKHNX;NG*}*`ZCK%o7}X0q+jt`dH_?Eh#Ai&A zY;+^k-RR*^s*$Ge>j|qzzwYPwYUNz}jF2uPWW&%#yRPa>7j>|~>t#6S18V()Xk+-{^k*W0|U*gL_R+0x(O zz67s&LxFnaf%6aUf8V*2=j+jvk3Dosc_XLJKg>RLnSi#-)5_%V*Wj&p-OomFYUd`~ zVJuO{<)@HzBQSh$x*nnk>$rT5GKbaPf1LNPdjF~EX)^A=Sj@HofqCyokz3hUvR3a! z3X&R78Ch-x@krc$*3{t&ufgVYX|mR`=aP3)Zd#{@GHS(rM?&qes^g{DN&2jWp#x^! z0xorfC!i+DQ6S)Ssm-)DtrX%>7*vW9-&dgR*h%;e?7(F-??!gaM%QXjsakNq*kTyt zG#}{@M+@|^){Hbyq+5di35yizo!HpeFwKZQ7%=tWn`U5-ZUjwzFMJ`=TR|^X-?K(@ z;ci$;E#g&&y_S2M_NKPACXR>{{&=nV1$bI24VuApdy>@{hA?p;8boRk-w=$41C1aK z#Y5!MVe2yHWqTYh2g7E)5yUqN`##LWt(qNx9lO~G(~QHMi$MVrZ3Hm4U@I@;m}v=^ zYS6c<5#n#MfjgQAcf&lGzBH5ZeO(r(Zz&NqJ$K}Nz0ddGc8i72Gt!_6daLjIuqmsE8-iAtJf3q+8lScDokm<%YzXGV`i zH{ZOQ_|idh0AEsTFqPr< z15|^2fNy|$h}uFpXgA4b6nalsii!wm0hGhv7-$3G*F&^2Ho~BZ_=FR6iC|RVWW+s! z0|C5+>X}~NgaM=AP~sKkl20CbMSF=YA#jRVa{i~}zNq@qq3+4XD?-TrR@oPtiW`9o9N z#2-<)bpnpNDI2~`IgBY9#y0G2%27uMy6qIg}!n#+2se-#v=a=z}#8% zE!BVE7T$4PAM(@rddu|>1Ek_;u9A=VWt{2S5~QzeZ9Do3N4&f_HPctA--yaIFt|x5 z;@7dm?I_Z=5RzV(G{a1!&%@JP2w8s%;>1!2B86R9BY=euy#fm?wJZ`*25ndXA{<8F zMcm~rVjE1PF%WAUL`vTT7DD=Vfr#mw#6m~kZY*^8zMfzrCZp%ng#2sZAt#7*9)puh z`VMw!HLFna6gebM$+O5)?>prllDhXE++~bOxQ?|hm4$H8W1 z3F;1Vn9Til)HKNzQ8x*m$s4bus2b$ADM!ablSNfOPXk2^BM3Z9B%T1FZ#E2lj24;g zYxXlwQJ~K0+>Oi$$l9}O#*ZxAdHgb8r_tg`W$%EEs&mg3-ehl#VYJ zHl^zavx>#Z)ztGA3i!mLVk4Z_ykOY78Rw1Y(sfmO+|bw{A+w zA%&UG4Y3Fy*Yn7gaog|p1FZel>4$)T%`V!~@3lp2o)2C%9pl97st9>eFfukRpGT@1nf2O1(0>~_vTpfSd#IoO3@JN&kV9y9KoZ^UbYolXqm(U3xz+duc>FuZ zZ`Zox*GXwQ>i8VSZZ}44^StSXTv~QREsV6QrnMfm2IQ{;&)eM=C|Csf%ZT0M#FPX)CGX#~5$nlnz zQr<(0DrqR?5%PVBeCuf^X{D<7@$r84K3bTeq{{Bgjt-BEjMX=HsML_-?vde<(Xo23 zGH1))cf3`7eKVcrQdFl#tlkg6hen3k>SOPLq3n+1RMnx8dY{*zHdSc0r3;lu3H^=q zp!GrUl%&lhC8`Vl;86Bhmasfj&wA}@^5QOL%@%G!dDp(Jvv_^8Yoo`F^xJ>c(=J?p z<2SWhqV^tAF=lyBtZ()3z<%)o)yK~?QP}K8QV*cWGudOuUG)1YMRhb{eI31%5~#DE zA>1g?+2hX)MSZ-`ix^}lSgb7AOD(xo=@(grNRlrKu;P#n|H%rw&O^<7_eGqUAdW^4=c zlAQ(pk)O)?U?8;FNCQR;Fk*m_e9H_`UVmNdD9_fmt&I<$J2qq+gZ4ss1{XU!OeL^K z;7--6P)V(TpJSmmaMh^)EZww$_!)d1+yPxZ!~IpAcsy27h3I6Sgb(8C;9`$n4d9|7 zxMn;2vObNQHh5VL*5&lF+DqV?>82KxQc*Xxi&G)EwxvSUmA}-8;SaYauwEwZC~Vqt0tAW@LQsH zIe`o38DO9hehnXfh~KncJUH4er1fzcu)IMwX;0+6P{`OWQB4r{B>u=S=+c6?(F_3BE?8avi z-|LUJ6Jm#(b0>KCUG&r#cASLR?dJ2v0tVEV7%LuQgmBRxfGVNa`7vaX@pFl!HQ==*)Udg@w}aq_Slj#;rgK@4t9pKSLO|mMSn^J6@0PQ7;V~8o`MR>9-uSV9mp#Zn7k- zDJ);jAJPzzxY~<9YXlD8RNT9)+oT`R20-5I!_>X34ea{hx-@Yyx^ueu!e*8&LEbkW2FbiF*k!AfAOek-f@$p4a?tVqbL5f*5v45DYsi!*K z*9kPN5W`8M<#ZE!hr{(*uqm^(vbs|&o73!yhxPY}6ds2JQu=p+6pc(1DZV4HAyOar zkg9jnG$?rUVDxq`LRk*yM{i%;hAxBRVX`nu=K4?yDJ*0F@p2- z0VsGP2}Pkn4A#tQ2!GTECPZeMDySj!~V=MK}wHJ{RE(>7+nyei_pg%9^6XcctL#7EH>USqLjva*&`kaC(3ogs&5`fm8nf_$tp614!m zq+o)B#(7O(OL&{H4Qlf(P(G5)aQbE_LPz46n<3@m7#tk$%@F0m2~LDm?BKt_0h`Jt z`(u7az}UXV%%Krf9YCen=Ne;Ro?&U4lav!~B2%u9`PYAG;bv ziu<%cM+#1FpolW%`=Q*#Td9o@vnKd5Lr3p5gsX#`#^*p8T(oVa#i6ZdZG32A&Gi5K zE{hv&B|qDo%ZJwN<4sC-34Fax{QE|1qaER2dx)te+BMXnNy&2%FNv_o*PIA)iIV8y zfm{R{Z`#f+h&GB>C zM0yer3dI^pp$Y9JLv&Bmfkg`Elz8N*J+{$3Pv?~|E9XNEV*6nOFc)v;N>f?BJncbK zF?jX}0z)q@D5e`}h+h}!YG|Vef<&ghY@-)#X)D{vka6DfCg8G3;kJ^5y+dE4uaQB| z^^;fz@n91K2E_2UztJZ)BRtUPr|;{qgun$=9Y*jS?V(HJXe<5dFVS(t<_0|)-bJ%{ zc>s{NP)P_EFrOD&8v`PZ-1sAb%8eaFENmV+VsAq%Ot3p*x7&#pj8?f;-pc`oJb)hB zv%CdkMQ@j78(Rntd^_MnT@X=%+#=(`7^~rSx(7*gZ|coy{`urbl7%Bl zJc6FNV~+TGj^Y={p`Jh@?o<{RZp6>Z(k1tCL9i=_r*D|_#NdsdquD2T=gsmSnT0-U z76s)QIE%C&p1~W1wyfdy-9mZM%HrA+&D`Y-@Rs3kT%%VU`8h*e(KF-${Egvn zR0$z-fQ7f_2!o^9Z*&3ZUD@|8;%z|UrjBN>^|aU<`3&+p#)6P^4kuynqDe@)>hOte z?VMmi%EA(^<*F;KP=n!pKS|efWtPh9Acr}hFg*=NoXP?q3~lu(*m%ih1sm`1%*L|C z04~Atqjhn`KB@roBz%O6`&BLe0`EgoKuqVlaiI#2-$NhgMs&INw%lNhR`UatXfcCh z<+%;HOsf}LoN6xKinZj+Kpl5-$!-?+txyRr-C2BRP}1!|Jf#SzPPd{Gdel($1nu=X z!WB<>^A)mOkHWzRI~QvOI(HQ!o?oP0z53p^BKaJZe@7k2H@wBZ{tk?*D{cLw>_QuH)PL(CCO<-rpp8iFJ@N(evR(aU z^05x)CGs_Q9QiQ{zCzBcgtbSj;KVwwSvS-eT?s-NhUs zEav38i+PWWy>BJQ3(zbot7TliMo54)(|GBYuaobm$Wf~`!*W;aq*|@L@_1Om6}nlA z)>5TeDE?Jirysc0Ivq|J8?4iGITydfv_`*!%y7=dU?*y7TcuGwD{)zHAlhiasB2Dx z5$<7#g?fWbX(4Wc0p$OT0`U?Cuw)kj<3gBtt?G2A&=n!q$=g6GGmOEBdP?as@E2@i;ek!7(`wOFmeOa|_4 zkQj%3kWM3Pxk3jZxOpJAHDc5B5MknbmGW=BSRO+U zDsBDf#&Ru`UjL*E<#7+G?|Y9Z&^pjDxxWBX8UH?OxV2XKgja)F>`BIeP2=yQ?i@W* z0;OCYue#tRk`Ugrw~97z2e{|*ql7VY>35RPMgjc8B0PstE$}nBPM}^|@ZLNnrG{)N zIzCImpC;#1xYPg? zW&v=gP|x+zU$B1LJ5tTW(3}&==c{;yJ)d`C`8?jDSi~dMc_)$2Usx=ZRf^n!+W3tH z`fpZwh(+?}sNKEz?@RfP7e4u!3#Xf`ZhhD6`AHw|)$1*uX?9*&CE^Kq8}Clj^Vm+D z{&fHy-s3<2$or{pwvqEPs#8xiqzE!pLU`9$y}2pT3!0O5qbBp!n)_S{E`z9SlW0n5 zY98w!p6TvtM$)4_4!y}speCu$M@QPjco3x%#tihI1X^(^*6KP zk)EY|E1{>EP(wErnq6nGX4e}mo)N9V;-GJ^IP4oN4@TQy`6GRUHR~G_Z?IU8h57F#8L)40EA0hx-e~6k1okKL6Zw(Rqy&>X&H$*C>iTPI4;{J~c zFOKels5wN3M_wXbP{D&OL{;i>Zv6KQdeprruMtfK7tWr}kK%Lg41W;9e=4ES7%!!G zK!^Y7gtU-bUL@xQa!4T}1LW(5gEMse$36LD6ehk){uDX?ikzP(=aL>Im{6Vs7M`XgE`5{g9ey)PZ0n>t7xP3=yq9EsE;sa*1@NHlp2zbQ#Q zGIW<3I%v~1XH)aP^$mkENqttt!5R-^F5OCV|9#&QFXxgDZh7fb4_edmCdtHEy`)5( zr6h%#6I#GpSFjW*1*?mdg(Y=0m5CulZ#v<9cp@R0BBzH6h3UU8Q2gx_ih`17tMv4f zOjFpve|=2y`%%OAXNtH>gomjjo=-+G&d#nD+IVhH)a0=H64h|)Z6K&HIZ48D8sb+g zK~^Xt3Wod)|MLg=a|o*|UcH3kZRG4GXN(-`&-F;pi5yd#^?h{OW$l53%M$np4WzJ2 Z(0f1h#SJQY^9S$z7*I`_kzi!g{|6Tv8t?!B literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_8_3_3.cpython-39.pyc b/__pycache__/Zeus_8_3_3.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e79b31d1d498ee9e38bed65f28db616e07b17a2 GIT binary patch literal 20922 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 F_#euf9994T literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_8_3_3_3.cpython-39.pyc b/__pycache__/Zeus_8_3_3_3.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..463e53d99376fada1a5109e85248379d40a7f62c GIT binary patch literal 11358 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 R6tEOO;Ke=4Pt`Koe*>GZ_dWms literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_8_4h.cpython-39.pyc b/__pycache__/Zeus_8_4h.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..57445fc6f876317742d1dfc4a91c19930091caaf GIT binary patch literal 6448 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 literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_8d.cpython-39.pyc b/__pycache__/Zeus_8d.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6d1f383f7806d5d4ee09acc0b571422f557d04d5 GIT binary patch literal 7163 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 IUZY_AFW5C{wg3PC literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_8d_1.cpython-39.pyc b/__pycache__/Zeus_8d_1.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5241dc762009c2e37bb351f5a71e774d039396a3 GIT binary patch literal 6369 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 literal 0 HcmV?d00001 diff --git a/__pycache__/Zeus_AI.cpython-39.pyc b/__pycache__/Zeus_AI.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..601972c1556871046f4584118022d19d83ec5046 GIT binary patch literal 20953 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 literal 0 HcmV?d00001 diff --git a/__pycache__/custom_indicators.cpython-39.pyc b/__pycache__/custom_indicators.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..473e402cff41110d819e6f38ad79f7375f1c2f31 GIT binary patch literal 6681 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 H@#ud6@Xba5 literal 0 HcmV?d00001 diff --git a/__pycache__/custom_stoploss_with_psar.cpython-39.pyc b/__pycache__/custom_stoploss_with_psar.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a5073b8d918898a51662fcdb1b155675459572ff GIT binary patch literal 2869 zcmc&$&yO567Pej0)z#fKJ(CH90J~tZXhpLyBe)Sot3ZGSAqGSuA(fCSp0c~ACiN?} zE0Y=ZoR&TAX-|tdBuD-V&inyfeC3q$-d3ROd+wR;VHgCrb+=u%pZ#3#z3+W4v9>lO zP?Gm|rvDlf@&gXemJkLvpqdBJNFr%TQtF>94dBU2R)uLuQ5KZFDoP{Yr)9s2)4@r9 zSdAE=q)L3dmL`5aEXUP29s7Q-T(36L4NCq-WF-4v64_VL--Gm$q_@ZSgsi9Hckd#RZhOji0LE0-w z8p$y2t628bKn?LHqp#tGWc+2Aj#Q%7R3Zmo2XZJ!Ff%?glgKrgSwAx~mg_LHAveH; zOYX|e&e*29Wt+BajQPB5v-yuwy!i*F{mkfOD`u-rBrj?uh1sNwWQ9EK-`D8AIS+Z|?B zt6IJ?;nS|pZPC*LehbPRkl}5Y`_-^+=HPxIZWRMDNcjKJR0X z>3vC#V;EDtgY7=^QozbT*%$11a6Ft3_5n4|l83!VS^PcY~7WavYDtw zd3dB`24EL{lU_$9VNo`@>zjL>Qb%gC=0c;&sXssuV1dRg*CT8aTp!&%5PD|NKc*{f zI?@UPU))!jDcxYH+|kU90D?BlA@rq6lUf}taq|t)e*|U!{doI>Pd*Bq`R_Ug- zE=0=mL7K#lS>XamY<(FvxW4Y{ssXh3?u)WhF3cNn&A#Lv@-hoo8T}Rr3?h%vG5mP%=+$3w;02q1;Vw*2@2;1iBf(MoV7LUQEZT?k z5xEOD!<3ykBs}_$&vWFvPfL-jy{42(^HUN0tMkFqJ4vSOU~l^92t1 zu9`MlEj7L4xe$2e_S<}A>1#_8GM@K9GdINyQi#KDKQ#l$*L;chrNV2#ccNMeuJLzI z@150}1-G0Lal|ni!1Jc;s(QIp!_`VW5?lacfJ1=!+B18_)aqAJcrJ3AOXtJ-1%G-s zqare5KZb0Rv7guz79CA~D||~0zlh!v?3iAQ=t(BoKiJPA_Wjb1iz6l*Wq`-w`l@Hk zrN1vEz*6P^D<(+7JDw=Z3DvK|=)y>>atL2`5sJ@Xu|emNc$5Bi{;W3cz;{5LWi_yU zmbqb;L5_DN_LD5T*NO5(($6y4rKl>SdFx) z8BT<7nOprA9B*Uu1~x0a`5w}R##<73#j|Zp!+uEpk3}?kl03IL++6o-CY!ETcNGv4 zt6LYfa5o6!SSuIj{b5P}Tc_UzTSW~_H-&uHNV0BhMOo}_iCtWf11FyL^vfvz(@F#6 zr)>1rJ=-4Qj#YNMXd!a_6PX*XgbPnRy;1{=YYJoGOGEjO3U9|nPH^${MNIyR&NLg) R#5AS}OTvU+r3p=f{{dOWEOY<> literal 0 HcmV?d00001 diff --git a/__pycache__/fixed_riskreward_loss.cpython-39.pyc b/__pycache__/fixed_riskreward_loss.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c734fda89b3fdb8ac87c5194fe57af8103fe7430 GIT binary patch literal 3588 zcmcgu%WoV>8SnS>jC;oKJP3jofn7!$Y!o4oq7@{t1Qr(WD)EKBw8~vI$adTFbv4t&8>Z4Y7G)Mq58KxW_x68@waD&+KTM zn@^3Q{~GofdqY!oKVUU`ELkpS@!`{2vRaH!u<$u%b*c&OzGPe^By@Rnuy}~Cng9rW zw8-Knj%IP2J1?!s;cnz|97}XyfG@iA$NiTc@ABT~PSg`CA`mNl<)zI7-Up_C2{YiU zzzi;7*7!Owt9%28*i@U3(@zC|l2*r0#0w_*M`cy9KbwZp3o-T&(<-dm1BKH{RADl$ z>N1Z3CK-6YJq+tI6ve2Ni3nLZB6XpJzFnymhVVvOYHbH6Aj_%_6P6{@4ESOU5q?sz zJWay1Nb8hk*l(sfe4euK(f*V0J7LBkRfsV}o{MlI*l|y5d^Dm`}4hon$9rT8A&v`VdHMX*{zuURwHbfy2~vc>GJjl0(7oEe#{5h?))# zO{BRJ8SGBS1ZvRej|^(ZHhwAVe~GX9Cjd7c zNQ%3FU2s@Oy_?Xx;Pe|`b8dk13+ynrr`vRa?;u6Hr$5q{M(If7O8y}!XIXT8%8|tD zFERF%I~t`~hN|S{qCPCSq|&4M^K7H&7t#k#b^(SfYNg#OMRF^9ENbPZ1s9(xZ(5|! zrb2aqi?cFO{c?i%pkfzK>qI#zA?-{Qs;epb>U{)YOiR_JS%R{!fVDaLZ`lF2c}WHeZsmb2de-oXwa4$ zh{f!nih9d+)YtjI)2e1D1kq|+9~Ovh+OCw26dOUAZFE_M7TZIaZl z4EYTl`yU_f{`})#{dD)R%*Ae1PV01cB*inGU-x%Whb4KN?Y5;k71gdTyfGQxT1#bN zXL6!euSCqd&|mEU7+zoortjP^dv;(tmTw16&m7o2YhVwoHOn!5^J|0$mt&pYy{1#_ zT+uzWRhJAKQ8!dR0*%p9u41>urajw1bIS3b;H!QCP`6p#nB08d0JLrsG;gD;ZyC2} z?r6NroiS+V)`f9ybN9kTwj%E-qlu1;>Fg<+)hI_g#9mv_F7dK3ltU4!Lv83Pt7ghZ zcTrAK!b@GMcRIY~7I+gdfb`OG}AkS0OLivjV}7mcm#IOf&U{~hdFJ~reE;^eSW4{;*OVW} zcpi#4R=qgROFqqL9>nppDa%?)CysfU#Ia6YwTb!|Unxw=pZ7Usqi9OHMRdfhM4|X5 z9fYa`3PhUJ^1HPD9)a%@SSG%nQT9uEu27k%ZUOkF6IiD8FVAsMCq2{qGPu9d+gQl0{w`hBN5Svejf yP*oQV|0B$Wls}-oJtAO1?zqlR%71@$V?QK+SL87tO(n1bCqRKj`$2Q*S^oy*k`o{R literal 0 HcmV?d00001 diff --git a/__pycache__/hlhb.cpython-39.pyc b/__pycache__/hlhb.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d2d94456a43956ddcaa346dd92ab40515b79e996 GIT binary patch literal 2727 zcmcgu&5s;M74NR@neF-5UGJ>po%Lti34{T5X0wTv5UsLyk~MaOClEU*YDFq~x@u<1 z?k`vM?0PJ2ID$yzfDktyY;HNhiR8u~fP|DNT5P&U|(=Mpcd%y2cS5zDrcy0P;?-L`&cF_)EYTCBv&zi@&DW?!;epbz8Qs zyc!naITs)5I9L50I z#hwJukq10av2TIrDEl^W^0an`@vwK}PgNhT%R}y;|IzuE{qLMQKZ>sUakj&iKjhNy zK~bGN)$%WmRlyTqi6oE5ekc;<7nvW0MKtu)Fq6ev!DZt2Gs&;}h2$wyZPWKf@c1Hy ziX<#VmIC&NMUkr)JDuI#-F7eRjdPK!c9bO@SP&-CLC18nhBI7KX5ck`J&!|?a%MW6 z5Lg)PUX~+1`0^(o9)IT_pKlE|yC41L?R$UO8r**OkDpC`_xmkrLsZMv_Iltvzy56C zZoKf^4+8h}>5cWlf^tETKnymCeIDeO^)LT z2UQWuVwBS;Oj*onl#S9tmtv8KLYI?JUgZT0;Pggg?J6F}+EYcA$C*;tc8X}0Qhq%e zhUtK7XHY~fBIluZ38`VVh<14rp4KI>&#Y@lDxphJ7H1MLXW9wbbqI3axO71~XJ35_ za3ujID2!6TaHAdq72&sjbs@&*6?haT+rWY~x4Z9yG4t%pqu@2>?hBpsuV38m46}rH zR5mI^r!V<6TnWC>87VF)gRJSOX|f2eI=C+Fe5{xDmUV~IJQPyBi38%=SoV!WChU=r zmZK}!VIR{_dkHT>Eb6l4#Ym=+gRN&5V$4PJ9X$X)x0$_)maJMUpbbY5yPH;FU9kaS zR+is$0L>X60pzjDd-$&Ic}g=+4L!FZ`VO|OZ$e+d(tH=_hXv%?&2pYf9A7)bcw>T^ z2JBZ0k?@>LkuhD$rHHsZ2sIN7Y}xW@c*zEuWi%`3bv1!~)C8!V?TcsSDxmirc?<)5 zG$+yQEr~RHZ#mEMQ49h_Mal$ZS|-(75U1*3EW24g=rrtxvp{Y%rwj6E_#8gX!>A@f=A?sL=Qakl$dZuYkfYSEOO9ccT_Llmtz+7wG=)^9R97iY zGByGoq`FS&wNV()YUU_qSp@E-3*F1xZ=5^($~OJ!Hb~a%UA>fNN}%#n5Yy-?$h)p3 zA{9v(Q<(|5Xg&_qMZ{1CL0PU3JyfLA9(K4s_IN+d`hCRI|VN-<@f^(u*K^)Nr80APr-Ez!V_9FA5W`n@|Dg8qS}JKE>TM zjg=@8+7WO{EK(Y#X;zq1PXRZzOfpc@Vw`iOmuCf-59Bw6125vbhEfiPOxhO%c>;Qp z({m2ezmRKKK8@yiH2Y~`5K4wbsjtDKehj8+yCs*nC1RWVKjn(+xVE_+KvV40-Tit% z_sSkxch0*{T=zctw|lp|lKx5FN~BRIFdLh`#<{p5bT9r$&~WP7Fv?S1V7`7l+|WJ7ho!c6ROd}wcT Tx?f+yIT+paY|pNnyXpK7`1a}0 literal 0 HcmV?d00001 diff --git a/__pycache__/jeroen_test.cpython-39.pyc b/__pycache__/jeroen_test.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1c6825f4dcddb257ac7d6330588d7d7b564648d8 GIT binary patch literal 3411 zcmZ`+&5s;M74Prq?)ljD?Ch6ql7wttfM80u=AX!Aq?^Vz4jyFz^>h=4mdR^~T zyx|*p_y5srDQX;GbeNu zZ>5dQ4PC|Csh9bouXrbIX3emfwZc}`4%wM!AJ>0yfF_(ECXw2jKV>7(W8V|I-{|s~0`nvRf_MjAT$wxneL=R0?|14%dL7tcs+N=>eyg@(aJ~5fc{12?qUC=aH3mT8LS?7t) zme?{RpEr4nH`yhO=&}`P+6$TNqH@&VmvUBiqnVkL-Jc_MF7I|W3w_Uv!_)$(GBg`Dz(aD#viQ5 zI%aI{e!BjbziEFr{&nNIx%K1WQ>@G|&Ev9ft0@iz_Npd&AkrLiE}eT=_z}T4Q;R3!tsK&5m#%1470Z8ZY z-B^r@i*xPl=OyNsPokoXMM>E!$hD@=E-y~*#ctS;99v{TD4`i+RVc^6Fpk`qu@o_A!9s*vD9>^5$O6 z%B(zWnkH?U@iP2)n7@A-OW9y8%0Axh%6)9;TIGv>L+Vr>N#K?$N9oOvba55(a%Hkh zrHfMR()MF3k8O;yVZC1YAL+$)^y$HFpV>{=E!g)kX7x~U%&#-1MtoQSpbw48BYIOq z&r;cuNqAXPBR)LSxEQ$(i--3Ha@}B?bp)0EvBy?FQM;)kh`2nv8{A^72nzlV7jYV} zWH{u6LBZawAQ`h{5SO_qf-(=vhddZ1@9=SO=M?MxVE*h#djP28&wyzsvRORLrzwjR z4El3H0plc21Wu7gQbhTLkHs6~yGc^p!_WTH$&)Zg0 zkY-v;K=vonS07c{i(s)b$tTkk(~Hj6{YUs@u@9n|7I53rI|jhb?1FQF;+DQ_Dr#5@ zx(<5Py)DxE0xq8Q(y}F1{h70RnRmDQrrCO@V@^#5jl6P2lZW$nHGk^FY51XM1(@OJD`% zR@@KB;c_N#+V7Q2sM-yI%WqiKAW2%x=#Uxa1QNf9C5TQ+j+bC|a z;ugtwiR=(Lk56%Q*o!8}CA5Mh*RoD5+tAhTe~xRJmeFl=-HzGa?OsxS`&Z=EvrE#} z{AP5tg_D|#VLaf%fFJ2#y2*GXXcG(iZy>$lbUY~YJS_w*D{f-(n8igs+tqWADE0zZ zZ$5@A!WrQb@rcmfN_NUPP4>6qefl&m>h2k5l(Bs6+!0`$e2Vtfeml7wFeB LTfhk50WjnLCWcwq literal 0 HcmV?d00001 diff --git a/__pycache__/mabStra.cpython-39.pyc b/__pycache__/mabStra.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c34e82847a3516bb02505d0e372b895b3718613a GIT binary patch literal 2406 zcmcIm&2Jk;6rY`)U9Z0q$7#Nrq^0H4sEtb+(pChVh_Uqr~6{yEW+*I0^o!zF7Xso@u*1^^r@?Q%w(ahx|*k(dT28@ z;TdL9A#V|`as2_|y2I|PW{R^_Qb_E98Yxhjxm^$5wuS9Efg@z@x>K%swi`1AMJ8{r z2Eq=U>NYyp?ZCc?;VWfS=)Mx*EW+&*g%F3B3MVGz3a4DsdC+9JYi~%&JCXA zDW2w;9cCsvezxNq9e<$XCms0JFk_JCfEn7+%yi7;J4`0l91bqS!)Ii|v)3?n)sL6n z`{~j7r=_aD{>jhn(l@2*=Ig2NKW~0n5|AlUU{bjx)%m$O$u7^%Uz6<0?A2@4gWX>q zFRy+5m85g)dlcgKI09x_s%x!nC|-g)6}#mIQuCX3+1VSwfGGs*nri0k(CYalT`Z{5 z;89_Hk%JL2gu^>Gp8W9bkB>`|`Ht&W6YUSj&wcTGse0$#KQ4TCU&m=l4&|<3xupRe zVxuc{;RG#FFRLJObfJyUd=qZ}BY=QxC~c)pHt2@Rl^xoKK3F(rStYizt9P+R%qA8{TY)+lE6}#7pyoMEN7Nd8KLxqF=ruMP%S(}6vHjr4_T9$&V5c!7 zM?sO(iHe*ktdNux@}aISFUb*5wNTaaYzv_nbavISvK27~^$_C#yF^@oul?7Gx9%+8D6Tg=r|36YL9JL3&L*@fcV>$%-w_rT zy68vyzUKJFPAktex8-oN(QLVJAXv3Jua)hfA^gjr>c0R$sIKUTsei$Hld)c}R!?7H z$A^8j1qO$%+JMS(Izc`Ko2wJ#vreDgWqo+)w7YODMUmkQ);*)|NM{b$D$uiG7wIc| zxK+g@0`4#|jQ|HWDQHr48)d2T+LlaX8-QVXwYqo?M!OjM1MxCW!V*Mq5d-pZK5=d! zoH`Fzi?Jk2e`PcWDWwtqNm=L}P(A$Kr%C!7J*3%tr+ZQw%l&T|g-nJSg<2ffAk66B zNBU({6fqHJ5zZhygD?tE(8MLQUjdMra5J1pX&3bmIX&*taCVr~cR z(__7r!k9EHs}6^wWyzEU8H^p)TH4Dji%f1eVL3834w>Z zcoyM#1l&hLMZj$(W)WUQcnP6^Fpuymf(9TN?889Jpg)Ij1>v~OLJNCM=m7Wv@UH?G z8dbv2fo?F$w48z;s-zBe8lu|EW=&XPQTUCH!brxC}YfC$LtB z{~Hx}X$gF7NWE#-x$Q^G83=5*wl-t0p=>#j#*rn?L2UT=eyfwlLR5k`@Wk*9fSi(1 IGAyJ11-$ce00000 literal 0 HcmV?d00001 diff --git a/__pycache__/wtc.cpython-39.pyc b/__pycache__/wtc.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b056877fabd1cf7aa11f223248a60d27736cdca8 GIT binary patch literal 2852 zcmZWrU5p$@5$^8!+1cMa@7ed`I6qDZ_8Pdmbx0f|C+iS9*q}QH&QV|pn#^s_jps5u zyY88_eb!zOvLpF{2wA|JBlg6TNu(rFBwiw2UU)-*0D)+k2b3VPSPtR=f)0nHs&{wp z9M5XMsrss?s;9cDcHLqzN8l)u)NDrk zyxuTuQ^_pMG%VXvvIcV2&MH|CbB(;6AFMCfMddGNm*8%MBaKmel#;IyZt~1!!ZTj> zl4kGVbb(Z?9z-Ye6)H!MdG(;-hR?dfZFq?%fXkj-NQ9es{t7C`+{Aqbji3};!fT0U z-HYQO^6Thi`;`IZD1777ln{^DloOkA${E+V&JEniHPhC(#j`xe^Sr=|yabHSNBAh; z!OMK?nr<6>r)~0GcE+>#IG^B?e2VYp_q=M@*_7EcU~(yQFTYQ%$*0Wy1Ev5>`3^tD zlo|RdrOeRJNXiWTjHb+RKRZ%p=%<`ALqB6FGxW35+vSaWyZ8gwG`^QVcv-h6wlNR! z&j2&Ijd_@V7MLl%4-Rg>)Xpb$|Me%I|MuJKAJzPyy?FW?`?@!4{;`c)H{UyatLFdW z2i5am*z45%E3eqUd-wgzHUH%F=H5^K{nuK>l$pi$iqmi}NNXU>rc!la459%QL_=c` zRsGVbH-F?^>8<-ueg6hVv|=v|Rmkj6nw6wV_UL1WrT)mlYSq>cJyt#B-@g9O-_9+*w=UVEjov0)JzU(w z=JS8~-4pAp_ui;U4RTP?q{aIk5X%hrmfUujNF#2!bdXKAQtP92 zspC8NKlZNFzjNb*8a$lk&>c(I9nymKaZ#0~@RGKO>KcR_ULzo6;rldv;{&jpi?=(J z(q)R&*H`dsy-j_T2B=xpGOj;3^tn;l~(S);3_S_KUT z)=Vd}YJsjNM%V0EYm{5SW;<3V%P>y%2x9ICfi)SB@?gz%a=b7_R`Z=aFa=z(Y{48M zuP|s*z$Mt_a(1JxMkIsN(~i$QB{g?3 zmKqPvNo@gC7PVw9aw8{cI#IhJjfK;vo<1SnUMvi7WEbXwXwJQ`PylZJ>3v z4q4Wh4M1}ln&2qf^(mtGjN+}7HwJuG@wq zY;~DAo3vKKU{Mz9q8Y~?cihG1IS*!~mz?*!NaSI$G*kr$3!g3^V5bW#PJu6YT@!~< zPET8%Q6Ihx23by%j$=6yUc@f}AMXYrIrg#6%5;2_vGM;gZTu6KVgF&9EPrwQjx48- z-j!sWeJaUVd+PP+d_Ve0AU?gBtqM>BTkYm5tcw`{$$#o>x7N&8c1z1~BAA7aBXf?^ zX!3T5a=~%Vw%u@O!Hu@???x>1>hYlhMU8(&9MKHmcOhxEpwVNQO@fA}My$XcsEqb= zTSRpfL1i9~LSVm(9SG_S)M?=C6=eiWfYhO`6M+d4Uq$#D0(Q+dz+(|OFR(e|7Xhp> zM$^xRnNMZPHq7imyKR~}!_z85Kj@n#Q>dZNXbyCRck6r0B^9kwmJg+kI(-i6ped#Y zB{;p+kT7zTNgRib$=o(WDRH4%r(t?QXV(*H4J9!ROYQQO^Fiyc^pmQgBeUg3+>QHL zGGNZNR>UJ<5RaoZ6TcLCu87o}Q@h=kFO@!3%hr+pxj%^q6C+I0LjR+|q@V@!|9NWw A`~Uy| literal 0 HcmV?d00001 diff --git a/custom_indicators.py b/custom_indicators.py new file mode 100644 index 0000000..6faa625 --- /dev/null +++ b/custom_indicators.py @@ -0,0 +1,222 @@ +""" +Solipsis Custom Indicators and Maths +""" +import numpy as np +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + +from pandas import DataFrame, Series + +""" +Misc. Helper Functions +""" + + +def same_length(bigger, shorter): + return np.concatenate((np.full((bigger.shape[0] - shorter.shape[0]), np.nan), shorter)) + + +""" +Maths +""" + + +def linear_growth(start: float, end: float, start_time: int, end_time: int, trade_time: int) -> float: + """ + Simple linear growth function. Grows from start to end after end_time minutes (starts after start_time minutes) + """ + time = max(0, trade_time - start_time) + rate = (end - start) / (end_time - start_time) + + return min(end, start + (rate * time)) + + +def linear_decay(start: float, end: float, start_time: int, end_time: int, trade_time: int) -> float: + """ + Simple linear decay function. Decays from start to end after end_time minutes (starts after start_time minutes) + """ + time = max(0, trade_time - start_time) + rate = (start - end) / (end_time - start_time) + + return max(end, start - (rate * time)) + + +""" +TA Indicators +""" + + +def zema(dataframe, period, field='close'): + """ + Source: https://github.com/freqtrade/technical/blob/master/technical/indicators/overlap_studies.py#L79 + Modified slightly to use ta.EMA instead of technical ema + """ + df = dataframe.copy() + + df['ema1'] = ta.EMA(df[field], timeperiod=period) + df['ema2'] = ta.EMA(df['ema1'], timeperiod=period) + df['d'] = df['ema1'] - df['ema2'] + df['zema'] = df['ema1'] + df['d'] + + return df['zema'] + + +def RMI(dataframe, *, length=20, mom=5): + """ + Source: https://github.com/freqtrade/technical/blob/master/technical/indicators/indicators.py#L912 + """ + df = dataframe.copy() + + df['maxup'] = (df['close'] - df['close'].shift(mom)).clip(lower=0) + df['maxdown'] = (df['close'].shift(mom) - df['close']).clip(lower=0) + + df.fillna(0, inplace=True) + + df["emaInc"] = ta.EMA(df, price='maxup', timeperiod=length) + df["emaDec"] = ta.EMA(df, price='maxdown', timeperiod=length) + + df['RMI'] = np.where(df['emaDec'] == 0, 0, 100 - 100 / (1 + df["emaInc"] / df["emaDec"])) + + return df["RMI"] + + +def mastreak(dataframe: DataFrame, period: int = 4, field='close') -> Series: + """ + MA Streak + Port of: https://www.tradingview.com/script/Yq1z7cIv-MA-Streak-Can-Show-When-a-Run-Is-Getting-Long-in-the-Tooth/ + """ + df = dataframe.copy() + + avgval = zema(df, period, field) + + arr = np.diff(avgval) + pos = np.clip(arr, 0, 1).astype(bool).cumsum() + neg = np.clip(arr, -1, 0).astype(bool).cumsum() + streak = np.where(arr >= 0, pos - np.maximum.accumulate(np.where(arr <= 0, pos, 0)), + -neg + np.maximum.accumulate(np.where(arr >= 0, neg, 0))) + + res = same_length(df['close'], streak) + + return res + + +def pcc(dataframe: DataFrame, period: int = 20, mult: int = 2): + """ + Percent Change Channel + PCC is like KC unless it uses percentage changes in price to set channel distance. + https://www.tradingview.com/script/6wwAWXA1-MA-Streak-Change-Channel/ + """ + df = dataframe.copy() + + df['previous_close'] = df['close'].shift() + + df['close_change'] = (df['close'] - df['previous_close']) / df['previous_close'] * 100 + df['high_change'] = (df['high'] - df['close']) / df['close'] * 100 + df['low_change'] = (df['low'] - df['close']) / df['close'] * 100 + + df['delta'] = df['high_change'] - df['low_change'] + + mid = zema(df, period, 'close_change') + rangema = zema(df, period, 'delta') + + upper = mid + rangema * mult + lower = mid - rangema * mult + + return upper, rangema, lower + + +def SSLChannels(dataframe, length=10, mode='sma'): + """ + Source: https://www.tradingview.com/script/xzIoaIJC-SSL-channel/ + Source: https://github.com/freqtrade/technical/blob/master/technical/indicators/indicators.py#L1025 + Usage: + dataframe['sslDown'], dataframe['sslUp'] = SSLChannels(dataframe, 10) + """ + if mode not in ('sma'): + raise ValueError(f"Mode {mode} not supported yet") + + df = dataframe.copy() + + if mode == 'sma': + df['smaHigh'] = df['high'].rolling(length).mean() + df['smaLow'] = df['low'].rolling(length).mean() + + df['hlv'] = np.where(df['close'] > df['smaHigh'], 1, + np.where(df['close'] < df['smaLow'], -1, np.NAN)) + df['hlv'] = df['hlv'].ffill() + + df['sslDown'] = np.where(df['hlv'] < 0, df['smaHigh'], df['smaLow']) + df['sslUp'] = np.where(df['hlv'] < 0, df['smaLow'], df['smaHigh']) + + return df['sslDown'], df['sslUp'] + + +def SSLChannels_ATR(dataframe, length=7): + """ + SSL Channels with ATR: https://www.tradingview.com/script/SKHqWzql-SSL-ATR-channel/ + Credit to @JimmyNixx for python + """ + df = dataframe.copy() + + df['ATR'] = ta.ATR(df, timeperiod=14) + df['smaHigh'] = df['high'].rolling(length).mean() + df['ATR'] + df['smaLow'] = df['low'].rolling(length).mean() - df['ATR'] + df['hlv'] = np.where(df['close'] > df['smaHigh'], 1, np.where(df['close'] < df['smaLow'], -1, np.NAN)) + df['hlv'] = df['hlv'].ffill() + df['sslDown'] = np.where(df['hlv'] < 0, df['smaHigh'], df['smaLow']) + df['sslUp'] = np.where(df['hlv'] < 0, df['smaLow'], df['smaHigh']) + + return df['sslDown'], df['sslUp'] + + +def WaveTrend(dataframe, chlen=10, avg=21, smalen=4): + """ + WaveTrend Ocillator by LazyBear + https://www.tradingview.com/script/2KE8wTuF-Indicator-WaveTrend-Oscillator-WT/ + """ + df = dataframe.copy() + + df['hlc3'] = (df['high'] + df['low'] + df['close']) / 3 + df['esa'] = ta.EMA(df['hlc3'], timeperiod=chlen) + df['d'] = ta.EMA((df['hlc3'] - df['esa']).abs(), timeperiod=chlen) + df['ci'] = (df['hlc3'] - df['esa']) / (0.015 * df['d']) + df['tci'] = ta.EMA(df['ci'], timeperiod=avg) + + df['wt1'] = df['tci'] + df['wt2'] = ta.SMA(df['wt1'], timeperiod=smalen) + df['wt1-wt2'] = df['wt1'] - df['wt2'] + + return df['wt1'], df['wt2'] + + +def T3(dataframe, length=5): + """ + T3 Average by HPotter on Tradingview + https://www.tradingview.com/script/qzoC9H1I-T3-Average/ + """ + df = dataframe.copy() + + df['xe1'] = ta.EMA(df['close'], timeperiod=length) + df['xe2'] = ta.EMA(df['xe1'], timeperiod=length) + df['xe3'] = ta.EMA(df['xe2'], timeperiod=length) + df['xe4'] = ta.EMA(df['xe3'], timeperiod=length) + df['xe5'] = ta.EMA(df['xe4'], timeperiod=length) + df['xe6'] = ta.EMA(df['xe5'], timeperiod=length) + b = 0.7 + c1 = -b * b * b + c2 = 3 * b * b + 3 * b * b * b + c3 = -6 * b * b - 3 * b - 3 * b * b * b + c4 = 1 + 3 * b + b * b * b + 3 * b * b + df['T3Average'] = c1 * df['xe6'] + c2 * df['xe5'] + c3 * df['xe4'] + c4 * df['xe3'] + + return df['T3Average'] + + +def SROC(dataframe, roclen=21, emalen=13, smooth=21): + df = dataframe.copy() + + roc = ta.ROC(df, timeperiod=roclen) + ema = ta.EMA(df, timeperiod=emalen) + sroc = ta.ROC(ema, timeperiod=smooth) + + return sroc diff --git a/custom_stoploss_with_psar.py b/custom_stoploss_with_psar.py new file mode 100644 index 0000000..92f3e0e --- /dev/null +++ b/custom_stoploss_with_psar.py @@ -0,0 +1,91 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from datetime import datetime +from freqtrade.persistence import Trade + + +class CustomStoplossWithPSAR(IStrategy): + """ + this is an example class, implementing a PSAR based trailing stop loss + you are supposed to take the `custom_stoploss()` and `populate_indicators()` + parts and adapt it to your own strategy + + the populate_buy_trend() function is pretty nonsencial + """ + timeframe = '1h' + stoploss = -0.2 + custom_info = {} + use_custom_stoploss = True + + def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, + current_rate: float, current_profit: float, **kwargs) -> float: + + result = 1 + if self.custom_info and pair in self.custom_info and trade: + # using current_time directly (like below) will only work in backtesting/hyperopt. + # in live / dry-run, it'll be really the current time + relative_sl = None + if self.dp: + # so we need to get analyzed_dataframe from dp + dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) + # only use .iat[-1] in callback methods, never in "populate_*" methods. + # see: https://www.freqtrade.io/en/latest/strategy-customization/#common-mistakes-when-developing-strategies + last_candle = dataframe.iloc[-1].squeeze() + relative_sl = last_candle['sar'] + + if (relative_sl is not None): + # print("custom_stoploss().relative_sl: {}".format(relative_sl)) + # calculate new_stoploss relative to current_rate + new_stoploss = (current_rate - relative_sl) / current_rate + # turn into relative negative offset required by `custom_stoploss` return implementation + result = new_stoploss - 1 + + # print("custom_stoploss() -> {}".format(result)) + return result + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['sar'] = ta.SAR(dataframe) + if self.dp.runmode.value in ('backtest', 'hyperopt'): + self.custom_info[metadata['pair']] = dataframe[['date', 'sar']].copy().set_index('date') + + # all "normal" indicators: + # e.g. + # dataframe['rsi'] = ta.RSI(dataframe) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Placeholder Strategy: buys when SAR is smaller then candle before + Based on TA indicators, populates the buy signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + dataframe.loc[ + ( + (dataframe['sar'] < dataframe['sar'].shift()) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Placeholder Strategy: does nothing + Based on TA indicators, populates the sell signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + # Deactivated sell signal to allow the strategy to work correctly + dataframe.loc[:, 'sell'] = 0 + return dataframe diff --git a/fixed_riskreward_loss.py b/fixed_riskreward_loss.py new file mode 100644 index 0000000..1a8d9fc --- /dev/null +++ b/fixed_riskreward_loss.py @@ -0,0 +1,120 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement +# isort: skip_file +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame + +from freqtrade.strategy.interface import IStrategy + +# -------------------------------- +# Add your lib to import here +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +from datetime import datetime +from freqtrade.persistence import Trade + +import logging +logger = logging.getLogger(__name__) + +class FixedRiskRewardLoss(IStrategy): + """ + This strategy uses custom_stoploss() to enforce a fixed risk/reward ratio + by first calculating a dynamic initial stoploss via ATR - last negative peak + + After that, we caculate that initial risk and multiply it with an risk_reward_ratio + Once this is reached, stoploss is set to it and sell signal is enabled + + Also there is a break even ratio. Once this is reached, the stoploss is adjusted to minimize + losses by setting it to the buy rate + fees. + """ + + custom_info = { + 'risk_reward_ratio': 3.5, + 'set_to_break_even_at_profit': 1, + } + use_custom_stoploss = True + stoploss = -0.9 + + def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, + current_rate: float, current_profit: float, **kwargs) -> float: + + """ + custom_stoploss using a risk/reward ratio + """ + result = break_even_sl = takeprofit_sl = -1 + custom_info_pair = self.custom_info.get(pair) + if custom_info_pair is not None: + # using current_time/open_date directly via custom_info_pair[trade.open_daten] + # would only work in backtesting/hyperopt. + # in live/dry-run, we have to search for nearest row before it + open_date_mask = custom_info_pair.index.unique().get_loc(trade.open_date_utc, method='ffill') + open_df = custom_info_pair.iloc[open_date_mask] + + # trade might be open too long for us to find opening candle + if(len(open_df) != 1): + return -1 # won't update current stoploss + + initial_sl_abs = open_df['stoploss_rate'] + + # calculate initial stoploss at open_date + initial_sl = initial_sl_abs/current_rate-1 + + # calculate take profit treshold + # by using the initial risk and multiplying it + risk_distance = trade.open_rate-initial_sl_abs + reward_distance = risk_distance*self.custom_info['risk_reward_ratio'] + # take_profit tries to lock in profit once price gets over + # risk/reward ratio treshold + take_profit_price_abs = trade.open_rate+reward_distance + # take_profit gets triggerd at this profit + take_profit_pct = take_profit_price_abs/trade.open_rate-1 + + # break_even tries to set sl at open_rate+fees (0 loss) + break_even_profit_distance = risk_distance*self.custom_info['set_to_break_even_at_profit'] + # break_even gets triggerd at this profit + break_even_profit_pct = (break_even_profit_distance+current_rate)/current_rate-1 + + result = initial_sl + if(current_profit >= break_even_profit_pct): + break_even_sl = (trade.open_rate*(1+trade.fee_open+trade.fee_close) / current_rate)-1 + result = break_even_sl + + if(current_profit >= take_profit_pct): + takeprofit_sl = take_profit_price_abs/current_rate-1 + result = takeprofit_sl + + return result + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['atr'] = ta.ATR(dataframe) + dataframe['stoploss_rate'] = dataframe['close']-(dataframe['atr']*2) + self.custom_info[metadata['pair']] = dataframe[['date', 'stoploss_rate']].copy().set_index('date') + + # all "normal" indicators: + # e.g. + # dataframe['rsi'] = ta.RSI(dataframe) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Placeholder Strategy: buys when SAR is smaller then candle before + Based on TA indicators, populates the buy signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + # Allways buys + dataframe.loc[:, 'buy'] = 1 + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ + Placeholder Strategy: does nothing + Based on TA indicators, populates the sell signal for the given dataframe + :param dataframe: DataFrame + :return: DataFrame with buy column + """ + + # Never sells + dataframe.loc[:, 'sell'] = 0 + return dataframe diff --git a/hlhb.py b/hlhb.py new file mode 100644 index 0000000..b3103e8 --- /dev/null +++ b/hlhb.py @@ -0,0 +1,128 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement + +import numpy as np # noqa +import pandas as pd # noqa +from pandas import DataFrame +from freqtrade.strategy import IStrategy +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib + + +class hlhb(IStrategy): + """ + The HLHB ("Huck loves her bucks!") System simply aims to catch short-term forex trends. + More information in https://www.babypips.com/trading/forex-hlhb-system-explained + """ + + INTERFACE_VERSION = 2 + + position_stacking = "True" + + # Minimal ROI designed for the strategy. + # This attribute will be overridden if the config file contains "minimal_roi". + minimal_roi = { + "0": 0.6225, + "703": 0.2187, + "2849": 0.0363, + "5520": 0 + } + + # Optimal stoploss designed for the strategy. + # This attribute will be overridden if the config file contains "stoploss". + stoploss = -0.3211 + + # Trailing stoploss + trailing_stop = True + trailing_stop_positive = 0.0117 + trailing_stop_positive_offset = 0.0186 + trailing_only_offset_is_reached = True + + # Optimal timeframe for the strategy. + timeframe = '4h' + + # Run "populate_indicators()" only for new candle. + process_only_new_candles = True + + # These values can be overridden in the "ask_strategy" section in the config. + use_sell_signal = True + sell_profit_only = False + ignore_roi_if_buy_signal = True + + # Number of candles the strategy requires before producing valid signals + startup_candle_count: int = 30 + + # Optional order type mapping. + order_types = { + 'buy': 'limit', + 'sell': 'limit', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optional order time in force. + order_time_in_force = { + 'buy': 'gtc', + 'sell': 'gtc' + } + + plot_config = { + # Main plot indicators (Moving averages, ...) + 'main_plot': { + 'ema5': {}, + 'ema10': {}, + }, + 'subplots': { + # Subplots - each dict defines one additional plot + "RSI": { + 'rsi': {'color': 'red'}, + }, + "ADX": { + 'adx': {}, + } + } + } + + def informative_pairs(self): + return [] + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe['hl2'] = (dataframe["close"] + dataframe["open"]) / 2 + + # Momentum Indicators + # ------------------------------------ + + # RSI + dataframe['rsi'] = ta.RSI(dataframe, timeperiod=10, price='hl2') + + # # EMA - Exponential Moving Average + dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5) + dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10) + + # ADX + dataframe['adx'] = ta.ADX(dataframe) + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (qtpylib.crossed_above(dataframe['rsi'], 50)) & + (qtpylib.crossed_above(dataframe['ema5'], dataframe['ema10'])) & + (dataframe['adx'] > 25) & + (dataframe['volume'] > 0) # Make sure Volume is not 0 + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (qtpylib.crossed_below(dataframe['rsi'], 50)) & + (qtpylib.crossed_below(dataframe['ema5'], dataframe['ema10'])) & + (dataframe['adx'] > 25) & + (dataframe['volume'] > 0) # Make sure Volume is not 0 + ), + 'sell'] = 1 + return dataframe + diff --git a/jeroen_test.py b/jeroen_test.py new file mode 100644 index 0000000..c582a90 --- /dev/null +++ b/jeroen_test.py @@ -0,0 +1,164 @@ + +from freqtrade.strategy.interface import IStrategy +from typing import Dict, List +from functools import reduce +from pandas import DataFrame +from freqtrade.persistence import Trade +from datetime import datetime, date, timedelta + + +import talib.abstract as ta +import freqtrade.vendor.qtpylib.indicators as qtpylib +import numpy # noqa +import logging + +logger = logging.getLogger(__name__) + +class jeroen_test(IStrategy): + + # Minimal ROI designed for the strategy. + minimal_roi = { + "0": 0.02 + } + + order_types = { + 'buy': 'market', + 'sell': 'market', + 'stoploss': 'market', + 'stoploss_on_exchange': False + } + + # Optimal stoploss designed for the strategy + stoploss = -10 + + # Optimal timeframe for the strategy + timeframe = '1m' + + def calc_profit(self, price: float, current: float) -> float: + fee = 1.0007 + profit = ((current*fee) - + (price*fee)) + + return float(f"{profit:.8f}") + + def calc_percentage_lower(self, price: float, current: float) -> float: + fee = 1.0007 + price = price*fee + current = current*fee + lowerpercent = ((price-current)/(price*fee))*100 + + return float(f"{lowerpercent:.8f}") + + def bot_loop_start(self, **kwargs) -> None: + print(" ") + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + """ Adds several different TA indicators to the given DataFrame + """ + profit = False + profit_percent = False + percent_lower = False + current_price = dataframe['close'].iloc[-1] + + dataframe['should_sell'] = False + dataframe['should_buy'] = False + + # Get the previous trade + trade = Trade.get_trades_proxy(is_open=False, pair=metadata['pair']) + if trade: + trade = trade[-1] + lsp = trade.close_rate + if lsp: + percent_lower = self.calc_percentage_lower(price=lsp, current=current_price) + # Found a bug? When force selling it doesnt close it + else: + lsp = trade.open_rate + if lsp: + percent_lower = self.calc_percentage_lower(price=lsp, current=current_price) + else: + lsp = 0.00 + + # Get the current Trade + trade = Trade.get_trades_proxy(is_open=True, pair=metadata['pair']) + if trade: + trade = trade[-1] + lbp = trade.open_rate + open_trade = True + profit = self.calc_profit(price=lbp, current=current_price) + profit_percent = (profit/lbp)*100 + else: + lbp = 0.00 + open_trade = False + profit = False + profit_percent = False + + + print("------------") + + print("Last Sold For:", lsp) + + if open_trade: + print("Bought for: ", lbp) + print("Current Price: ", current_price) + if profit: + print("Current Profit: ", profit, " ", float(f"{profit_percent:.8f}"), "%") + if percent_lower and not open_trade: + print("Percent Lower: ", float(f"{percent_lower:.8f}"), "%") + + + # Should we Sell? + if profit_percent: + if profit_percent > 1: + dataframe['should_sell'] = True + + # Should we buy? + if not open_trade: + if (lsp == 0.00 ) & (lbp == 0.00): + dataframe['should_buy'] = True + # Is the percentage of what we sold for and the current price 2% lower + if percent_lower > 2: + dataframe['should_buy'] = True + + + dataframe['last_sell_price'] = lsp + dataframe['last_buy_price'] = lbp + + + print("Current Dataframe:") + print(dataframe.tail(1)) + + + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[( + # We have not bought or sold anything yet, lets buy! + ((dataframe['last_sell_price'] == 0.00) & (dataframe['last_buy_price'] == 0.00) ) | + ( + # Make sure the last selling price is higher than the current price + ((dataframe['last_sell_price']) > dataframe['close']) & + + # Calculated earlier + (dataframe['should_buy'] == True) + ) + ), 'buy' + ] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[( + # Make at least profit + (dataframe['last_buy_price'] < (dataframe['close'])) & + + # Calculated earlier + (dataframe['should_sell'] == True) & + + # If we have nothing we bought, there is nothing to sell + (dataframe['last_buy_price'] > 0.00) + ), 'sell' + ] = 1 + + return dataframe \ No newline at end of file diff --git a/mabStra.py b/mabStra.py new file mode 100644 index 0000000..1791c92 --- /dev/null +++ b/mabStra.py @@ -0,0 +1,97 @@ +# Author: @Mablue (Masoud Azizi) +# github: https://github.com/mablue/ +# IMPORTANT: DO NOT USE IT WITHOUT HYPEROPT: +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces all --strategy mabStra --config config.json -e 100 + +# --- Do not remove these libs --- +from freqtrade.strategy.parameters import IntParameter, DecimalParameter +from freqtrade.strategy.interface import IStrategy +from pandas import DataFrame +# -------------------------------- + +# Add your lib to import here +import talib.abstract as ta + + +class mabStra(IStrategy): + + # #################### RESULTS PASTE PLACE #################### + # ROI table: + minimal_roi = { + "0": 0.598, + "644": 0.166, + "3269": 0.115, + "7289": 0 + } + + # Stoploss: + stoploss = -0.128 + # Buy hypers + timeframe = '4h' + + # #################### END OF RESULT PLACE #################### + + # buy params + buy_mojo_ma_timeframe = IntParameter(2, 100, default=7, space='buy') + buy_fast_ma_timeframe = IntParameter(2, 100, default=14, space='buy') + buy_slow_ma_timeframe = IntParameter(2, 100, default=28, space='buy') + buy_div_max = DecimalParameter( + 0, 2, decimals=4, default=2.25446, space='buy') + buy_div_min = DecimalParameter( + 0, 2, decimals=4, default=0.29497, space='buy') + # sell params + sell_mojo_ma_timeframe = IntParameter(2, 100, default=7, space='sell') + sell_fast_ma_timeframe = IntParameter(2, 100, default=14, space='sell') + sell_slow_ma_timeframe = IntParameter(2, 100, default=28, space='sell') + sell_div_max = DecimalParameter( + 0, 2, decimals=4, default=1.54593, space='sell') + sell_div_min = DecimalParameter( + 0, 2, decimals=4, default=2.81436, space='sell') + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # SMA - ex Moving Average + dataframe['buy-mojoMA'] = ta.SMA(dataframe, + timeperiod=self.buy_mojo_ma_timeframe.value) + dataframe['buy-fastMA'] = ta.SMA(dataframe, + timeperiod=self.buy_fast_ma_timeframe.value) + dataframe['buy-slowMA'] = ta.SMA(dataframe, + timeperiod=self.buy_slow_ma_timeframe.value) + dataframe['sell-mojoMA'] = ta.SMA(dataframe, + timeperiod=self.sell_mojo_ma_timeframe.value) + dataframe['sell-fastMA'] = ta.SMA(dataframe, + timeperiod=self.sell_fast_ma_timeframe.value) + dataframe['sell-slowMA'] = ta.SMA(dataframe, + timeperiod=self.sell_slow_ma_timeframe.value) + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + dataframe.loc[ + ( + (dataframe['buy-mojoMA'].div(dataframe['buy-fastMA']) + > self.buy_div_min.value) & + (dataframe['buy-mojoMA'].div(dataframe['buy-fastMA']) + < self.buy_div_max.value) & + (dataframe['buy-fastMA'].div(dataframe['buy-slowMA']) + > self.buy_div_min.value) & + (dataframe['buy-fastMA'].div(dataframe['buy-slowMA']) + < self.buy_div_max.value) + ), + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (dataframe['sell-fastMA'].div(dataframe['sell-mojoMA']) + > self.sell_div_min.value) & + (dataframe['sell-fastMA'].div(dataframe['sell-mojoMA']) + < self.sell_div_max.value) & + (dataframe['sell-slowMA'].div(dataframe['sell-fastMA']) + > self.sell_div_min.value) & + (dataframe['sell-slowMA'].div(dataframe['sell-fastMA']) + < self.sell_div_max.value) + ), + 'sell'] = 1 + return dataframe diff --git a/wtc.json b/wtc.json new file mode 100644 index 0000000..7282144 --- /dev/null +++ b/wtc.json @@ -0,0 +1,39 @@ +{ + "strategy_name": "wtc", + "params": { + "roi": { + "0": 0.030873, + "569": 0.016689, + "3211": 0.006473, + "7617": 0 + }, + "stoploss": { + "stoploss": -0.128 + }, + "trailing": { + "trailing_stop": false, + "trailing_stop_positive": null, + "trailing_stop_positive_offset": 0.0, + "trailing_only_offset_is_reached": false + }, + "buy": { + "buy_max": 0.99, + "buy_max0": 0.6186, + "buy_max1": 0.8614, + "buy_min": -0.6307, + "buy_min0": 0.006, + "buy_min1": 0.6576 + }, + "sell": { + "sell_max": -0.7979, + "sell_max0": 0.82, + "sell_max1": 0.9821, + "sell_min": -0.5377, + "sell_min0": 0.0628, + "sell_min1": 0.4461 + }, + "protection": {} + }, + "ft_stratparam_v": 1, + "export_time": "2022-03-30 20:42:52.793044+00:00" +} \ No newline at end of file diff --git a/wtc.py b/wtc.py new file mode 100644 index 0000000..bd48090 --- /dev/null +++ b/wtc.py @@ -0,0 +1,156 @@ +# WTC Strategy: WTC(World Trade Center Tabriz) +# is the biggest skyscraper of Tabriz, city of Iran +# (What you want?it not enough for you?that's just it!) +# No, no, I'm kidding. It's also mean Wave Trend with Crosses +# algo by LazyBare(in TradingView) that I reduce it +# signals noise with dividing it to Stoch-RSI indicator. +# Also thanks from discord: @aurax for his/him +# request to making this strategy. +# hope you enjoy and get profit +# Author: @Mablue (Masoud Azizi) +# IMPORTANT: install sklearn befoure you run this strategy: +# pip install sklearn +# github: https://github.com/mablue/ +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy sell --strategy wtc + +import freqtrade.vendor.qtpylib.indicators as qtpylib +import talib.abstract as ta +from freqtrade.strategy import DecimalParameter +from freqtrade.strategy import IStrategy +from pandas import DataFrame +# +# --- Do not remove these libs --- +import numpy as np # noqa +import pandas as pd # noqa +from sklearn import preprocessing + +# -------------------------------- +# Add your lib to import here + + +class wtc(IStrategy): + ################################ SETTINGS ################################ + # 61 trades. 16/0/45 Wins/Draws/Losses. + # * Avg profit: 132.53%. + # Median profit: -12.97%. + # Total profit: 0.80921449 BTC ( 809.21Σ%). + # Avg duration 4 days, 7:47:00 min. + # Objective: -15.73417 + + # Config: + # "max_open_trades": 10, + # "stake_currency": "BTC", + # "stake_amount": 0.01, + # "tradable_balance_ratio": 0.99, + # "timeframe": "30m", + # "dry_run_wallet": 0.1, + + # Buy hyperspace params: + buy_params = { + "buy_max": 0.9609, + "buy_max0": 0.8633, + "buy_max1": 0.9133, + "buy_min": 0.0019, + "buy_min0": 0.0102, + "buy_min1": 0.6864, + } + + # Sell hyperspace params: + sell_params = { + "sell_max": -0.7979, + "sell_max0": 0.82, + "sell_max1": 0.9821, + "sell_min": -0.5377, + "sell_min0": 0.0628, + "sell_min1": 0.4461, + } + minimal_roi = { + "0": 0.030873, + "569": 0.016689, + "3211": 0.006473, + "7617": 0 + } + stoploss = -0.128 + ############################## END SETTINGS ############################## + timeframe = '5m' + + buy_max = DecimalParameter(-1, 1, decimals=4, default=0.4393, space='buy') + buy_min = DecimalParameter(-1, 1, decimals=4, default=-0.4676, space='buy') + sell_max = DecimalParameter(-1, 1, decimals=4, + default=-0.9512, space='sell') + sell_min = DecimalParameter(-1, 1, decimals=4, + default=0.6519, space='sell') + + buy_max0 = DecimalParameter(0, 1, decimals=4, default=0.4393, space='buy') + buy_min0 = DecimalParameter(0, 1, decimals=4, default=-0.4676, space='buy') + sell_max0 = DecimalParameter( + 0, 1, decimals=4, default=-0.9512, space='sell') + sell_min0 = DecimalParameter( + 0, 1, decimals=4, default=0.6519, space='sell') + + buy_max1 = DecimalParameter(0, 1, decimals=4, default=0.4393, space='buy') + buy_min1 = DecimalParameter(0, 1, decimals=4, default=-0.4676, space='buy') + sell_max1 = DecimalParameter( + 0, 1, decimals=4, default=-0.9512, space='sell') + sell_min1 = DecimalParameter( + 0, 1, decimals=4, default=0.6519, space='sell') + + def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + + # WAVETREND + try: + ap = (dataframe['high']+dataframe['low'] + dataframe['close'])/3 + + esa = ta.EMA(ap, 10) + + d = ta.EMA((ap - esa).abs(), 10) + ci = (ap - esa).div(0.0015 * d) + tci = ta.EMA(ci, 21) + + wt1 = tci + wt2 = ta.SMA(np.nan_to_num(wt1), 4) + + dataframe['wt1'], dataframe['wt2'] = wt1, wt2 + + stoch = ta.STOCH(dataframe, 14) + slowk = stoch['slowk'] + dataframe['slowk'] = slowk + # print(dataframe.iloc[:, 6:].keys()) + x = dataframe.iloc[:, 6:].values # returns a numpy array + min_max_scaler = preprocessing.MinMaxScaler() + x_scaled = min_max_scaler.fit_transform(x) + dataframe.iloc[:, 6:] = pd.DataFrame(x_scaled) + # print('wt:\t', dataframe['wt'].min(), dataframe['wt'].max()) + # print('stoch:\t', dataframe['stoch'].min(), dataframe['stoch'].max()) + dataframe['def'] = dataframe['slowk']-dataframe['wt1'] + # print('def:\t', dataframe['def'].min(), "\t", dataframe['def'].max()) + except: + dataframe['wt1'], dataframe['wt2'], dataframe['def'], dataframe['slowk'] = 0, 10, 100, 1000 + return dataframe + + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + dataframe.loc[ + ( + (qtpylib.crossed_above(dataframe['wt1'], dataframe['wt2'])) + & (dataframe['wt1'].between(self.buy_min0.value, self.buy_max0.value)) + & (dataframe['slowk'].between(self.buy_min1.value, self.buy_max1.value)) + & (dataframe['def'].between(self.buy_min.value, self.buy_max.value)) + + ), + + 'buy'] = 1 + + return dataframe + + def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + # print(dataframe['slowk']/dataframe['wt1']) + # dataframe.loc[ + # ( + # (qtpylib.crossed_below(dataframe['wt1'], dataframe['wt2'])) + # & (dataframe['wt1'].between(self.sell_min0.value, self.sell_max0.value)) + # & (dataframe['slowk'].between(self.sell_min1.value, self.sell_max1.value)) + # & (dataframe['def'].between(self.sell_min.value, self.sell_max.value)) + # + # ), + # 'sell'] = 1 + return dataframe diff --git a/wtc.txt b/wtc.txt new file mode 100644 index 0000000..269aa93 --- /dev/null +++ b/wtc.txt @@ -0,0 +1,84 @@ +Result for strategy wtc +=========================================================== BACKTESTING REPORT =========================================================== +| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | Avg Duration | Win Draw Loss Win% | +|-----------+--------+----------------+----------------+-------------------+----------------+------------------+-------------------------| +| AVAX/USDT | 128 | 3.14 | 401.62 | 402.019 | 40.20 | 2 days, 0:00:00 | 82 1 45 64.1 | +| SAND/USDT | 151 | 2.59 | 390.41 | 390.799 | 39.08 | 1 day, 17:30:00 | 86 4 61 57.0 | +| SOL/USDT | 115 | 3.09 | 355.10 | 355.451 | 35.55 | 2 days, 2:56:00 | 73 2 40 63.5 | +| BNB/USDT | 102 | 2.63 | 268.01 | 268.276 | 26.83 | 2 days, 17:16:00 | 61 9 32 59.8 | +| IOTX/USDT | 116 | 2.13 | 247.36 | 247.609 | 24.76 | 2 days, 2:29:00 | 67 5 44 57.8 | +| CELR/USDT | 141 | 1.57 | 221.10 | 221.319 | 22.13 | 1 day, 16:46:00 | 77 4 60 54.6 | +| EGLD/USDT | 98 | 2.17 | 212.36 | 212.577 | 21.26 | 2 days, 16:51:00 | 55 9 34 56.1 | +| ETH/USDT | 90 | 2.07 | 186.13 | 186.314 | 18.63 | 3 days, 4:18:00 | 61 7 22 67.8 | +| ROSE/USDT | 133 | 1.08 | 143.78 | 143.929 | 14.39 | 1 day, 23:51:00 | 75 4 54 56.4 | +| XRP/USDT | 100 | 1.30 | 129.63 | 129.765 | 12.98 | 2 days, 17:02:00 | 56 7 37 56.0 | +| TRX/USDT | 104 | 1.19 | 123.99 | 124.118 | 12.41 | 2 days, 19:17:00 | 64 7 33 61.5 | +| ADA/USDT | 99 | 1.05 | 103.63 | 103.729 | 10.37 | 2 days, 19:35:00 | 53 9 37 53.5 | +| ZEC/USDT | 113 | 0.92 | 103.53 | 103.630 | 10.36 | 2 days, 9:05:00 | 68 4 41 60.2 | +| BTC/USDT | 75 | 0.26 | 19.80 | 19.822 | 1.98 | 3 days, 20:41:00 | 46 7 22 61.3 | +| GALA/USDT | 22 | -0.76 | -16.79 | -16.809 | -1.68 | 2 days, 0:28:00 | 8 2 12 36.4 | +| TOTAL | 1587 | 1.82 | 2889.66 | 2892.547 | 289.25 | 2 days, 9:12:00 | 932 81 574 58.7 | +=========================================================== BUY TAG STATS =========================================================== +| TAG | Buys | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | Avg Duration | Win Draw Loss Win% | +|-------+--------+----------------+----------------+-------------------+----------------+-----------------+-------------------------| +| TOTAL | 1587 | 1.82 | 2889.66 | 2892.547 | 289.25 | 2 days, 9:12:00 | 932 81 574 58.7 | +===================================================== SELL REASON STATS ===================================================== +| Sell Reason | Sells | Win Draws Loss Win% | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | +|---------------+---------+--------------------------+----------------+----------------+-------------------+----------------| +| roi | 1007 | 926 81 0 100 | 10.19 | 10259.8 | 10270.1 | 683.99 | +| stop_loss | 568 | 0 0 568 0 | -12.97 | -7369.36 | -7376.73 | -491.29 | +| force_sell | 12 | 6 0 6 50.0 | -0.07 | -0.82 | -0.818 | -0.05 | +======================================================== LEFT OPEN TRADES REPORT ========================================================= +| Pair | Buys | Avg Profit % | Cum Profit % | Tot Profit USDT | Tot Profit % | Avg Duration | Win Draw Loss Win% | +|-----------+--------+----------------+----------------+-------------------+----------------+------------------+-------------------------| +| SOL/USDT | 1 | 8.51 | 8.51 | 8.514 | 0.85 | 18:20:00 | 1 0 0 100 | +| TRX/USDT | 1 | 2.87 | 2.87 | 2.873 | 0.29 | 18:20:00 | 1 0 0 100 | +| ETH/USDT | 1 | 2.48 | 2.48 | 2.486 | 0.25 | 2 days, 17:25:00 | 1 0 0 100 | +| SAND/USDT | 1 | 1.97 | 1.97 | 1.969 | 0.20 | 1:55:00 | 1 0 0 100 | +| CELR/USDT | 1 | 1.04 | 1.04 | 1.043 | 0.10 | 1 day, 4:50:00 | 1 0 0 100 | +| BTC/USDT | 1 | 0.35 | 0.35 | 0.355 | 0.04 | 1 day, 5:50:00 | 1 0 0 100 | +| BNB/USDT | 1 | -0.23 | -0.23 | -0.234 | -0.02 | 2 days, 5:00:00 | 0 0 1 0 | +| ZEC/USDT | 1 | -0.70 | -0.70 | -0.701 | -0.07 | 2 days, 15:10:00 | 0 0 1 0 | +| XRP/USDT | 1 | -1.76 | -1.76 | -1.762 | -0.18 | 2 days, 12:00:00 | 0 0 1 0 | +| ADA/USDT | 1 | -2.20 | -2.20 | -2.198 | -0.22 | 3 days, 11:45:00 | 0 0 1 0 | +| EGLD/USDT | 1 | -4.22 | -4.22 | -4.225 | -0.42 | 5:00:00 | 0 0 1 0 | +| ROSE/USDT | 1 | -8.93 | -8.93 | -8.938 | -0.89 | 7:20:00 | 0 0 1 0 | +| TOTAL | 12 | -0.07 | -0.82 | -0.818 | -0.08 | 1 day, 12:15:00 | 6 0 6 50.0 | +=============== SUMMARY METRICS ================ +| Metric | Value | +|------------------------+---------------------| +| Backtesting from | 2021-01-01 00:00:00 | +| Backtesting to | 2021-11-20 00:00:00 | +| Max open trades | 15 | +| | | +| Total/Daily Avg Trades | 1587 / 4.91 | +| Starting balance | 1000.000 USDT | +| Final balance | 3892.547 USDT | +| Absolute profit | 2892.547 USDT | +| Total profit % | 289.25% | +| Trades per day | 4.91 | +| Avg. daily profit % | 0.90% | +| Avg. stake amount | 100.000 USDT | +| Total trade volume | 158700.000 USDT | +| | | +| Best Pair | AVAX/USDT 401.62% | +| Worst Pair | GALA/USDT -16.79% | +| Best trade | XRP/USDT 30.84% | +| Worst trade | ZEC/USDT -12.97% | +| Best day | 183.579 USDT | +| Worst day | -231.257 USDT | +| Days win/draw/lose | 213 / 9 / 102 | +| Avg. Duration Winners | 2 days, 4:26:00 | +| Avg. Duration Loser | 2 days, 3:07:00 | +| Rejected Buy signals | 0 | +| | | +| Min balance | 1007.271 USDT | +| Max balance | 3959.355 USDT | +| Drawdown | 1001.50% | +| Drawdown | 1002.499 USDT | +| Drawdown high | 2542.409 USDT | +| Drawdown low | 1539.911 USDT | +| Drawdown Start | 2021-05-07 00:25:00 | +| Drawdown End | 2021-06-22 13:40:00 | +| Market change | 2716.79% | +================================================ \ No newline at end of file