|
| 1 | +from jesse.strategies import Strategy, cached |
| 2 | +import jesse.indicators as ta |
| 3 | +from jesse import utils |
| 4 | +import numpy as np |
| 5 | + |
| 6 | +# https://medium.com/@gaea.enquiries/quantitative-strategy-research-series-one-the-dual-thrust-38380b38c2fa |
| 7 | +# Dual Thrust by Michael Chalek |
| 8 | + |
| 9 | +class DUAL_THRUST(Strategy): |
| 10 | + |
| 11 | + def should_long(self) -> bool: |
| 12 | + return self.long_cond |
| 13 | + |
| 14 | + def should_short(self) -> bool: |
| 15 | + return self.short_cond |
| 16 | + |
| 17 | + def go_long(self): |
| 18 | + entry = self.price |
| 19 | + stop = entry - self.atr * self.hp['stop_loss_atr_rate'] |
| 20 | + qty = utils.risk_to_qty(self.capital, 2, entry, stop) |
| 21 | + self.buy = qty, entry |
| 22 | + self.stop_loss = qty, stop |
| 23 | + |
| 24 | + def go_short(self): |
| 25 | + entry = self.price |
| 26 | + stop = entry + self.atr * self.hp['stop_loss_atr_rate'] |
| 27 | + qty = utils.risk_to_qty(self.capital, 2, entry, stop) |
| 28 | + self.sell = qty, entry |
| 29 | + self.stop_loss = qty, stop |
| 30 | + |
| 31 | + def update_position(self): |
| 32 | + if (self.is_long and self.short_cond) or (self.is_short and self.long_cond): |
| 33 | + self.liquidate() |
| 34 | + |
| 35 | + def should_cancel(self) -> bool: |
| 36 | + return True |
| 37 | + |
| 38 | + ################################################################ |
| 39 | + # # # # # # # # # # # # # indicators # # # # # # # # # # # # # # |
| 40 | + ################################################################ |
| 41 | + |
| 42 | + @property |
| 43 | + def up_min_low(self): |
| 44 | + return np.min(self.candles[:, 4][-self.hp['up_length']:]) |
| 45 | + |
| 46 | + @property |
| 47 | + def up_min_close(self): |
| 48 | + return np.min(self.candles[:, 2][-self.hp['up_length']:]) |
| 49 | + |
| 50 | + @property |
| 51 | + def up_max_close(self): |
| 52 | + return np.max(self.candles[:, 2][-self.hp['up_length']:]) |
| 53 | + |
| 54 | + @property |
| 55 | + def up_max_high(self): |
| 56 | + return np.max(self.candles[:, 3][-self.hp['up_length']:]) |
| 57 | + |
| 58 | + @property |
| 59 | + def down_min_low(self): |
| 60 | + return np.min(self.candles[:, 4][-self.hp['down_length']:]) |
| 61 | + |
| 62 | + @property |
| 63 | + def down_min_close(self): |
| 64 | + return np.min(self.candles[:, 2][-self.hp['down_length']:]) |
| 65 | + |
| 66 | + @property |
| 67 | + def down_max_close(self): |
| 68 | + return np.max(self.candles[:, 2][-self.hp['down_length']:]) |
| 69 | + |
| 70 | + @property |
| 71 | + def down_max_high(self): |
| 72 | + return np.max(self.candles[:, 4][-self.hp['down_length']:]) |
| 73 | + |
| 74 | + @property |
| 75 | + def up_thurst(self): |
| 76 | + return self.anchor_candles[:, 1][-1] + self.hp['up_coeff'] * max(self.up_max_close - self.up_min_low, self.up_max_high - self.up_min_close) |
| 77 | + |
| 78 | + @property |
| 79 | + def down_thrust(self): |
| 80 | + return self.anchor_candles[:, 1][-1] - self.hp['down_coeff'] * max(self.down_max_close - self.down_min_low, self.down_max_high - self.down_min_close) |
| 81 | + |
| 82 | + @property |
| 83 | + @cached |
| 84 | + def anchor_candles(self): |
| 85 | + return self.get_candles(self.exchange, self.symbol, utils.anchor_timeframe(self.timeframe)) |
| 86 | + |
| 87 | + @property |
| 88 | + def short_cond(self): |
| 89 | + return self.price < self.down_thrust |
| 90 | + |
| 91 | + @property |
| 92 | + def long_cond(self): |
| 93 | + return self.price > self.up_thurst |
| 94 | + |
| 95 | + @property |
| 96 | + def atr(self): |
| 97 | + return ta.atr(self.candles) |
| 98 | + |
| 99 | + |
| 100 | + # # # # # # # # # # # # # # # # # # # # # # # # # # # # |
| 101 | + # Genetic |
| 102 | + # # # # # # # # # # # # # # # # # # # # # # # # # # # # |
| 103 | + |
| 104 | + def hyperparameters(self): |
| 105 | + return [ |
| 106 | + {'name': 'stop_loss_atr_rate', 'type': float, 'min': 0.1, 'max': 2.0, 'default': 2}, |
| 107 | + {'name': 'down_length', 'type': int, 'min': 3, 'max': 30, 'default': 21}, |
| 108 | + {'name': 'up_length', 'type': int, 'min': 3, 'max': 30, 'default': 21}, |
| 109 | + {'name': 'down_coeff', 'type': float, 'min': 0.1, 'max': 3.0, 'default': 0.67}, |
| 110 | + {'name': 'up_coeff', 'type': float, 'min': 0.1, 'max': 3.0, 'default': 0.71}, |
| 111 | + ] |
| 112 | + |
0 commit comments