mirror of
https://github.com/freqtrade/freqtrade.git
synced 2025-11-29 08:33:07 +00:00
Compare commits
607 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28399aaab2 | ||
|
|
560085ab92 | ||
|
|
17c315bf4a | ||
|
|
e8145e0057 | ||
|
|
ae9073885f | ||
|
|
61886942c5 | ||
|
|
12d31c4acb | ||
|
|
14cfdb13c5 | ||
|
|
e58ab7852a | ||
|
|
dfae7ca2ec | ||
|
|
17cd0452ec | ||
|
|
9fe361f6fc | ||
|
|
da7f35db8d | ||
|
|
4f216eb861 | ||
|
|
53383f3184 | ||
|
|
b51c937e87 | ||
|
|
f3d7f67671 | ||
|
|
87001d0806 | ||
|
|
ffce0dfb2e | ||
|
|
c1d08019d9 | ||
|
|
bfa95ad976 | ||
|
|
4a4b39f898 | ||
|
|
215499a3f8 | ||
|
|
8c11974644 | ||
|
|
7dcbd8108e | ||
|
|
e00b74c0e0 | ||
|
|
5ef439dd0f | ||
|
|
538c7b3c97 | ||
|
|
81456b2340 | ||
|
|
dfec3f16ab | ||
|
|
e67499e6cc | ||
|
|
a1b5e7899f | ||
|
|
a88a149999 | ||
|
|
c646abc05d | ||
|
|
f2a1561d31 | ||
|
|
d7a3aeaa1b | ||
|
|
4b9a63de19 | ||
|
|
07985c2cae | ||
|
|
0c13414f14 | ||
|
|
1021c7a7af | ||
|
|
5306fb3709 | ||
|
|
1afddf9db4 | ||
|
|
929538dd5f | ||
|
|
f432e65a14 | ||
|
|
dfc1193c52 | ||
|
|
25077a1008 | ||
|
|
b3b3bf6c1d | ||
|
|
a7ebbd89c0 | ||
|
|
ee25635c79 | ||
|
|
b8e19ae78a | ||
|
|
937bd892fd | ||
|
|
adce6e4f68 | ||
|
|
0b1ba0458d | ||
|
|
7668b95900 | ||
|
|
47465f198c | ||
|
|
f3d03dd26a | ||
|
|
ecdd84efd8 | ||
|
|
0ad6a6a951 | ||
|
|
dc97f709d7 | ||
|
|
34ccf141d1 | ||
|
|
f0b16c277c | ||
|
|
68061444d8 | ||
|
|
93356286d8 | ||
|
|
84e816fc5b | ||
|
|
12a482a6ba | ||
|
|
a26a40d00f | ||
|
|
64c6b4718d | ||
|
|
d1ebdf5df2 | ||
|
|
496c4947bd | ||
|
|
82f98746bd | ||
|
|
65418c7c00 | ||
|
|
bbf6bade7c | ||
|
|
b6cde05f0d | ||
|
|
0a3a1d7eca | ||
|
|
947cbdd858 | ||
|
|
8c999cd60c | ||
|
|
ad5d2a36bc | ||
|
|
1666746692 | ||
|
|
3a74fc85b4 | ||
|
|
92a1c304a8 | ||
|
|
d8c24663fc | ||
|
|
195c15c8dc | ||
|
|
44c06f6d20 | ||
|
|
c268f20d18 | ||
|
|
83c92f3ac1 | ||
|
|
b4a3eccfa3 | ||
|
|
837d006e11 | ||
|
|
c1f1639807 | ||
|
|
8f71e45666 | ||
|
|
17fa2c9933 | ||
|
|
22fdf157ef | ||
|
|
6c8b39d32a | ||
|
|
5a2b3d9d87 | ||
|
|
8641796d04 | ||
|
|
c5c5cfb4d5 | ||
|
|
c74033d7f1 | ||
|
|
d8c6873ee9 | ||
|
|
b0cc2a2a1d | ||
|
|
226f9b7857 | ||
|
|
1abf72855e | ||
|
|
b044410e42 | ||
|
|
b64052850f | ||
|
|
8868b10042 | ||
|
|
b18190891d | ||
|
|
5b001eec3e | ||
|
|
882ca44816 | ||
|
|
60157606fe | ||
|
|
cbb0294cd2 | ||
|
|
1b4802ab73 | ||
|
|
6b7f210ef3 | ||
|
|
571b154345 | ||
|
|
9a2b5a1219 | ||
|
|
fe9de41d58 | ||
|
|
daff50e79e | ||
|
|
09b687d66f | ||
|
|
665ff9f14e | ||
|
|
bd1a12ceaa | ||
|
|
d33e931a0d | ||
|
|
fbe38c362c | ||
|
|
718efc828a | ||
|
|
6d3ed84807 | ||
|
|
28e5efc902 | ||
|
|
510c47bd78 | ||
|
|
2e0065216f | ||
|
|
ce024c74e1 | ||
|
|
90e2935abf | ||
|
|
12cd654bce | ||
|
|
c3ea793abb | ||
|
|
c901f6d1dd | ||
|
|
193cfb634c | ||
|
|
a2b7620b64 | ||
|
|
9d91a4a298 | ||
|
|
3b6dbaccf8 | ||
|
|
3fe9e6b82b | ||
|
|
b6614045aa | ||
|
|
cbf27f0bdb | ||
|
|
deafe39f76 | ||
|
|
1f63eca1a8 | ||
|
|
2c0db908b5 | ||
|
|
ca9403f981 | ||
|
|
a8d96087c1 | ||
|
|
1649505121 | ||
|
|
41a063e2ac | ||
|
|
ba019ad143 | ||
|
|
6f3071f6ce | ||
|
|
5cdb2dd601 | ||
|
|
4db41f0225 | ||
|
|
cc8581164d | ||
|
|
7d50353697 | ||
|
|
d742f8b88f | ||
|
|
22bf036a66 | ||
|
|
c45351beff | ||
|
|
6d1d0751e8 | ||
|
|
15012ec58c | ||
|
|
fed91ff5a1 | ||
|
|
5b92af6a90 | ||
|
|
442a1ba50d | ||
|
|
c3f6aa17ee | ||
|
|
8dc278f1a0 | ||
|
|
5f907d4219 | ||
|
|
c39c5f254f | ||
|
|
0dfc4ed696 | ||
|
|
950a0df8b1 | ||
|
|
2a0dd4cf55 | ||
|
|
575c381e65 | ||
|
|
87061bcce8 | ||
|
|
a6d3995013 | ||
|
|
d0d40f4fce | ||
|
|
ce7d81325d | ||
|
|
7cfecab7e2 | ||
|
|
8f8da51808 | ||
|
|
f94fd7d5fc | ||
|
|
befc41ae54 | ||
|
|
02092926ab | ||
|
|
a49b4ab93a | ||
|
|
bd7549bc8b | ||
|
|
47e1d209db | ||
|
|
5d2f5ec12f | ||
|
|
2d2dc7f14a | ||
|
|
43bd2a060c | ||
|
|
b518d66aa2 | ||
|
|
2ba59cebe3 | ||
|
|
f165c85e69 | ||
|
|
fca454bba3 | ||
|
|
3dc1adfad4 | ||
|
|
38754e0598 | ||
|
|
8fb4446d82 | ||
|
|
3725ac145b | ||
|
|
7df0d06005 | ||
|
|
4e855b638b | ||
|
|
9783aba09c | ||
|
|
1936b20309 | ||
|
|
69de9687d0 | ||
|
|
bdeeca7d43 | ||
|
|
a7ba3cad85 | ||
|
|
ef5fcad8cf | ||
|
|
d8de6c8f7b | ||
|
|
c3239da266 | ||
|
|
1edf948e49 | ||
|
|
224c4a717b | ||
|
|
6202436bfd | ||
|
|
122144857e | ||
|
|
92bcf672fb | ||
|
|
6f4ffa0758 | ||
|
|
82186c8a7f | ||
|
|
50596e1a6a | ||
|
|
9edcbbb9a8 | ||
|
|
a59805d739 | ||
|
|
b96c06ffdc | ||
|
|
8abe358c1e | ||
|
|
2142b2aea3 | ||
|
|
53ba3ced06 | ||
|
|
a0f336c31f | ||
|
|
081fa2209a | ||
|
|
ca67d3fb2c | ||
|
|
5a6b43da46 | ||
|
|
7d3fa41911 | ||
|
|
b8f3f5e8d2 | ||
|
|
4849d5413f | ||
|
|
8b8bf6f97d | ||
|
|
9eea958e17 | ||
|
|
f8a151c2a5 | ||
|
|
1013c32316 | ||
|
|
d9ed7e1fb2 | ||
|
|
689da4c479 | ||
|
|
87d954a322 | ||
|
|
74f601b352 | ||
|
|
a3359b62d4 | ||
|
|
064708a354 | ||
|
|
066a46d7aa | ||
|
|
333660e628 | ||
|
|
81f80a6b22 | ||
|
|
dd613ac86c | ||
|
|
73c28890d7 | ||
|
|
61dd94ceb0 | ||
|
|
1fec5a873e | ||
|
|
9c4abcc927 | ||
|
|
b74e38ef63 | ||
|
|
86f640627f | ||
|
|
e27568a9d8 | ||
|
|
149133cc44 | ||
|
|
1b2d5a357f | ||
|
|
ecc6371733 | ||
|
|
0c66180cf3 | ||
|
|
5954c64cc3 | ||
|
|
d0b7a52202 | ||
|
|
c18085d173 | ||
|
|
b11119dbe5 | ||
|
|
67f88ea97a | ||
|
|
14353afa78 | ||
|
|
cefc833ab1 | ||
|
|
5c7c3a88fb | ||
|
|
14e2e160af | ||
|
|
000eb875a2 | ||
|
|
33c8969411 | ||
|
|
d90666d2e8 | ||
|
|
f568d63c41 | ||
|
|
a77618cc6b | ||
|
|
cac2c363fa | ||
|
|
ced1ce340a | ||
|
|
78943bf88d | ||
|
|
1b8a42d472 | ||
|
|
1d99b7d0f9 | ||
|
|
5c47a75f63 | ||
|
|
1ef2eeb456 | ||
|
|
faa398053a | ||
|
|
c32f8e972a | ||
|
|
23e4943b3a | ||
|
|
b3ec69fa8a | ||
|
|
dc4aaf0a82 | ||
|
|
d5b9c6dde8 | ||
|
|
bdfd3396cc | ||
|
|
c9256e0d9f | ||
|
|
c7e02b9a7f | ||
|
|
7d5cd998a3 | ||
|
|
96e8319e82 | ||
|
|
12d8fc8c3a | ||
|
|
1ad0e423c9 | ||
|
|
2ef1e203c6 | ||
|
|
959f81f4ac | ||
|
|
3090e47a12 | ||
|
|
02ab0eb139 | ||
|
|
5d8759de78 | ||
|
|
b13b00df17 | ||
|
|
04492e75b2 | ||
|
|
8d0ca7f5c1 | ||
|
|
d8b51875bf | ||
|
|
7f12f3a0e4 | ||
|
|
6afcc80937 | ||
|
|
efdb726362 | ||
|
|
3ca7407b09 | ||
|
|
22ba0e61e3 | ||
|
|
6e83890a13 | ||
|
|
8a8cc5e563 | ||
|
|
e1bf3bb825 | ||
|
|
0a4f82bc22 | ||
|
|
5ee7f8037d | ||
|
|
12728c0fcc | ||
|
|
875216cdc5 | ||
|
|
362f0895f4 | ||
|
|
903b580026 | ||
|
|
073b625355 | ||
|
|
b3b21e6b93 | ||
|
|
b8b4b2d2f3 | ||
|
|
0cf1f6dc88 | ||
|
|
502d50c988 | ||
|
|
ed8b8fbf61 | ||
|
|
956398ab21 | ||
|
|
f3154423bd | ||
|
|
fb64ac942b | ||
|
|
3fc40f45b3 | ||
|
|
ca5ccc8799 | ||
|
|
057cc2538e | ||
|
|
ed22789a1a | ||
|
|
2abf22e37b | ||
|
|
9b08b51ad8 | ||
|
|
83cdf76636 | ||
|
|
f545113840 | ||
|
|
f86bc71c43 | ||
|
|
8af9875d45 | ||
|
|
8a9b31eccd | ||
|
|
124c051432 | ||
|
|
67bcf38ea5 | ||
|
|
a98dd0eea3 | ||
|
|
103f64227f | ||
|
|
5b481009d6 | ||
|
|
3da6a8146e | ||
|
|
a8b313d387 | ||
|
|
574419f3a4 | ||
|
|
5793888113 | ||
|
|
b835af58c5 | ||
|
|
ca0b6998f0 | ||
|
|
77c3894fe2 | ||
|
|
0c46afaee4 | ||
|
|
b6e9609039 | ||
|
|
1b6dadbc87 | ||
|
|
1b18856d0b | ||
|
|
35e9805ef1 | ||
|
|
dcf38557ee | ||
|
|
54d78fd1c6 | ||
|
|
6040c391d3 | ||
|
|
b4a5f66f29 | ||
|
|
e105ea660b | ||
|
|
1149393789 | ||
|
|
d4655660f4 | ||
|
|
e656063771 | ||
|
|
0c6ed588b5 | ||
|
|
45dc667233 | ||
|
|
10d79f6232 | ||
|
|
3c20106e7d | ||
|
|
1e1c9a28f2 | ||
|
|
6a01985fd1 | ||
|
|
be572ba046 | ||
|
|
f711afd843 | ||
|
|
d6f58cd6cf | ||
|
|
9dd1ce71ca | ||
|
|
0a0a8428d5 | ||
|
|
fdc248a1b1 | ||
|
|
bfadd54ea3 | ||
|
|
ed23dc0f72 | ||
|
|
27e3ae8c24 | ||
|
|
f6cb446bff | ||
|
|
72786ca706 | ||
|
|
3e2a799d9f | ||
|
|
841f57800e | ||
|
|
5b6ea5ca90 | ||
|
|
162c1c606c | ||
|
|
de0759c36a | ||
|
|
1dbef58fe9 | ||
|
|
a63280bfa5 | ||
|
|
093a3f0e25 | ||
|
|
f8fc8a7f33 | ||
|
|
f8bf850673 | ||
|
|
d06a531c72 | ||
|
|
b341bc5a8c | ||
|
|
5ac1cf929b | ||
|
|
eca2ca3dc2 | ||
|
|
1d8fd059c6 | ||
|
|
a95972ae3f | ||
|
|
37985dfeea | ||
|
|
768efc45ff | ||
|
|
e6b5233d2f | ||
|
|
6fa1133c61 | ||
|
|
b1adb2b13d | ||
|
|
16378d32d7 | ||
|
|
33105a996e | ||
|
|
20fca07d8f | ||
|
|
8ee40ade45 | ||
|
|
a6d76cad39 | ||
|
|
30ead79e11 | ||
|
|
5c859d929b | ||
|
|
c89058788e | ||
|
|
35c3868c56 | ||
|
|
3d1568783e | ||
|
|
194d53acf8 | ||
|
|
85689ebc1c | ||
|
|
4fcc9dd587 | ||
|
|
05f19d574a | ||
|
|
1a24559729 | ||
|
|
7a51c9d540 | ||
|
|
1d22377cad | ||
|
|
82cd343fc1 | ||
|
|
751d98495f | ||
|
|
49b119f1dc | ||
|
|
6ba0eaf593 | ||
|
|
87f07d5f91 | ||
|
|
a24f5d64a1 | ||
|
|
30eb32862c | ||
|
|
6ae5724ad1 | ||
|
|
90a57e7384 | ||
|
|
16379bbcc5 | ||
|
|
05bb7e2fad | ||
|
|
6e98635345 | ||
|
|
de8dd83340 | ||
|
|
b3296b75e9 | ||
|
|
2f9ec970a5 | ||
|
|
568c579cab | ||
|
|
e116b71400 | ||
|
|
a85dc6972d | ||
|
|
feab28de91 | ||
|
|
0aa05855d5 | ||
|
|
44591053b6 | ||
|
|
c11de3ac38 | ||
|
|
b797a5bf2f | ||
|
|
1d9d2fce7f | ||
|
|
0e92372b0b | ||
|
|
68fbab5c32 | ||
|
|
596f6711f3 | ||
|
|
c2296d83c3 | ||
|
|
9a3ada65f5 | ||
|
|
df50e03239 | ||
|
|
9d0f5df549 | ||
|
|
6b41594c53 | ||
|
|
2d194995bc | ||
|
|
904580b914 | ||
|
|
9373779fba | ||
|
|
692bb36c80 | ||
|
|
8de5e2b7b8 | ||
|
|
6185761727 | ||
|
|
6c625c77c0 | ||
|
|
9950ed715e | ||
|
|
9b9ef6ae6d | ||
|
|
8075106286 | ||
|
|
6b033c211e | ||
|
|
8be31275d7 | ||
|
|
a3d3a4de51 | ||
|
|
f1b890954a | ||
|
|
0f2ff70bdf | ||
|
|
4c42bd7100 | ||
|
|
9acf8943ed | ||
|
|
4986f356ee | ||
|
|
ca390eea6a | ||
|
|
cd9c45e875 | ||
|
|
c90ac30077 | ||
|
|
466914d5c7 | ||
|
|
f86f955e24 | ||
|
|
affab24185 | ||
|
|
125126fb86 | ||
|
|
010ed6992b | ||
|
|
d8bbd4054e | ||
|
|
baefaeb311 | ||
|
|
288db16198 | ||
|
|
eb4199fe80 | ||
|
|
9a151b1ecf | ||
|
|
7dddab18c4 | ||
|
|
a85afd9af3 | ||
|
|
d09efa6dd1 | ||
|
|
f817b21d17 | ||
|
|
b7298f472c | ||
|
|
96baa90003 | ||
|
|
fae7198bb4 | ||
|
|
55be046933 | ||
|
|
38fa7068ca | ||
|
|
e5389be209 | ||
|
|
19a997a2db | ||
|
|
eac440649b | ||
|
|
21a47bb1ac | ||
|
|
b839e159a8 | ||
|
|
91df1257e7 | ||
|
|
88f8e831d7 | ||
|
|
627eee5fcf | ||
|
|
26c7752b7c | ||
|
|
1aaf0c2034 | ||
|
|
ac3a5900b4 | ||
|
|
60695fe205 | ||
|
|
2e2f40ca10 | ||
|
|
1f09b90dbe | ||
|
|
9c39b99ec6 | ||
|
|
2e343b9fbd | ||
|
|
90aaaa50fc | ||
|
|
087fb31f26 | ||
|
|
3edc442f48 | ||
|
|
b6cf3cc57d | ||
|
|
be2d97b559 | ||
|
|
a3f23fd4fb | ||
|
|
68f32d76ae | ||
|
|
bffb3120c1 | ||
|
|
bb596d6f8d | ||
|
|
ca573a828f | ||
|
|
e9cd840f5b | ||
|
|
3fcf6559ab | ||
|
|
7894c08b83 | ||
|
|
296c14afc0 | ||
|
|
b2b1518708 | ||
|
|
93967ad8f0 | ||
|
|
6082beb58d | ||
|
|
8309853439 | ||
|
|
2522a3be61 | ||
|
|
19b38a02fa | ||
|
|
4935e23271 | ||
|
|
a4f9a25007 | ||
|
|
795a0b81ee | ||
|
|
e5268a0449 | ||
|
|
c81761e3e2 | ||
|
|
8fbb4e1e60 | ||
|
|
937aa5c70e | ||
|
|
db647ab4d5 | ||
|
|
98652bfd89 | ||
|
|
63010bc5e9 | ||
|
|
dd388a51e0 | ||
|
|
22f109bab1 | ||
|
|
67e2d9c730 | ||
|
|
07fb941758 | ||
|
|
6fca35adae | ||
|
|
85f4a8daea | ||
|
|
2595479e43 | ||
|
|
ac9b26cc57 | ||
|
|
31aa397492 | ||
|
|
b77739b5ca | ||
|
|
b603771242 | ||
|
|
722d5b2319 | ||
|
|
91ace759c5 | ||
|
|
3d8d2fc0c6 | ||
|
|
58154d76ae | ||
|
|
9c1183bc59 | ||
|
|
38feb90f9e | ||
|
|
4632839fc5 | ||
|
|
26ea4fdcc9 | ||
|
|
3637d7a54c | ||
|
|
583e20dc9d | ||
|
|
6b78b1c882 | ||
|
|
ee3d46c8fa | ||
|
|
59e52bb601 | ||
|
|
553dbccec7 | ||
|
|
2e06eb0e7b | ||
|
|
31c4d35932 | ||
|
|
eb03382b2d | ||
|
|
bb08880c4a | ||
|
|
948487518d | ||
|
|
be5d158761 | ||
|
|
4f2681500a | ||
|
|
00237d68e4 | ||
|
|
20325a2b5e | ||
|
|
be40e828a9 | ||
|
|
916ef43f7f | ||
|
|
2b01d2e06b | ||
|
|
fcd0c1d606 | ||
|
|
0553486e55 | ||
|
|
4fa8c3f9ab | ||
|
|
543c77fe00 | ||
|
|
e77ce8d481 | ||
|
|
285867f8c6 | ||
|
|
276ef6cdde | ||
|
|
c5088e6b66 | ||
|
|
62f05964b4 | ||
|
|
b2898cf742 | ||
|
|
31625befd1 | ||
|
|
394535c2e8 | ||
|
|
16576d37b1 | ||
|
|
53bd2d71a0 | ||
|
|
df4b44d09e | ||
|
|
b6b3429b62 | ||
|
|
0adb264c9b | ||
|
|
445c3a67db | ||
|
|
06c4b661f7 | ||
|
|
b8dffe0eb0 | ||
|
|
85772ac7f7 | ||
|
|
3ec72f88e2 | ||
|
|
4f4f927b97 | ||
|
|
46fab55378 | ||
|
|
1d44f75659 | ||
|
|
a4416b885a | ||
|
|
209c2e0ceb | ||
|
|
324aada2bc | ||
|
|
6f58e01f8e | ||
|
|
50d60fad89 | ||
|
|
7286568447 | ||
|
|
3116fd34cf | ||
|
|
f70815dd8e | ||
|
|
47151e77e1 | ||
|
|
5a1f96d35c | ||
|
|
fe03bf7ce0 | ||
|
|
d09160fff8 | ||
|
|
f46c8cdc4a | ||
|
|
db57f83922 | ||
|
|
bbf0ac83a1 | ||
|
|
a9714727b1 | ||
|
|
b6a65ce125 | ||
|
|
315421037f | ||
|
|
4ff200d315 | ||
|
|
5700f789e2 | ||
|
|
689767e3ca | ||
|
|
66efc9a469 | ||
|
|
4c3e477667 | ||
|
|
8cdae5f56e | ||
|
|
347295ecaf | ||
|
|
7272204d58 |
8
.github/ISSUE_TEMPLATE/bug_report.md
vendored
8
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: Bug report
|
name: Bug report
|
||||||
about: Create a report to help us improve
|
about: Create a report to help us improve. Do not use this for strategy assistance.
|
||||||
title: ''
|
title: ''
|
||||||
labels: "Triage Needed"
|
labels: "Triage Needed"
|
||||||
assignees: ''
|
assignees: ''
|
||||||
@@ -12,7 +12,11 @@ Have you searched for similar issues before posting it?
|
|||||||
If you have discovered a bug in the bot, please [search the issue tracker](https://github.com/freqtrade/freqtrade/issues?q=is%3Aissue).
|
If you have discovered a bug in the bot, please [search the issue tracker](https://github.com/freqtrade/freqtrade/issues?q=is%3Aissue).
|
||||||
If it hasn't been reported, please create a new issue.
|
If it hasn't been reported, please create a new issue.
|
||||||
|
|
||||||
Please do not use bug reports to request new features.
|
Has your strategy or configuration been generated by an AI model, and is now not working?
|
||||||
|
This is almost certainly NOT a bug in Freqtrade, but a problem with the code your AI model generated.
|
||||||
|
Please consult the documentation. We'll close such issues and point to the documentation.
|
||||||
|
|
||||||
|
Please do not use the bug report template to request new features.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## Describe your environment
|
## Describe your environment
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: Feature request
|
name: Feature request
|
||||||
about: Suggest an idea for this project
|
about: Suggest a new feature or idea for this project
|
||||||
title: ''
|
title: ''
|
||||||
labels: ''
|
labels: ''
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|||||||
9
.github/ISSUE_TEMPLATE/question.md
vendored
9
.github/ISSUE_TEMPLATE/question.md
vendored
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: Question
|
name: Question
|
||||||
about: Ask a question you could not find an answer in the docs
|
about: Ask a question you could not find an answer in the docs. Use this template if you've got problems with your strategy.
|
||||||
title: ''
|
title: ''
|
||||||
labels: "Question"
|
labels: "Question"
|
||||||
assignees: ''
|
assignees: ''
|
||||||
@@ -8,9 +8,12 @@ assignees: ''
|
|||||||
---
|
---
|
||||||
<!--
|
<!--
|
||||||
Have you searched for similar issues before posting it?
|
Have you searched for similar issues before posting it?
|
||||||
Did you have a VERY good look at the [documentation](https://www.freqtrade.io/en/latest/) and are sure that the question is not explained there
|
Did you have a VERY good look at the [documentation](https://www.freqtrade.io/) and are sure that the question is not explained there
|
||||||
|
|
||||||
Please do not use the question template to report bugs or to request new features.
|
Please do not use the question template to report bugs or to request new features.
|
||||||
|
|
||||||
|
Has your strategy or configuration been generated by an AI model, and is now not working?
|
||||||
|
Please consult the documentation. We'll close such issues and point to the documentation.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## Describe your environment
|
## Describe your environment
|
||||||
@@ -22,4 +25,4 @@ Please do not use the question template to report bugs or to request new feature
|
|||||||
|
|
||||||
## Your question
|
## Your question
|
||||||
|
|
||||||
*Ask the question you have not been able to find an answer in the [Documentation](https://www.freqtrade.io/en/latest/)*
|
*Ask the question you have not been able to find an answer in the [Documentation](https://www.freqtrade.io/)*
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
run: python build_helpers/binance_update_lev_tiers.py
|
run: python build_helpers/binance_update_lev_tiers.py
|
||||||
|
|
||||||
|
|
||||||
- uses: peter-evans/create-pull-request@v7
|
- uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.REPO_SCOPED_TOKEN }}
|
token: ${{ secrets.REPO_SCOPED_TOKEN }}
|
||||||
add-paths: freqtrade/exchange/binance_leverage_tiers.json
|
add-paths: freqtrade/exchange/binance_leverage_tiers.json
|
||||||
|
|||||||
46
.github/workflows/ci.yml
vendored
46
.github/workflows/ci.yml
vendored
@@ -38,8 +38,9 @@ jobs:
|
|||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@v5
|
uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||||
with:
|
with:
|
||||||
|
activate-environment: true
|
||||||
enable-cache: true
|
enable-cache: true
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
cache-dependency-glob: "requirements**.txt"
|
cache-dependency-glob: "requirements**.txt"
|
||||||
@@ -73,17 +74,17 @@ jobs:
|
|||||||
python build_helpers/freqtrade_client_version_align.py
|
python build_helpers/freqtrade_client_version_align.py
|
||||||
|
|
||||||
- name: Tests
|
- name: Tests
|
||||||
if: (!(runner.os == 'Linux' && matrix.python-version == '3.12' && matrix.os == 'ubuntu-22.04'))
|
if: (!(runner.os == 'Linux' && matrix.python-version == '3.12' && matrix.os == 'ubuntu-24.04'))
|
||||||
run: |
|
run: |
|
||||||
pytest --random-order
|
pytest --random-order
|
||||||
|
|
||||||
- name: Tests with Coveralls
|
- name: Tests with Coveralls
|
||||||
if: (runner.os == 'Linux' && matrix.python-version == '3.12' && matrix.os == 'ubuntu-22.04')
|
if: (runner.os == 'Linux' && matrix.python-version == '3.12' && matrix.os == 'ubuntu-24.04')
|
||||||
run: |
|
run: |
|
||||||
pytest --random-order --cov=freqtrade --cov=freqtrade_client --cov-config=.coveragerc
|
pytest --random-order --cov=freqtrade --cov=freqtrade_client --cov-config=.coveragerc
|
||||||
|
|
||||||
- name: Coveralls
|
- name: Coveralls
|
||||||
if: (runner.os == 'Linux' && matrix.python-version == '3.12' && matrix.os == 'ubuntu-22.04')
|
if: (runner.os == 'Linux' && matrix.python-version == '3.12' && matrix.os == 'ubuntu-24.04')
|
||||||
env:
|
env:
|
||||||
# Coveralls token. Not used as secret due to github not providing secrets to forked repositories
|
# Coveralls token. Not used as secret due to github not providing secrets to forked repositories
|
||||||
COVERALLS_REPO_TOKEN: 6D1m0xupS3FgutfuGao8keFf9Hc0FpIXu
|
COVERALLS_REPO_TOKEN: 6D1m0xupS3FgutfuGao8keFf9Hc0FpIXu
|
||||||
@@ -139,11 +140,12 @@ jobs:
|
|||||||
ruff format --check
|
ruff format --check
|
||||||
|
|
||||||
- name: Mypy
|
- name: Mypy
|
||||||
|
if: matrix.os == 'ubuntu-24.04'
|
||||||
run: |
|
run: |
|
||||||
mypy freqtrade scripts tests
|
mypy freqtrade scripts tests
|
||||||
|
|
||||||
- name: Discord notification
|
- name: Discord notification
|
||||||
uses: rjstone/discord-webhook-notify@v1
|
uses: rjstone/discord-webhook-notify@1399c1b2d57cc05894d506d2cfdc33c5f012b993 #v1.1.1
|
||||||
if: failure() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
|
if: failure() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
|
||||||
with:
|
with:
|
||||||
severity: error
|
severity: error
|
||||||
@@ -169,8 +171,9 @@ jobs:
|
|||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@v5
|
uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||||
with:
|
with:
|
||||||
|
activate-environment: true
|
||||||
enable-cache: true
|
enable-cache: true
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
cache-dependency-glob: "requirements**.txt"
|
cache-dependency-glob: "requirements**.txt"
|
||||||
@@ -264,11 +267,12 @@ jobs:
|
|||||||
ruff format --check
|
ruff format --check
|
||||||
|
|
||||||
- name: Mypy
|
- name: Mypy
|
||||||
|
if: matrix.os == 'macos-15'
|
||||||
run: |
|
run: |
|
||||||
mypy freqtrade scripts
|
mypy freqtrade scripts
|
||||||
|
|
||||||
- name: Discord notification
|
- name: Discord notification
|
||||||
uses: rjstone/discord-webhook-notify@v1
|
uses: rjstone/discord-webhook-notify@1399c1b2d57cc05894d506d2cfdc33c5f012b993 #v1.1.1
|
||||||
if: failure() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
|
if: failure() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
|
||||||
with:
|
with:
|
||||||
severity: info
|
severity: info
|
||||||
@@ -294,8 +298,9 @@ jobs:
|
|||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@v5
|
uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||||
with:
|
with:
|
||||||
|
activate-environment: true
|
||||||
enable-cache: true
|
enable-cache: true
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
cache-dependency-glob: "requirements**.txt"
|
cache-dependency-glob: "requirements**.txt"
|
||||||
@@ -361,7 +366,7 @@ jobs:
|
|||||||
shell: powershell
|
shell: powershell
|
||||||
|
|
||||||
- name: Discord notification
|
- name: Discord notification
|
||||||
uses: rjstone/discord-webhook-notify@v1
|
uses: rjstone/discord-webhook-notify@1399c1b2d57cc05894d506d2cfdc33c5f012b993 #v1.1.1
|
||||||
if: failure() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
|
if: failure() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
|
||||||
with:
|
with:
|
||||||
severity: error
|
severity: error
|
||||||
@@ -395,7 +400,7 @@ jobs:
|
|||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.12"
|
python-version: "3.12"
|
||||||
- uses: pre-commit/action@v3.0.1
|
- uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
|
||||||
|
|
||||||
docs-check:
|
docs-check:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
@@ -419,7 +424,7 @@ jobs:
|
|||||||
mkdocs build
|
mkdocs build
|
||||||
|
|
||||||
- name: Discord notification
|
- name: Discord notification
|
||||||
uses: rjstone/discord-webhook-notify@v1
|
uses: rjstone/discord-webhook-notify@1399c1b2d57cc05894d506d2cfdc33c5f012b993 #v1.1.1
|
||||||
if: failure() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
|
if: failure() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
|
||||||
with:
|
with:
|
||||||
severity: error
|
severity: error
|
||||||
@@ -429,7 +434,7 @@ jobs:
|
|||||||
|
|
||||||
build-linux-online:
|
build-linux-online:
|
||||||
# Run pytest with "live" checks
|
# Run pytest with "live" checks
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
@@ -441,8 +446,9 @@ jobs:
|
|||||||
python-version: "3.12"
|
python-version: "3.12"
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@v5
|
uses: astral-sh/setup-uv@f0ec1fc3b38f5e7cd731bb6ce540c5af426746bb # v6.1.0
|
||||||
with:
|
with:
|
||||||
|
activate-environment: true
|
||||||
enable-cache: true
|
enable-cache: true
|
||||||
python-version: "3.12"
|
python-version: "3.12"
|
||||||
cache-dependency-glob: "requirements**.txt"
|
cache-dependency-glob: "requirements**.txt"
|
||||||
@@ -499,14 +505,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Check user permission
|
- name: Check user permission
|
||||||
id: check
|
id: check
|
||||||
uses: scherermichael-oss/action-has-permission@1.0.6
|
uses: scherermichael-oss/action-has-permission@136e061bfe093832d87f090dd768e14e27a740d3 # 1.0.6
|
||||||
with:
|
with:
|
||||||
required-permission: write
|
required-permission: write
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Discord notification
|
- name: Discord notification
|
||||||
uses: rjstone/discord-webhook-notify@v1
|
uses: rjstone/discord-webhook-notify@1399c1b2d57cc05894d506d2cfdc33c5f012b993 #v1.1.1
|
||||||
if: always() && steps.check.outputs.has-permission && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
|
if: always() && steps.check.outputs.has-permission && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
|
||||||
with:
|
with:
|
||||||
severity: info
|
severity: info
|
||||||
@@ -578,7 +584,7 @@ jobs:
|
|||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Publish to PyPI (Test)
|
- name: Publish to PyPI (Test)
|
||||||
uses: pypa/gh-action-pypi-publish@v1.12.4
|
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
|
||||||
with:
|
with:
|
||||||
repository-url: https://test.pypi.org/legacy/
|
repository-url: https://test.pypi.org/legacy/
|
||||||
|
|
||||||
@@ -607,7 +613,7 @@ jobs:
|
|||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Publish to PyPI
|
- name: Publish to PyPI
|
||||||
uses: pypa/gh-action-pypi-publish@v1.12.4
|
uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
|
||||||
|
|
||||||
|
|
||||||
deploy-docker:
|
deploy-docker:
|
||||||
@@ -648,11 +654,11 @@ jobs:
|
|||||||
docker version -f '{{.Server.Experimental}}'
|
docker version -f '{{.Server.Experimental}}'
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
id: buildx
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 #v3.10.0
|
||||||
|
|
||||||
- name: Available platforms
|
- name: Available platforms
|
||||||
run: echo ${PLATFORMS}
|
run: echo ${PLATFORMS}
|
||||||
@@ -701,7 +707,7 @@ jobs:
|
|||||||
build_helpers/publish_docker_arm64.sh
|
build_helpers/publish_docker_arm64.sh
|
||||||
|
|
||||||
- name: Discord notification
|
- name: Discord notification
|
||||||
uses: rjstone/discord-webhook-notify@v1
|
uses: rjstone/discord-webhook-notify@1399c1b2d57cc05894d506d2cfdc33c5f012b993 #v1.1.1
|
||||||
if: always() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false) && (github.event_name != 'schedule')
|
if: always() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false) && (github.event_name != 'schedule')
|
||||||
with:
|
with:
|
||||||
severity: info
|
severity: info
|
||||||
|
|||||||
4
.github/workflows/devcontainer-build.yml
vendored
4
.github/workflows/devcontainer-build.yml
vendored
@@ -28,13 +28,13 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Pre-build dev container image
|
- name: Pre-build dev container image
|
||||||
uses: devcontainers/ci@v0.3
|
uses: devcontainers/ci@8bf61b26e9c3a98f69cb6ce2f88d24ff59b785c6 # v0.3.19
|
||||||
with:
|
with:
|
||||||
subFolder: .github
|
subFolder: .github
|
||||||
imageName: ghcr.io/${{ github.repository }}-devcontainer
|
imageName: ghcr.io/${{ github.repository }}-devcontainer
|
||||||
|
|||||||
2
.github/workflows/docker-update-readme.yml
vendored
2
.github/workflows/docker-update-readme.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Docker Hub Description
|
- name: Docker Hub Description
|
||||||
uses: peter-evans/dockerhub-description@v4
|
uses: peter-evans/dockerhub-description@432a30c9e07499fd01da9f8a49f0faf9e0ca5b77 # v4.0.2
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|||||||
2
.github/workflows/pre-commit-update.yml
vendored
2
.github/workflows/pre-commit-update.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
|||||||
- name: Run auto-update
|
- name: Run auto-update
|
||||||
run: pre-commit autoupdate
|
run: pre-commit autoupdate
|
||||||
|
|
||||||
- uses: peter-evans/create-pull-request@v7
|
- uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.REPO_SCOPED_TOKEN }}
|
token: ${{ secrets.REPO_SCOPED_TOKEN }}
|
||||||
add-paths: .pre-commit-config.yaml
|
add-paths: .pre-commit-config.yaml
|
||||||
|
|||||||
@@ -1,8 +1,20 @@
|
|||||||
# See https://pre-commit.com for more information
|
# See https://pre-commit.com for more information
|
||||||
# See https://pre-commit.com/hooks.html for more hooks
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
repos:
|
repos:
|
||||||
|
|
||||||
|
- repo: local
|
||||||
|
# Keep json schema in sync with the config schema
|
||||||
|
# This will write the files - and fail pre-commit if a file has been changed.
|
||||||
|
hooks:
|
||||||
|
- id: Extract config json schema
|
||||||
|
name: extract-config-json-schema
|
||||||
|
entry: "python build_helpers/extract_config_json_schema.py"
|
||||||
|
language: python
|
||||||
|
pass_filenames: false
|
||||||
|
additional_dependencies: ["python-rapidjson", "jsonschema"]
|
||||||
|
|
||||||
- repo: https://github.com/pycqa/flake8
|
- repo: https://github.com/pycqa/flake8
|
||||||
rev: "7.1.2"
|
rev: "7.2.0"
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: flake8
|
||||||
additional_dependencies: [Flake8-pyproject]
|
additional_dependencies: [Flake8-pyproject]
|
||||||
@@ -14,12 +26,12 @@ repos:
|
|||||||
- id: mypy
|
- id: mypy
|
||||||
exclude: build_helpers
|
exclude: build_helpers
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- types-cachetools==5.5.0.20240820
|
- types-cachetools==6.0.0.20250525
|
||||||
- types-filelock==3.2.7
|
- types-filelock==3.2.7
|
||||||
- types-requests==2.32.0.20250306
|
- types-requests==2.32.0.20250515
|
||||||
- types-tabulate==0.9.0.20241207
|
- types-tabulate==0.9.0.20241207
|
||||||
- types-python-dateutil==2.9.0.20241206
|
- types-python-dateutil==2.9.0.20250516
|
||||||
- SQLAlchemy==2.0.39
|
- SQLAlchemy==2.0.41
|
||||||
# stages: [push]
|
# stages: [push]
|
||||||
|
|
||||||
- repo: https://github.com/pycqa/isort
|
- repo: https://github.com/pycqa/isort
|
||||||
@@ -31,7 +43,7 @@ repos:
|
|||||||
|
|
||||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||||
# Ruff version.
|
# Ruff version.
|
||||||
rev: 'v0.11.2'
|
rev: 'v0.11.11'
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
- id: ruff-format
|
- id: ruff-format
|
||||||
@@ -70,6 +82,6 @@ repos:
|
|||||||
|
|
||||||
# Ensure github actions remain safe
|
# Ensure github actions remain safe
|
||||||
- repo: https://github.com/woodruffw/zizmor-pre-commit
|
- repo: https://github.com/woodruffw/zizmor-pre-commit
|
||||||
rev: v1.5.2
|
rev: v1.8.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: zizmor
|
- id: zizmor
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM python:3.12.9-slim-bookworm as base
|
FROM python:3.12.10-slim-bookworm as base
|
||||||
|
|
||||||
# Setup env
|
# Setup env
|
||||||
ENV LANG C.UTF-8
|
ENV LANG C.UTF-8
|
||||||
|
|||||||
@@ -12,7 +12,12 @@ secret = os.environ.get("FREQTRADE__EXCHANGE__SECRET")
|
|||||||
proxy = os.environ.get("CI_WEB_PROXY")
|
proxy = os.environ.get("CI_WEB_PROXY")
|
||||||
|
|
||||||
exchange = ccxt.binance(
|
exchange = ccxt.binance(
|
||||||
{"apiKey": key, "secret": secret, "httpsProxy": proxy, "options": {"defaultType": "swap"}}
|
{
|
||||||
|
"apiKey": key,
|
||||||
|
"secret": secret,
|
||||||
|
"httpsProxy": proxy,
|
||||||
|
"options": {"defaultType": "swap"},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
_ = exchange.load_markets()
|
_ = exchange.load_markets()
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import subprocess
|
import subprocess # noqa: S404, RUF100
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,23 @@ from pathlib import Path
|
|||||||
|
|
||||||
import rapidjson
|
import rapidjson
|
||||||
|
|
||||||
from freqtrade.configuration.config_schema import CONF_SCHEMA
|
|
||||||
|
|
||||||
|
|
||||||
def extract_config_json_schema():
|
def extract_config_json_schema():
|
||||||
|
try:
|
||||||
|
# Try to import from the installed package
|
||||||
|
from freqtrade.config_schema import CONF_SCHEMA
|
||||||
|
except ImportError:
|
||||||
|
# If freqtrade is not installed, add the parent directory to sys.path
|
||||||
|
# to import directly from the source
|
||||||
|
import sys
|
||||||
|
|
||||||
|
script_dir = Path(__file__).parent
|
||||||
|
freqtrade_dir = script_dir.parent
|
||||||
|
sys.path.insert(0, str(freqtrade_dir))
|
||||||
|
|
||||||
|
# Now try to import from the source
|
||||||
|
from freqtrade.config_schema import CONF_SCHEMA
|
||||||
|
|
||||||
schema_filename = Path(__file__).parent / "schema.json"
|
schema_filename = Path(__file__).parent / "schema.json"
|
||||||
with schema_filename.open("w") as f:
|
with schema_filename.open("w") as f:
|
||||||
rapidjson.dump(CONF_SCHEMA, f, indent=2)
|
rapidjson.dump(CONF_SCHEMA, f, indent=2)
|
||||||
|
|||||||
Binary file not shown.
@@ -610,11 +610,11 @@
|
|||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"chat_id": {
|
"chat_id": {
|
||||||
"description": "Telegram chat or group ID",
|
"description": "Telegram chat or group ID. Recommended to be set via environment variable FREQTRADE__TELEGRAM__CHAT_ID",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"topic_id": {
|
"topic_id": {
|
||||||
"description": "Telegram topic ID - only applicable for group chats",
|
"description": "Telegram topic ID - only applicable for group chats. Recommended to be set via environment variable FREQTRADE__TELEGRAM__TOPIC_ID",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"authorized_users": {
|
"authorized_users": {
|
||||||
@@ -773,9 +773,11 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"enabled": {
|
"enabled": {
|
||||||
|
"description": "Enable webhook notifications.",
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
"url": {
|
"url": {
|
||||||
|
"description": "Webhook URL. Recommended to be set via environment variable FREQTRADE__WEBHOOK__URL",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"format": {
|
"format": {
|
||||||
@@ -853,6 +855,7 @@
|
|||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
"webhook_url": {
|
"webhook_url": {
|
||||||
|
"description": "Discord webhook URL. Recommended to be set via environment variable FREQTRADE__DISCORD__WEBHOOK_URL",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"exit_fill": {
|
"exit_fill": {
|
||||||
@@ -1032,6 +1035,7 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
"running",
|
"running",
|
||||||
|
"paused",
|
||||||
"stopped"
|
"stopped"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -1167,28 +1171,35 @@
|
|||||||
"description": "Name of the exchange.",
|
"description": "Name of the exchange.",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"enable_ws": {
|
|
||||||
"description": "Enable WebSocket connections to the exchange.",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": true
|
|
||||||
},
|
|
||||||
"key": {
|
"key": {
|
||||||
"description": "API key for the exchange.",
|
"description": "API key for the exchange. Recommended to be set via environment variable FREQTRADE__EXCHANGE__KEY",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": ""
|
"default": ""
|
||||||
},
|
},
|
||||||
"secret": {
|
"secret": {
|
||||||
"description": "API secret for the exchange.",
|
"description": "API secret for the exchange. Recommended to be set via environment variable FREQTRADE__EXCHANGE__SECRET",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": ""
|
"default": ""
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"description": "Password for the exchange, if required.",
|
"description": "Password for the exchange, if required. Recommended to be set via environment variable FREQTRADE__EXCHANGE__PASSWORD",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": ""
|
"default": ""
|
||||||
},
|
},
|
||||||
"uid": {
|
"uid": {
|
||||||
"description": "User ID for the exchange, if required.",
|
"description": "User ID for the exchange, if required. Recommended to be set via environment variable FREQTRADE__EXCHANGE__UID",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"account_id": {
|
||||||
|
"description": "Account ID for the exchange, if required. Recommended to be set via environment variable FREQTRADE__EXCHANGE__ACCOUNT_ID",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"wallet_address": {
|
||||||
|
"description": "Wallet address for the exchange, if required. Usually used by DEX exchanges. Recommended to be set via environment variable FREQTRADE__EXCHANGE__WALLET_ADDRESS",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"private_key": {
|
||||||
|
"description": "Private key for the exchange, if required. Usually used by DEX exchanges. Recommended to be set via environment variable FREQTRADE__EXCHANGE__PRIVATE_KEY",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"pair_whitelist": {
|
"pair_whitelist": {
|
||||||
@@ -1212,6 +1223,11 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
|
"enable_ws": {
|
||||||
|
"description": "Enable WebSocket connections to the exchange.",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
"unknown_fee_rate": {
|
"unknown_fee_rate": {
|
||||||
"description": "Fee rate for unknown markets.",
|
"description": "Fee rate for unknown markets.",
|
||||||
"type": "number"
|
"type": "number"
|
||||||
@@ -1515,6 +1531,14 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
|
"indicator_periods_candles": {
|
||||||
|
"description": "Time periods to calculate indicators for. The indicators are added to the base indicator dataset.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "number",
|
||||||
|
"minimum": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
"use_SVM_to_remove_outliers": {
|
"use_SVM_to_remove_outliers": {
|
||||||
"description": "Use SVM to remove outliers from the features.",
|
"description": "Use SVM to remove outliers from the features.",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM python:3.11.11-slim-bookworm as base
|
FROM python:3.11.12-slim-bookworm as base
|
||||||
|
|
||||||
# Setup env
|
# Setup env
|
||||||
ENV LANG C.UTF-8
|
ENV LANG C.UTF-8
|
||||||
|
|||||||
@@ -161,56 +161,53 @@ class MyAwesomeStrategy(IStrategy):
|
|||||||
|
|
||||||
### Overriding Base estimator
|
### Overriding Base estimator
|
||||||
|
|
||||||
You can define your own estimator for Hyperopt by implementing `generate_estimator()` in the Hyperopt subclass.
|
You can define your own optuna sampler for Hyperopt by implementing `generate_estimator()` in the Hyperopt subclass.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class MyAwesomeStrategy(IStrategy):
|
class MyAwesomeStrategy(IStrategy):
|
||||||
class HyperOpt:
|
class HyperOpt:
|
||||||
def generate_estimator(dimensions: List['Dimension'], **kwargs):
|
def generate_estimator(dimensions: List['Dimension'], **kwargs):
|
||||||
return "RF"
|
return "NSGAIIISampler"
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Possible values are either one of "GP", "RF", "ET", "GBRT" (Details can be found in the [scikit-optimize documentation](https://scikit-optimize.github.io/)), or "an instance of a class that inherits from `RegressorMixin` (from sklearn) and where the `predict` method has an optional `return_std` argument, which returns `std(Y | x)` along with `E[Y | x]`".
|
Possible values are either one of "NSGAIISampler", "TPESampler", "GPSampler", "CmaEsSampler", "NSGAIIISampler", "QMCSampler" (Details can be found in the [optuna-samplers documentation](https://optuna.readthedocs.io/en/stable/reference/samplers/index.html)), or "an instance of a class that inherits from `optuna.samplers.BaseSampler`".
|
||||||
|
|
||||||
Some research will be necessary to find additional Regressors.
|
Some research will be necessary to find additional Samplers (from optunahub) for example.
|
||||||
|
|
||||||
Example for `ExtraTreesRegressor` ("ET") with additional parameters:
|
|
||||||
|
|
||||||
```python
|
|
||||||
class MyAwesomeStrategy(IStrategy):
|
|
||||||
class HyperOpt:
|
|
||||||
def generate_estimator(dimensions: List['Dimension'], **kwargs):
|
|
||||||
from skopt.learning import ExtraTreesRegressor
|
|
||||||
# Corresponds to "ET" - but allows additional parameters.
|
|
||||||
return ExtraTreesRegressor(n_estimators=100)
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
The `dimensions` parameter is the list of `skopt.space.Dimension` objects corresponding to the parameters to be optimized. It can be used to create isotropic kernels for the `skopt.learning.GaussianProcessRegressor` estimator. Here's an example:
|
|
||||||
|
|
||||||
```python
|
|
||||||
class MyAwesomeStrategy(IStrategy):
|
|
||||||
class HyperOpt:
|
|
||||||
def generate_estimator(dimensions: List['Dimension'], **kwargs):
|
|
||||||
from skopt.utils import cook_estimator
|
|
||||||
from skopt.learning.gaussian_process.kernels import (Matern, ConstantKernel)
|
|
||||||
kernel_bounds = (0.0001, 10000)
|
|
||||||
kernel = (
|
|
||||||
ConstantKernel(1.0, kernel_bounds) *
|
|
||||||
Matern(length_scale=np.ones(len(dimensions)), length_scale_bounds=[kernel_bounds for d in dimensions], nu=2.5)
|
|
||||||
)
|
|
||||||
kernel += (
|
|
||||||
ConstantKernel(1.0, kernel_bounds) *
|
|
||||||
Matern(length_scale=np.ones(len(dimensions)), length_scale_bounds=[kernel_bounds for d in dimensions], nu=1.5)
|
|
||||||
)
|
|
||||||
|
|
||||||
return cook_estimator("GP", space=dimensions, kernel=kernel, n_restarts_optimizer=2)
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! Note
|
!!! Note
|
||||||
While custom estimators can be provided, it's up to you as User to do research on possible parameters and analyze / understand which ones should be used.
|
While custom estimators can be provided, it's up to you as User to do research on possible parameters and analyze / understand which ones should be used.
|
||||||
If you're unsure about this, best use one of the Defaults (`"ET"` has proven to be the most versatile) without further parameters.
|
If you're unsure about this, best use one of the Defaults (`"NSGAIIISampler"` has proven to be the most versatile) without further parameters.
|
||||||
|
|
||||||
|
??? Example "Using `AutoSampler` from Optunahub"
|
||||||
|
|
||||||
|
[AutoSampler docs](https://hub.optuna.org/samplers/auto_sampler/)
|
||||||
|
|
||||||
|
Install the necessary dependencies
|
||||||
|
``` bash
|
||||||
|
pip install optunahub cmaes torch scipy
|
||||||
|
```
|
||||||
|
Implement `generate_estimator()` in your strategy
|
||||||
|
|
||||||
|
``` python
|
||||||
|
# ...
|
||||||
|
from freqtrade.strategy.interface import IStrategy
|
||||||
|
from typing import List
|
||||||
|
import optunahub
|
||||||
|
# ...
|
||||||
|
|
||||||
|
class my_strategy(IStrategy):
|
||||||
|
class HyperOpt:
|
||||||
|
def generate_estimator(dimensions: List["Dimension"], **kwargs):
|
||||||
|
if "random_state" in kwargs.keys():
|
||||||
|
return optunahub.load_module("samplers/auto_sampler").AutoSampler(seed=kwargs["random_state"])
|
||||||
|
else:
|
||||||
|
return optunahub.load_module("samplers/auto_sampler").AutoSampler()
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Obviously the same approach will work for all other Samplers optuna supports.
|
||||||
|
|
||||||
|
|
||||||
## Space options
|
## Space options
|
||||||
|
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ sudo loginctl enable-linger "$USER"
|
|||||||
If you run the bot as a service, you can use systemd service manager as a software watchdog monitoring freqtrade bot
|
If you run the bot as a service, you can use systemd service manager as a software watchdog monitoring freqtrade bot
|
||||||
state and restarting it in the case of failures. If the `internals.sd_notify` parameter is set to true in the
|
state and restarting it in the case of failures. If the `internals.sd_notify` parameter is set to true in the
|
||||||
configuration or the `--sd-notify` command line option is used, the bot will send keep-alive ping messages to systemd
|
configuration or the `--sd-notify` command line option is used, the bot will send keep-alive ping messages to systemd
|
||||||
using the sd_notify (systemd notifications) protocol and will also tell systemd its current state (Running or Stopped)
|
using the sd_notify (systemd notifications) protocol and will also tell systemd its current state (Running, Paused or Stopped)
|
||||||
when it changes.
|
when it changes.
|
||||||
|
|
||||||
The `freqtrade.service.watchdog` file contains an example of the service unit configuration file which uses systemd
|
The `freqtrade.service.watchdog` file contains an example of the service unit configuration file which uses systemd
|
||||||
@@ -189,13 +189,15 @@ as the watchdog.
|
|||||||
## Advanced Logging
|
## Advanced Logging
|
||||||
|
|
||||||
Freqtrade uses the default logging module provided by python.
|
Freqtrade uses the default logging module provided by python.
|
||||||
Python allows for extensive [logging configuration](https://docs.python.org/3/library/logging.config.html#logging.config.dictConfig) in this regards - way more than what can be covered here.
|
Python allows for extensive [logging configuration](https://docs.python.org/3/library/logging.config.html#logging.config.dictConfig) in this regard - way more than what can be covered here.
|
||||||
|
|
||||||
Default logging (Colored terminal output) is setup by default if no `log_config` is provided.
|
Default logging format (coloured terminal output) is set up by default if no `log_config` is provided in your freqtrade configuration.
|
||||||
Using `--logfile logfile.log` will enable the RotatingFileHandler.
|
Using `--logfile logfile.log` will enable the RotatingFileHandler.
|
||||||
If you're not content with the log format - or with the default settings provided for the RotatingFileHandler, you can customize logging to your liking.
|
|
||||||
|
|
||||||
The default configuration looks roughly like the below - with the file handler being provided - but not enabled.
|
If you're not content with the log format, or with the default settings provided for the RotatingFileHandler, you can customize logging to your liking by adding the `log_config` configuration to your freqtrade configuration file(s).
|
||||||
|
|
||||||
|
The default configuration looks roughly like the below, with the file handler being provided but not enabled as the `filename` is commented out.
|
||||||
|
Uncomment this line and supply a valid path/filename to enable it.
|
||||||
|
|
||||||
``` json hl_lines="5-7 13-16 27"
|
``` json hl_lines="5-7 13-16 27"
|
||||||
{
|
{
|
||||||
@@ -237,12 +239,12 @@ The default configuration looks roughly like the below - with the file handler b
|
|||||||
Highlighted lines in the above code-block define the Rich handler and belong together.
|
Highlighted lines in the above code-block define the Rich handler and belong together.
|
||||||
The formatter "standard" and "file" will belong to the FileHandler.
|
The formatter "standard" and "file" will belong to the FileHandler.
|
||||||
|
|
||||||
Each handler must use one of the defined formatters (by name) - and it's class must be available and a valid logging class.
|
Each handler must use one of the defined formatters (by name), its class must be available, and must be a valid logging class.
|
||||||
To actually use a handler - it must be in the "handlers" section inside the "root" segment.
|
To actually use a handler, it must be in the "handlers" section inside the "root" segment.
|
||||||
If this section is left out, freqtrade will provide no output (in the non-configured handler, anyway).
|
If this section is left out, freqtrade will provide no output (in the non-configured handler, anyway).
|
||||||
|
|
||||||
!!! Tip "Explicit log configuration"
|
!!! Tip "Explicit log configuration"
|
||||||
We recommend to extract the logging configuration from your main configuration, and provide it to your bot via [multiple configuration files](configuration.md#multiple-configuration-files) functionality. This will avoid unnecessary code duplication.
|
We recommend to extract the logging configuration from your main freqtrade configuration file, and provide it to your bot via [multiple configuration files](configuration.md#multiple-configuration-files) functionality. This will avoid unnecessary code duplication.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -313,7 +315,6 @@ $RepeatedMsgReduction on
|
|||||||
|
|
||||||
The syslog address can be either a Unix domain socket (socket filename) or a UDP socket specification, consisting of IP address and UDP port, separated by the `:` character.
|
The syslog address can be either a Unix domain socket (socket filename) or a UDP socket specification, consisting of IP address and UDP port, separated by the `:` character.
|
||||||
|
|
||||||
|
|
||||||
So, the following are the examples of possible addresses:
|
So, the following are the examples of possible addresses:
|
||||||
|
|
||||||
* `"address": "/dev/log"` -- log to syslog (rsyslog) using the `/dev/log` socket, suitable for most systems.
|
* `"address": "/dev/log"` -- log to syslog (rsyslog) using the `/dev/log` socket, suitable for most systems.
|
||||||
@@ -321,20 +322,18 @@ So, the following are the examples of possible addresses:
|
|||||||
* `"address": "localhost:514"` -- log to local syslog using UDP socket, if it listens on port 514.
|
* `"address": "localhost:514"` -- log to local syslog using UDP socket, if it listens on port 514.
|
||||||
* `"address": "<ip>:514"` -- log to remote syslog at IP address and port 514. This may be used on Windows for remote logging to an external syslog server.
|
* `"address": "<ip>:514"` -- log to remote syslog at IP address and port 514. This may be used on Windows for remote logging to an external syslog server.
|
||||||
|
|
||||||
|
|
||||||
??? Info "Deprecated - configure syslog via command line"
|
??? Info "Deprecated - configure syslog via command line"
|
||||||
|
`--logfile syslog:<syslog_address>` -- send log messages to `syslog` service using the `<syslog_address>` as the syslog address.
|
||||||
|
|
||||||
`--logfile syslog:<syslog_address>` -- send log messages to `syslog` service using the `<syslog_address>` as the syslog address.
|
The syslog address can be either a Unix domain socket (socket filename) or a UDP socket specification, consisting of IP address and UDP port, separated by the `:` character.
|
||||||
|
|
||||||
The syslog address can be either a Unix domain socket (socket filename) or a UDP socket specification, consisting of IP address and UDP port, separated by the `:` character.
|
So, the following are the examples of possible usages:
|
||||||
|
|
||||||
So, the following are the examples of possible usages:
|
* `--logfile syslog:/dev/log` -- log to syslog (rsyslog) using the `/dev/log` socket, suitable for most systems.
|
||||||
|
* `--logfile syslog` -- same as above, the shortcut for `/dev/log`.
|
||||||
* `--logfile syslog:/dev/log` -- log to syslog (rsyslog) using the `/dev/log` socket, suitable for most systems.
|
* `--logfile syslog:/var/run/syslog` -- log to syslog (rsyslog) using the `/var/run/syslog` socket. Use this on MacOS.
|
||||||
* `--logfile syslog` -- same as above, the shortcut for `/dev/log`.
|
* `--logfile syslog:localhost:514` -- log to local syslog using UDP socket, if it listens on port 514.
|
||||||
* `--logfile syslog:/var/run/syslog` -- log to syslog (rsyslog) using the `/var/run/syslog` socket. Use this on MacOS.
|
* `--logfile syslog:<ip>:514` -- log to remote syslog at IP address and port 514. This may be used on Windows for remote logging to an external syslog server.
|
||||||
* `--logfile syslog:localhost:514` -- log to local syslog using UDP socket, if it listens on port 514.
|
|
||||||
* `--logfile syslog:<ip>:514` -- log to remote syslog at IP address and port 514. This may be used on Windows for remote logging to an external syslog server.
|
|
||||||
|
|
||||||
### Logging to journald
|
### Logging to journald
|
||||||
|
|
||||||
|
|||||||
BIN
docs/assets/freqUI-chart-annotations-dark.png
Normal file
BIN
docs/assets/freqUI-chart-annotations-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 111 KiB |
BIN
docs/assets/freqUI-chart-annotations-light.png
Normal file
BIN
docs/assets/freqUI-chart-annotations-light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 109 KiB |
@@ -59,7 +59,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ usage: freqtrade hyperopt [-h] [-v] [--no-color] [--logfile FILE] [-V]
|
|||||||
[--random-state INT] [--min-trades INT]
|
[--random-state INT] [--min-trades INT]
|
||||||
[--hyperopt-loss NAME] [--disable-param-export]
|
[--hyperopt-loss NAME] [--disable-param-export]
|
||||||
[--ignore-missing-spaces] [--analyze-per-epoch]
|
[--ignore-missing-spaces] [--analyze-per-epoch]
|
||||||
|
[--early-stop INT]
|
||||||
|
|
||||||
options:
|
options:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
@@ -79,6 +80,7 @@ options:
|
|||||||
SortinoHyperOptLoss, SortinoHyperOptLossDaily,
|
SortinoHyperOptLoss, SortinoHyperOptLossDaily,
|
||||||
CalmarHyperOptLoss, MaxDrawDownHyperOptLoss,
|
CalmarHyperOptLoss, MaxDrawDownHyperOptLoss,
|
||||||
MaxDrawDownRelativeHyperOptLoss,
|
MaxDrawDownRelativeHyperOptLoss,
|
||||||
|
MaxDrawDownPerPairHyperOptLoss,
|
||||||
ProfitDrawDownHyperOptLoss, MultiMetricHyperOptLoss
|
ProfitDrawDownHyperOptLoss, MultiMetricHyperOptLoss
|
||||||
--disable-param-export
|
--disable-param-export
|
||||||
Disable automatic hyperopt parameter export.
|
Disable automatic hyperopt parameter export.
|
||||||
@@ -86,6 +88,8 @@ options:
|
|||||||
Suppress errors for any requested Hyperopt spaces that
|
Suppress errors for any requested Hyperopt spaces that
|
||||||
do not contain any parameters.
|
do not contain any parameters.
|
||||||
--analyze-per-epoch Run populate_indicators once per epoch.
|
--analyze-per-epoch Run populate_indicators once per epoch.
|
||||||
|
--early-stop INT Early stop hyperopt if no improvement after (default:
|
||||||
|
0) epochs.
|
||||||
|
|
||||||
Common arguments:
|
Common arguments:
|
||||||
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
|
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
|
||||||
@@ -102,7 +106,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
```
|
```
|
||||||
usage: freqtrade install-ui [-h] [--erase] [--ui-version UI_VERSION]
|
usage: freqtrade install-ui [-h] [--erase] [--prerelease]
|
||||||
|
[--ui-version UI_VERSION]
|
||||||
|
|
||||||
options:
|
options:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
--erase Clean UI folder, don't download new version.
|
--erase Clean UI folder, don't download new version.
|
||||||
|
--prerelease Install the latest pre-release version of FreqUI. This
|
||||||
|
is not recommended for production use.
|
||||||
--ui-version UI_VERSION
|
--ui-version UI_VERSION
|
||||||
Specify a specific version of FreqUI to install. Not
|
Specify a specific version of FreqUI to install. Not
|
||||||
specifying this installs the latest version.
|
specifying this installs the latest version.
|
||||||
|
|||||||
@@ -41,7 +41,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -63,7 +63,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ Common arguments:
|
|||||||
exists). Multiple --config options may be used. Can be
|
exists). Multiple --config options may be used. Can be
|
||||||
set to `-` to read config from stdin.
|
set to `-` to read config from stdin.
|
||||||
-d PATH, --datadir PATH, --data-dir PATH
|
-d PATH, --datadir PATH, --data-dir PATH
|
||||||
Path to directory with historical backtesting data.
|
Path to the base directory of the exchange with
|
||||||
|
historical backtesting data. To see futures data, use
|
||||||
|
trading-mode additionally.
|
||||||
--userdir PATH, --user-data-dir PATH
|
--userdir PATH, --user-data-dir PATH
|
||||||
Path to userdata directory.
|
Path to userdata directory.
|
||||||
|
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi
|
|||||||
| `minimal_roi` | **Required.** Set the threshold as ratio the bot will use to exit a trade. [More information below](#understand-minimal_roi). [Strategy Override](#parameters-in-the-strategy). <br> **Datatype:** Dict
|
| `minimal_roi` | **Required.** Set the threshold as ratio the bot will use to exit a trade. [More information below](#understand-minimal_roi). [Strategy Override](#parameters-in-the-strategy). <br> **Datatype:** Dict
|
||||||
| `stoploss` | **Required.** Value as ratio of the stoploss used by the bot. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). <br> **Datatype:** Float (as ratio)
|
| `stoploss` | **Required.** Value as ratio of the stoploss used by the bot. More details in the [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). <br> **Datatype:** Float (as ratio)
|
||||||
| `trailing_stop` | Enables trailing stoploss (based on `stoploss` in either configuration or strategy file). More details in the [stoploss documentation](stoploss.md#trailing-stop-loss). [Strategy Override](#parameters-in-the-strategy). <br> **Datatype:** Boolean
|
| `trailing_stop` | Enables trailing stoploss (based on `stoploss` in either configuration or strategy file). More details in the [stoploss documentation](stoploss.md#trailing-stop-loss). [Strategy Override](#parameters-in-the-strategy). <br> **Datatype:** Boolean
|
||||||
| `trailing_stop_positive` | Changes stoploss once profit has been reached. More details in the [stoploss documentation](stoploss.md#trailing-stop-loss-custom-positive-loss). [Strategy Override](#parameters-in-the-strategy). <br> **Datatype:** Float
|
| `trailing_stop_positive` | Changes stoploss once profit has been reached. More details in the [stoploss documentation](stoploss.md#trailing-stop-loss-different-positive-loss). [Strategy Override](#parameters-in-the-strategy). <br> **Datatype:** Float
|
||||||
| `trailing_stop_positive_offset` | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive. More details in the [stoploss documentation](stoploss.md#trailing-stop-loss-only-once-the-trade-has-reached-a-certain-offset). [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `0.0` (no offset).* <br> **Datatype:** Float
|
| `trailing_stop_positive_offset` | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive. More details in the [stoploss documentation](stoploss.md#trailing-stop-loss-only-once-the-trade-has-reached-a-certain-offset). [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `0.0` (no offset).* <br> **Datatype:** Float
|
||||||
| `trailing_only_offset_is_reached` | Only apply trailing stoploss when the offset is reached. [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `false`.* <br> **Datatype:** Boolean
|
| `trailing_only_offset_is_reached` | Only apply trailing stoploss when the offset is reached. [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `false`.* <br> **Datatype:** Boolean
|
||||||
| `fee` | Fee used during backtesting / dry-runs. Should normally not be configured, which has freqtrade fall back to the exchange default fee. Set as ratio (e.g. 0.001 = 0.1%). Fee is applied twice for each trade, once when buying, once when selling. <br> **Datatype:** Float (as ratio)
|
| `fee` | Fee used during backtesting / dry-runs. Should normally not be configured, which has freqtrade fall back to the exchange default fee. Set as ratio (e.g. 0.001 = 0.1%). Fee is applied twice for each trade, once when buying, once when selling. <br> **Datatype:** Float (as ratio)
|
||||||
@@ -205,7 +205,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi
|
|||||||
| `exit_pricing.use_order_book` | Enable exiting of open trades using [Order Book Exit](#exit-price-with-orderbook-enabled). <br> *Defaults to `true`.*<br> **Datatype:** Boolean
|
| `exit_pricing.use_order_book` | Enable exiting of open trades using [Order Book Exit](#exit-price-with-orderbook-enabled). <br> *Defaults to `true`.*<br> **Datatype:** Boolean
|
||||||
| `exit_pricing.order_book_top` | Bot will use the top N rate in Order Book "price_side" to exit. I.e. a value of 2 will allow the bot to pick the 2nd ask rate in [Order Book Exit](#exit-price-with-orderbook-enabled)<br>*Defaults to `1`.* <br> **Datatype:** Positive Integer
|
| `exit_pricing.order_book_top` | Bot will use the top N rate in Order Book "price_side" to exit. I.e. a value of 2 will allow the bot to pick the 2nd ask rate in [Order Book Exit](#exit-price-with-orderbook-enabled)<br>*Defaults to `1`.* <br> **Datatype:** Positive Integer
|
||||||
| `custom_price_max_distance_ratio` | Configure maximum distance ratio between current and custom entry or exit price. <br>*Defaults to `0.02` 2%).*<br> **Datatype:** Positive float
|
| `custom_price_max_distance_ratio` | Configure maximum distance ratio between current and custom entry or exit price. <br>*Defaults to `0.02` 2%).*<br> **Datatype:** Positive float
|
||||||
| | **TODO**
|
| | **Order/Signal handling**
|
||||||
| `use_exit_signal` | Use exit signals produced by the strategy in addition to the `minimal_roi`. <br>Setting this to false disables the usage of `"exit_long"` and `"exit_short"` columns. Has no influence on other exit methods (Stoploss, ROI, callbacks). [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `true`.* <br> **Datatype:** Boolean
|
| `use_exit_signal` | Use exit signals produced by the strategy in addition to the `minimal_roi`. <br>Setting this to false disables the usage of `"exit_long"` and `"exit_short"` columns. Has no influence on other exit methods (Stoploss, ROI, callbacks). [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `true`.* <br> **Datatype:** Boolean
|
||||||
| `exit_profit_only` | Wait until the bot reaches `exit_profit_offset` before taking an exit decision. [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `false`.* <br> **Datatype:** Boolean
|
| `exit_profit_only` | Wait until the bot reaches `exit_profit_offset` before taking an exit decision. [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `false`.* <br> **Datatype:** Boolean
|
||||||
| `exit_profit_offset` | Exit-signal is only active above this value. Only active in combination with `exit_profit_only=True`. [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `0.0`.* <br> **Datatype:** Float (as ratio)
|
| `exit_profit_offset` | Exit-signal is only active above this value. Only active in combination with `exit_profit_only=True`. [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `0.0`.* <br> **Datatype:** Float (as ratio)
|
||||||
@@ -266,7 +266,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi
|
|||||||
| `bot_name` | Name of the bot. Passed via API to a client - can be shown to distinguish / name bots.<br> *Defaults to `freqtrade`*<br> **Datatype:** String
|
| `bot_name` | Name of the bot. Passed via API to a client - can be shown to distinguish / name bots.<br> *Defaults to `freqtrade`*<br> **Datatype:** String
|
||||||
| `external_message_consumer` | Enable [Producer/Consumer mode](producer-consumer.md) for more details. <br> **Datatype:** Dict
|
| `external_message_consumer` | Enable [Producer/Consumer mode](producer-consumer.md) for more details. <br> **Datatype:** Dict
|
||||||
| | **Other**
|
| | **Other**
|
||||||
| `initial_state` | Defines the initial application state. If set to stopped, then the bot has to be explicitly started via `/start` RPC command. <br>*Defaults to `stopped`.* <br> **Datatype:** Enum, either `stopped` or `running`
|
| `initial_state` | Defines the initial application state. If set to stopped, then the bot has to be explicitly started via `/start` RPC command. <br>*Defaults to `stopped`.* <br> **Datatype:** Enum, either `running`, `paused` or `stopped`
|
||||||
| `force_entry_enable` | Enables the RPC Commands to force a Trade entry. More information below. <br> **Datatype:** Boolean
|
| `force_entry_enable` | Enables the RPC Commands to force a Trade entry. More information below. <br> **Datatype:** Boolean
|
||||||
| `disable_dataframe_checks` | Disable checking the OHLCV dataframe returned from the strategy methods for correctness. Only use when intentionally changing the dataframe and understand what you are doing. [Strategy Override](#parameters-in-the-strategy).<br> *Defaults to `False`*. <br> **Datatype:** Boolean
|
| `disable_dataframe_checks` | Disable checking the OHLCV dataframe returned from the strategy methods for correctness. Only use when intentionally changing the dataframe and understand what you are doing. [Strategy Override](#parameters-in-the-strategy).<br> *Defaults to `False`*. <br> **Datatype:** Boolean
|
||||||
| `internals.process_throttle_secs` | Set the process throttle, or minimum loop duration for one bot iteration loop. Value in second. <br>*Defaults to `5` seconds.* <br> **Datatype:** Positive Integer
|
| `internals.process_throttle_secs` | Set the process throttle, or minimum loop duration for one bot iteration loop. Value in second. <br>*Defaults to `5` seconds.* <br> **Datatype:** Positive Integer
|
||||||
@@ -281,7 +281,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi
|
|||||||
| `add_config_files` | Additional config files. These files will be loaded and merged with the current config file. The files are resolved relative to the initial file.<br> *Defaults to `[]`*. <br> **Datatype:** List of strings
|
| `add_config_files` | Additional config files. These files will be loaded and merged with the current config file. The files are resolved relative to the initial file.<br> *Defaults to `[]`*. <br> **Datatype:** List of strings
|
||||||
| `dataformat_ohlcv` | Data format to use to store historical candle (OHLCV) data. <br> *Defaults to `feather`*. <br> **Datatype:** String
|
| `dataformat_ohlcv` | Data format to use to store historical candle (OHLCV) data. <br> *Defaults to `feather`*. <br> **Datatype:** String
|
||||||
| `dataformat_trades` | Data format to use to store historical trades data. <br> *Defaults to `feather`*. <br> **Datatype:** String
|
| `dataformat_trades` | Data format to use to store historical trades data. <br> *Defaults to `feather`*. <br> **Datatype:** String
|
||||||
| `reduce_df_footprint` | Recast all numeric columns to float32/int32, with the objective of reducing ram/disk usage (and decreasing train/inference timing in FreqAI). (Currently only affects FreqAI use-cases) <br> **Datatype:** Boolean. <br> Default: `False`.
|
| `reduce_df_footprint` | Recast all numeric columns to float32/int32, with the objective of reducing ram/disk usage (and decreasing train/inference timing backtesting/hyperopt and in FreqAI). <br> **Datatype:** Boolean. <br> Default: `False`.
|
||||||
| `log_config` | Dictionary containing the log config for python logging. [more info](advanced-setup.md#advanced-logging) <br> **Datatype:** dict. <br> Default: `FtRichHandler`
|
| `log_config` | Dictionary containing the log config for python logging. [more info](advanced-setup.md#advanced-logging) <br> **Datatype:** dict. <br> Default: `FtRichHandler`
|
||||||
|
|
||||||
### Parameters in the strategy
|
### Parameters in the strategy
|
||||||
@@ -672,7 +672,7 @@ Should you experience problems you suspect are caused by websockets, you can dis
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Should you be required to use a proxy, please refer to the [proxy section](#using-proxy-with-freqtrade) for more information.
|
Should you be required to use a proxy, please refer to the [proxy section](#using-a-proxy-with-freqtrade) for more information.
|
||||||
|
|
||||||
!!! Info "Rollout"
|
!!! Info "Rollout"
|
||||||
We're implementing this out slowly, ensuring stability of your bots.
|
We're implementing this out slowly, ensuring stability of your bots.
|
||||||
|
|||||||
@@ -363,6 +363,29 @@ Hyperliquid handles deposits and withdrawals on the Arbitrum One chain, a Layer
|
|||||||
* Create a different software wallet, only transfer the funds you want to trade with to that wallet, and use that wallet to trade on Hyperliquid.
|
* Create a different software wallet, only transfer the funds you want to trade with to that wallet, and use that wallet to trade on Hyperliquid.
|
||||||
* If you have funds you don't want to use for trading (after making a profit for example), transfer them back to your hardware wallet.
|
* If you have funds you don't want to use for trading (after making a profit for example), transfer them back to your hardware wallet.
|
||||||
|
|
||||||
|
### Historic Hyperliquid data
|
||||||
|
|
||||||
|
The Hyperliquid API does not provide historic data beyond the single call to fetch current data, so downloading data is not possible, as the downloaded data would not constitute proper historic data.
|
||||||
|
|
||||||
|
## Bitvavo
|
||||||
|
|
||||||
|
If your account is required to use an operatorId, you can set it in the configuration file as follows:
|
||||||
|
|
||||||
|
``` json
|
||||||
|
"exchange": {
|
||||||
|
"name": "bitvavo",
|
||||||
|
"key": "",
|
||||||
|
"secret": "",
|
||||||
|
"ccxt_config": {
|
||||||
|
"options": {
|
||||||
|
"operatorId": "123567"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Bitvavo expects the `operatorId` to be an integer.
|
||||||
|
|
||||||
## All exchanges
|
## All exchanges
|
||||||
|
|
||||||
Should you experience constant errors with Nonce (like `InvalidNonce`), it is best to regenerate the API keys. Resetting Nonce is difficult and it's usually easier to regenerate the API keys.
|
Should you experience constant errors with Nonce (like `InvalidNonce`), it is best to regenerate the API keys. Resetting Nonce is difficult and it's usually easier to regenerate the API keys.
|
||||||
|
|||||||
13
docs/faq.md
13
docs/faq.md
@@ -102,6 +102,14 @@ You can use "current" market data by using the [dataprovider](strategy-customiza
|
|||||||
|
|
||||||
You can use the `/stopentry` command in Telegram to prevent future trade entry, followed by `/forceexit all` (sell all open trades).
|
You can use the `/stopentry` command in Telegram to prevent future trade entry, followed by `/forceexit all` (sell all open trades).
|
||||||
|
|
||||||
|
### I sold the bot's capital and now there's errors in the log
|
||||||
|
|
||||||
|
Freqtrade assumes that the trades it opens are managed only though the bot.
|
||||||
|
If you happen to (accidentally) sell the bot's capital, freqtrade will try to recover by trying to re-find on-exchange orders.
|
||||||
|
|
||||||
|
This is a best-effort approach, and will not work in all cases, especially when using order types that are not supported by freqtrade (OCO, iceberg, etc.), or when working with older trades (where the exchange no longer provides full order information).
|
||||||
|
The exact limits will vary between exchanges - with the details usually being documented in the exchange's API documentation.
|
||||||
|
|
||||||
### I want to run multiple bots on the same machine
|
### I want to run multiple bots on the same machine
|
||||||
|
|
||||||
Please look at the [advanced setup documentation Page](advanced-setup.md#running-multiple-instances-of-freqtrade).
|
Please look at the [advanced setup documentation Page](advanced-setup.md#running-multiple-instances-of-freqtrade).
|
||||||
@@ -219,10 +227,7 @@ On Windows, the `--logfile` option is also supported by Freqtrade and you can us
|
|||||||
First of all, most indicator libraries don't have GPU support - as such, there would be little benefit for indicator calculations.
|
First of all, most indicator libraries don't have GPU support - as such, there would be little benefit for indicator calculations.
|
||||||
The GPU improvements would only apply to pandas-native calculations - or ones written by yourself.
|
The GPU improvements would only apply to pandas-native calculations - or ones written by yourself.
|
||||||
|
|
||||||
For hyperopt, freqtrade is using scikit-optimize, which is built on top of scikit-learn.
|
GPU's are only good at crunching numbers (floating point operations).
|
||||||
Their statement about GPU support is [pretty clear](https://scikit-learn.org/stable/faq.html#will-you-add-gpu-support).
|
|
||||||
|
|
||||||
GPU's also are only good at crunching numbers (floating point operations).
|
|
||||||
For hyperopt, we need both number-crunching (find next parameters) and running python code (running backtesting).
|
For hyperopt, we need both number-crunching (find next parameters) and running python code (running backtesting).
|
||||||
As such, GPU's are not too well suited for most parts of hyperopt.
|
As such, GPU's are not too well suited for most parts of hyperopt.
|
||||||
|
|
||||||
|
|||||||
@@ -258,6 +258,8 @@ freqtrade trade --config config_examples/config_freqai.example.json --strategy F
|
|||||||
We do provide an explicit docker-compose file for this in `docker/docker-compose-freqai.yml` - which can be used via `docker compose -f docker/docker-compose-freqai.yml run ...` - or can be copied to replace the original docker file.
|
We do provide an explicit docker-compose file for this in `docker/docker-compose-freqai.yml` - which can be used via `docker compose -f docker/docker-compose-freqai.yml run ...` - or can be copied to replace the original docker file.
|
||||||
This docker-compose file also contains a (disabled) section to enable GPU resources within docker containers. This obviously assumes the system has GPU resources available.
|
This docker-compose file also contains a (disabled) section to enable GPU resources within docker containers. This obviously assumes the system has GPU resources available.
|
||||||
|
|
||||||
|
PyTorch dropped support for macOS x64 (intel based Apple devices) in version 2.3. Subsequently, freqtrade also dropped support for PyTorch on this platform.
|
||||||
|
|
||||||
### Structure
|
### Structure
|
||||||
|
|
||||||
#### Model
|
#### Model
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ You can ask for each of the defined features to be included also for informative
|
|||||||
In total, the number of features the user of the presented example strategy has created is: length of `include_timeframes` * no. features in `feature_engineering_expand_*()` * length of `include_corr_pairlist` * no. `include_shifted_candles` * length of `indicator_periods_candles`
|
In total, the number of features the user of the presented example strategy has created is: length of `include_timeframes` * no. features in `feature_engineering_expand_*()` * length of `include_corr_pairlist` * no. `include_shifted_candles` * length of `indicator_periods_candles`
|
||||||
$= 3 * 3 * 3 * 2 * 2 = 108$.
|
$= 3 * 3 * 3 * 2 * 2 = 108$.
|
||||||
|
|
||||||
!!! note "Learn more about creative feature engineering"
|
!!! note "Learn more about creative feature engineering"
|
||||||
Check out our [medium article](https://emergentmethods.medium.com/freqai-from-price-to-prediction-6fadac18b665) geared toward helping users learn how to creatively engineer features.
|
Check out our [medium article](https://emergentmethods.medium.com/freqai-from-price-to-prediction-6fadac18b665) geared toward helping users learn how to creatively engineer features.
|
||||||
|
|
||||||
### Gain finer control over `feature_engineering_*` functions with `metadata`
|
### Gain finer control over `feature_engineering_*` functions with `metadata`
|
||||||
@@ -310,7 +310,7 @@ class MyCoolTransform(BaseTransform):
|
|||||||
|
|
||||||
If you have created your own custom `IFreqaiModel` with a custom `train()`/`predict()` function, *and* you still rely on `data_cleaning_train/predict()`, then you will need to migrate to the new pipeline. If your model does *not* rely on `data_cleaning_train/predict()`, then you do not need to worry about this migration.
|
If you have created your own custom `IFreqaiModel` with a custom `train()`/`predict()` function, *and* you still rely on `data_cleaning_train/predict()`, then you will need to migrate to the new pipeline. If your model does *not* rely on `data_cleaning_train/predict()`, then you do not need to worry about this migration.
|
||||||
|
|
||||||
More details about the migration can be found [here](strategy_migration.md#freqai---new-data-pipeline).
|
More details about the migration can be found [here](strategy_migration.md#freqai-new-data-pipeline).
|
||||||
|
|
||||||
## Outlier detection
|
## Outlier detection
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ pip install -r requirements-freqai.txt
|
|||||||
If you are using docker, a dedicated tag with FreqAI dependencies is available as `:freqai`. As such - you can replace the image line in your docker compose file with `image: freqtradeorg/freqtrade:stable_freqai`. This image contains the regular FreqAI dependencies. Similar to native installs, Catboost will not be available on ARM based devices. If you would like to use PyTorch or Reinforcement learning, you should use the torch or RL tags, `image: freqtradeorg/freqtrade:stable_freqaitorch`, `image: freqtradeorg/freqtrade:stable_freqairl`.
|
If you are using docker, a dedicated tag with FreqAI dependencies is available as `:freqai`. As such - you can replace the image line in your docker compose file with `image: freqtradeorg/freqtrade:stable_freqai`. This image contains the regular FreqAI dependencies. Similar to native installs, Catboost will not be available on ARM based devices. If you would like to use PyTorch or Reinforcement learning, you should use the torch or RL tags, `image: freqtradeorg/freqtrade:stable_freqaitorch`, `image: freqtradeorg/freqtrade:stable_freqairl`.
|
||||||
|
|
||||||
!!! note "docker-compose-freqai.yml"
|
!!! note "docker-compose-freqai.yml"
|
||||||
We do provide an explicit docker-compose file for this in `docker/docker-compose-freqai.yml` - which can be used via `docker compose -f docker/docker-compose-freqai.yml run ...` - or can be copied to replace the original docker file. This docker-compose file also contains a (disabled) section to enable GPU resources within docker containers. This obviously assumes the system has GPU resources available.
|
We do provide an explicit docker-compose file for this in `docker/docker-compose-freqai.yml` - which can be used via `docker compose -f docker/docker-compose-freqai.yml run ...` - or can be copied to replace the original docker file. This docker-compose file also contains a (disabled) section to enable GPU resources within docker containers. This obviously assumes the system has GPU resources available.
|
||||||
|
|
||||||
### FreqAI position in open-source machine learning landscape
|
### FreqAI position in open-source machine learning landscape
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# Hyperopt
|
# Hyperopt
|
||||||
|
|
||||||
This page explains how to tune your strategy by finding the optimal
|
This page explains how to tune your strategy by finding the optimal
|
||||||
parameters, a process called hyperparameter optimization. The bot uses algorithms included in the `scikit-optimize` package to accomplish this.
|
parameters, a process called hyperparameter optimization. The bot uses algorithms included in the `optuna` package to accomplish this.
|
||||||
The search will burn all your CPU cores, make your laptop sound like a fighter jet and still take a long time.
|
The search will burn all your CPU cores, make your laptop sound like a fighter jet and still take a long time.
|
||||||
|
|
||||||
In general, the search for best parameters starts with a few random combinations (see [below](#reproducible-results) for more details) and then uses Bayesian search with a ML regressor algorithm (currently ExtraTreesRegressor) to quickly find a combination of parameters in the search hyperspace that minimizes the value of the [loss function](#loss-functions).
|
In general, the search for best parameters starts with a few random combinations (see [below](#reproducible-results) for more details) and then uses one of optuna's sampler algorithms (currently NSGAIIISampler) to quickly find a combination of parameters in the search hyperspace that minimizes the value of the [loss function](#loss-functions).
|
||||||
|
|
||||||
Hyperopt requires historic data to be available, just as backtesting does (hyperopt runs backtesting many times with different parameters).
|
Hyperopt requires historic data to be available, just as backtesting does (hyperopt runs backtesting many times with different parameters).
|
||||||
To learn how to get data for the pairs and exchange you're interested in, head over to the [Data Downloading](data-download.md) section of the documentation.
|
To learn how to get data for the pairs and exchange you're interested in, head over to the [Data Downloading](data-download.md) section of the documentation.
|
||||||
@@ -471,6 +471,7 @@ Currently, the following loss functions are builtin:
|
|||||||
* `SortinoHyperOptLossDaily` - optimizes Sortino Ratio calculated on **daily** trade returns relative to **downside** standard deviation.
|
* `SortinoHyperOptLossDaily` - optimizes Sortino Ratio calculated on **daily** trade returns relative to **downside** standard deviation.
|
||||||
* `MaxDrawDownHyperOptLoss` - Optimizes Maximum absolute drawdown.
|
* `MaxDrawDownHyperOptLoss` - Optimizes Maximum absolute drawdown.
|
||||||
* `MaxDrawDownRelativeHyperOptLoss` - Optimizes both maximum absolute drawdown while also adjusting for maximum relative drawdown.
|
* `MaxDrawDownRelativeHyperOptLoss` - Optimizes both maximum absolute drawdown while also adjusting for maximum relative drawdown.
|
||||||
|
* `MaxDrawDownPerPairHyperOptLoss` - Calculates the profit/drawdown ratio per pair and returns the worst result as objective, forcing hyperopt to optimize the parameters for all pairs in the pairlist. This way, we prevent one or more pairs with good results from inflating the metrics, while the pairs with poor results are not represented and therefore not optimized.
|
||||||
* `CalmarHyperOptLoss` - Optimizes Calmar Ratio calculated on trade returns relative to max drawdown.
|
* `CalmarHyperOptLoss` - Optimizes Calmar Ratio calculated on trade returns relative to max drawdown.
|
||||||
* `ProfitDrawDownHyperOptLoss` - Optimizes by max Profit & min Drawdown objective. `DRAWDOWN_MULT` variable within the hyperoptloss file can be adjusted to be stricter or more flexible on drawdown purposes.
|
* `ProfitDrawDownHyperOptLoss` - Optimizes by max Profit & min Drawdown objective. `DRAWDOWN_MULT` variable within the hyperoptloss file can be adjusted to be stricter or more flexible on drawdown purposes.
|
||||||
* `MultiMetricHyperOptLoss` - Optimizes by several key metrics to achieve balanced performance. The primary focus is on maximizing Profit and minimizing Drawdown, while also considering additional metrics such as Profit Factor, Expectancy Ratio and Winrate. Moreover, it applies a penalty for epochs with a low number of trades, encouraging strategies with adequate trade frequency.
|
* `MultiMetricHyperOptLoss` - Optimizes by several key metrics to achieve balanced performance. The primary focus is on maximizing Profit and minimizing Drawdown, while also considering additional metrics such as Profit Factor, Expectancy Ratio and Winrate. Moreover, it applies a penalty for epochs with a low number of trades, encouraging strategies with adequate trade frequency.
|
||||||
@@ -489,6 +490,8 @@ freqtrade hyperopt --config config.json --hyperopt-loss <hyperoptlossname> --str
|
|||||||
```
|
```
|
||||||
|
|
||||||
The `-e` option will set how many evaluations hyperopt will do. Since hyperopt uses Bayesian search, running too many epochs at once may not produce greater results. Experience has shown that best results are usually not improving much after 500-1000 epochs.
|
The `-e` option will set how many evaluations hyperopt will do. Since hyperopt uses Bayesian search, running too many epochs at once may not produce greater results. Experience has shown that best results are usually not improving much after 500-1000 epochs.
|
||||||
|
The `--early-stop` option will set after how many epochs with no improvements hyperopt will stop. A good value is 20-30% of the total epochs. Any value greater than 0 and lower than 20 it will be replaced by 20. Early stop is by default disabled (`--early-stop=0`)
|
||||||
|
|
||||||
Doing multiple runs (executions) with a few 1000 epochs and different random state will most likely produce different results.
|
Doing multiple runs (executions) with a few 1000 epochs and different random state will most likely produce different results.
|
||||||
|
|
||||||
The `--spaces all` option determines that all possible parameters should be optimized. Possibilities are listed below.
|
The `--spaces all` option determines that all possible parameters should be optimized. Possibilities are listed below.
|
||||||
@@ -531,7 +534,7 @@ Legal values are:
|
|||||||
* `trailing`: search for the best trailing stop values
|
* `trailing`: search for the best trailing stop values
|
||||||
* `trades`: search for the best max open trades values
|
* `trades`: search for the best max open trades values
|
||||||
* `protection`: search for the best protection parameters (read the [protections section](#optimizing-protections) on how to properly define these)
|
* `protection`: search for the best protection parameters (read the [protections section](#optimizing-protections) on how to properly define these)
|
||||||
* `default`: `all` except `trailing` and `protection`
|
* `default`: `all` except `trailing`, `trades` and `protection`
|
||||||
* space-separated list of any of the above values for example `--spaces roi stoploss`
|
* space-separated list of any of the above values for example `--spaces roi stoploss`
|
||||||
|
|
||||||
The default Hyperopt Search Space, used when no `--space` command line option is specified, does not include the `trailing` hyperspace. We recommend you to run optimization for the `trailing` hyperspace separately, when the best parameters for other hyperspaces were found, validated and pasted into your custom strategy.
|
The default Hyperopt Search Space, used when no `--space` command line option is specified, does not include the `trailing` hyperspace. We recommend you to run optimization for the `trailing` hyperspace separately, when the best parameters for other hyperspaces were found, validated and pasted into your custom strategy.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ This section will highlight a few projects from members of the community.
|
|||||||
The projects below are for the most part not maintained by the freqtrade , therefore use your own caution before using them.
|
The projects below are for the most part not maintained by the freqtrade , therefore use your own caution before using them.
|
||||||
|
|
||||||
- [Example freqtrade strategies](https://github.com/freqtrade/freqtrade-strategies/)
|
- [Example freqtrade strategies](https://github.com/freqtrade/freqtrade-strategies/)
|
||||||
- [FrequentHippo - Grafana dashboard with dry/live runs and backtests](http://frequenthippo.ddns.net:3000/) (by hippocritical).
|
- [FrequentHippo - Statistics of dry/live runs and backtests](http://frequenthippo.ddns.net) (by hippocritical).
|
||||||
- [Online pairlist generator](https://remotepairlist.com/) (by Blood4rc).
|
- [Online pairlist generator](https://remotepairlist.com/) (by Blood4rc).
|
||||||
- [Freqtrade Backtesting Project](https://strat.ninja/) (by Blood4rc).
|
- [Freqtrade Backtesting Project](https://strat.ninja/) (by Blood4rc).
|
||||||
- [Freqtrade analysis notebook](https://github.com/froggleston/freqtrade_analysis_notebook) (by Froggleston).
|
- [Freqtrade analysis notebook](https://github.com/froggleston/freqtrade_analysis_notebook) (by Froggleston).
|
||||||
|
|||||||
18
docs/includes/strategy-exit-comparisons.md
Normal file
18
docs/includes/strategy-exit-comparisons.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
## Exit logic comparisons
|
||||||
|
|
||||||
|
Freqtrade allows your strategy to implement different exit logic using signal-based or callback-based functions.
|
||||||
|
This section aims to compare each different function, helping you to choose the one that best fits your needs.
|
||||||
|
|
||||||
|
* **`populate_exit_trend()`** - Vectorized signal-based exit logic using indicators in the main dataframe
|
||||||
|
✅ **Use** to define exit signals based on indicators or other data that can be calculated in a vectorized manner.
|
||||||
|
🚫 **Don't use** to customize exit conditions for each individual trade, or if trade data is necessary to make an exit decision.
|
||||||
|
* **`custom_exit()`** - Custom exit logic that will fully exit a trade immediately, called for every open trade at every bot loop iteration until a trade is closed.
|
||||||
|
✅ **Use** to specify exit conditions for each individual trade (including any additional adjusted orders using `adjust_trade_position()`), or if trade data is necessary to make an exit decision, e.g. using profit data to exit.
|
||||||
|
🚫 **Don't use** when you want to exit using vectorised indicator-based data (use a `populate_exit_trend()` signal instead), or as a proxy for `custom_stoploss()`, and be aware that rate-based exits in backtesting can be inaccurate.
|
||||||
|
* **`custom_stoploss()`** - Custom trailing stoploss, called for every open trade every iteration until a trade is closed. The value returned here is also used for [stoploss on exchange](stoploss.md#stop-loss-on-exchangefreqtrade).
|
||||||
|
✅ **Use** to customize the stoploss logic to set a dynamic stoploss based on trade data or other conditions.
|
||||||
|
🚫 **Don't use** to exit a trade immediately based on a specific condition. Use `custom_exit()` for that purpose.
|
||||||
|
* **`custom_roi()`** - Custom ROI, called for every open trade every iteration until a trade is closed.
|
||||||
|
✅ **Use** to specify a minimum ROI threshold ("take-profit") to exit a trade at this ROI level at some point within the trade duration, based on profit or other conditions.
|
||||||
|
🚫 **Don't use** to exit a trade immediately based on a specific condition. Use `custom_exit()`.
|
||||||
|
🚫 **Don't use** for static ROI. Use `minimal_roi`.
|
||||||
@@ -87,7 +87,7 @@ OS Specific steps are listed first, the common section below is necessary for al
|
|||||||
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt-get install python3-venv libatlas-base-dev cmake curl
|
sudo apt-get install python3-venv libatlas-base-dev cmake curl libffi-dev
|
||||||
# Use piwheels.org to speed up installation
|
# Use piwheels.org to speed up installation
|
||||||
sudo echo "[global]\nextra-index-url=https://www.piwheels.org/simple" > tee /etc/pip.conf
|
sudo echo "[global]\nextra-index-url=https://www.piwheels.org/simple" > tee /etc/pip.conf
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,21 @@
|
|||||||
# Lookahead analysis
|
# Lookahead analysis
|
||||||
|
|
||||||
This page explains how to validate your strategy in terms of look ahead bias.
|
This page explains how to validate your strategy in terms of lookahead bias.
|
||||||
|
|
||||||
Checking look ahead bias is the bane of any strategy since it is sometimes very easy to introduce backtest bias -
|
Lookahead bias is the bane of any strategy since it is sometimes very easy to introduce this bias, but can be very hard to detect.
|
||||||
but very hard to detect.
|
|
||||||
|
|
||||||
Backtesting initializes all timestamps at once and calculates all indicators in the beginning.
|
Backtesting initializes all timestamps (loads the whole dataframe into memory) and calculates all indicators at once.
|
||||||
This means that if your indicators or entry/exit signals could look into future candles and falsify your backtest.
|
This means that if your indicators or entry/exit signals look into future candles, this will falsify your backtest.
|
||||||
|
|
||||||
Lookahead-analysis requires historic data to be available.
|
The `lookahead-analysis` command requires historic data to be available.
|
||||||
To learn how to get data for the pairs and exchange you're interested in,
|
To learn how to get data for the pairs and exchange you're interested in,
|
||||||
head over to the [Data Downloading](data-download.md) section of the documentation.
|
head over to the [Data Downloading](data-download.md) section of the documentation.
|
||||||
|
`lookahead-analysis` also supports freqai strategies.
|
||||||
|
|
||||||
This command is built upon backtesting since it internally chains backtests and pokes at the strategy to provoke it to show look ahead bias.
|
This command internally chains backtests and pokes at the strategy to provoke it to show lookahead bias.
|
||||||
This is done by not looking at the strategy itself - but at the results it returned.
|
This is done by not looking at the strategy code itself, but at changed indicator values and moved entries/exits compared to the full backtest.
|
||||||
The results are things like changed indicator-values and moved entries/exits compared to the full backtest.
|
|
||||||
|
|
||||||
You can use commands of [Backtesting](backtesting.md).
|
`lookahead-analysis` can use the typical options of [Backtesting](backtesting.md), but forces the following options:
|
||||||
It also supports the lookahead-analysis of freqai strategies.
|
|
||||||
|
|
||||||
- `--cache` is forced to "none".
|
- `--cache` is forced to "none".
|
||||||
- `--max-open-trades` is forced to be at least equal to the number of pairs.
|
- `--max-open-trades` is forced to be at least equal to the number of pairs.
|
||||||
@@ -25,48 +23,83 @@ It also supports the lookahead-analysis of freqai strategies.
|
|||||||
- `--stake-amount` is forced to be a static 10000 (10k).
|
- `--stake-amount` is forced to be a static 10000 (10k).
|
||||||
- `--enable-protections` is forced to be off.
|
- `--enable-protections` is forced to be off.
|
||||||
|
|
||||||
Those are set to avoid users accidentally generating false positives.
|
These are set to avoid users accidentally generating false positives.
|
||||||
|
|
||||||
## Lookahead-analysis command reference
|
## Lookahead-analysis command reference
|
||||||
|
|
||||||
--8<-- "commands/lookahead-analysis.md"
|
--8<-- "commands/lookahead-analysis.md"
|
||||||
|
|
||||||
!!! Note ""
|
!!! Note
|
||||||
The above Output was reduced to options `lookahead-analysis` adds on top of regular backtesting commands.
|
The above output was reduced to options that `lookahead-analysis` adds on top of regular backtesting commands.
|
||||||
|
|
||||||
### Summary
|
|
||||||
|
|
||||||
Checks a given strategy for look ahead bias via lookahead-analysis
|
|
||||||
Look ahead bias means that the backtest uses data from future candles thereby not making it viable beyond backtesting
|
|
||||||
and producing false hopes for the one backtesting.
|
|
||||||
|
|
||||||
### Introduction
|
### Introduction
|
||||||
|
|
||||||
Many strategies - without the programmer knowing - have fallen prey to look ahead bias.
|
Many strategies, without the programmer knowing, have fallen prey to lookahead bias.
|
||||||
|
This typically makes the strategy backtest look profitable, sometimes to extremes, but this is not realistic as the strategy is "cheating" by looking at data it would not have in dry or live modes.
|
||||||
|
|
||||||
Any backtest will populate the full dataframe including all time stamps at the beginning.
|
The reason why strategies can "cheat" is because the freqtrade backtesting process populates the full dataframe including all candle timestamps at the outset.
|
||||||
If the programmer is not careful or oblivious how things work internally
|
If the programmer is not careful or oblivious how things work internally
|
||||||
(which sometimes can be really hard to find out) then it will just look into the future making the strategy amazing
|
(which sometimes can be really hard to find out) then the strategy will look into the future.
|
||||||
but not realistic.
|
|
||||||
|
|
||||||
This command is made to try to verify the validity in the form of the aforementioned look ahead bias.
|
This command is made to try to verify the validity in the form of the aforementioned lookahead bias.
|
||||||
|
|
||||||
### How does the command work?
|
### How does the command work?
|
||||||
|
|
||||||
It will start with a backtest of all pairs to generate a baseline for indicators and entries/exits.
|
It will start with a backtest of all pairs to generate a baseline for indicators and entries/exits.
|
||||||
After the backtest ran, it will look if the `minimum-trade-amount` is met
|
After this initial backtest runs, it will look if the `minimum-trade-amount` is met and if not cancel the lookahead-analysis for this strategy.
|
||||||
and if not cancel the lookahead-analysis for this strategy.
|
If this happens, use a wider timerange to get more trades for the analysis, or use a timerange where more trades occur.
|
||||||
|
|
||||||
After setting the baseline it will then do additional runs for every entry and exit separately.
|
After setting the baseline it will then do additional backtest runs for every entry and exit separately.
|
||||||
When a verification-backtest is done, it will compare the indicators as the signal (either entry or exit) and report the bias.
|
When these verification backtests complete, it will compare the indicators at the signal candles (both entry or exit)
|
||||||
After all signals have been verified or falsified a result-table will be generated for the user to see.
|
and report the bias.
|
||||||
|
After all signals have been verified or falsified a result table will be generated for the user to see.
|
||||||
|
|
||||||
|
### How to find and remove bias? How can I salvage a biased strategy?
|
||||||
|
|
||||||
|
If you found a biased strategy online and want to have the same results, just without bias,
|
||||||
|
then you will be out of luck most of the time.
|
||||||
|
Usually the bias in the strategy is THE driving factor for "too good to be true" profits.
|
||||||
|
Removing conditions or indicators that push the profits up from bias will usually make the strategy significantly worse.
|
||||||
|
You might be able to salvage it partially if the biased indicators or conditions are not the core of the strategy, or there
|
||||||
|
are other entry and exit signals that are not biased.
|
||||||
|
|
||||||
|
### Examples of lookahead-bias
|
||||||
|
|
||||||
|
- `shift(-10)` looks 10 candles into the future.
|
||||||
|
- Using `iloc[]` in populate_* functions to access a specific row in the dataframe.
|
||||||
|
- For-loops are prone to introduce lookahead bias if you don't tightly control which numbers are looped through.
|
||||||
|
- Aggregation functions like `.mean()`, `.min()` and `.max()`, without a rolling window,
|
||||||
|
will calculate the value over the **whole** dataframe, so the signal candle will "see" a value including future candles.
|
||||||
|
A non-biased example would be to look back candles using `rolling()` instead:
|
||||||
|
e.g. `dataframe['volume_mean_12'] = dataframe['volume'].rolling(12).mean()`
|
||||||
|
- `ta.MACD(dataframe, 12, 26, 1)` will introduce bias with a signalperiod of 1.
|
||||||
|
|
||||||
|
### What do the columns in the results table mean?
|
||||||
|
|
||||||
|
- `filename`: name of the checked strategy file
|
||||||
|
- `strategy`: checked strategy class name
|
||||||
|
- `has_bias`: result of the lookahead-analysis. `No` would be good, `Yes` would be bad.
|
||||||
|
- `total_signals`: number of checked signals (default is 20)
|
||||||
|
- `biased_entry_signals`: found bias in that many entries
|
||||||
|
- `biased_exit_signals`: found bias in that many exits
|
||||||
|
- `biased_indicators`: shows you the indicators themselves that are defined in populate_indicators
|
||||||
|
|
||||||
|
You might get false positives in the `biased_exit_signals` if you have biased entry signals paired with those exits.
|
||||||
|
However, a biased entry will usually result in a biased exit too,
|
||||||
|
even if the exit itself does not produce the bias -
|
||||||
|
especially if your entry and exit conditions use the same biased indicator.
|
||||||
|
|
||||||
|
**Address the bias in the entries first, then address the exits.**
|
||||||
|
|
||||||
### Caveats
|
### Caveats
|
||||||
|
|
||||||
- `lookahead-analysis` can only verify / falsify the trades it calculated and verified.
|
- `lookahead-analysis` can only verify / falsify the trades it calculated and verified.
|
||||||
If the strategy has many different signals / signal types, it's up to you to select appropriate parameters to ensure that all signals have triggered at least once. Not triggered signals will not have been verified.
|
If the strategy has many different signals / signal types, it's up to you to select appropriate parameters to ensure that all signals have triggered at least once. Signals that are not triggered will not have been verified.
|
||||||
This could lead to a false-negative (the strategy will then be reported as non-biased).
|
This would lead to a false-negative, i.e. the strategy will be reported as non-biased.
|
||||||
- `lookahead-analysis` has access to everything that backtesting has too.
|
- `lookahead-analysis` has access to the same backtesting options and this can introduce problems.
|
||||||
Please don't provoke any configs like enabling position stacking.
|
Please don't use any options like enabling position stacking as this will distort the number of checked signals.
|
||||||
If you decide to do so, then make doubly sure that you won't ever run out of `max_open_trades` amount and neither leftover money in your wallet.
|
If you decide to do so, then make doubly sure that you won't ever run out of `max_open_trades` slots,
|
||||||
- In the results table, the `biased_indicators` column will falsely flag FreqAI target indicators defined in `set_freqai_targets()` as biased. These are not biased and can safely be ignored.
|
and that you have enough capital in the backtest wallet configuration.
|
||||||
|
- In the results table, the `biased_indicators` column
|
||||||
|
will falsely flag FreqAI target indicators defined in `set_freqai_targets()` as biased.
|
||||||
|
**These are not biased and can safely be ignored.**
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ Enable subscribing to an instance by adding the `external_message_consumer` sect
|
|||||||
| `ping_timeout` | Ping timeout <br>*Defaults to `10`.*<br> **Datatype:** Integer - in seconds.
|
| `ping_timeout` | Ping timeout <br>*Defaults to `10`.*<br> **Datatype:** Integer - in seconds.
|
||||||
| `sleep_time` | Sleep time before retrying to connect.<br>*Defaults to `10`.*<br> **Datatype:** Integer - in seconds.
|
| `sleep_time` | Sleep time before retrying to connect.<br>*Defaults to `10`.*<br> **Datatype:** Integer - in seconds.
|
||||||
| `remove_entry_exit_signals` | Remove signal columns from the dataframe (set them to 0) on dataframe receipt.<br>*Defaults to `false`.*<br> **Datatype:** Boolean.
|
| `remove_entry_exit_signals` | Remove signal columns from the dataframe (set them to 0) on dataframe receipt.<br>*Defaults to `false`.*<br> **Datatype:** Boolean.
|
||||||
|
| `initial_candle_limit` | Initial candles to expect from the Producer.<br>*Defaults to `1500`.*<br> **Datatype:** Integer - Number of candles.
|
||||||
| `message_size_limit` | Size limit per message<br>*Defaults to `8`.*<br> **Datatype:** Integer - Megabytes.
|
| `message_size_limit` | Size limit per message<br>*Defaults to `8`.*<br> **Datatype:** Integer - Megabytes.
|
||||||
|
|
||||||
Instead of (or as well as) calculating indicators in `populate_indicators()` the follower instance listens on the connection to a producer instance's messages (or multiple producer instances in advanced configurations) and requests the producer's most recently analyzed dataframes for each pair in the active whitelist.
|
Instead of (or as well as) calculating indicators in `populate_indicators()` the follower instance listens on the connection to a producer instance's messages (or multiple producer instances in advanced configurations) and requests the producer's most recently analyzed dataframes for each pair in the active whitelist.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
markdown==3.7
|
markdown==3.8
|
||||||
mkdocs==1.6.1
|
mkdocs==1.6.1
|
||||||
mkdocs-material==9.6.9
|
mkdocs-material==9.6.14
|
||||||
mdx_truly_sane_lists==1.3
|
mdx_truly_sane_lists==1.3
|
||||||
pymdown-extensions==10.14.3
|
pymdown-extensions==10.15
|
||||||
jinja2==3.1.6
|
jinja2==3.1.6
|
||||||
mike==2.1.3
|
mike==2.1.3
|
||||||
|
|||||||
@@ -268,6 +268,9 @@ show_config
|
|||||||
start
|
start
|
||||||
Start the bot if it's in the stopped state.
|
Start the bot if it's in the stopped state.
|
||||||
|
|
||||||
|
pause
|
||||||
|
Pause the bot if it's in the running state. If triggered on stopped state will handle open positions.
|
||||||
|
|
||||||
stats
|
stats
|
||||||
Return the stats report (durations, sell-reasons).
|
Return the stats report (durations, sell-reasons).
|
||||||
|
|
||||||
@@ -333,6 +336,7 @@ All endpoints in the below table need to be prefixed with the base URL of the AP
|
|||||||
|-----------|--------|--------------------------|
|
|-----------|--------|--------------------------|
|
||||||
| `/ping` | GET | Simple command testing the API Readiness - requires no authentication.
|
| `/ping` | GET | Simple command testing the API Readiness - requires no authentication.
|
||||||
| `/start` | POST | Starts the trader.
|
| `/start` | POST | Starts the trader.
|
||||||
|
| `/pause` | POST | Pause the trader. Gracefully handle open trades according to their rules. Do not enter new positions.
|
||||||
| `/stop` | POST | Stops the trader.
|
| `/stop` | POST | Stops the trader.
|
||||||
| `/stopbuy` | POST | Stops the trader from opening new trades. Gracefully closes open trades according to their rules.
|
| `/stopbuy` | POST | Stops the trader from opening new trades. Gracefully closes open trades according to their rules.
|
||||||
| `/reload_config` | POST | Reloads the configuration file.
|
| `/reload_config` | POST | Reloads the configuration file.
|
||||||
|
|||||||
@@ -165,18 +165,23 @@ If there is any significant difference, verify that your entry and exit signals
|
|||||||
|
|
||||||
## Controlling or monitoring a running bot
|
## Controlling or monitoring a running bot
|
||||||
|
|
||||||
Once your bot is running in dry or live mode, Freqtrade has five mechanisms to control or monitor a running bot:
|
Once your bot is running in dry or live mode, Freqtrade has six mechanisms to control or monitor a running bot:
|
||||||
|
|
||||||
- **[FreqUI](freq-ui.md)**: The easiest to get started with, FreqUI is a web interface to see and control current activity of your bot.
|
- **[FreqUI](freq-ui.md)**: The easiest to get started with, FreqUI is a web interface to see and control current activity of your bot.
|
||||||
- **[Telegram](telegram-usage.md)**: On mobile devices, Telegram integration is available to get alerts about your bot activity and to control certain aspects.
|
- **[Telegram](telegram-usage.md)**: On mobile devices, Telegram integration is available to get alerts about your bot activity and to control certain aspects.
|
||||||
- **[FTUI](https://github.com/freqtrade/ftui)**: FTUI is a terminal (command line) interface to Freqtrade, and allows monitoring of a running bot only.
|
- **[FTUI](https://github.com/freqtrade/ftui)**: FTUI is a terminal (command line) interface to Freqtrade, and allows monitoring of a running bot only.
|
||||||
- **[REST API](rest-api.md)**: The REST API allows programmers to develop their own tools to interact with a Freqtrade bot.
|
- **[freqtrade-client](rest-api.md#consuming-the-api)**: A python implementation of the REST API, making it easy to make requests and consume bot responses from your python apps or the command line.
|
||||||
|
- **[REST API endpoints](rest-api.md#available-endpoints)**: The REST API allows programmers to develop their own tools to interact with a Freqtrade bot.
|
||||||
- **[Webhooks](webhook-config.md)**: Freqtrade can send information to other services, e.g. discord, by webhooks.
|
- **[Webhooks](webhook-config.md)**: Freqtrade can send information to other services, e.g. discord, by webhooks.
|
||||||
|
|
||||||
### Logs
|
### Logs
|
||||||
|
|
||||||
Freqtrade generates extensive debugging logs to help you understand what's happening. Please familiarise yourself with the information and error messages you might see in your bot logs.
|
Freqtrade generates extensive debugging logs to help you understand what's happening. Please familiarise yourself with the information and error messages you might see in your bot logs.
|
||||||
|
|
||||||
|
Logging by default occurs on standard out (the command line). If you want to write out to a file instead, many freqtrade commands, including the `trade` command, accept the `--logfile` option to write to a file.
|
||||||
|
|
||||||
|
Check the [FAQ](faq.md#how-do-i-search-the-bot-logs-for-something) for examples.
|
||||||
|
|
||||||
## Final Thoughts
|
## Final Thoughts
|
||||||
|
|
||||||
Algo trading is difficult, and most public strategies are not good performers due to the time and effort to make a strategy work profitably in multiple scenarios.
|
Algo trading is difficult, and most public strategies are not good performers due to the time and effort to make a strategy work profitably in multiple scenarios.
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ Currently available callbacks:
|
|||||||
* [`custom_stake_amount()`](#stake-size-management)
|
* [`custom_stake_amount()`](#stake-size-management)
|
||||||
* [`custom_exit()`](#custom-exit-signal)
|
* [`custom_exit()`](#custom-exit-signal)
|
||||||
* [`custom_stoploss()`](#custom-stoploss)
|
* [`custom_stoploss()`](#custom-stoploss)
|
||||||
|
* [`custom_roi()`](#custom-roi)
|
||||||
* [`custom_entry_price()` and `custom_exit_price()`](#custom-order-price-rules)
|
* [`custom_entry_price()` and `custom_exit_price()`](#custom-order-price-rules)
|
||||||
* [`check_entry_timeout()` and `check_exit_timeout()`](#custom-order-timeout-rules)
|
* [`check_entry_timeout()` and `check_exit_timeout()`](#custom-order-timeout-rules)
|
||||||
* [`confirm_trade_entry()`](#trade-entry-buy-order-confirmation)
|
* [`confirm_trade_entry()`](#trade-entry-buy-order-confirmation)
|
||||||
@@ -26,6 +27,9 @@ Currently available callbacks:
|
|||||||
|
|
||||||
--8<-- "includes/strategy-imports.md"
|
--8<-- "includes/strategy-imports.md"
|
||||||
|
|
||||||
|
--8<-- "includes/strategy-exit-comparisons.md"
|
||||||
|
|
||||||
|
|
||||||
## Bot start
|
## Bot start
|
||||||
|
|
||||||
A simple callback which is called once when the strategy is loaded.
|
A simple callback which is called once when the strategy is loaded.
|
||||||
@@ -121,7 +125,7 @@ Freqtrade will fall back to the `proposed_stake` value should your code raise an
|
|||||||
|
|
||||||
Called for open trade every throttling iteration (roughly every 5 seconds) until a trade is closed.
|
Called for open trade every throttling iteration (roughly every 5 seconds) until a trade is closed.
|
||||||
|
|
||||||
Allows to define custom exit signals, indicating that specified position should be sold. This is very useful when we need to customize exit conditions for each individual trade, or if you need trade data to make an exit decision.
|
Allows to define custom exit signals, indicating that specified position should be closed (full exit). This is very useful when we need to customize exit conditions for each individual trade, or if you need trade data to make an exit decision.
|
||||||
|
|
||||||
For example you could implement a 1:2 risk-reward ROI with `custom_exit()`.
|
For example you could implement a 1:2 risk-reward ROI with `custom_exit()`.
|
||||||
|
|
||||||
@@ -178,6 +182,8 @@ Returning `None` will be interpreted as "no desire to change", and is the only s
|
|||||||
|
|
||||||
Stoploss on exchange works similar to `trailing_stop`, and the stoploss on exchange is updated as configured in `stoploss_on_exchange_interval` ([More details about stoploss on exchange](stoploss.md#stop-loss-on-exchangefreqtrade)).
|
Stoploss on exchange works similar to `trailing_stop`, and the stoploss on exchange is updated as configured in `stoploss_on_exchange_interval` ([More details about stoploss on exchange](stoploss.md#stop-loss-on-exchangefreqtrade)).
|
||||||
|
|
||||||
|
If you're on futures markets, please take note of the [stoploss and leverage](stoploss.md#stoploss-and-leverage) section, as the stoploss value returned from `custom_stoploss` is the risk for this trade - not the relative price movement.
|
||||||
|
|
||||||
!!! Note "Use of dates"
|
!!! Note "Use of dates"
|
||||||
All time-based calculations should be done based on `current_time` - using `datetime.now()` or `datetime.utcnow()` is discouraged, as this will break backtesting support.
|
All time-based calculations should be done based on `current_time` - using `datetime.now()` or `datetime.utcnow()` is discouraged, as this will break backtesting support.
|
||||||
|
|
||||||
@@ -233,7 +239,7 @@ class AwesomeStrategy(IStrategy):
|
|||||||
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
|
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
|
||||||
:return float: New stoploss value, relative to the current_rate
|
:return float: New stoploss value, relative to the current_rate
|
||||||
"""
|
"""
|
||||||
return -0.04
|
return -0.04 * trade.leverage
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Time based trailing stop
|
#### Time based trailing stop
|
||||||
@@ -255,9 +261,9 @@ class AwesomeStrategy(IStrategy):
|
|||||||
|
|
||||||
# Make sure you have the longest interval first - these conditions are evaluated from top to bottom.
|
# Make sure you have the longest interval first - these conditions are evaluated from top to bottom.
|
||||||
if current_time - timedelta(minutes=120) > trade.open_date_utc:
|
if current_time - timedelta(minutes=120) > trade.open_date_utc:
|
||||||
return -0.05
|
return -0.05 * trade.leverage
|
||||||
elif current_time - timedelta(minutes=60) > trade.open_date_utc:
|
elif current_time - timedelta(minutes=60) > trade.open_date_utc:
|
||||||
return -0.10
|
return -0.10 * trade.leverage
|
||||||
return None
|
return None
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -284,9 +290,9 @@ class AwesomeStrategy(IStrategy):
|
|||||||
return stoploss_from_open(0.10, current_profit, is_short=trade.is_short, leverage=trade.leverage)
|
return stoploss_from_open(0.10, current_profit, is_short=trade.is_short, leverage=trade.leverage)
|
||||||
# Make sure you have the longest interval first - these conditions are evaluated from top to bottom.
|
# Make sure you have the longest interval first - these conditions are evaluated from top to bottom.
|
||||||
if current_time - timedelta(minutes=120) > trade.open_date_utc:
|
if current_time - timedelta(minutes=120) > trade.open_date_utc:
|
||||||
return -0.05
|
return -0.05 * trade.leverage
|
||||||
elif current_time - timedelta(minutes=60) > trade.open_date_utc:
|
elif current_time - timedelta(minutes=60) > trade.open_date_utc:
|
||||||
return -0.10
|
return -0.10 * trade.leverage
|
||||||
return None
|
return None
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -309,10 +315,10 @@ class AwesomeStrategy(IStrategy):
|
|||||||
**kwargs) -> float | None:
|
**kwargs) -> float | None:
|
||||||
|
|
||||||
if pair in ("ETH/BTC", "XRP/BTC"):
|
if pair in ("ETH/BTC", "XRP/BTC"):
|
||||||
return -0.10
|
return -0.10 * trade.leverage
|
||||||
elif pair in ("LTC/BTC"):
|
elif pair in ("LTC/BTC"):
|
||||||
return -0.05
|
return -0.05 * trade.leverage
|
||||||
return -0.15
|
return -0.15 * trade.leverage
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Trailing stoploss with positive offset
|
#### Trailing stoploss with positive offset
|
||||||
@@ -341,7 +347,7 @@ class AwesomeStrategy(IStrategy):
|
|||||||
desired_stoploss = current_profit / 2
|
desired_stoploss = current_profit / 2
|
||||||
|
|
||||||
# Use a minimum of 2.5% and a maximum of 5%
|
# Use a minimum of 2.5% and a maximum of 5%
|
||||||
return max(min(desired_stoploss, 0.05), 0.025)
|
return max(min(desired_stoploss, 0.05), 0.025) * trade.leverage
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Stepped stoploss
|
#### Stepped stoploss
|
||||||
@@ -497,6 +503,135 @@ The helper function `stoploss_from_absolute()` can be used to convert from an ab
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Custom ROI
|
||||||
|
|
||||||
|
Called for open trade every iteration (roughly every 5 seconds) until a trade is closed.
|
||||||
|
|
||||||
|
The usage of the custom ROI method must be enabled by setting `use_custom_roi=True` on the strategy object.
|
||||||
|
|
||||||
|
This method allows you to define a custom minimum ROI threshold for exiting a trade, expressed as a ratio (e.g., `0.05` for 5% profit). If both `minimal_roi` and `custom_roi` are defined, the lower of the two thresholds will trigger an exit. For example, if `minimal_roi` is set to `{"0": 0.10}` (10% at 0 minutes) and `custom_roi` returns `0.05`, the trade will exit when the profit reaches 5%. Also, if `custom_roi` returns `0.10` and `minimal_roi` is set to `{"0": 0.05}` (5% at 0 minutes), the trade will be closed when the profit reaches 5%.
|
||||||
|
|
||||||
|
The method must return a float representing the new ROI threshold as a ratio, or `None` to fall back to the `minimal_roi` logic. Returning `NaN` or `inf` values is considered invalid and will be treated as `None`, causing the bot to use the `minimal_roi` configuration.
|
||||||
|
|
||||||
|
### Custom ROI examples
|
||||||
|
|
||||||
|
The following examples illustrate how to use the `custom_roi` function to implement different ROI logics.
|
||||||
|
|
||||||
|
#### Custom ROI per side
|
||||||
|
|
||||||
|
Use different ROI thresholds depending on the `side`. In this example, 5% for long entries and 2% for short entries.
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Default imports
|
||||||
|
|
||||||
|
class AwesomeStrategy(IStrategy):
|
||||||
|
|
||||||
|
use_custom_roi = True
|
||||||
|
|
||||||
|
# ... populate_* methods
|
||||||
|
|
||||||
|
def custom_roi(self, pair: str, trade: Trade, current_time: datetime, trade_duration: int,
|
||||||
|
entry_tag: str | None, side: str, **kwargs) -> float | None:
|
||||||
|
"""
|
||||||
|
Custom ROI logic, returns a new minimum ROI threshold (as a ratio, e.g., 0.05 for +5%).
|
||||||
|
Only called when use_custom_roi is set to True.
|
||||||
|
|
||||||
|
If used at the same time as minimal_roi, an exit will be triggered when the lower
|
||||||
|
threshold is reached. Example: If minimal_roi = {"0": 0.01} and custom_roi returns 0.05,
|
||||||
|
an exit will be triggered if profit reaches 5%.
|
||||||
|
|
||||||
|
:param pair: Pair that's currently analyzed.
|
||||||
|
:param trade: trade object.
|
||||||
|
:param current_time: datetime object, containing the current datetime.
|
||||||
|
:param trade_duration: Current trade duration in minutes.
|
||||||
|
:param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal.
|
||||||
|
:param side: 'long' or 'short' - indicating the direction of the current trade.
|
||||||
|
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
|
||||||
|
:return float: New ROI value as a ratio, or None to fall back to minimal_roi logic.
|
||||||
|
"""
|
||||||
|
return 0.05 if side == "long" else 0.02
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Custom ROI per pair
|
||||||
|
|
||||||
|
Use different ROI thresholds depending on the `pair`.
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Default imports
|
||||||
|
|
||||||
|
class AwesomeStrategy(IStrategy):
|
||||||
|
|
||||||
|
use_custom_roi = True
|
||||||
|
|
||||||
|
# ... populate_* methods
|
||||||
|
|
||||||
|
def custom_roi(self, pair: str, trade: Trade, current_time: datetime, trade_duration: int,
|
||||||
|
entry_tag: str | None, side: str, **kwargs) -> float | None:
|
||||||
|
|
||||||
|
stake = trade.stake_currency
|
||||||
|
roi_map = {
|
||||||
|
f"BTC/{stake}": 0.02, # 2% for BTC
|
||||||
|
f"ETH/{stake}": 0.03, # 3% for ETH
|
||||||
|
f"XRP/{stake}": 0.04, # 4% for XRP
|
||||||
|
}
|
||||||
|
|
||||||
|
return roi_map.get(pair, 0.01) # 1% for any other pair
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Custom ROI per entry tag
|
||||||
|
|
||||||
|
Use different ROI thresholds depending on the `entry_tag` provided with the buy signal.
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Default imports
|
||||||
|
|
||||||
|
class AwesomeStrategy(IStrategy):
|
||||||
|
|
||||||
|
use_custom_roi = True
|
||||||
|
|
||||||
|
# ... populate_* methods
|
||||||
|
|
||||||
|
def custom_roi(self, pair: str, trade: Trade, current_time: datetime, trade_duration: int,
|
||||||
|
entry_tag: str | None, side: str, **kwargs) -> float | None:
|
||||||
|
|
||||||
|
roi_by_tag = {
|
||||||
|
"breakout": 0.08, # 8% if tag is "breakout"
|
||||||
|
"rsi_overbought": 0.05, # 5% if tag is "rsi_overbought"
|
||||||
|
"mean_reversion": 0.03, # 3% if tag is "mean_reversion"
|
||||||
|
}
|
||||||
|
|
||||||
|
return roi_by_tag.get(entry_tag, 0.01) # 1% if tag is unknown
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Custom ROI based on ATR
|
||||||
|
|
||||||
|
ROI value may be derived from indicators stored in dataframe. This example uses the ATR ratio as ROI.
|
||||||
|
|
||||||
|
``` python
|
||||||
|
# Default imports
|
||||||
|
# <...>
|
||||||
|
import talib.abstract as ta
|
||||||
|
|
||||||
|
class AwesomeStrategy(IStrategy):
|
||||||
|
|
||||||
|
use_custom_roi = True
|
||||||
|
|
||||||
|
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||||
|
# <...>
|
||||||
|
dataframe["atr"] = ta.ATR(dataframe, timeperiod=10)
|
||||||
|
|
||||||
|
def custom_roi(self, pair: str, trade: Trade, current_time: datetime, trade_duration: int,
|
||||||
|
entry_tag: str | None, side: str, **kwargs) -> float | None:
|
||||||
|
|
||||||
|
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
|
||||||
|
last_candle = dataframe.iloc[-1].squeeze()
|
||||||
|
atr_ratio = last_candle["atr"] / last_candle["close"]
|
||||||
|
|
||||||
|
return atr_ratio # Returns the ATR value as ratio
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Custom order price rules
|
## Custom order price rules
|
||||||
|
|
||||||
By default, freqtrade use the orderbook to automatically set an order price([Relevant documentation](configuration.md#prices-used-for-orders)), you also have the option to create custom order prices based on your strategy.
|
By default, freqtrade use the orderbook to automatically set an order price([Relevant documentation](configuration.md#prices-used-for-orders)), you also have the option to create custom order prices based on your strategy.
|
||||||
@@ -1107,3 +1242,119 @@ class AwesomeStrategy(IStrategy):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Plot annotations callback
|
||||||
|
|
||||||
|
The plot annotations callback is called whenever freqUI requests data to display a chart.
|
||||||
|
This callback has no meaning in the trade cycle context and is only used for charting purposes.
|
||||||
|
|
||||||
|
The strategy can then return a list of `AnnotationType` objects to be displayed on the chart.
|
||||||
|
Depending on the content returned - the chart can display horizontal areas, vertical areas, or boxes.
|
||||||
|
|
||||||
|
The full object looks like this:
|
||||||
|
|
||||||
|
``` json
|
||||||
|
{
|
||||||
|
"type": "area", // Type of the annotation, currently only "area" is supported
|
||||||
|
"start": "2024-01-01 15:00:00", // Start date of the area
|
||||||
|
"end": "2024-01-01 16:00:00", // End date of the area
|
||||||
|
"y_start": 94000.2, // Price / y axis value
|
||||||
|
"y_end": 98000, // Price / y axis value
|
||||||
|
"color": "",
|
||||||
|
"label": "some label"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The below example will mark the chart with areas for the hours 8 and 15, with a grey color, highlighting the market open and close hours.
|
||||||
|
This is obviously a very basic example.
|
||||||
|
|
||||||
|
``` python
|
||||||
|
# Default imports
|
||||||
|
|
||||||
|
class AwesomeStrategy(IStrategy):
|
||||||
|
def plot_annotations(
|
||||||
|
self, pair: str, start_date: datetime, end_date: datetime, dataframe: DataFrame, **kwargs
|
||||||
|
) -> list[AnnotationType]:
|
||||||
|
"""
|
||||||
|
Retrieve area annotations for a chart.
|
||||||
|
Must be returned as array, with type, label, color, start, end, y_start, y_end.
|
||||||
|
All settings except for type are optional - though it usually makes sense to include either
|
||||||
|
"start and end" or "y_start and y_end" for either horizontal or vertical plots
|
||||||
|
(or all 4 for boxes).
|
||||||
|
:param pair: Pair that's currently analyzed
|
||||||
|
:param start_date: Start date of the chart data being requested
|
||||||
|
:param end_date: End date of the chart data being requested
|
||||||
|
:param dataframe: DataFrame with the analyzed data for the chart
|
||||||
|
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
|
||||||
|
:return: List of AnnotationType objects
|
||||||
|
"""
|
||||||
|
annotations = []
|
||||||
|
while start_dt < end_date:
|
||||||
|
start_dt += timedelta(hours=1)
|
||||||
|
if start_dt.hour in (8, 15):
|
||||||
|
annotations.append(
|
||||||
|
{
|
||||||
|
"type": "area",
|
||||||
|
"label": "Trade open and close hours",
|
||||||
|
"start": start_dt,
|
||||||
|
"end": start_dt + timedelta(hours=1),
|
||||||
|
# Omitting y_start and y_end will result in a vertical area spanning the whole height of the main Chart
|
||||||
|
"color": "rgba(133, 133, 133, 0.4)",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return annotations
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Entries will be validated, and won't be passed to the UI if they don't correspond to the expected schema and will log an error if they don't.
|
||||||
|
|
||||||
|
!!! Warning "Many annotations"
|
||||||
|
Using too many annotations can cause the UI to hang, especially when plotting large amounts of historic data.
|
||||||
|
Use the annotation feature with care.
|
||||||
|
|
||||||
|
### Plot annotations example
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
??? Info "Code used for the plot above"
|
||||||
|
This is an example code and should be treated as such.
|
||||||
|
|
||||||
|
``` python
|
||||||
|
# Default imports
|
||||||
|
|
||||||
|
class AwesomeStrategy(IStrategy):
|
||||||
|
def plot_annotations(
|
||||||
|
self, pair: str, start_date: datetime, end_date: datetime, dataframe: DataFrame, **kwargs
|
||||||
|
) -> list[AnnotationType]:
|
||||||
|
annotations = []
|
||||||
|
while start_dt < end_date:
|
||||||
|
start_dt += timedelta(hours=1)
|
||||||
|
if (start_dt.hour % 4) == 0:
|
||||||
|
mark_areas.append(
|
||||||
|
{
|
||||||
|
"type": "area",
|
||||||
|
"label": "4h",
|
||||||
|
"start": start_dt,
|
||||||
|
"end": start_dt + timedelta(hours=1),
|
||||||
|
"color": "rgba(133, 133, 133, 0.4)",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
elif (start_dt.hour % 2) == 0:
|
||||||
|
price = dataframe.loc[dataframe["date"] == start_dt, ["close"]].mean()
|
||||||
|
mark_areas.append(
|
||||||
|
{
|
||||||
|
"type": "area",
|
||||||
|
"label": "2h",
|
||||||
|
"start": start_dt,
|
||||||
|
"end": start_dt + timedelta(hours=1),
|
||||||
|
"y_end": price * 1.01,
|
||||||
|
"y_start": price * 0.99,
|
||||||
|
"color": "rgba(0, 255, 0, 0.4)",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return annotations
|
||||||
|
|
||||||
|
```
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ You can create your own keyboard in `config.json`:
|
|||||||
!!! Note "Supported Commands"
|
!!! Note "Supported Commands"
|
||||||
Only the following commands are allowed. Command arguments are not supported!
|
Only the following commands are allowed. Command arguments are not supported!
|
||||||
|
|
||||||
`/start`, `/stop`, `/status`, `/status table`, `/trades`, `/profit`, `/performance`, `/daily`, `/stats`, `/count`, `/locks`, `/balance`, `/stopentry`, `/reload_config`, `/show_config`, `/logs`, `/whitelist`, `/blacklist`, `/edge`, `/help`, `/version`, `/marketdir`
|
`/start`, `/pause`, `/stop`, `/status`, `/status table`, `/trades`, `/profit`, `/performance`, `/daily`, `/stats`, `/count`, `/locks`, `/balance`, `/stopentry`, `/reload_config`, `/show_config`, `/logs`, `/whitelist`, `/blacklist`, `/edge`, `/help`, `/version`, `/marketdir`
|
||||||
|
|
||||||
## Telegram commands
|
## Telegram commands
|
||||||
|
|
||||||
@@ -200,8 +200,8 @@ official commands. You can ask at any moment for help with `/help`.
|
|||||||
|----------|-------------|
|
|----------|-------------|
|
||||||
| **System commands**
|
| **System commands**
|
||||||
| `/start` | Starts the trader
|
| `/start` | Starts the trader
|
||||||
|
| `/pause | /stopentry | /stopbuy` | Pause the trader. Gracefully handle open trades according to their rules. Do not enter new positions.
|
||||||
| `/stop` | Stops the trader
|
| `/stop` | Stops the trader
|
||||||
| `/stopbuy | /stopentry` | Stops the trader from opening new trades. Gracefully closes open trades according to their rules.
|
|
||||||
| `/reload_config` | Reloads the configuration file
|
| `/reload_config` | Reloads the configuration file
|
||||||
| `/show_config` | Shows part of the current configuration with relevant settings to operation
|
| `/show_config` | Shows part of the current configuration with relevant settings to operation
|
||||||
| `/logs [limit]` | Show last log messages.
|
| `/logs [limit]` | Show last log messages.
|
||||||
@@ -250,25 +250,27 @@ Below, example of Telegram message you will receive for each command.
|
|||||||
|
|
||||||
> **Status:** `running`
|
> **Status:** `running`
|
||||||
|
|
||||||
|
### /pause | /stopentry | /stopbuy
|
||||||
|
|
||||||
|
> **Status:** `paused, no more entries will occur from now. Run /start to enable entries.`
|
||||||
|
|
||||||
|
Prevents the bot from opening new trades by changing the state to `paused`.
|
||||||
|
Open trades will continue to be managed according to their regular rules (ROI/exit signals, stop-loss, etc.).
|
||||||
|
Note that position adjustment remains active, but only on the exit side — meaning that when the bot is `paused`, it can only reduce the position size of open trades.
|
||||||
|
|
||||||
|
After this, give the bot time to close off open trades (can be checked via `/status table`).
|
||||||
|
Once all positions are closed, run `/stop` to completely stop the bot.
|
||||||
|
|
||||||
|
Use `/start` to resume the bot to the `running` state, allowing it to open new positions.
|
||||||
|
|
||||||
|
!!! Warning
|
||||||
|
The pause/stopentry signal is ONLY active while the bot is running, and is not persisted anyway, so restarting the bot will cause this to reset.
|
||||||
|
|
||||||
### /stop
|
### /stop
|
||||||
|
|
||||||
> `Stopping trader ...`
|
> `Stopping trader ...`
|
||||||
> **Status:** `stopped`
|
> **Status:** `stopped`
|
||||||
|
|
||||||
### /stopbuy
|
|
||||||
|
|
||||||
> **status:** `Setting max_open_trades to 0. Run /reload_config to reset.`
|
|
||||||
|
|
||||||
Prevents the bot from opening new trades by temporarily setting "max_open_trades" to 0. Open trades will be handled via their regular rules (ROI / Sell-signal, stoploss, ...).
|
|
||||||
|
|
||||||
After this, give the bot time to close off open trades (can be checked via `/status table`).
|
|
||||||
Once all positions are sold, run `/stop` to completely stop the bot.
|
|
||||||
|
|
||||||
`/reload_config` resets "max_open_trades" to the value set in the configuration and resets this command.
|
|
||||||
|
|
||||||
!!! Warning
|
|
||||||
The stop-buy signal is ONLY active while the bot is running, and is not persisted anyway, so restarting the bot will cause this to reset.
|
|
||||||
|
|
||||||
### /status
|
### /status
|
||||||
|
|
||||||
For each open trade, the bot will send you the following message.
|
For each open trade, the bot will send you the following message.
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ The following attributes / properties are available for each individual trade -
|
|||||||
| `close_date_utc` | datetime | Timestamp when trade was closed - in UTC. |
|
| `close_date_utc` | datetime | Timestamp when trade was closed - in UTC. |
|
||||||
| `close_profit` | float | Relative profit at the time of trade closure. `0.01` == 1% |
|
| `close_profit` | float | Relative profit at the time of trade closure. `0.01` == 1% |
|
||||||
| `close_profit_abs` | float | Absolute profit (in stake currency) at the time of trade closure. |
|
| `close_profit_abs` | float | Absolute profit (in stake currency) at the time of trade closure. |
|
||||||
|
| `realized_profit` | float | Absolute already realized profit (in stake currency) while the trade is still open. |
|
||||||
| `leverage` | float | Leverage used for this trade - defaults to 1.0 in spot markets. |
|
| `leverage` | float | Leverage used for this trade - defaults to 1.0 in spot markets. |
|
||||||
| `enter_tag` | string | Tag provided on entry via the `enter_tag` column in the dataframe. |
|
| `enter_tag` | string | Tag provided on entry via the `enter_tag` column in the dataframe. |
|
||||||
| `is_short` | boolean | True for short trades, False otherwise. |
|
| `is_short` | boolean | True for short trades, False otherwise. |
|
||||||
@@ -133,15 +134,17 @@ Most properties here can be None as they are dependent on the exchange response.
|
|||||||
|------------|-------------|-------------|
|
|------------|-------------|-------------|
|
||||||
| `trade` | Trade | Trade object this order is attached to |
|
| `trade` | Trade | Trade object this order is attached to |
|
||||||
| `ft_pair` | string | Pair this order is for |
|
| `ft_pair` | string | Pair this order is for |
|
||||||
| `ft_is_open` | boolean | is the order filled? |
|
| `ft_is_open` | boolean | is the order still open? |
|
||||||
| `order_type` | string | Order type as defined on the exchange - usually market, limit or stoploss |
|
| `order_type` | string | Order type as defined on the exchange - usually market, limit or stoploss |
|
||||||
| `status` | string | Status as defined by ccxt. Usually open, closed, expired or canceled |
|
| `status` | string | Status as defined by [ccxt's order structure](https://docs.ccxt.com/#/README?id=order-structure). Usually open, closed, expired, canceled or rejected |
|
||||||
| `side` | string | Buy or Sell |
|
| `side` | string | buy or sell |
|
||||||
| `price` | float | Price the order was placed at |
|
| `price` | float | Price the order was placed at |
|
||||||
| `average` | float | Average price the order filled at |
|
| `average` | float | Average price the order filled at |
|
||||||
| `amount` | float | Amount in base currency |
|
| `amount` | float | Amount in base currency |
|
||||||
| `filled` | float | Filled amount (in base currency) |
|
| `filled` | float | Filled amount (in base currency) (use `safe_filled` instead) |
|
||||||
| `remaining` | float | Remaining amount |
|
| `safe_filled` | float | Filled amount (in base currency) - guaranteed to not be None |
|
||||||
|
| `remaining` | float | Remaining amount (use `safe_remaining` instead) |
|
||||||
|
| `safe_remaining` | float | Remaining amount - either taken from the exchange or calculated. |
|
||||||
| `cost` | float | Cost of the order - usually average * filled (*Exchange dependent on futures, may contain the cost with or without leverage and may be in contracts.*) |
|
| `cost` | float | Cost of the order - usually average * filled (*Exchange dependent on futures, may contain the cost with or without leverage and may be in contracts.*) |
|
||||||
| `stake_amount` | float | Stake amount used for this order. *Added in 2023.7.* |
|
| `stake_amount` | float | Stake amount used for this order. *Added in 2023.7.* |
|
||||||
| `stake_amount_filled` | float | Filled Stake amount used for this order. *Added in 2024.11.* |
|
| `stake_amount_filled` | float | Filled Stake amount used for this order. *Added in 2024.11.* |
|
||||||
|
|||||||
@@ -45,4 +45,4 @@ Please refer to the corresponding installation sections (common problems linked
|
|||||||
|
|
||||||
Common problems and their solutions:
|
Common problems and their solutions:
|
||||||
|
|
||||||
* [ta-lib update on windows](windows_installation.md#2-install-ta-lib)
|
* [ta-lib update on windows](windows_installation.md#install-ta-lib)
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
"""Freqtrade bot"""
|
"""Freqtrade bot"""
|
||||||
|
|
||||||
__version__ = "2025.3"
|
__version__ = "2025.5"
|
||||||
|
|
||||||
if "dev" in __version__:
|
if "dev" in __version__:
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import subprocess # noqa: S404
|
import subprocess # noqa: S404, RUF100
|
||||||
|
|
||||||
freqtrade_basedir = Path(__file__).parent
|
freqtrade_basedir = Path(__file__).parent
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ ARGS_COMMON = [
|
|||||||
"user_data_dir",
|
"user_data_dir",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
ARGS_MAIN = ["version_main"]
|
||||||
|
|
||||||
ARGS_STRATEGY = [
|
ARGS_STRATEGY = [
|
||||||
"strategy",
|
"strategy",
|
||||||
"strategy_path",
|
"strategy_path",
|
||||||
@@ -43,7 +45,8 @@ ARGS_COMMON_OPTIMIZE = [
|
|||||||
"pairs",
|
"pairs",
|
||||||
]
|
]
|
||||||
|
|
||||||
ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + [
|
ARGS_BACKTEST = [
|
||||||
|
*ARGS_COMMON_OPTIMIZE,
|
||||||
"position_stacking",
|
"position_stacking",
|
||||||
"enable_protections",
|
"enable_protections",
|
||||||
"dry_run_wallet",
|
"dry_run_wallet",
|
||||||
@@ -56,7 +59,8 @@ ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + [
|
|||||||
"freqai_backtest_live_models",
|
"freqai_backtest_live_models",
|
||||||
]
|
]
|
||||||
|
|
||||||
ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + [
|
ARGS_HYPEROPT = [
|
||||||
|
*ARGS_COMMON_OPTIMIZE,
|
||||||
"hyperopt",
|
"hyperopt",
|
||||||
"hyperopt_path",
|
"hyperopt_path",
|
||||||
"position_stacking",
|
"position_stacking",
|
||||||
@@ -74,9 +78,10 @@ ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + [
|
|||||||
"disableparamexport",
|
"disableparamexport",
|
||||||
"hyperopt_ignore_missing_space",
|
"hyperopt_ignore_missing_space",
|
||||||
"analyze_per_epoch",
|
"analyze_per_epoch",
|
||||||
|
"early_stop",
|
||||||
]
|
]
|
||||||
|
|
||||||
ARGS_EDGE = ARGS_COMMON_OPTIMIZE + ["stoploss_range"]
|
ARGS_EDGE = [*ARGS_COMMON_OPTIMIZE, "stoploss_range"]
|
||||||
|
|
||||||
ARGS_LIST_STRATEGIES = [
|
ARGS_LIST_STRATEGIES = [
|
||||||
"strategy_path",
|
"strategy_path",
|
||||||
@@ -125,7 +130,7 @@ ARGS_BUILD_STRATEGY = ["user_data_dir", "strategy", "strategy_path", "template"]
|
|||||||
|
|
||||||
ARGS_CONVERT_DATA_TRADES = ["pairs", "format_from_trades", "format_to", "erase", "exchange"]
|
ARGS_CONVERT_DATA_TRADES = ["pairs", "format_from_trades", "format_to", "erase", "exchange"]
|
||||||
ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase", "exchange"]
|
ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase", "exchange"]
|
||||||
ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes", "trading_mode", "candle_types"]
|
ARGS_CONVERT_DATA_OHLCV = [*ARGS_CONVERT_DATA, "timeframes", "trading_mode", "candle_types"]
|
||||||
|
|
||||||
ARGS_CONVERT_TRADES = [
|
ARGS_CONVERT_TRADES = [
|
||||||
"pairs",
|
"pairs",
|
||||||
@@ -191,7 +196,7 @@ ARGS_PLOT_PROFIT = [
|
|||||||
|
|
||||||
ARGS_CONVERT_DB = ["db_url", "db_url_from"]
|
ARGS_CONVERT_DB = ["db_url", "db_url_from"]
|
||||||
|
|
||||||
ARGS_INSTALL_UI = ["erase_ui_only", "ui_version"]
|
ARGS_INSTALL_UI = ["erase_ui_only", "ui_prerelease", "ui_version"]
|
||||||
|
|
||||||
ARGS_SHOW_TRADES = ["db_url", "trade_ids", "print_json"]
|
ARGS_SHOW_TRADES = ["db_url", "trade_ids", "print_json"]
|
||||||
|
|
||||||
@@ -347,7 +352,7 @@ class Arguments:
|
|||||||
self.parser = ArgumentParser(
|
self.parser = ArgumentParser(
|
||||||
prog="freqtrade", description="Free, open source crypto trading bot"
|
prog="freqtrade", description="Free, open source crypto trading bot"
|
||||||
)
|
)
|
||||||
self._build_args(optionlist=["version_main"], parser=self.parser)
|
self._build_args(optionlist=ARGS_MAIN, parser=self.parser)
|
||||||
|
|
||||||
from freqtrade.commands import (
|
from freqtrade.commands import (
|
||||||
start_analysis_entries_exits,
|
start_analysis_entries_exits,
|
||||||
|
|||||||
@@ -83,7 +83,8 @@ AVAILABLE_CLI_OPTIONS = {
|
|||||||
"-d",
|
"-d",
|
||||||
"--datadir",
|
"--datadir",
|
||||||
"--data-dir",
|
"--data-dir",
|
||||||
help="Path to directory with historical backtesting data.",
|
help="Path to the base directory of the exchange with historical backtesting data. "
|
||||||
|
"To see futures data, use trading-mode additionally.",
|
||||||
metavar="PATH",
|
metavar="PATH",
|
||||||
),
|
),
|
||||||
"user_data_dir": Arg(
|
"user_data_dir": Arg(
|
||||||
@@ -261,6 +262,13 @@ AVAILABLE_CLI_OPTIONS = {
|
|||||||
metavar="INT",
|
metavar="INT",
|
||||||
default=constants.HYPEROPT_EPOCH,
|
default=constants.HYPEROPT_EPOCH,
|
||||||
),
|
),
|
||||||
|
"early_stop": Arg(
|
||||||
|
"--early-stop",
|
||||||
|
help="Early stop hyperopt if no improvement after (default: %(default)d) epochs.",
|
||||||
|
type=check_int_positive,
|
||||||
|
metavar="INT",
|
||||||
|
default=0, # 0 to disable by default
|
||||||
|
),
|
||||||
"spaces": Arg(
|
"spaces": Arg(
|
||||||
"--spaces",
|
"--spaces",
|
||||||
help="Specify which parameters to hyperopt. Space-separated list.",
|
help="Specify which parameters to hyperopt. Space-separated list.",
|
||||||
@@ -463,7 +471,7 @@ AVAILABLE_CLI_OPTIONS = {
|
|||||||
"format_from_trades": Arg(
|
"format_from_trades": Arg(
|
||||||
"--format-from",
|
"--format-from",
|
||||||
help="Source format for data conversion.",
|
help="Source format for data conversion.",
|
||||||
choices=constants.AVAILABLE_DATAHANDLERS + ["kraken_csv"],
|
choices=[*constants.AVAILABLE_DATAHANDLERS, "kraken_csv"],
|
||||||
required=True,
|
required=True,
|
||||||
),
|
),
|
||||||
"format_from": Arg(
|
"format_from": Arg(
|
||||||
@@ -527,6 +535,15 @@ AVAILABLE_CLI_OPTIONS = {
|
|||||||
),
|
),
|
||||||
type=str,
|
type=str,
|
||||||
),
|
),
|
||||||
|
"ui_prerelease": Arg(
|
||||||
|
"--prerelease",
|
||||||
|
help=(
|
||||||
|
"Install the latest pre-release version of FreqUI. "
|
||||||
|
"This is not recommended for production use."
|
||||||
|
),
|
||||||
|
action="store_true",
|
||||||
|
default=False,
|
||||||
|
),
|
||||||
# Templating options
|
# Templating options
|
||||||
"template": Arg(
|
"template": Arg(
|
||||||
"--template",
|
"--template",
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ def start_create_userdir(args: dict[str, Any]) -> None:
|
|||||||
"""
|
"""
|
||||||
from freqtrade.configuration.directory_operations import copy_sample_files, create_userdata_dir
|
from freqtrade.configuration.directory_operations import copy_sample_files, create_userdata_dir
|
||||||
|
|
||||||
if "user_data_dir" in args and args["user_data_dir"]:
|
if user_data_dir := args.get("user_data_dir"):
|
||||||
userdir = create_userdata_dir(args["user_data_dir"], create_dir=True)
|
userdir = create_userdata_dir(user_data_dir, create_dir=True)
|
||||||
copy_sample_files(userdir, overwrite=args["reset"])
|
copy_sample_files(userdir, overwrite=args["reset"])
|
||||||
else:
|
else:
|
||||||
logger.warning("`create-userdir` requires --userdir to be set.")
|
logger.warning("`create-userdir` requires --userdir to be set.")
|
||||||
@@ -85,22 +85,22 @@ def start_new_strategy(args: dict[str, Any]) -> None:
|
|||||||
|
|
||||||
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
|
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
|
||||||
|
|
||||||
if "strategy" in args and args["strategy"]:
|
if strategy := args.get("strategy"):
|
||||||
if "strategy_path" in args and args["strategy_path"]:
|
if strategy_path := args.get("strategy_path"):
|
||||||
strategy_dir = Path(args["strategy_path"])
|
strategy_dir = Path(strategy_path)
|
||||||
else:
|
else:
|
||||||
strategy_dir = config["user_data_dir"] / USERPATH_STRATEGIES
|
strategy_dir = config["user_data_dir"] / USERPATH_STRATEGIES
|
||||||
if not strategy_dir.is_dir():
|
if not strategy_dir.is_dir():
|
||||||
logger.info(f"Creating strategy directory {strategy_dir}")
|
logger.info(f"Creating strategy directory {strategy_dir}")
|
||||||
strategy_dir.mkdir(parents=True)
|
strategy_dir.mkdir(parents=True)
|
||||||
new_path = strategy_dir / (args["strategy"] + ".py")
|
new_path = strategy_dir / (strategy + ".py")
|
||||||
|
|
||||||
if new_path.exists():
|
if new_path.exists():
|
||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
f"`{new_path}` already exists. Please choose another Strategy Name."
|
f"`{new_path}` already exists. Please choose another Strategy Name."
|
||||||
)
|
)
|
||||||
|
|
||||||
deploy_new_strategy(args["strategy"], new_path, args["template"])
|
deploy_new_strategy(strategy, new_path, args["template"])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise ConfigurationError("`new-strategy` requires --strategy to be set.")
|
raise ConfigurationError("`new-strategy` requires --strategy to be set.")
|
||||||
@@ -116,7 +116,9 @@ def start_install_ui(args: dict[str, Any]) -> None:
|
|||||||
|
|
||||||
dest_folder = Path(__file__).parents[1] / "rpc/api_server/ui/installed/"
|
dest_folder = Path(__file__).parents[1] / "rpc/api_server/ui/installed/"
|
||||||
# First make sure the assets are removed.
|
# First make sure the assets are removed.
|
||||||
dl_url, latest_version = get_ui_download_url(args.get("ui_version"))
|
dl_url, latest_version = get_ui_download_url(
|
||||||
|
args.get("ui_version"), args.get("ui_prerelease", False)
|
||||||
|
)
|
||||||
|
|
||||||
curr_version = read_ui_version(dest_folder)
|
curr_version = read_ui_version(dest_folder)
|
||||||
if curr_version == latest_version and not args.get("erase_ui_only"):
|
if curr_version == latest_version and not args.get("erase_ui_only"):
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ def download_and_install_ui(dest_folder: Path, dl_url: str, version: str):
|
|||||||
f.write(version)
|
f.write(version)
|
||||||
|
|
||||||
|
|
||||||
def get_ui_download_url(version: str | None = None) -> tuple[str, str]:
|
def get_ui_download_url(version: str | None, prerelease: bool) -> tuple[str, str]:
|
||||||
base_url = "https://api.github.com/repos/freqtrade/frequi/"
|
base_url = "https://api.github.com/repos/freqtrade/frequi/"
|
||||||
# Get base UI Repo path
|
# Get base UI Repo path
|
||||||
|
|
||||||
@@ -61,14 +61,18 @@ def get_ui_download_url(version: str | None = None) -> tuple[str, str]:
|
|||||||
|
|
||||||
if version:
|
if version:
|
||||||
tmp = [x for x in r if x["name"] == version]
|
tmp = [x for x in r if x["name"] == version]
|
||||||
if tmp:
|
|
||||||
latest_version = tmp[0]["name"]
|
|
||||||
assets = tmp[0].get("assets", [])
|
|
||||||
else:
|
|
||||||
raise ValueError("UI-Version not found.")
|
|
||||||
else:
|
else:
|
||||||
latest_version = r[0]["name"]
|
tmp = [x for x in r if prerelease or not x.get("prerelease")]
|
||||||
assets = r[0].get("assets", [])
|
|
||||||
|
if tmp:
|
||||||
|
# Ensure we have the latest version
|
||||||
|
if version is None:
|
||||||
|
tmp.sort(key=lambda x: x["created_at"], reverse=True)
|
||||||
|
latest_version = tmp[0]["name"]
|
||||||
|
assets = tmp[0].get("assets", [])
|
||||||
|
else:
|
||||||
|
raise ValueError("UI-Version not found.")
|
||||||
|
|
||||||
dl_url = ""
|
dl_url = ""
|
||||||
if assets and len(assets) > 0:
|
if assets and len(assets) > 0:
|
||||||
dl_url = assets[0]["browser_download_url"]
|
dl_url = assets[0]["browser_download_url"]
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ from typing import Any
|
|||||||
|
|
||||||
from freqtrade.enums import RunMode
|
from freqtrade.enums import RunMode
|
||||||
from freqtrade.exceptions import ConfigurationError, OperationalException
|
from freqtrade.exceptions import ConfigurationError, OperationalException
|
||||||
from freqtrade.ft_types import ValidExchangesType
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -21,6 +20,7 @@ def start_list_exchanges(args: dict[str, Any]) -> None:
|
|||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
|
|
||||||
from freqtrade.exchange import list_available_exchanges
|
from freqtrade.exchange import list_available_exchanges
|
||||||
|
from freqtrade.ft_types import ValidExchangesType
|
||||||
from freqtrade.loggers.rich_console import get_rich_console
|
from freqtrade.loggers.rich_console import get_rich_console
|
||||||
|
|
||||||
available_exchanges: list[ValidExchangesType] = list_available_exchanges(
|
available_exchanges: list[ValidExchangesType] = list_available_exchanges(
|
||||||
|
|||||||
4
freqtrade/config_schema/__init__.py
Normal file
4
freqtrade/config_schema/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
from freqtrade.config_schema.config_schema import CONF_SCHEMA
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["CONF_SCHEMA"]
|
||||||
@@ -26,6 +26,8 @@ __MESSAGE_TYPE_DICT: dict[str, dict[str, str]] = {x: {"type": "object"} for x in
|
|||||||
|
|
||||||
__IN_STRATEGY = "\nUsually specified in the strategy and missing in the configuration."
|
__IN_STRATEGY = "\nUsually specified in the strategy and missing in the configuration."
|
||||||
|
|
||||||
|
__VIA_ENV = "Recommended to be set via environment variable"
|
||||||
|
|
||||||
CONF_SCHEMA = {
|
CONF_SCHEMA = {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -468,11 +470,16 @@ CONF_SCHEMA = {
|
|||||||
},
|
},
|
||||||
"token": {"description": "Telegram bot token.", "type": "string"},
|
"token": {"description": "Telegram bot token.", "type": "string"},
|
||||||
"chat_id": {
|
"chat_id": {
|
||||||
"description": "Telegram chat or group ID",
|
"description": (
|
||||||
|
f"Telegram chat or group ID. {__VIA_ENV} FREQTRADE__TELEGRAM__CHAT_ID"
|
||||||
|
),
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"topic_id": {
|
"topic_id": {
|
||||||
"description": "Telegram topic ID - only applicable for group chats",
|
"description": (
|
||||||
|
"Telegram topic ID - only applicable for group chats. "
|
||||||
|
f"{__VIA_ENV} FREQTRADE__TELEGRAM__TOPIC_ID"
|
||||||
|
),
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"authorized_users": {
|
"authorized_users": {
|
||||||
@@ -574,8 +581,11 @@ CONF_SCHEMA = {
|
|||||||
"description": "Webhook settings.",
|
"description": "Webhook settings.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"enabled": {"type": "boolean"},
|
"enabled": {"description": "Enable webhook notifications.", "type": "boolean"},
|
||||||
"url": {"type": "string"},
|
"url": {
|
||||||
|
"description": f"Webhook URL. {__VIA_ENV} FREQTRADE__WEBHOOK__URL",
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
"format": {"type": "string", "enum": WEBHOOK_FORMAT_OPTIONS, "default": "form"},
|
"format": {"type": "string", "enum": WEBHOOK_FORMAT_OPTIONS, "default": "form"},
|
||||||
"retries": {"type": "integer", "minimum": 0},
|
"retries": {"type": "integer", "minimum": 0},
|
||||||
"retry_delay": {"type": "number", "minimum": 0},
|
"retry_delay": {"type": "number", "minimum": 0},
|
||||||
@@ -587,7 +597,12 @@ CONF_SCHEMA = {
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"enabled": {"type": "boolean"},
|
"enabled": {"type": "boolean"},
|
||||||
"webhook_url": {"type": "string"},
|
"webhook_url": {
|
||||||
|
"description": (
|
||||||
|
f"Discord webhook URL. {__VIA_ENV} FREQTRADE__DISCORD__WEBHOOK_URL"
|
||||||
|
),
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
"exit_fill": {
|
"exit_fill": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {"type": "object"},
|
"items": {"type": "object"},
|
||||||
@@ -689,7 +704,7 @@ CONF_SCHEMA = {
|
|||||||
"initial_state": {
|
"initial_state": {
|
||||||
"description": "Initial state of the system.",
|
"description": "Initial state of the system.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["running", "stopped"],
|
"enum": ["running", "paused", "stopped"],
|
||||||
},
|
},
|
||||||
"force_entry_enable": {
|
"force_entry_enable": {
|
||||||
"description": "Force enable entry.",
|
"description": "Force enable entry.",
|
||||||
@@ -806,27 +821,57 @@ CONF_SCHEMA = {
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {"description": "Name of the exchange.", "type": "string"},
|
"name": {"description": "Name of the exchange.", "type": "string"},
|
||||||
"enable_ws": {
|
|
||||||
"description": "Enable WebSocket connections to the exchange.",
|
|
||||||
"type": "boolean",
|
|
||||||
"default": True,
|
|
||||||
},
|
|
||||||
"key": {
|
"key": {
|
||||||
"description": "API key for the exchange.",
|
"description": (
|
||||||
|
f"API key for the exchange. {__VIA_ENV} FREQTRADE__EXCHANGE__KEY"
|
||||||
|
),
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "",
|
"default": "",
|
||||||
},
|
},
|
||||||
"secret": {
|
"secret": {
|
||||||
"description": "API secret for the exchange.",
|
"description": (
|
||||||
|
f"API secret for the exchange. {__VIA_ENV} FREQTRADE__EXCHANGE__SECRET"
|
||||||
|
),
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "",
|
"default": "",
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"description": "Password for the exchange, if required.",
|
"description": (
|
||||||
|
"Password for the exchange, if required. "
|
||||||
|
f"{__VIA_ENV} FREQTRADE__EXCHANGE__PASSWORD"
|
||||||
|
),
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "",
|
"default": "",
|
||||||
},
|
},
|
||||||
"uid": {"description": "User ID for the exchange, if required.", "type": "string"},
|
"uid": {
|
||||||
|
"description": (
|
||||||
|
"User ID for the exchange, if required. "
|
||||||
|
f"{__VIA_ENV} FREQTRADE__EXCHANGE__UID"
|
||||||
|
),
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"account_id": {
|
||||||
|
"description": (
|
||||||
|
"Account ID for the exchange, if required. "
|
||||||
|
f"{__VIA_ENV} FREQTRADE__EXCHANGE__ACCOUNT_ID"
|
||||||
|
),
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"wallet_address": {
|
||||||
|
"description": (
|
||||||
|
"Wallet address for the exchange, if required. "
|
||||||
|
"Usually used by DEX exchanges. "
|
||||||
|
f"{__VIA_ENV} FREQTRADE__EXCHANGE__WALLET_ADDRESS"
|
||||||
|
),
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
|
"private_key": {
|
||||||
|
"description": (
|
||||||
|
"Private key for the exchange, if required. Usually used by DEX exchanges. "
|
||||||
|
f"{__VIA_ENV} FREQTRADE__EXCHANGE__PRIVATE_KEY"
|
||||||
|
),
|
||||||
|
"type": "string",
|
||||||
|
},
|
||||||
"pair_whitelist": {
|
"pair_whitelist": {
|
||||||
"description": "List of whitelisted trading pairs.",
|
"description": "List of whitelisted trading pairs.",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
@@ -847,6 +892,11 @@ CONF_SCHEMA = {
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": False,
|
"default": False,
|
||||||
},
|
},
|
||||||
|
"enable_ws": {
|
||||||
|
"description": "Enable WebSocket connections to the exchange.",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": True,
|
||||||
|
},
|
||||||
"unknown_fee_rate": {
|
"unknown_fee_rate": {
|
||||||
"description": "Fee rate for unknown markets.",
|
"description": "Fee rate for unknown markets.",
|
||||||
"type": "number",
|
"type": "number",
|
||||||
@@ -1146,6 +1196,14 @@ CONF_SCHEMA = {
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": False,
|
"default": False,
|
||||||
},
|
},
|
||||||
|
"indicator_periods_candles": {
|
||||||
|
"description": (
|
||||||
|
"Time periods to calculate indicators for. "
|
||||||
|
"The indicators are added to the base indicator dataset."
|
||||||
|
),
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "number", "minimum": 1},
|
||||||
|
},
|
||||||
"use_SVM_to_remove_outliers": {
|
"use_SVM_to_remove_outliers": {
|
||||||
"description": "Use SVM to remove outliers from the features.",
|
"description": "Use SVM to remove outliers from the features.",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@@ -1338,7 +1396,8 @@ SCHEMA_BACKTEST_REQUIRED = [
|
|||||||
"dataformat_ohlcv",
|
"dataformat_ohlcv",
|
||||||
"dataformat_trades",
|
"dataformat_trades",
|
||||||
]
|
]
|
||||||
SCHEMA_BACKTEST_REQUIRED_FINAL = SCHEMA_BACKTEST_REQUIRED + [
|
SCHEMA_BACKTEST_REQUIRED_FINAL = [
|
||||||
|
*SCHEMA_BACKTEST_REQUIRED,
|
||||||
"stoploss",
|
"stoploss",
|
||||||
"minimal_roi",
|
"minimal_roi",
|
||||||
"max_open_trades",
|
"max_open_trades",
|
||||||
@@ -1350,6 +1409,4 @@ SCHEMA_MINIMAL_REQUIRED = [
|
|||||||
"dataformat_ohlcv",
|
"dataformat_ohlcv",
|
||||||
"dataformat_trades",
|
"dataformat_trades",
|
||||||
]
|
]
|
||||||
SCHEMA_MINIMAL_WEBSERVER = SCHEMA_MINIMAL_REQUIRED + [
|
SCHEMA_MINIMAL_WEBSERVER = [*SCHEMA_MINIMAL_REQUIRED, "api_server"]
|
||||||
"api_server",
|
|
||||||
]
|
|
||||||
@@ -29,6 +29,7 @@ def sanitize_config(config: Config, *, show_sensitive: bool = False) -> Config:
|
|||||||
"telegram.chat_id",
|
"telegram.chat_id",
|
||||||
"discord.webhook_url",
|
"discord.webhook_url",
|
||||||
"api_server.password",
|
"api_server.password",
|
||||||
|
"webhook.url",
|
||||||
]
|
]
|
||||||
config = deepcopy(config)
|
config = deepcopy(config)
|
||||||
for key in keys_to_remove:
|
for key in keys_to_remove:
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from typing import Any
|
|||||||
from jsonschema import Draft4Validator, validators
|
from jsonschema import Draft4Validator, validators
|
||||||
from jsonschema.exceptions import ValidationError, best_match
|
from jsonschema.exceptions import ValidationError, best_match
|
||||||
|
|
||||||
from freqtrade.configuration.config_schema import (
|
from freqtrade.config_schema.config_schema import (
|
||||||
CONF_SCHEMA,
|
CONF_SCHEMA,
|
||||||
SCHEMA_BACKTEST_REQUIRED,
|
SCHEMA_BACKTEST_REQUIRED,
|
||||||
SCHEMA_BACKTEST_REQUIRED_FINAL,
|
SCHEMA_BACKTEST_REQUIRED_FINAL,
|
||||||
@@ -104,7 +104,7 @@ def _validate_unlimited_amount(conf: dict[str, Any]) -> None:
|
|||||||
"""
|
"""
|
||||||
if (
|
if (
|
||||||
not conf.get("edge", {}).get("enabled")
|
not conf.get("edge", {}).get("enabled")
|
||||||
and conf.get("max_open_trades") == float("inf")
|
and (conf.get("max_open_trades") == float("inf") or conf.get("max_open_trades") == -1)
|
||||||
and conf.get("stake_amount") == UNLIMITED_STAKE_AMOUNT
|
and conf.get("stake_amount") == UNLIMITED_STAKE_AMOUNT
|
||||||
):
|
):
|
||||||
raise ConfigurationError("`max_open_trades` and `stake_amount` cannot both be unlimited.")
|
raise ConfigurationError("`max_open_trades` and `stake_amount` cannot both be unlimited.")
|
||||||
@@ -361,7 +361,7 @@ def _validate_freqai_include_timeframes(conf: dict[str, Any], preliminary: bool)
|
|||||||
# Ensure that the base timeframe is included in the include_timeframes list
|
# Ensure that the base timeframe is included in the include_timeframes list
|
||||||
if not preliminary and main_tf not in freqai_include_timeframes:
|
if not preliminary and main_tf not in freqai_include_timeframes:
|
||||||
feature_parameters = conf.get("freqai", {}).get("feature_parameters", {})
|
feature_parameters = conf.get("freqai", {}).get("feature_parameters", {})
|
||||||
include_timeframes = [main_tf] + freqai_include_timeframes
|
include_timeframes = [main_tf, *freqai_include_timeframes]
|
||||||
conf.get("freqai", {}).get("feature_parameters", {}).update(
|
conf.get("freqai", {}).get("feature_parameters", {}).update(
|
||||||
{**feature_parameters, "include_timeframes": include_timeframes}
|
{**feature_parameters, "include_timeframes": include_timeframes}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ class Configuration:
|
|||||||
{"verbosity": safe_value_fallback(self.args, "verbosity", default_value=0)}
|
{"verbosity": safe_value_fallback(self.args, "verbosity", default_value=0)}
|
||||||
)
|
)
|
||||||
|
|
||||||
if "logfile" in self.args and self.args["logfile"]:
|
if self.args.get("logfile"):
|
||||||
config.update({"logfile": self.args["logfile"]})
|
config.update({"logfile": self.args["logfile"]})
|
||||||
|
|
||||||
if "print_colorized" in self.args and not self.args["print_colorized"]:
|
if "print_colorized" in self.args and not self.args["print_colorized"]:
|
||||||
@@ -187,7 +187,7 @@ class Configuration:
|
|||||||
logger.warning("`force_entry_enable` RPC message enabled.")
|
logger.warning("`force_entry_enable` RPC message enabled.")
|
||||||
|
|
||||||
# Support for sd_notify
|
# Support for sd_notify
|
||||||
if "sd_notify" in self.args and self.args["sd_notify"]:
|
if self.args.get("sd_notify"):
|
||||||
config["internals"].update({"sd_notify": True})
|
config["internals"].update({"sd_notify": True})
|
||||||
|
|
||||||
def _process_datadir_options(self, config: Config) -> None:
|
def _process_datadir_options(self, config: Config) -> None:
|
||||||
@@ -196,14 +196,14 @@ class Configuration:
|
|||||||
--user-data, --datadir
|
--user-data, --datadir
|
||||||
"""
|
"""
|
||||||
# Check exchange parameter here - otherwise `datadir` might be wrong.
|
# Check exchange parameter here - otherwise `datadir` might be wrong.
|
||||||
if "exchange" in self.args and self.args["exchange"]:
|
if self.args.get("exchange"):
|
||||||
config["exchange"]["name"] = self.args["exchange"]
|
config["exchange"]["name"] = self.args["exchange"]
|
||||||
logger.info(f"Using exchange {config['exchange']['name']}")
|
logger.info(f"Using exchange {config['exchange']['name']}")
|
||||||
|
|
||||||
if "pair_whitelist" not in config["exchange"]:
|
if "pair_whitelist" not in config["exchange"]:
|
||||||
config["exchange"]["pair_whitelist"] = []
|
config["exchange"]["pair_whitelist"] = []
|
||||||
|
|
||||||
if "user_data_dir" in self.args and self.args["user_data_dir"]:
|
if self.args.get("user_data_dir"):
|
||||||
config.update({"user_data_dir": self.args["user_data_dir"]})
|
config.update({"user_data_dir": self.args["user_data_dir"]})
|
||||||
elif "user_data_dir" not in config:
|
elif "user_data_dir" not in config:
|
||||||
# Default to cwd/user_data (legacy option ...)
|
# Default to cwd/user_data (legacy option ...)
|
||||||
@@ -251,7 +251,7 @@ class Configuration:
|
|||||||
logstring="Parameter --enable-protections detected, enabling Protections. ...",
|
logstring="Parameter --enable-protections detected, enabling Protections. ...",
|
||||||
)
|
)
|
||||||
|
|
||||||
if "max_open_trades" in self.args and self.args["max_open_trades"]:
|
if self.args.get("max_open_trades"):
|
||||||
config.update({"max_open_trades": self.args["max_open_trades"]})
|
config.update({"max_open_trades": self.args["max_open_trades"]})
|
||||||
logger.info(
|
logger.info(
|
||||||
"Parameter --max-open-trades detected, overriding max_open_trades to: %s ...",
|
"Parameter --max-open-trades detected, overriding max_open_trades to: %s ...",
|
||||||
@@ -314,7 +314,7 @@ class Configuration:
|
|||||||
self._args_to_config_loop(config, configurations)
|
self._args_to_config_loop(config, configurations)
|
||||||
|
|
||||||
# Edge section:
|
# Edge section:
|
||||||
if "stoploss_range" in self.args and self.args["stoploss_range"]:
|
if self.args.get("stoploss_range"):
|
||||||
txt_range = ast.literal_eval(self.args["stoploss_range"])
|
txt_range = ast.literal_eval(self.args["stoploss_range"])
|
||||||
config["edge"].update({"stoploss_range_min": txt_range[0]})
|
config["edge"].update({"stoploss_range_min": txt_range[0]})
|
||||||
config["edge"].update({"stoploss_range_max": txt_range[1]})
|
config["edge"].update({"stoploss_range_max": txt_range[1]})
|
||||||
@@ -334,6 +334,19 @@ class Configuration:
|
|||||||
("print_all", "Parameter --print-all detected ..."),
|
("print_all", "Parameter --print-all detected ..."),
|
||||||
]
|
]
|
||||||
self._args_to_config_loop(config, configurations)
|
self._args_to_config_loop(config, configurations)
|
||||||
|
es_epochs = self.args.get("early_stop", 0)
|
||||||
|
if es_epochs > 0:
|
||||||
|
if es_epochs < 20:
|
||||||
|
logger.warning(
|
||||||
|
f"Early stop epochs {es_epochs} lower than 20. It will be replaced with 20."
|
||||||
|
)
|
||||||
|
config.update({"early_stop": 20})
|
||||||
|
else:
|
||||||
|
config.update({"early_stop": self.args["early_stop"]})
|
||||||
|
logger.info(
|
||||||
|
f"Parameter --early-stop detected ... Will early stop hyperopt if no improvement "
|
||||||
|
f"after {config.get('early_stop')} epochs ..."
|
||||||
|
)
|
||||||
|
|
||||||
configurations = [
|
configurations = [
|
||||||
("print_json", "Parameter --print-json detected ..."),
|
("print_json", "Parameter --print-json detected ..."),
|
||||||
@@ -493,7 +506,7 @@ class Configuration:
|
|||||||
config["exchange"]["pair_whitelist"] = config["pairs"]
|
config["exchange"]["pair_whitelist"] = config["pairs"]
|
||||||
return
|
return
|
||||||
|
|
||||||
if "pairs_file" in self.args and self.args["pairs_file"]:
|
if self.args.get("pairs_file"):
|
||||||
pairs_file = Path(self.args["pairs_file"])
|
pairs_file = Path(self.args["pairs_file"])
|
||||||
logger.info(f'Reading pairs file "{pairs_file}".')
|
logger.info(f'Reading pairs file "{pairs_file}".')
|
||||||
# Download pairs from the pairs file if no config is specified
|
# Download pairs from the pairs file if no config is specified
|
||||||
@@ -505,7 +518,7 @@ class Configuration:
|
|||||||
config["pairs"].sort()
|
config["pairs"].sort()
|
||||||
return
|
return
|
||||||
|
|
||||||
if "config" in self.args and self.args["config"]:
|
if self.args.get("config"):
|
||||||
logger.info("Using pairlist from configuration.")
|
logger.info("Using pairlist from configuration.")
|
||||||
config["pairs"] = config.get("exchange", {}).get("pair_whitelist")
|
config["pairs"] = config.get("exchange", {}).get("pair_whitelist")
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ def chown_user_directory(directory: Path) -> None:
|
|||||||
"""
|
"""
|
||||||
if running_in_docker():
|
if running_in_docker():
|
||||||
try:
|
try:
|
||||||
import subprocess # noqa: S404
|
import subprocess # noqa: S404, RUF100
|
||||||
|
|
||||||
subprocess.check_output(["sudo", "chown", "-R", "ftuser:", str(directory.resolve())])
|
subprocess.check_output(["sudo", "chown", "-R", "ftuser:", str(directory.resolve())])
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ HYPEROPT_LOSS_BUILTIN = [
|
|||||||
"CalmarHyperOptLoss",
|
"CalmarHyperOptLoss",
|
||||||
"MaxDrawDownHyperOptLoss",
|
"MaxDrawDownHyperOptLoss",
|
||||||
"MaxDrawDownRelativeHyperOptLoss",
|
"MaxDrawDownRelativeHyperOptLoss",
|
||||||
|
"MaxDrawDownPerPairHyperOptLoss",
|
||||||
"ProfitDrawDownHyperOptLoss",
|
"ProfitDrawDownHyperOptLoss",
|
||||||
"MultiMetricHyperOptLoss",
|
"MultiMetricHyperOptLoss",
|
||||||
]
|
]
|
||||||
@@ -70,6 +71,19 @@ DEFAULT_DATAFRAME_COLUMNS = ["date", "open", "high", "low", "close", "volume"]
|
|||||||
# it has wide consequences for stored trades files
|
# it has wide consequences for stored trades files
|
||||||
DEFAULT_TRADES_COLUMNS = ["timestamp", "id", "type", "side", "price", "amount", "cost"]
|
DEFAULT_TRADES_COLUMNS = ["timestamp", "id", "type", "side", "price", "amount", "cost"]
|
||||||
DEFAULT_ORDERFLOW_COLUMNS = ["level", "bid", "ask", "delta"]
|
DEFAULT_ORDERFLOW_COLUMNS = ["level", "bid", "ask", "delta"]
|
||||||
|
ORDERFLOW_ADDED_COLUMNS = [
|
||||||
|
"trades",
|
||||||
|
"orderflow",
|
||||||
|
"imbalances",
|
||||||
|
"stacked_imbalances_bid",
|
||||||
|
"stacked_imbalances_ask",
|
||||||
|
"max_delta",
|
||||||
|
"min_delta",
|
||||||
|
"bid",
|
||||||
|
"ask",
|
||||||
|
"delta",
|
||||||
|
"total_trades",
|
||||||
|
]
|
||||||
TRADES_DTYPES = {
|
TRADES_DTYPES = {
|
||||||
"timestamp": "int64",
|
"timestamp": "int64",
|
||||||
"id": "str",
|
"id": "str",
|
||||||
@@ -99,7 +113,7 @@ DL_DATA_TIMEFRAMES = ["1m", "5m"]
|
|||||||
ENV_VAR_PREFIX = "FREQTRADE__"
|
ENV_VAR_PREFIX = "FREQTRADE__"
|
||||||
|
|
||||||
CANCELED_EXCHANGE_STATES = ("cancelled", "canceled", "expired", "rejected")
|
CANCELED_EXCHANGE_STATES = ("cancelled", "canceled", "expired", "rejected")
|
||||||
NON_OPEN_EXCHANGE_STATES = CANCELED_EXCHANGE_STATES + ("closed",)
|
NON_OPEN_EXCHANGE_STATES = (*CANCELED_EXCHANGE_STATES, "closed")
|
||||||
|
|
||||||
# Define decimals per coin for outputs
|
# Define decimals per coin for outputs
|
||||||
# Only used for outputs.
|
# Only used for outputs.
|
||||||
|
|||||||
32
freqtrade/data/btanalysis/__init__.py
Normal file
32
freqtrade/data/btanalysis/__init__.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# flake8: noqa: F401
|
||||||
|
from .bt_fileutils import (
|
||||||
|
BT_DATA_COLUMNS,
|
||||||
|
delete_backtest_result,
|
||||||
|
extract_trades_of_period,
|
||||||
|
find_existing_backtest_stats,
|
||||||
|
get_backtest_market_change,
|
||||||
|
get_backtest_result,
|
||||||
|
get_backtest_resultlist,
|
||||||
|
get_latest_backtest_filename,
|
||||||
|
get_latest_hyperopt_file,
|
||||||
|
get_latest_hyperopt_filename,
|
||||||
|
get_latest_optimize_filename,
|
||||||
|
load_and_merge_backtest_result,
|
||||||
|
load_backtest_analysis_data,
|
||||||
|
load_backtest_data,
|
||||||
|
load_backtest_metadata,
|
||||||
|
load_backtest_stats,
|
||||||
|
load_exit_signal_candles,
|
||||||
|
load_file_from_zip,
|
||||||
|
load_rejected_signals,
|
||||||
|
load_signal_candles,
|
||||||
|
load_trades,
|
||||||
|
load_trades_from_db,
|
||||||
|
trade_list_to_dataframe,
|
||||||
|
update_backtest_metadata,
|
||||||
|
)
|
||||||
|
from .historic_precision import get_tick_size_over_time
|
||||||
|
from .trade_parallelism import (
|
||||||
|
analyze_trade_parallelism,
|
||||||
|
evaluate_result_multi,
|
||||||
|
)
|
||||||
@@ -13,7 +13,7 @@ from typing import Any, Literal
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
from freqtrade.constants import LAST_BT_RESULT_FN, IntOrInf
|
from freqtrade.constants import LAST_BT_RESULT_FN
|
||||||
from freqtrade.exceptions import ConfigurationError, OperationalException
|
from freqtrade.exceptions import ConfigurationError, OperationalException
|
||||||
from freqtrade.ft_types import BacktestHistoryEntryType, BacktestResultType
|
from freqtrade.ft_types import BacktestHistoryEntryType, BacktestResultType
|
||||||
from freqtrade.misc import file_dump_json, json_load
|
from freqtrade.misc import file_dump_json, json_load
|
||||||
@@ -52,6 +52,7 @@ BT_DATA_COLUMNS = [
|
|||||||
"open_timestamp",
|
"open_timestamp",
|
||||||
"close_timestamp",
|
"close_timestamp",
|
||||||
"orders",
|
"orders",
|
||||||
|
"funding_fees",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -356,6 +357,8 @@ def _load_backtest_data_df_compatibility(df: pd.DataFrame) -> pd.DataFrame:
|
|||||||
df["max_stake_amount"] = df["stake_amount"]
|
df["max_stake_amount"] = df["stake_amount"]
|
||||||
if "orders" not in df.columns:
|
if "orders" not in df.columns:
|
||||||
df["orders"] = None
|
df["orders"] = None
|
||||||
|
if "funding_fees" not in df.columns:
|
||||||
|
df["funding_fees"] = 0.0
|
||||||
return df
|
return df
|
||||||
|
|
||||||
|
|
||||||
@@ -376,7 +379,7 @@ def load_backtest_data(filename: Path | str, strategy: str | None = None) -> pd.
|
|||||||
|
|
||||||
if not strategy:
|
if not strategy:
|
||||||
if len(data["strategy"]) == 1:
|
if len(data["strategy"]) == 1:
|
||||||
strategy = list(data["strategy"].keys())[0]
|
strategy = next(iter(data["strategy"].keys()))
|
||||||
else:
|
else:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Detected backtest result with more than one strategy. "
|
"Detected backtest result with more than one strategy. "
|
||||||
@@ -491,55 +494,6 @@ def load_exit_signal_candles(backtest_dir: Path) -> dict[str, dict[str, pd.DataF
|
|||||||
return load_backtest_analysis_data(backtest_dir, "exited")
|
return load_backtest_analysis_data(backtest_dir, "exited")
|
||||||
|
|
||||||
|
|
||||||
def analyze_trade_parallelism(results: pd.DataFrame, timeframe: str) -> pd.DataFrame:
|
|
||||||
"""
|
|
||||||
Find overlapping trades by expanding each trade once per period it was open
|
|
||||||
and then counting overlaps.
|
|
||||||
:param results: Results Dataframe - can be loaded
|
|
||||||
:param timeframe: Timeframe used for backtest
|
|
||||||
:return: dataframe with open-counts per time-period in timeframe
|
|
||||||
"""
|
|
||||||
from freqtrade.exchange import timeframe_to_resample_freq
|
|
||||||
|
|
||||||
timeframe_freq = timeframe_to_resample_freq(timeframe)
|
|
||||||
dates = [
|
|
||||||
pd.Series(
|
|
||||||
pd.date_range(
|
|
||||||
row[1]["open_date"],
|
|
||||||
row[1]["close_date"],
|
|
||||||
freq=timeframe_freq,
|
|
||||||
# Exclude right boundary - the date is the candle open date.
|
|
||||||
inclusive="left",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
for row in results[["open_date", "close_date"]].iterrows()
|
|
||||||
]
|
|
||||||
deltas = [len(x) for x in dates]
|
|
||||||
dates = pd.Series(pd.concat(dates).values, name="date")
|
|
||||||
df2 = pd.DataFrame(np.repeat(results.values, deltas, axis=0), columns=results.columns)
|
|
||||||
|
|
||||||
df2 = pd.concat([dates, df2], axis=1)
|
|
||||||
df2 = df2.set_index("date")
|
|
||||||
df_final = df2.resample(timeframe_freq)[["pair"]].count()
|
|
||||||
df_final = df_final.rename({"pair": "open_trades"}, axis=1)
|
|
||||||
return df_final
|
|
||||||
|
|
||||||
|
|
||||||
def evaluate_result_multi(
|
|
||||||
results: pd.DataFrame, timeframe: str, max_open_trades: IntOrInf
|
|
||||||
) -> pd.DataFrame:
|
|
||||||
"""
|
|
||||||
Find overlapping trades by expanding each trade once per period it was open
|
|
||||||
and then counting overlaps
|
|
||||||
:param results: Results Dataframe - can be loaded
|
|
||||||
:param timeframe: Frequency used for the backtest
|
|
||||||
:param max_open_trades: parameter max_open_trades used during backtest run
|
|
||||||
:return: dataframe with open-counts per time-period in freq
|
|
||||||
"""
|
|
||||||
df_final = analyze_trade_parallelism(results, timeframe)
|
|
||||||
return df_final[df_final["open_trades"] > max_open_trades]
|
|
||||||
|
|
||||||
|
|
||||||
def trade_list_to_dataframe(trades: list[Trade] | list[LocalTrade]) -> pd.DataFrame:
|
def trade_list_to_dataframe(trades: list[Trade] | list[LocalTrade]) -> pd.DataFrame:
|
||||||
"""
|
"""
|
||||||
Convert list of Trade objects to pandas Dataframe
|
Convert list of Trade objects to pandas Dataframe
|
||||||
27
freqtrade/data/btanalysis/historic_precision.py
Normal file
27
freqtrade/data/btanalysis/historic_precision.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
from pandas import DataFrame, Series
|
||||||
|
|
||||||
|
|
||||||
|
def get_tick_size_over_time(candles: DataFrame) -> Series:
|
||||||
|
"""
|
||||||
|
Calculate the number of significant digits for candles over time.
|
||||||
|
It's using the Monthly maximum of the number of significant digits for each month.
|
||||||
|
:param candles: DataFrame with OHLCV data
|
||||||
|
:return: Series with the average number of significant digits for each month
|
||||||
|
"""
|
||||||
|
# count the number of significant digits for the open and close prices
|
||||||
|
for col in ["open", "high", "low", "close"]:
|
||||||
|
candles[f"{col}_count"] = (
|
||||||
|
candles[col].round(14).astype(str).str.extract(r"\.(\d*[1-9])")[0].str.len()
|
||||||
|
)
|
||||||
|
candles["max_count"] = candles[["open_count", "close_count", "high_count", "low_count"]].max(
|
||||||
|
axis=1
|
||||||
|
)
|
||||||
|
|
||||||
|
candles1 = candles.set_index("date", drop=True)
|
||||||
|
# Group by month and calculate the average number of significant digits
|
||||||
|
monthly_count_avg1 = candles1["max_count"].resample("MS").max()
|
||||||
|
# monthly_open_count_avg
|
||||||
|
# convert monthly_open_count_avg from 5.0 to 0.00001, 4.0 to 0.0001, ...
|
||||||
|
monthly_open_count_avg = 1 / 10**monthly_count_avg1
|
||||||
|
|
||||||
|
return monthly_open_count_avg
|
||||||
60
freqtrade/data/btanalysis/trade_parallelism.py
Normal file
60
freqtrade/data/btanalysis/trade_parallelism.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
from freqtrade.constants import IntOrInf
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_trade_parallelism(trades: pd.DataFrame, timeframe: str) -> pd.DataFrame:
|
||||||
|
"""
|
||||||
|
Find overlapping trades by expanding each trade once per period it was open
|
||||||
|
and then counting overlaps.
|
||||||
|
:param trades: Trades Dataframe - can be loaded from backtest, or created
|
||||||
|
via trade_list_to_dataframe
|
||||||
|
:param timeframe: Timeframe used for backtest
|
||||||
|
:return: dataframe with open-counts per time-period in timeframe
|
||||||
|
"""
|
||||||
|
from freqtrade.exchange import timeframe_to_resample_freq
|
||||||
|
|
||||||
|
timeframe_freq = timeframe_to_resample_freq(timeframe)
|
||||||
|
dates = [
|
||||||
|
pd.Series(
|
||||||
|
pd.date_range(
|
||||||
|
row[1]["open_date"],
|
||||||
|
row[1]["close_date"],
|
||||||
|
freq=timeframe_freq,
|
||||||
|
# Exclude right boundary - the date is the candle open date.
|
||||||
|
inclusive="left",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for row in trades[["open_date", "close_date"]].iterrows()
|
||||||
|
]
|
||||||
|
deltas = [len(x) for x in dates]
|
||||||
|
dates = pd.Series(pd.concat(dates).values, name="date")
|
||||||
|
df2 = pd.DataFrame(np.repeat(trades.values, deltas, axis=0), columns=trades.columns)
|
||||||
|
|
||||||
|
df2 = pd.concat([dates, df2], axis=1)
|
||||||
|
df2 = df2.set_index("date")
|
||||||
|
df_final = df2.resample(timeframe_freq)[["pair"]].count()
|
||||||
|
df_final = df_final.rename({"pair": "open_trades"}, axis=1)
|
||||||
|
return df_final
|
||||||
|
|
||||||
|
|
||||||
|
def evaluate_result_multi(
|
||||||
|
trades: pd.DataFrame, timeframe: str, max_open_trades: IntOrInf
|
||||||
|
) -> pd.DataFrame:
|
||||||
|
"""
|
||||||
|
Find overlapping trades by expanding each trade once per period it was open
|
||||||
|
and then counting overlaps
|
||||||
|
:param trades: Trades Dataframe - can be loaded from backtest, or created
|
||||||
|
via trade_list_to_dataframe
|
||||||
|
:param timeframe: Frequency used for the backtest
|
||||||
|
:param max_open_trades: parameter max_open_trades used during backtest run
|
||||||
|
:return: dataframe with open-counts per time-period in freq
|
||||||
|
"""
|
||||||
|
df_final = analyze_trade_parallelism(trades, timeframe)
|
||||||
|
return df_final[df_final["open_trades"] > max_open_trades]
|
||||||
@@ -9,26 +9,12 @@ from datetime import datetime
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
from freqtrade.constants import DEFAULT_ORDERFLOW_COLUMNS, Config
|
from freqtrade.constants import DEFAULT_ORDERFLOW_COLUMNS, ORDERFLOW_ADDED_COLUMNS, Config
|
||||||
from freqtrade.exceptions import DependencyException
|
from freqtrade.exceptions import DependencyException
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
ORDERFLOW_ADDED_COLUMNS = [
|
|
||||||
"trades",
|
|
||||||
"orderflow",
|
|
||||||
"imbalances",
|
|
||||||
"stacked_imbalances_bid",
|
|
||||||
"stacked_imbalances_ask",
|
|
||||||
"max_delta",
|
|
||||||
"min_delta",
|
|
||||||
"bid",
|
|
||||||
"ask",
|
|
||||||
"delta",
|
|
||||||
"total_trades",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def _init_dataframe_with_trades_columns(dataframe: pd.DataFrame):
|
def _init_dataframe_with_trades_columns(dataframe: pd.DataFrame):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ def _merge_dfs(
|
|||||||
):
|
):
|
||||||
merge_on = ["pair", "open_date"]
|
merge_on = ["pair", "open_date"]
|
||||||
signal_wide_indicators = list(set(available_inds) - set(BT_DATA_COLUMNS))
|
signal_wide_indicators = list(set(available_inds) - set(BT_DATA_COLUMNS))
|
||||||
columns_to_keep = merge_on + ["enter_reason", "exit_reason"]
|
columns_to_keep = [*merge_on, "enter_reason", "exit_reason"]
|
||||||
|
|
||||||
if exit_df is None or exit_df.empty or entry_only is True:
|
if exit_df is None or exit_df.empty or entry_only is True:
|
||||||
return entry_df[columns_to_keep + available_inds]
|
return entry_df[columns_to_keep + available_inds]
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ def load_data(
|
|||||||
result[pair] = hist
|
result[pair] = hist
|
||||||
else:
|
else:
|
||||||
if candle_type is CandleType.FUNDING_RATE and user_futures_funding_rate is not None:
|
if candle_type is CandleType.FUNDING_RATE and user_futures_funding_rate is not None:
|
||||||
logger.warn(f"{pair} using user specified [{user_futures_funding_rate}]")
|
logger.warning(f"{pair} using user specified [{user_futures_funding_rate}]")
|
||||||
elif candle_type not in (CandleType.SPOT, CandleType.FUTURES):
|
elif candle_type not in (CandleType.SPOT, CandleType.FUTURES):
|
||||||
result[pair] = DataFrame(columns=["date", "open", "close", "high", "low", "volume"])
|
result[pair] = DataFrame(columns=["date", "open", "close", "high", "low", "volume"])
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ import pandas as pd
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def calculate_market_change(data: dict[str, pd.DataFrame], column: str = "close") -> float:
|
def calculate_market_change(
|
||||||
|
data: dict[str, pd.DataFrame], column: str = "close", min_date: datetime | None = None
|
||||||
|
) -> float:
|
||||||
"""
|
"""
|
||||||
Calculate market change based on "column".
|
Calculate market change based on "column".
|
||||||
Calculation is done by taking the first non-null and the last non-null element of each column
|
Calculation is done by taking the first non-null and the last non-null element of each column
|
||||||
@@ -19,14 +21,24 @@ def calculate_market_change(data: dict[str, pd.DataFrame], column: str = "close"
|
|||||||
|
|
||||||
:param data: Dict of Dataframes, dict key should be pair.
|
:param data: Dict of Dataframes, dict key should be pair.
|
||||||
:param column: Column in the original dataframes to use
|
:param column: Column in the original dataframes to use
|
||||||
|
:param min_date: Minimum date to consider for calculations. Market change should only be
|
||||||
|
calculated for data actually backtested, excluding startup periods.
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
tmp_means = []
|
tmp_means = []
|
||||||
for pair, df in data.items():
|
for pair, df in data.items():
|
||||||
start = df[column].dropna().iloc[0]
|
df1 = df
|
||||||
end = df[column].dropna().iloc[-1]
|
if min_date is not None:
|
||||||
|
df1 = df1[df1["date"] >= min_date]
|
||||||
|
if df1.empty:
|
||||||
|
logger.warning(f"Pair {pair} has no data after {min_date}.")
|
||||||
|
continue
|
||||||
|
start = df1[column].dropna().iloc[0]
|
||||||
|
end = df1[column].dropna().iloc[-1]
|
||||||
tmp_means.append((end - start) / start)
|
tmp_means.append((end - start) / start)
|
||||||
|
|
||||||
|
if not tmp_means:
|
||||||
|
return 0.0
|
||||||
return float(np.mean(tmp_means))
|
return float(np.mean(tmp_means))
|
||||||
|
|
||||||
|
|
||||||
@@ -118,7 +130,7 @@ def _calc_drawdown_series(
|
|||||||
) -> pd.DataFrame:
|
) -> pd.DataFrame:
|
||||||
max_drawdown_df = pd.DataFrame()
|
max_drawdown_df = pd.DataFrame()
|
||||||
max_drawdown_df["cumulative"] = profit_results[value_col].cumsum()
|
max_drawdown_df["cumulative"] = profit_results[value_col].cumsum()
|
||||||
max_drawdown_df["high_value"] = max_drawdown_df["cumulative"].cummax()
|
max_drawdown_df["high_value"] = np.maximum(0, max_drawdown_df["cumulative"].cummax())
|
||||||
max_drawdown_df["drawdown"] = max_drawdown_df["cumulative"] - max_drawdown_df["high_value"]
|
max_drawdown_df["drawdown"] = max_drawdown_df["cumulative"] - max_drawdown_df["high_value"]
|
||||||
max_drawdown_df["date"] = profit_results.loc[:, date_col]
|
max_drawdown_df["date"] = profit_results.loc[:, date_col]
|
||||||
if starting_balance:
|
if starting_balance:
|
||||||
@@ -201,13 +213,11 @@ def calculate_max_drawdown(
|
|||||||
if relative
|
if relative
|
||||||
else max_drawdown_df["drawdown"].idxmin()
|
else max_drawdown_df["drawdown"].idxmin()
|
||||||
)
|
)
|
||||||
if idxmin == 0:
|
|
||||||
raise ValueError("No losing trade, therefore no drawdown.")
|
high_idx = max_drawdown_df.iloc[: idxmin + 1]["high_value"].idxmax()
|
||||||
high_date = profit_results.loc[max_drawdown_df.iloc[:idxmin]["high_value"].idxmax(), date_col]
|
high_date = profit_results.loc[high_idx, date_col]
|
||||||
low_date = profit_results.loc[idxmin, date_col]
|
low_date = profit_results.loc[idxmin, date_col]
|
||||||
high_val = max_drawdown_df.loc[
|
high_val = max_drawdown_df.loc[high_idx, "cumulative"]
|
||||||
max_drawdown_df.iloc[:idxmin]["high_value"].idxmax(), "cumulative"
|
|
||||||
]
|
|
||||||
low_val = max_drawdown_df.loc[idxmin, "cumulative"]
|
low_val = max_drawdown_df.loc[idxmin, "cumulative"]
|
||||||
max_drawdown_rel = max_drawdown_df.loc[idxmin, "drawdown_relative"]
|
max_drawdown_rel = max_drawdown_df.loc[idxmin, "drawdown_relative"]
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ class State(Enum):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
RUNNING = 1
|
RUNNING = 1
|
||||||
STOPPED = 2
|
PAUSED = 2
|
||||||
RELOAD_CONFIG = 3
|
STOPPED = 3
|
||||||
|
RELOAD_CONFIG = 4
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.name.lower()}"
|
return f"{self.name.lower()}"
|
||||||
|
|||||||
@@ -32,19 +32,23 @@ class Binance(Exchange):
|
|||||||
"stop_price_param": "stopPrice",
|
"stop_price_param": "stopPrice",
|
||||||
"stop_price_prop": "stopPrice",
|
"stop_price_prop": "stopPrice",
|
||||||
"stoploss_order_types": {"limit": "stop_loss_limit"},
|
"stoploss_order_types": {"limit": "stop_loss_limit"},
|
||||||
|
"stoploss_blocks_assets": True, # By default stoploss orders block assets
|
||||||
"order_time_in_force": ["GTC", "FOK", "IOC", "PO"],
|
"order_time_in_force": ["GTC", "FOK", "IOC", "PO"],
|
||||||
"trades_pagination": "id",
|
"trades_pagination": "id",
|
||||||
"trades_pagination_arg": "fromId",
|
"trades_pagination_arg": "fromId",
|
||||||
"trades_has_history": True,
|
"trades_has_history": True,
|
||||||
|
"fetch_orders_limit_minutes": None,
|
||||||
"l2_limit_range": [5, 10, 20, 50, 100, 500, 1000],
|
"l2_limit_range": [5, 10, 20, 50, 100, 500, 1000],
|
||||||
"ws_enabled": True,
|
"ws_enabled": True,
|
||||||
}
|
}
|
||||||
_ft_has_futures: FtHas = {
|
_ft_has_futures: FtHas = {
|
||||||
"funding_fee_candle_limit": 1000,
|
"funding_fee_candle_limit": 1000,
|
||||||
"stoploss_order_types": {"limit": "stop", "market": "stop_market"},
|
"stoploss_order_types": {"limit": "stop", "market": "stop_market"},
|
||||||
|
"stoploss_blocks_assets": False, # Stoploss orders do not block assets
|
||||||
"order_time_in_force": ["GTC", "FOK", "IOC"],
|
"order_time_in_force": ["GTC", "FOK", "IOC"],
|
||||||
"tickers_have_price": False,
|
"tickers_have_price": False,
|
||||||
"floor_leverage": True,
|
"floor_leverage": True,
|
||||||
|
"fetch_orders_limit_minutes": 7 * 1440, # "fetch_orders" is limited to 7 days
|
||||||
"stop_price_type_field": "workingType",
|
"stop_price_type_field": "workingType",
|
||||||
"order_props_in_contracts": ["amount", "cost", "filled", "remaining"],
|
"order_props_in_contracts": ["amount", "cost", "filled", "remaining"],
|
||||||
"stop_price_type_value_mapping": {
|
"stop_price_type_value_mapping": {
|
||||||
@@ -143,7 +147,7 @@ class Binance(Exchange):
|
|||||||
Does not work for other exchanges, which don't return the earliest data when called with "0"
|
Does not work for other exchanges, which don't return the earliest data when called with "0"
|
||||||
:param candle_type: Any of the enum CandleType (must match trading mode!)
|
:param candle_type: Any of the enum CandleType (must match trading mode!)
|
||||||
"""
|
"""
|
||||||
if is_new_pair:
|
if is_new_pair and candle_type in (CandleType.SPOT, CandleType.FUTURES, CandleType.MARK):
|
||||||
with self._loop_lock:
|
with self._loop_lock:
|
||||||
x = self.loop.run_until_complete(
|
x = self.loop.run_until_complete(
|
||||||
self._async_get_candle_history(pair, timeframe, candle_type, 0)
|
self._async_get_candle_history(pair, timeframe, candle_type, 0)
|
||||||
@@ -400,7 +404,7 @@ class Binance(Exchange):
|
|||||||
since = max(since, listing_date)
|
since = max(since, listing_date)
|
||||||
|
|
||||||
_, res = await download_archive_trades(
|
_, res = await download_archive_trades(
|
||||||
CandleType.SPOT,
|
CandleType.FUTURES if self.trading_mode == "futures" else CandleType.SPOT,
|
||||||
pair,
|
pair,
|
||||||
since_ms=since,
|
since_ms=since,
|
||||||
until_ms=until,
|
until_ms=until,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,6 @@ from freqtrade.exceptions import DDosProtection, ExchangeError, OperationalExcep
|
|||||||
from freqtrade.exchange import Exchange
|
from freqtrade.exchange import Exchange
|
||||||
from freqtrade.exchange.common import retrier
|
from freqtrade.exchange.common import retrier
|
||||||
from freqtrade.exchange.exchange_types import CcxtOrder, FtHas
|
from freqtrade.exchange.exchange_types import CcxtOrder, FtHas
|
||||||
from freqtrade.util.datetime_helpers import dt_now, dt_ts
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -35,6 +34,7 @@ class Bybit(Exchange):
|
|||||||
"order_time_in_force": ["GTC", "FOK", "IOC", "PO"],
|
"order_time_in_force": ["GTC", "FOK", "IOC", "PO"],
|
||||||
"ws_enabled": True,
|
"ws_enabled": True,
|
||||||
"trades_has_history": False, # Endpoint doesn't support pagination
|
"trades_has_history": False, # Endpoint doesn't support pagination
|
||||||
|
"fetch_orders_limit_minutes": 7 * 1440, # 7 days
|
||||||
"exchange_has_overrides": {
|
"exchange_has_overrides": {
|
||||||
# Bybit spot does not support fetch_order
|
# Bybit spot does not support fetch_order
|
||||||
# Unless the account is unified.
|
# Unless the account is unified.
|
||||||
@@ -49,6 +49,7 @@ class Bybit(Exchange):
|
|||||||
"funding_fee_candle_limit": 200,
|
"funding_fee_candle_limit": 200,
|
||||||
"stoploss_on_exchange": True,
|
"stoploss_on_exchange": True,
|
||||||
"stoploss_order_types": {"limit": "limit", "market": "market"},
|
"stoploss_order_types": {"limit": "limit", "market": "market"},
|
||||||
|
"stoploss_blocks_assets": False,
|
||||||
# bybit response parsing fails to populate stopLossPrice
|
# bybit response parsing fails to populate stopLossPrice
|
||||||
"stop_price_prop": "stopPrice",
|
"stop_price_prop": "stopPrice",
|
||||||
"stop_price_type_field": "triggerBy",
|
"stop_price_type_field": "triggerBy",
|
||||||
@@ -139,6 +140,21 @@ class Bybit(Exchange):
|
|||||||
params["position_idx"] = 0
|
params["position_idx"] = 0
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
def _get_stop_params(self, side: BuySell, ordertype: str, stop_price: float) -> dict:
|
||||||
|
params = super()._get_stop_params(
|
||||||
|
side=side,
|
||||||
|
ordertype=ordertype,
|
||||||
|
stop_price=stop_price,
|
||||||
|
)
|
||||||
|
# work around ccxt bug introduced in https://github.com/ccxt/ccxt/pull/25887
|
||||||
|
# Where create_order ain't returning an ID any longer.
|
||||||
|
params.update(
|
||||||
|
{
|
||||||
|
"method": "privatePostV5OrderCreate",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return params
|
||||||
|
|
||||||
def _order_needs_price(self, side: BuySell, ordertype: str) -> bool:
|
def _order_needs_price(self, side: BuySell, ordertype: str) -> bool:
|
||||||
# Bybit requires price for market orders - but only for classic accounts,
|
# Bybit requires price for market orders - but only for classic accounts,
|
||||||
# and only in spot mode
|
# and only in spot mode
|
||||||
@@ -234,25 +250,6 @@ class Bybit(Exchange):
|
|||||||
logger.warning(f"Could not update funding fees for {pair}.")
|
logger.warning(f"Could not update funding fees for {pair}.")
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
def fetch_orders(
|
|
||||||
self, pair: str, since: datetime, params: dict | None = None
|
|
||||||
) -> list[CcxtOrder]:
|
|
||||||
"""
|
|
||||||
Fetch all orders for a pair "since"
|
|
||||||
:param pair: Pair for the query
|
|
||||||
:param since: Starting time for the query
|
|
||||||
"""
|
|
||||||
# On bybit, the distance between since and "until" can't exceed 7 days.
|
|
||||||
# we therefore need to split the query into multiple queries.
|
|
||||||
orders = []
|
|
||||||
|
|
||||||
while since < dt_now():
|
|
||||||
until = since + timedelta(days=7, minutes=-1)
|
|
||||||
orders += super().fetch_orders(pair, since, params={"until": dt_ts(until)})
|
|
||||||
since = until
|
|
||||||
|
|
||||||
return orders
|
|
||||||
|
|
||||||
def fetch_order(self, order_id: str, pair: str, params: dict | None = None) -> CcxtOrder:
|
def fetch_order(self, order_id: str, pair: str, params: dict | None = None) -> CcxtOrder:
|
||||||
if self.exchange_has("fetchOrder"):
|
if self.exchange_has("fetchOrder"):
|
||||||
# Set acknowledged to True to avoid ccxt exception
|
# Set acknowledged to True to avoid ccxt exception
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ class Exchange:
|
|||||||
"stop_price_param": "stopLossPrice", # Used for stoploss_on_exchange request
|
"stop_price_param": "stopLossPrice", # Used for stoploss_on_exchange request
|
||||||
"stop_price_prop": "stopLossPrice", # Used for stoploss_on_exchange response parsing
|
"stop_price_prop": "stopLossPrice", # Used for stoploss_on_exchange response parsing
|
||||||
"stoploss_order_types": {},
|
"stoploss_order_types": {},
|
||||||
|
"stoploss_blocks_assets": True, # By default stoploss orders block assets
|
||||||
"order_time_in_force": ["GTC"],
|
"order_time_in_force": ["GTC"],
|
||||||
"ohlcv_params": {},
|
"ohlcv_params": {},
|
||||||
"ohlcv_has_history": True, # Some exchanges (Kraken) don't provide history via ohlcv
|
"ohlcv_has_history": True, # Some exchanges (Kraken) don't provide history via ohlcv
|
||||||
@@ -148,12 +149,14 @@ class Exchange:
|
|||||||
"trades_has_history": False,
|
"trades_has_history": False,
|
||||||
"l2_limit_range": None,
|
"l2_limit_range": None,
|
||||||
"l2_limit_range_required": True, # Allow Empty L2 limit (kucoin)
|
"l2_limit_range_required": True, # Allow Empty L2 limit (kucoin)
|
||||||
|
"l2_limit_upper": None, # Upper limit for L2 limit
|
||||||
"mark_ohlcv_price": "mark",
|
"mark_ohlcv_price": "mark",
|
||||||
"mark_ohlcv_timeframe": "8h",
|
"mark_ohlcv_timeframe": "8h",
|
||||||
"funding_fee_timeframe": "8h",
|
"funding_fee_timeframe": "8h",
|
||||||
"ccxt_futures_name": "swap",
|
"ccxt_futures_name": "swap",
|
||||||
"needs_trading_fees": False, # use fetch_trading_fees to cache fees
|
"needs_trading_fees": False, # use fetch_trading_fees to cache fees
|
||||||
"order_props_in_contracts": ["amount", "filled", "remaining"],
|
"order_props_in_contracts": ["amount", "filled", "remaining"],
|
||||||
|
"fetch_orders_limit_minutes": None, # "fetch_orders" is not time-limited by default
|
||||||
# Override createMarketBuyOrderRequiresPrice where ccxt has it wrong
|
# Override createMarketBuyOrderRequiresPrice where ccxt has it wrong
|
||||||
"marketOrderRequiresPrice": False,
|
"marketOrderRequiresPrice": False,
|
||||||
"exchange_has_overrides": {}, # Dictionary overriding ccxt's "has".
|
"exchange_has_overrides": {}, # Dictionary overriding ccxt's "has".
|
||||||
@@ -266,11 +269,11 @@ class Exchange:
|
|||||||
exchange_conf.get("ccxt_async_config", {}), ccxt_async_config
|
exchange_conf.get("ccxt_async_config", {}), ccxt_async_config
|
||||||
)
|
)
|
||||||
self._api_async = self._init_ccxt(exchange_conf, False, ccxt_async_config)
|
self._api_async = self._init_ccxt(exchange_conf, False, ccxt_async_config)
|
||||||
self._has_watch_ohlcv = self.exchange_has("watchOHLCV") and self._ft_has["ws_enabled"]
|
_has_watch_ohlcv = self.exchange_has("watchOHLCV") and self._ft_has["ws_enabled"]
|
||||||
if (
|
if (
|
||||||
self._config["runmode"] in TRADE_MODES
|
self._config["runmode"] in TRADE_MODES
|
||||||
and exchange_conf.get("enable_ws", True)
|
and exchange_conf.get("enable_ws", True)
|
||||||
and self._has_watch_ohlcv
|
and _has_watch_ohlcv
|
||||||
):
|
):
|
||||||
self._ws_async = self._init_ccxt(exchange_conf, False, ccxt_async_config)
|
self._ws_async = self._init_ccxt(exchange_conf, False, ccxt_async_config)
|
||||||
self._exchange_ws = ExchangeWS(self._config, self._ws_async)
|
self._exchange_ws = ExchangeWS(self._config, self._ws_async)
|
||||||
@@ -960,7 +963,7 @@ class Exchange:
|
|||||||
return 1 / pow(10, precision)
|
return 1 / pow(10, precision)
|
||||||
|
|
||||||
def get_min_pair_stake_amount(
|
def get_min_pair_stake_amount(
|
||||||
self, pair: str, price: float, stoploss: float, leverage: float | None = 1.0
|
self, pair: str, price: float, stoploss: float, leverage: float = 1.0
|
||||||
) -> float | None:
|
) -> float | None:
|
||||||
return self._get_stake_amount_limit(pair, price, stoploss, "min", leverage)
|
return self._get_stake_amount_limit(pair, price, stoploss, "min", leverage)
|
||||||
|
|
||||||
@@ -979,7 +982,7 @@ class Exchange:
|
|||||||
price: float,
|
price: float,
|
||||||
stoploss: float,
|
stoploss: float,
|
||||||
limit: Literal["min", "max"],
|
limit: Literal["min", "max"],
|
||||||
leverage: float | None = 1.0,
|
leverage: float = 1.0,
|
||||||
) -> float | None:
|
) -> float | None:
|
||||||
isMin = limit == "min"
|
isMin = limit == "min"
|
||||||
|
|
||||||
@@ -988,6 +991,8 @@ class Exchange:
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
raise ValueError(f"Can't get market information for symbol {pair}")
|
raise ValueError(f"Can't get market information for symbol {pair}")
|
||||||
|
|
||||||
|
stake_limits = []
|
||||||
|
limits = market["limits"]
|
||||||
if isMin:
|
if isMin:
|
||||||
# reserve some percent defined in config (5% default) + stoploss
|
# reserve some percent defined in config (5% default) + stoploss
|
||||||
margin_reserve: float = 1.0 + self._config.get(
|
margin_reserve: float = 1.0 + self._config.get(
|
||||||
@@ -997,11 +1002,12 @@ class Exchange:
|
|||||||
# it should not be more than 50%
|
# it should not be more than 50%
|
||||||
stoploss_reserve = max(min(stoploss_reserve, 1.5), 1)
|
stoploss_reserve = max(min(stoploss_reserve, 1.5), 1)
|
||||||
else:
|
else:
|
||||||
|
# is_max
|
||||||
margin_reserve = 1.0
|
margin_reserve = 1.0
|
||||||
stoploss_reserve = 1.0
|
stoploss_reserve = 1.0
|
||||||
|
if max_from_tiers := self._get_max_notional_from_tiers(pair, leverage=leverage):
|
||||||
|
stake_limits.append(max_from_tiers)
|
||||||
|
|
||||||
stake_limits = []
|
|
||||||
limits = market["limits"]
|
|
||||||
if limits["cost"][limit] is not None:
|
if limits["cost"][limit] is not None:
|
||||||
stake_limits.append(
|
stake_limits.append(
|
||||||
self._contracts_to_amount(pair, limits["cost"][limit]) * stoploss_reserve
|
self._contracts_to_amount(pair, limits["cost"][limit]) * stoploss_reserve
|
||||||
@@ -1365,8 +1371,8 @@ class Exchange:
|
|||||||
ordertype = available_order_Types[user_order_type]
|
ordertype = available_order_Types[user_order_type]
|
||||||
else:
|
else:
|
||||||
# Otherwise pick only one available
|
# Otherwise pick only one available
|
||||||
ordertype = list(available_order_Types.values())[0]
|
ordertype = next(iter(available_order_Types.values()))
|
||||||
user_order_type = list(available_order_Types.keys())[0]
|
user_order_type = next(iter(available_order_Types.keys()))
|
||||||
return ordertype, user_order_type
|
return ordertype, user_order_type
|
||||||
|
|
||||||
def _get_stop_limit_rate(self, stop_price: float, order_types: dict, side: str) -> float:
|
def _get_stop_limit_rate(self, stop_price: float, order_types: dict, side: str) -> float:
|
||||||
@@ -1738,7 +1744,7 @@ class Exchange:
|
|||||||
return orders
|
return orders
|
||||||
|
|
||||||
@retrier(retries=0)
|
@retrier(retries=0)
|
||||||
def fetch_orders(
|
def _fetch_orders(
|
||||||
self, pair: str, since: datetime, params: dict | None = None
|
self, pair: str, since: datetime, params: dict | None = None
|
||||||
) -> list[CcxtOrder]:
|
) -> list[CcxtOrder]:
|
||||||
"""
|
"""
|
||||||
@@ -1777,6 +1783,24 @@ class Exchange:
|
|||||||
except ccxt.BaseError as e:
|
except ccxt.BaseError as e:
|
||||||
raise OperationalException(e) from e
|
raise OperationalException(e) from e
|
||||||
|
|
||||||
|
def fetch_orders(
|
||||||
|
self, pair: str, since: datetime, params: dict | None = None
|
||||||
|
) -> list[CcxtOrder]:
|
||||||
|
if self._config["dry_run"]:
|
||||||
|
return []
|
||||||
|
if (limit := self._ft_has.get("fetch_orders_limit_minutes")) is not None:
|
||||||
|
orders = []
|
||||||
|
while since < dt_now():
|
||||||
|
orders += self._fetch_orders(pair, since)
|
||||||
|
# Since with 1 minute overlap
|
||||||
|
since = since + timedelta(minutes=limit - 1)
|
||||||
|
# Ensure each order is unique based on order id
|
||||||
|
orders = list({order["id"]: order for order in orders}.values())
|
||||||
|
return orders
|
||||||
|
|
||||||
|
else:
|
||||||
|
return self._fetch_orders(pair, since, params=params)
|
||||||
|
|
||||||
@retrier
|
@retrier
|
||||||
def fetch_trading_fees(self) -> dict[str, Any]:
|
def fetch_trading_fees(self) -> dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
@@ -1955,14 +1979,18 @@ class Exchange:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_next_limit_in_list(
|
def get_next_limit_in_list(
|
||||||
limit: int, limit_range: list[int] | None, range_required: bool = True
|
limit: int,
|
||||||
|
limit_range: list[int] | None,
|
||||||
|
range_required: bool = True,
|
||||||
|
upper_limit: int | None = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Get next greater value in the list.
|
Get next greater value in the list.
|
||||||
Used by fetch_l2_order_book if the api only supports a limited range
|
Used by fetch_l2_order_book if the api only supports a limited range
|
||||||
|
if both limit_range and upper_limit is provided, limit_range wins.
|
||||||
"""
|
"""
|
||||||
if not limit_range:
|
if not limit_range:
|
||||||
return limit
|
return min(limit, upper_limit) if upper_limit else limit
|
||||||
|
|
||||||
result = min([x for x in limit_range if limit <= x] + [max(limit_range)])
|
result = min([x for x in limit_range if limit <= x] + [max(limit_range)])
|
||||||
if not range_required and limit > result:
|
if not range_required and limit > result:
|
||||||
@@ -1979,7 +2007,10 @@ class Exchange:
|
|||||||
{'asks': [price, volume], 'bids': [price, volume]}
|
{'asks': [price, volume], 'bids': [price, volume]}
|
||||||
"""
|
"""
|
||||||
limit1 = self.get_next_limit_in_list(
|
limit1 = self.get_next_limit_in_list(
|
||||||
limit, self._ft_has["l2_limit_range"], self._ft_has["l2_limit_range_required"]
|
limit,
|
||||||
|
self._ft_has["l2_limit_range"],
|
||||||
|
self._ft_has["l2_limit_range_required"],
|
||||||
|
self._ft_has["l2_limit_upper"],
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
return self._api.fetch_l2_order_book(pair, limit1)
|
return self._api.fetch_l2_order_book(pair, limit1)
|
||||||
@@ -2403,6 +2434,53 @@ class Exchange:
|
|||||||
data = sorted(data, key=lambda x: x[0])
|
data = sorted(data, key=lambda x: x[0])
|
||||||
return pair, timeframe, candle_type, data, self._ohlcv_partial_candle
|
return pair, timeframe, candle_type, data, self._ohlcv_partial_candle
|
||||||
|
|
||||||
|
def _try_build_from_websocket(
|
||||||
|
self, pair: str, timeframe: str, candle_type: CandleType
|
||||||
|
) -> Coroutine[Any, Any, OHLCVResponse] | None:
|
||||||
|
"""
|
||||||
|
Try to build a coroutine to get data from websocket.
|
||||||
|
"""
|
||||||
|
if self._can_use_websocket(self._exchange_ws, pair, timeframe, candle_type):
|
||||||
|
candle_ts = dt_ts(timeframe_to_prev_date(timeframe))
|
||||||
|
prev_candle_ts = dt_ts(date_minus_candles(timeframe, 1))
|
||||||
|
candles = self._exchange_ws.ohlcvs(pair, timeframe)
|
||||||
|
half_candle = int(candle_ts - (candle_ts - prev_candle_ts) * 0.5)
|
||||||
|
last_refresh_time = int(
|
||||||
|
self._exchange_ws.klines_last_refresh.get((pair, timeframe, candle_type), 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
candles
|
||||||
|
and (
|
||||||
|
(len(candles) > 1 and candles[-1][0] >= prev_candle_ts)
|
||||||
|
# Edgecase on reconnect, where 1 candle is available but it's the current one
|
||||||
|
or (len(candles) == 1 and candles[-1][0] < candle_ts)
|
||||||
|
)
|
||||||
|
and last_refresh_time >= half_candle
|
||||||
|
):
|
||||||
|
# Usable result, candle contains the previous candle.
|
||||||
|
# Also, we check if the last refresh time is no more than half the candle ago.
|
||||||
|
logger.debug(f"reuse watch result for {pair}, {timeframe}, {last_refresh_time}")
|
||||||
|
|
||||||
|
return self._exchange_ws.get_ohlcv(pair, timeframe, candle_type, candle_ts)
|
||||||
|
logger.info(
|
||||||
|
f"Couldn't reuse watch for {pair}, {timeframe}, falling back to REST api. "
|
||||||
|
f"{candle_ts < last_refresh_time}, {candle_ts}, {last_refresh_time}, "
|
||||||
|
f"{format_ms_time(candle_ts)}, {format_ms_time(last_refresh_time)} "
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _can_use_websocket(
|
||||||
|
self, exchange_ws: ExchangeWS | None, pair: str, timeframe: str, candle_type: CandleType
|
||||||
|
) -> TypeGuard[ExchangeWS]:
|
||||||
|
"""
|
||||||
|
Check if we can use websocket for this pair.
|
||||||
|
Acts as typeguard for exchangeWs
|
||||||
|
"""
|
||||||
|
if exchange_ws and candle_type in (CandleType.SPOT, CandleType.FUTURES):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def _build_coroutine(
|
def _build_coroutine(
|
||||||
self,
|
self,
|
||||||
pair: str,
|
pair: str,
|
||||||
@@ -2412,8 +2490,8 @@ class Exchange:
|
|||||||
cache: bool,
|
cache: bool,
|
||||||
) -> Coroutine[Any, Any, OHLCVResponse]:
|
) -> Coroutine[Any, Any, OHLCVResponse]:
|
||||||
not_all_data = cache and self.required_candle_call_count > 1
|
not_all_data = cache and self.required_candle_call_count > 1
|
||||||
if cache and candle_type in (CandleType.SPOT, CandleType.FUTURES):
|
if cache:
|
||||||
if self._has_watch_ohlcv and self._exchange_ws:
|
if self._can_use_websocket(self._exchange_ws, pair, timeframe, candle_type):
|
||||||
# Subscribe to websocket
|
# Subscribe to websocket
|
||||||
self._exchange_ws.schedule_ohlcv(pair, timeframe, candle_type)
|
self._exchange_ws.schedule_ohlcv(pair, timeframe, candle_type)
|
||||||
|
|
||||||
@@ -2421,30 +2499,9 @@ class Exchange:
|
|||||||
candle_limit = self.ohlcv_candle_limit(timeframe, candle_type)
|
candle_limit = self.ohlcv_candle_limit(timeframe, candle_type)
|
||||||
min_ts = dt_ts(date_minus_candles(timeframe, candle_limit - 5))
|
min_ts = dt_ts(date_minus_candles(timeframe, candle_limit - 5))
|
||||||
|
|
||||||
if self._exchange_ws:
|
if ws_resp := self._try_build_from_websocket(pair, timeframe, candle_type):
|
||||||
candle_ts = dt_ts(timeframe_to_prev_date(timeframe))
|
# We have a usable websocket response
|
||||||
prev_candle_ts = dt_ts(date_minus_candles(timeframe, 1))
|
return ws_resp
|
||||||
candles = self._exchange_ws.ohlcvs(pair, timeframe)
|
|
||||||
half_candle = int(candle_ts - (candle_ts - prev_candle_ts) * 0.5)
|
|
||||||
last_refresh_time = int(
|
|
||||||
self._exchange_ws.klines_last_refresh.get((pair, timeframe, candle_type), 0)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (
|
|
||||||
candles
|
|
||||||
and candles[-1][0] >= prev_candle_ts
|
|
||||||
and last_refresh_time >= half_candle
|
|
||||||
):
|
|
||||||
# Usable result, candle contains the previous candle.
|
|
||||||
# Also, we check if the last refresh time is no more than half the candle ago.
|
|
||||||
logger.debug(f"reuse watch result for {pair}, {timeframe}, {last_refresh_time}")
|
|
||||||
|
|
||||||
return self._exchange_ws.get_ohlcv(pair, timeframe, candle_type, candle_ts)
|
|
||||||
logger.info(
|
|
||||||
f"Couldn't reuse watch for {pair}, {timeframe}, falling back to REST api. "
|
|
||||||
f"{candle_ts < last_refresh_time}, {candle_ts}, {last_refresh_time}, "
|
|
||||||
f"{format_ms_time(candle_ts)}, {format_ms_time(last_refresh_time)} "
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check if 1 call can get us updated candles without hole in the data.
|
# Check if 1 call can get us updated candles without hole in the data.
|
||||||
if min_ts < self._pairs_last_refresh_time.get((pair, timeframe, candle_type), 0):
|
if min_ts < self._pairs_last_refresh_time.get((pair, timeframe, candle_type), 0):
|
||||||
@@ -2790,7 +2847,7 @@ class Exchange:
|
|||||||
pair, timeframe, candle_type = pairwt
|
pair, timeframe, candle_type = pairwt
|
||||||
since_ms = None
|
since_ms = None
|
||||||
new_ticks: list = []
|
new_ticks: list = []
|
||||||
all_stored_ticks_df = DataFrame(columns=DEFAULT_TRADES_COLUMNS + ["date"])
|
all_stored_ticks_df = DataFrame(columns=[*DEFAULT_TRADES_COLUMNS, "date"])
|
||||||
first_candle_ms = self.needed_candle_for_trades_ms(timeframe, candle_type)
|
first_candle_ms = self.needed_candle_for_trades_ms(timeframe, candle_type)
|
||||||
# refresh, if
|
# refresh, if
|
||||||
# a. not in _trades
|
# a. not in _trades
|
||||||
@@ -2835,7 +2892,7 @@ class Exchange:
|
|||||||
else:
|
else:
|
||||||
# Skip cache, it's too old
|
# Skip cache, it's too old
|
||||||
all_stored_ticks_df = DataFrame(
|
all_stored_ticks_df = DataFrame(
|
||||||
columns=DEFAULT_TRADES_COLUMNS + ["date"]
|
columns=[*DEFAULT_TRADES_COLUMNS, "date"]
|
||||||
)
|
)
|
||||||
|
|
||||||
# from_id overrules with exchange set to id paginate
|
# from_id overrules with exchange set to id paginate
|
||||||
@@ -3353,42 +3410,22 @@ class Exchange:
|
|||||||
pair_tiers = self._leverage_tiers[pair]
|
pair_tiers = self._leverage_tiers[pair]
|
||||||
|
|
||||||
if stake_amount == 0:
|
if stake_amount == 0:
|
||||||
return self._leverage_tiers[pair][0]["maxLeverage"] # Max lev for lowest amount
|
return pair_tiers[0]["maxLeverage"] # Max lev for lowest amount
|
||||||
|
|
||||||
for tier_index in range(len(pair_tiers)):
|
# Find the appropriate tier based on stake_amount
|
||||||
tier = pair_tiers[tier_index]
|
prior_max_lev = None
|
||||||
lev = tier["maxLeverage"]
|
for tier in pair_tiers:
|
||||||
|
min_stake = tier["minNotional"] / (prior_max_lev or tier["maxLeverage"])
|
||||||
|
max_stake = tier["maxNotional"] / tier["maxLeverage"]
|
||||||
|
prior_max_lev = tier["maxLeverage"]
|
||||||
|
# Adjust notional by leverage to do a proper comparison
|
||||||
|
if min_stake <= stake_amount <= max_stake:
|
||||||
|
return tier["maxLeverage"]
|
||||||
|
|
||||||
if tier_index < len(pair_tiers) - 1:
|
# else: # if on the last tier
|
||||||
next_tier = pair_tiers[tier_index + 1]
|
if stake_amount > max_stake:
|
||||||
next_floor = next_tier["minNotional"] / next_tier["maxLeverage"]
|
# If stake is > than max tradeable amount
|
||||||
if next_floor > stake_amount: # Next tier min too high for stake amount
|
raise InvalidOrderException(f"Amount {stake_amount} too high for {pair}")
|
||||||
return min((tier["maxNotional"] / stake_amount), lev)
|
|
||||||
#
|
|
||||||
# With the two leverage tiers below,
|
|
||||||
# - a stake amount of 150 would mean a max leverage of (10000 / 150) = 66.66
|
|
||||||
# - stakes below 133.33 = max_lev of 75
|
|
||||||
# - stakes between 133.33-200 = max_lev of 10000/stake = 50.01-74.99
|
|
||||||
# - stakes from 200 + 1000 = max_lev of 50
|
|
||||||
#
|
|
||||||
# {
|
|
||||||
# "min": 0, # stake = 0.0
|
|
||||||
# "max": 10000, # max_stake@75 = 10000/75 = 133.33333333333334
|
|
||||||
# "lev": 75,
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "min": 10000, # stake = 200.0
|
|
||||||
# "max": 50000, # max_stake@50 = 50000/50 = 1000.0
|
|
||||||
# "lev": 50,
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
|
|
||||||
else: # if on the last tier
|
|
||||||
if stake_amount > tier["maxNotional"]:
|
|
||||||
# If stake is > than max tradeable amount
|
|
||||||
raise InvalidOrderException(f"Amount {stake_amount} too high for {pair}")
|
|
||||||
else:
|
|
||||||
return tier["maxLeverage"]
|
|
||||||
|
|
||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
"Looped through all tiers without finding a max leverage. Should never be reached"
|
"Looped through all tiers without finding a max leverage. Should never be reached"
|
||||||
@@ -3403,6 +3440,23 @@ class Exchange:
|
|||||||
else:
|
else:
|
||||||
return 1.0
|
return 1.0
|
||||||
|
|
||||||
|
def _get_max_notional_from_tiers(self, pair: str, leverage: float) -> float | None:
|
||||||
|
"""
|
||||||
|
get max_notional from leverage_tiers
|
||||||
|
:param pair: The base/quote currency pair being traded
|
||||||
|
:param leverage: The leverage to be used
|
||||||
|
:return: The maximum notional value for the given leverage or None if not found
|
||||||
|
"""
|
||||||
|
if self.trading_mode != TradingMode.FUTURES:
|
||||||
|
return None
|
||||||
|
if pair not in self._leverage_tiers:
|
||||||
|
return None
|
||||||
|
pair_tiers = self._leverage_tiers[pair]
|
||||||
|
for tier in reversed(pair_tiers):
|
||||||
|
if leverage <= tier["maxLeverage"]:
|
||||||
|
return tier["maxNotional"]
|
||||||
|
return None
|
||||||
|
|
||||||
@retrier
|
@retrier
|
||||||
def _set_leverage(
|
def _set_leverage(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ class FtHas(TypedDict, total=False):
|
|||||||
stop_price_type_field: str
|
stop_price_type_field: str
|
||||||
stop_price_type_value_mapping: dict
|
stop_price_type_value_mapping: dict
|
||||||
stoploss_order_types: dict[str, str]
|
stoploss_order_types: dict[str, str]
|
||||||
|
stoploss_blocks_assets: bool
|
||||||
# ohlcv
|
# ohlcv
|
||||||
ohlcv_params: dict
|
ohlcv_params: dict
|
||||||
ohlcv_candle_limit: int
|
ohlcv_candle_limit: int
|
||||||
@@ -37,6 +38,9 @@ class FtHas(TypedDict, total=False):
|
|||||||
# Orderbook
|
# Orderbook
|
||||||
l2_limit_range: list[int] | None
|
l2_limit_range: list[int] | None
|
||||||
l2_limit_range_required: bool
|
l2_limit_range_required: bool
|
||||||
|
l2_limit_upper: int | None
|
||||||
|
# fetch_orders
|
||||||
|
fetch_orders_limit_minutes: int | None
|
||||||
# Futures
|
# Futures
|
||||||
ccxt_futures_name: str # usually swap
|
ccxt_futures_name: str # usually swap
|
||||||
mark_ohlcv_price: str
|
mark_ohlcv_price: str
|
||||||
@@ -44,6 +48,7 @@ class FtHas(TypedDict, total=False):
|
|||||||
funding_fee_timeframe: str
|
funding_fee_timeframe: str
|
||||||
funding_fee_candle_limit: int
|
funding_fee_candle_limit: int
|
||||||
floor_leverage: bool
|
floor_leverage: bool
|
||||||
|
uses_leverage_tiers: bool
|
||||||
needs_trading_fees: bool
|
needs_trading_fees: bool
|
||||||
order_props_in_contracts: list[Literal["amount", "cost", "filled", "remaining"]]
|
order_props_in_contracts: list[Literal["amount", "cost", "filled", "remaining"]]
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ class Gate(Exchange):
|
|||||||
"stoploss_order_types": {"limit": "limit"},
|
"stoploss_order_types": {"limit": "limit"},
|
||||||
"stop_price_param": "stopPrice",
|
"stop_price_param": "stopPrice",
|
||||||
"stop_price_prop": "stopPrice",
|
"stop_price_prop": "stopPrice",
|
||||||
|
"l2_limit_upper": 1000,
|
||||||
"marketOrderRequiresPrice": True,
|
"marketOrderRequiresPrice": True,
|
||||||
"trades_has_history": False, # Endpoint would support this - but ccxt doesn't.
|
"trades_has_history": False, # Endpoint would support this - but ccxt doesn't.
|
||||||
}
|
}
|
||||||
@@ -44,6 +45,8 @@ class Gate(Exchange):
|
|||||||
"marketOrderRequiresPrice": False,
|
"marketOrderRequiresPrice": False,
|
||||||
"funding_fee_candle_limit": 90,
|
"funding_fee_candle_limit": 90,
|
||||||
"stop_price_type_field": "price_type",
|
"stop_price_type_field": "price_type",
|
||||||
|
"l2_limit_upper": 300,
|
||||||
|
"stoploss_blocks_assets": False,
|
||||||
"stop_price_type_value_mapping": {
|
"stop_price_type_value_mapping": {
|
||||||
PriceType.LAST: 0,
|
PriceType.LAST: 0,
|
||||||
PriceType.MARK: 1,
|
PriceType.MARK: 1,
|
||||||
|
|||||||
@@ -32,9 +32,11 @@ class Hyperliquid(Exchange):
|
|||||||
_ft_has_futures: FtHas = {
|
_ft_has_futures: FtHas = {
|
||||||
"stoploss_on_exchange": True,
|
"stoploss_on_exchange": True,
|
||||||
"stoploss_order_types": {"limit": "limit"},
|
"stoploss_order_types": {"limit": "limit"},
|
||||||
|
"stoploss_blocks_assets": False,
|
||||||
"stop_price_prop": "stopPrice",
|
"stop_price_prop": "stopPrice",
|
||||||
"funding_fee_timeframe": "1h",
|
"funding_fee_timeframe": "1h",
|
||||||
"funding_fee_candle_limit": 500,
|
"funding_fee_candle_limit": 500,
|
||||||
|
"uses_leverage_tiers": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
_supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [
|
_supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from freqtrade.enums import MarginMode, TradingMode
|
|||||||
from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError
|
from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError
|
||||||
from freqtrade.exchange import Exchange
|
from freqtrade.exchange import Exchange
|
||||||
from freqtrade.exchange.common import retrier
|
from freqtrade.exchange.common import retrier
|
||||||
from freqtrade.exchange.exchange_types import CcxtBalances, FtHas, Tickers
|
from freqtrade.exchange.exchange_types import CcxtBalances, FtHas
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -49,18 +49,6 @@ class Kraken(Exchange):
|
|||||||
|
|
||||||
return parent_check and market.get("darkpool", False) is False
|
return parent_check and market.get("darkpool", False) is False
|
||||||
|
|
||||||
def get_tickers(
|
|
||||||
self,
|
|
||||||
symbols: list[str] | None = None,
|
|
||||||
*,
|
|
||||||
cached: bool = False,
|
|
||||||
market_type: TradingMode | None = None,
|
|
||||||
) -> Tickers:
|
|
||||||
# Only fetch tickers for current stake currency
|
|
||||||
# Otherwise the request for kraken becomes too large.
|
|
||||||
symbols = list(self.get_markets(quote_currencies=[self._config["stake_currency"]]))
|
|
||||||
return super().get_tickers(symbols=symbols, cached=cached, market_type=market_type)
|
|
||||||
|
|
||||||
def consolidate_balances(self, balances: CcxtBalances) -> CcxtBalances:
|
def consolidate_balances(self, balances: CcxtBalances) -> CcxtBalances:
|
||||||
"""
|
"""
|
||||||
Consolidate balances for the same currency.
|
Consolidate balances for the same currency.
|
||||||
@@ -69,7 +57,7 @@ class Kraken(Exchange):
|
|||||||
consolidated: CcxtBalances = {}
|
consolidated: CcxtBalances = {}
|
||||||
for currency, balance in balances.items():
|
for currency, balance in balances.items():
|
||||||
base_currency = currency[:-2] if currency.endswith(".F") else currency
|
base_currency = currency[:-2] if currency.endswith(".F") else currency
|
||||||
base_currency = self._api.commonCurrencies.get(base_currency, base_currency)
|
|
||||||
if base_currency in consolidated:
|
if base_currency in consolidated:
|
||||||
consolidated[base_currency]["free"] += balance["free"]
|
consolidated[base_currency]["free"] += balance["free"]
|
||||||
consolidated[base_currency]["used"] += balance["used"]
|
consolidated[base_currency]["used"] += balance["used"]
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ class Okx(Exchange):
|
|||||||
PriceType.MARK: "index",
|
PriceType.MARK: "index",
|
||||||
PriceType.INDEX: "mark",
|
PriceType.INDEX: "mark",
|
||||||
},
|
},
|
||||||
|
"stoploss_blocks_assets": False,
|
||||||
"ws_enabled": True,
|
"ws_enabled": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -318,13 +318,13 @@ class BaseReinforcementLearningModel(IFreqaiModel):
|
|||||||
rename_dict = {
|
rename_dict = {
|
||||||
"%-raw_open": "open",
|
"%-raw_open": "open",
|
||||||
"%-raw_low": "low",
|
"%-raw_low": "low",
|
||||||
"%-raw_high": " high",
|
"%-raw_high": "high",
|
||||||
"%-raw_close": "close",
|
"%-raw_close": "close",
|
||||||
}
|
}
|
||||||
rename_dict_old = {
|
rename_dict_old = {
|
||||||
f"%-{pair}raw_open_{tf}": "open",
|
f"%-{pair}raw_open_{tf}": "open",
|
||||||
f"%-{pair}raw_low_{tf}": "low",
|
f"%-{pair}raw_low_{tf}": "low",
|
||||||
f"%-{pair}raw_high_{tf}": " high",
|
f"%-{pair}raw_high_{tf}": "high",
|
||||||
f"%-{pair}raw_close_{tf}": "close",
|
f"%-{pair}raw_close_{tf}": "close",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -569,7 +569,7 @@ class FreqaiDataDrawer:
|
|||||||
dk.training_features_list = dk.data["training_features_list"]
|
dk.training_features_list = dk.data["training_features_list"]
|
||||||
dk.label_list = dk.data["label_list"]
|
dk.label_list = dk.data["label_list"]
|
||||||
|
|
||||||
def load_data(self, coin: str, dk: FreqaiDataKitchen) -> Any: # noqa: C901
|
def load_data(self, coin: str, dk: FreqaiDataKitchen) -> Any:
|
||||||
"""
|
"""
|
||||||
loads all data required to make a prediction on a sub-train time range
|
loads all data required to make a prediction on a sub-train time range
|
||||||
:returns:
|
:returns:
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user