├── .gitignore ├── DUAL_THRUST └── __init__.py ├── Donchian ├── ADA-USDT_6h_results.txt ├── BAT-USDT_6h_results.txt ├── BTC-USDT_6h_results.txt ├── ETH-USDT_6h_results.txt ├── NEO-USDT_6h_results.txt └── __init__.py ├── IFR2 ├── ADA-USDT_1h_results.txt ├── BAT-USDT_1h_results.txt ├── NEO-USDT_1h_results.txt ├── REEF-USDT_1h_results.txt ├── UNI-USDT_1h_results.txt └── __init__.py ├── KDJstrategy ├── BTC-USDT_1D_Results.txt └── __init__.py ├── LICENSE ├── MACD_EMA └── __init__.py ├── MAGen └── __init__.py ├── README.md ├── RSI2 ├── RSI2-results.txt └── __init__.py ├── SMACrossover ├── SMACrossover-results.txt └── __init__.py ├── SimpleBollinger ├── BTC-USDT_results.txt ├── ETH-USDT_results.txt └── __init__.py ├── TradingView_RSI ├── BTC-USDT-results.txt ├── ETH-USDT-results.txt └── __init__.py ├── TurtleRules └── __init__.py └── example-strategies-master ├── Donchian ├── ADA-USDT_6h_results.txt ├── BAT-USDT_6h_results.txt ├── BTC-USDT_6h_results.txt ├── ETH-USDT_6h_results.txt ├── NEO-USDT_6h_results.txt └── __init__.py ├── IFR2 ├── ADA-USDT_1h_results.txt ├── BAT-USDT_1h_results.txt ├── NEO-USDT_1h_results.txt ├── REEF-USDT_1h_results.txt ├── UNI-USDT_1h_results.txt └── __init__.py ├── KDJ ├── BTC-USDT_1D_results.txt └── __init__.py ├── LICENSE ├── README.md ├── RSI2 ├── RSI2-results.txt └── __init__.py ├── SMACrossover ├── SMACrossover-results.txt └── __init__.py ├── SimpleBollinger ├── BTC-USDT_results.txt ├── ETH-USDT_results.txt └── __init__.py ├── TradingView_RSI ├── BTC-USDT-results.txt ├── ETH-USDT-results.txt └── __init__.py └── TurtleRules └── __init__.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 98 | __pypackages__/ 99 | 100 | # Celery stuff 101 | celerybeat-schedule 102 | celerybeat.pid 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | 134 | # pytype static type analyzer 135 | .pytype/ 136 | 137 | # Cython debug symbols 138 | cython_debug/ 139 | 140 | # IDE 141 | /.vscode 142 | /.idea 143 | 144 | .DS_Store 145 | /storage/*.key 146 | /storage/*.sqlite 147 | /storage/*.gz 148 | /.vagrant 149 | testing-*.py 150 | /storage/full-reports/*.html 151 | /storage/logs/* 152 | -------------------------------------------------------------------------------- /DUAL_THRUST/__init__.py: -------------------------------------------------------------------------------- 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.balance, 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.balance, 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_entry(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 | 113 | -------------------------------------------------------------------------------- /Donchian/ADA-USDT_6h_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 731 days (2.0 years) 5 | starting-ending date | 2019-01-01 => 2021-01-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | ADA-USDT | 6h | Donchian | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 34.98 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+--------------------------- 19 | Total Closed Trades | 19 20 | Total Net Profit | 55,272.4782 (552.72%) 21 | Starting => Finishing Balance | 10,000 => 65,272.48 22 | Total Open Trades | 1 23 | Open PL | 4,714.81 24 | Total Paid Fees | 1,277.14 25 | Max Drawdown | -36.15% 26 | Annual Return | 154.83% 27 | Expectancy | 2,909.08 (29.09%) 28 | Avg Win | Avg Loss | 8,936.79 | 1,474.71 29 | Ratio Avg Win / Avg Loss | 6.06 30 | Percent Profitable | 42% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 2 weeks, 2 days, 20 hours 33 | Winning Trades Avg Holding Time | 3 weeks, 2 days, 16 hours 34 | Losing Trades Avg Holding Time | 1 week, 4 days, 20 hours 35 | Sharpe Ratio | 1.68 36 | Market Change | 347.53% -------------------------------------------------------------------------------- /Donchian/BAT-USDT_6h_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 701 days (1.92 years) 5 | starting-ending date | 2019-05-05 => 2021-04-05 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | BAT-USDT | 6h | Donchian | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 33.2 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+--------------------------- 19 | Total Closed Trades | 17 20 | Total Net Profit | 21,211.9829 (212.12%) 21 | Starting => Finishing Balance | 10,000 => 31,211.98 22 | Total Open Trades | 1 23 | Open PL | 177.78 24 | Total Paid Fees | 409.07 25 | Max Drawdown | -36.55% 26 | Annual Return | 80.73% 27 | Expectancy | 1,247.76 (12.48%) 28 | Avg Win | Avg Loss | 2,755.04 | 905.49 29 | Ratio Avg Win / Avg Loss | 3.04 30 | Percent Profitable | 59% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 2 weeks, 2 days, 22 hours 33 | Winning Trades Avg Holding Time | 2 weeks, 6 days, 18 hours 34 | Losing Trades Avg Holding Time | 1 week, 4 days, 12 hours 35 | Sharpe Ratio | 1.13 36 | Market Change | 224.43% -------------------------------------------------------------------------------- /Donchian/BTC-USDT_6h_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 731 days (2.0 years) 5 | starting-ending date | 2019-01-01 => 2021-01-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | BTC-USDT | 6h | Donchian | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 35.53 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+--------------------------- 19 | Total Closed Trades | 18 20 | Total Net Profit | 45,194.3507 (451.94%) 21 | Starting => Finishing Balance | 10,000 => 55,194.35 22 | Total Open Trades | 1 23 | Open PL | 18,320.21 24 | Total Paid Fees | 760.56 25 | Max Drawdown | -46.89% 26 | Annual Return | 134.39% 27 | Expectancy | 2,510.8 (25.11%) 28 | Avg Win | Avg Loss | 5,824.01 | 1,630.71 29 | Ratio Avg Win / Avg Loss | 3.57 30 | Percent Profitable | 56% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 2 weeks, 6 days, 14 hours 33 | Winning Trades Avg Holding Time | 3 weeks, 6 days, 21 hours 34 | Losing Trades Avg Holding Time | 1 week, 4 days, 12 hours 35 | Sharpe Ratio | 2.03 36 | Market Change | 681.2% -------------------------------------------------------------------------------- /Donchian/ETH-USDT_6h_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 731 days (2.0 years) 5 | starting-ending date | 2019-01-01 => 2021-01-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | ETH-USDT | 6h | Donchian | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 36.79 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+-------------------------- 19 | Total Closed Trades | 24 20 | Total Net Profit | 33,528.8877 (335.29%) 21 | Starting => Finishing Balance | 10,000 => 43,528.89 22 | Total Open Trades | 1 23 | Open PL | 5,487.64 24 | Total Paid Fees | 1,027.9 25 | Max Drawdown | -34.14% 26 | Annual Return | 108.22% 27 | Expectancy | 1,397.04 (13.97%) 28 | Avg Win | Avg Loss | 4,302.78 | 1,508.71 29 | Ratio Avg Win / Avg Loss | 2.85 30 | Percent Profitable | 50% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 2 weeks, 1 day, 4 hours 33 | Winning Trades Avg Holding Time | 3 weeks, 2 days, 8 hours 34 | Losing Trades Avg Holding Time | 1 week 35 | Sharpe Ratio | 1.53 36 | Market Change | 460.23% -------------------------------------------------------------------------------- /Donchian/NEO-USDT_6h_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 731 days (2.0 years) 5 | starting-ending date | 2019-01-01 => 2021-01-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | NEO-USDT | 6h | Donchian | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 33.83 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+------------------------------ 19 | Total Closed Trades | 18 20 | Total Net Profit | 20,801.5637 (208.02%) 21 | Starting => Finishing Balance | 10,000 => 30,801.56 22 | Total Open Trades | 0 23 | Open PL | 0 24 | Total Paid Fees | 626.48 25 | Max Drawdown | -32.92% 26 | Annual Return | 75.23% 27 | Expectancy | 1,155.64 (11.56%) 28 | Avg Win | Avg Loss | 2,924.3 | 1,623.68 29 | Ratio Avg Win / Avg Loss | 1.8 30 | Percent Profitable | 61% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 2 weeks, 4 days, 2 hours 33 | Winning Trades Avg Holding Time | 3 weeks, 3 days, 14 hours 34 | Losing Trades Avg Holding Time | 1 week, 22 hours, 17 minutes 35 | Sharpe Ratio | 1.14 36 | Market Change | 92.41% 37 | -------------------------------------------------------------------------------- /Donchian/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple Donchian Strategy 3 | Repo: https://github.com/gabrielweich/jesse-strategies 4 | Uses Donchian Channels to capture long term uptrends, the idea is to buy 5 | when price closes above the upperband and only exit the position when 6 | prices closes below the lowerband. 7 | """ 8 | 9 | from jesse.strategies import Strategy 10 | import jesse.indicators as ta 11 | from jesse import utils 12 | 13 | 14 | class Donchian(Strategy): 15 | @property 16 | def donchian(self): 17 | # Previous Donchian Channels with default parameters 18 | return ta.donchian(self.candles[:-1]) 19 | 20 | @property 21 | def ma_trend(self): 22 | return ta.sma(self.candles, period=200) 23 | 24 | def filter_trend(self): 25 | # Only opens a long position when close is above 200 SMA 26 | return self.close > self.ma_trend 27 | 28 | def filters(self): 29 | return [self.filter_trend] 30 | 31 | def should_long(self) -> bool: 32 | # Go long if candle closes above upperband 33 | return self.close > self.donchian.upperband 34 | 35 | def should_short(self) -> bool: 36 | return False 37 | 38 | def should_cancel_entry(self) -> bool: 39 | return True 40 | 41 | def go_long(self): 42 | # Open long position using entire balance 43 | qty = utils.size_to_qty(self.balance, self.price, fee_rate=self.fee_rate) 44 | self.buy = qty, self.price 45 | 46 | def go_short(self): 47 | pass 48 | 49 | def update_position(self): 50 | # Close the position when candle closes below lowerband 51 | if self.close < self.donchian.lowerband: 52 | self.liquidate() 53 | -------------------------------------------------------------------------------- /IFR2/ADA-USDT_1h_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 812 days (2.22 years) 5 | starting-ending date | 2019-01-10 => 2021-04-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | ADA-USDT | 1h | IFR2 | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 39.22 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+--------------------------------- 19 | Total Closed Trades | 260 20 | Total Net Profit | 14,122.7117 (141.23%) 21 | Starting => Finishing Balance | 10,000 => 24,122.71 22 | Total Open Trades | 0 23 | Open PL | 0 24 | Total Paid Fees | 6,313.33 25 | Max Drawdown | -26.61% 26 | Annual Return | 48.49% 27 | Expectancy | 54.32 (0.54%) 28 | Avg Win | Avg Loss | 224.07 | 320.81 29 | Ratio Avg Win / Avg Loss | 0.7 30 | Percent Profitable | 69% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 7 hours, 12 minutes 33 | Winning Trades Avg Holding Time | 5 hours, 7 minutes, 2 seconds 34 | Losing Trades Avg Holding Time | 11 hours, 48 minutes, 8 seconds 35 | Sharpe Ratio | 1.38 36 | Market Change | 2218.44% -------------------------------------------------------------------------------- /IFR2/BAT-USDT_1h_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 731 days (2.0 years) 5 | starting-ending date | 2019-04-01 => 2021-04-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | BAT-USDT | 1h | IFR2 | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 31.54 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+---------------------------------- 19 | Total Closed Trades | 249 20 | Total Net Profit | 30,380.8971 (303.81%) 21 | Starting => Finishing Balance | 10,000 => 40,380.9 22 | Total Open Trades | 1 23 | Open PL | 1,482.82 24 | Total Paid Fees | 8,669.0 25 | Max Drawdown | -20.59% 26 | Annual Return | 100.57% 27 | Expectancy | 122.01 (1.22%) 28 | Avg Win | Avg Loss | 366.49 | 434.51 29 | Ratio Avg Win / Avg Loss | 0.84 30 | Percent Profitable | 69% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 7 hours, 8 minutes, 26 seconds 33 | Winning Trades Avg Holding Time | 4 hours, 53 minutes, 24 seconds 34 | Losing Trades Avg Holding Time | 12 hours, 15 minutes, 47 seconds 35 | Sharpe Ratio | 1.98 36 | Market Change | 296.08% -------------------------------------------------------------------------------- /IFR2/NEO-USDT_1h_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 821 days (2.25 years) 5 | starting-ending date | 2019-01-01 => 2021-04-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | NEO-USDT | 1h | IFR2 | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 36.23 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+---------------------------------- 19 | Total Closed Trades | 279 20 | Total Net Profit | 17,204.8247 (172.05%) 21 | Starting => Finishing Balance | 10,000 => 27,204.82 22 | Total Open Trades | 0 23 | Open PL | 0 24 | Total Paid Fees | 7,434.63 25 | Max Drawdown | -21.44% 26 | Annual Return | 55.96% 27 | Expectancy | 61.67 (0.62%) 28 | Avg Win | Avg Loss | 228.66 | 256.66 29 | Ratio Avg Win / Avg Loss | 0.89 30 | Percent Profitable | 66% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 6 hours, 55 minutes, 41 seconds 33 | Winning Trades Avg Holding Time | 4 hours, 58 minutes, 41 seconds 34 | Losing Trades Avg Holding Time | 10 hours, 38 minutes, 45 seconds 35 | Sharpe Ratio | 1.65 36 | Market Change | 585.36% -------------------------------------------------------------------------------- /IFR2/REEF-USDT_1h_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 81 days (2.7 months) 5 | starting-ending date | 2021-01-10 => 2021-04-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+-----------+-------------+------------+------- 10 | Binance | REEF-USDT | 1h | IFR2 | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 3.98 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+--------------------------------- 19 | Total Closed Trades | 32 20 | Total Net Profit | 20,562.7827 (205.63%) 21 | Starting => Finishing Balance | 10,000 => 30,562.78 22 | Total Open Trades | 0 23 | Open PL | 0 24 | Total Paid Fees | 1,546.58 25 | Max Drawdown | -22.51% 26 | Annual Return | 14344.42% 27 | Expectancy | 642.59 (6.43%) 28 | Avg Win | Avg Loss | 1,109.73 | 2,627.41 29 | Ratio Avg Win / Avg Loss | 0.42 30 | Percent Profitable | 88% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 6 hours, 13 minutes, 7 seconds 33 | Winning Trades Avg Holding Time | 5 hours, 34 minutes, 17 seconds 34 | Losing Trades Avg Holding Time | 10 hours, 45 minutes 35 | Sharpe Ratio | 5.71 36 | Market Change | 299.82% -------------------------------------------------------------------------------- /IFR2/UNI-USDT_1h_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 182 days (6.07 months) 5 | starting-ending date | 2020-10-01 => 2021-04-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | UNI-USDT | 1h | IFR2 | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 9.07 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+---------------------------------- 19 | Total Closed Trades | 83 20 | Total Net Profit | 16,466.1687 (164.66%) 21 | Starting => Finishing Balance | 10,000 => 26,466.17 22 | Total Open Trades | 0 23 | Open PL | 0 24 | Total Paid Fees | 2,558.48 25 | Max Drawdown | -20.7% 26 | Annual Return | 596.74% 27 | Expectancy | 198.39 (1.98%) 28 | Avg Win | Avg Loss | 431.57 | 448.16 29 | Ratio Avg Win / Avg Loss | 0.96 30 | Percent Profitable | 73% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 7 hours, 21 minutes, 41 seconds 33 | Winning Trades Avg Holding Time | 5 hours, 3 minutes, 56 seconds 34 | Losing Trades Avg Holding Time | 13 hours, 43 minutes, 38 seconds 35 | Sharpe Ratio | 3.75 36 | Market Change | 575.41% 37 | -------------------------------------------------------------------------------- /IFR2/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | IFR2 (portuguese translation of RSI2) 3 | Repo: https://github.com/gabrielweich/jesse-strategies 4 | Simillar to RSI2 described by Larry Connors but with some modifications. 5 | Uses Ichimoku Cloud with parameters adapted to crypto market and also 6 | Hilbert Transform Trendmode as filters to only buy in an uptrend. 7 | The exit occurs when the close is above the highest high of the two 8 | previous cadles. 9 | References: 10 | - RSI2: https://school.stockcharts.com/doku.php?id=trading_strategies:rsi2 11 | - Ichimoku Crypto Parameters: https://www.altcointrading.net/ichimoku-cloud/ 12 | """ 13 | 14 | from jesse.strategies import Strategy 15 | import jesse.indicators as ta 16 | from jesse import utils 17 | 18 | 19 | class IFR2(Strategy): 20 | @property 21 | def rsi(self): 22 | return ta.rsi(self.candles, period=2) 23 | 24 | @property 25 | def trend_mode(self): 26 | return ta.ht_trendmode(self.candles) 27 | 28 | @property 29 | def ichimoku(self): 30 | # Ichimoku cloud using parameters adapted to crypto market 31 | return ta.ichimoku_cloud( 32 | self.candles, 33 | conversion_line_period=20, 34 | base_line_period=30, 35 | lagging_line_period=120, 36 | displacement=60, 37 | ) 38 | 39 | def filter_trend_ichimoku(self): 40 | # Only opens a long position when close is above ichimoku cloud 41 | return self.close > self.ichimoku.span_a and self.close > self.ichimoku.span_b 42 | 43 | def filter_trend_mode(self): 44 | # Only opens a long position when trend_mode indicates a trend 45 | return self.trend_mode == 1 46 | 47 | def filters(self): 48 | return [self.filter_trend_ichimoku, self.filter_trend_mode] 49 | 50 | def should_long(self) -> bool: 51 | # Go long if candle RSI2 is below 10 52 | return self.rsi < 10 53 | 54 | def should_short(self) -> bool: 55 | return False 56 | 57 | def should_cancel_entry(self) -> bool: 58 | return True 59 | 60 | def go_long(self): 61 | # Open long position using entire balance 62 | qty = utils.size_to_qty(self.balance, self.price, fee_rate=self.fee_rate) 63 | self.buy = qty, self.price 64 | 65 | def go_short(self): 66 | pass 67 | 68 | def update_position(self): 69 | # Close the position when candle is above the highest high of the two previous cadles 70 | if ( 71 | self.close > self.candles[-2:, 3][-2] 72 | and self.close > self.candles[-3:, 3][-3] 73 | ): 74 | self.liquidate() -------------------------------------------------------------------------------- /KDJstrategy/BTC-USDT_1D_Results.txt: -------------------------------------------------------------------------------- 1 | CANDLES | 2 | ----------------------+-------------------------- 3 | period | 731 days (2.0 years) 4 | starting-ending date | 2019-01-01 => 2021-01-01 5 | 6 | 7 | exchange | symbol | timeframe | strategy | DNA 8 | ------------+----------+-------------+-----------------+------- 9 | Binance | BTC-USDT | 1D | AwesomeStrategy | 10 | 11 | 12 | Executing simulation... [####################################] 100% 13 | Executed backtest simulation in: 29.89 seconds 14 | 15 | 16 | METRICS | 17 | ---------------------------------+----------------------------- 18 | Total Closed Trades | 155 19 | Total Net Profit | 897.7349 (89.77%) 20 | Starting => Finishing Balance | 1,000 => 1,897.73 21 | Total Open Trades | 1 22 | Open PL | 1.59 23 | Total Paid Fees | 252.25 24 | Max Drawdown | -22.4% 25 | Annual Return | 37.64% 26 | Expectancy | 5.79 (0.58%) 27 | Avg Win | Avg Loss | 34.2 | 25.3 28 | Ratio Avg Win / Avg Loss | 1.35 29 | Percent Profitable | 52% 30 | Longs | Shorts | 100% | 0% 31 | Avg Holding Time | 2 days, 9 hours, 8 minutes 32 | Winning Trades Avg Holding Time | 2 days, 18 hours, 4 minutes 33 | Losing Trades Avg Holding Time | 1 day, 23 hours, 21 minutes 34 | Sharpe Ratio | 1.31 35 | Market Change | 681.2% 36 | -------------------------------------------------------------------------------- /KDJstrategy/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple KDJ Indicator Strategy 3 | 1D Timeframe 4 | Repo: https://github.com/matty5690/example-strategies 5 | Opens position when J value is above K and D values and closes when J value is below K and D values 6 | 7 | 8 | 9 | """ 10 | 11 | from jesse.strategies import Strategy 12 | import jesse.indicators as ta 13 | from jesse import utils 14 | 15 | 16 | class KDJstrategy(Strategy): 17 | 18 | @property 19 | def KDJIndicator(self): #KDJ Indicator 20 | return ta.kdj(self.candles) #returns namedtuple KDJ(k,d,j) with default parameters 21 | 22 | @property 23 | def LastKDJ(self): 24 | return ta.kdj(self.candles[:-1]) #returns the previous candles KDJ values 25 | 26 | @property 27 | def atr(self): 28 | return ta.atr(self.candles, period = 14) #14 period ATR used for calculating stop loss 29 | 30 | def should_long(self) -> bool: 31 | #long signal if J value crosses above K and D values 32 | return self.KDJIndicator[2] > (self.KDJIndicator[0] and self.KDJIndicator[1]) 33 | 34 | def should_short(self) -> bool: 35 | return False 36 | 37 | def should_cancel(self) -> bool: 38 | return True 39 | 40 | def go_long(self): 41 | # Open long position and risk 5% of available capital 42 | risk_perc = 5 43 | entry = self.price 44 | stop = entry - 2 * self.atr #trailing stop loss of 2x ATR 45 | qty = utils.risk_to_qty(self.capital, risk_perc, entry, stop) 46 | self.buy = qty, self.price 47 | self.stop_loss = qty, stop 48 | 49 | def go_short(self): 50 | return False 51 | 52 | def update_position(self): 53 | #if position is in profit it will close if the J value at close is less than the previous J value 54 | if self.is_long and self.position.pnl_percentage > 0 and self.LastKDJ[2] > self.KDJIndicator[2]: 55 | self.liquidate() 56 | #if position is not in profit and the J value crosses under the K and D values then the position will close 57 | elif self.is_long and self.KDJIndicator[2] < (self.KDJIndicator[0] and self.KDJIndicator[1]): 58 | self.liquidate() 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 jesse-ai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MACD_EMA/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | MACD indicator with 100 period exponential moving average (EMA) 3 | Timeframe: 1h 4 | Repo: https://github.com/gabrielweich/jesse-strategies 5 | When the MACD line crosses the signal line AND the closing price of the last candle is above 6 | the 100 period EMA a long order is placed. The script has been seet up to use the built in 7 | optimization, but the optimization was never completed due to lack of processing power. 8 | Change the default values in the hyperparameters function to manually tune parameters. 9 | """ 10 | 11 | """ 12 | author = "Connor McDonald" 13 | copyright = "Free For Use" 14 | version = "1.0" 15 | email = "connormcd98@gmail.com" 16 | """ 17 | 18 | from jesse.strategies import Strategy, cached 19 | import jesse.indicators as ta 20 | from jesse import utils 21 | 22 | 23 | 24 | class MACD_EMA(Strategy): 25 | @property 26 | def macd(self): #this returns: macd, signal and hist which can be referenced as self.macd[0], self.macd[1] and self.macd[2], respectively 27 | return ta.macd(self.candles, self.hp['fastperiod'],self.hp['slowperiod'],self.hp['signalperiod']) 28 | 29 | @property 30 | def ema(self): #this returns a single value which is the 100EMA at the latest candle 31 | return ta.ema(self.candles, self.hp['ema']) 32 | 33 | def should_long(self): 34 | # return true if close is above EMA and MACD line is above signal line 35 | if self.close > self.ema and self.macd[0] > self.macd[1]: 36 | return True 37 | return False 38 | 39 | def should_short(self): 40 | return False 41 | 42 | def should_cancel_entry(self) -> bool: 43 | return True 44 | 45 | 46 | def go_long(self): 47 | # Open long position using entire balance 48 | qty = utils.size_to_qty(self.balance, self.price, fee_rate=self.fee_rate) 49 | self.buy = qty, self.price 50 | 51 | def go_short(self): 52 | pass 53 | 54 | 55 | def update_position(self): 56 | # Close the position when MACD crosses below the signal line and closing prices is less than 100EMA 57 | if self.macd[0] < self.macd[1] and self.close < self.ema: 58 | self.liquidate() 59 | 60 | 61 | 62 | def hyperparameters(self): # This is set up for optimization but if you just want to backtest with your own values then change the default value only. 63 | return [ 64 | {'name': 'ema', 'type': int, 'min': 50, 'max': 200, 'default': 100}, 65 | {'name': 'fastperiod', 'type': int, 'min': 10, 'max': 18, 'default': 12}, 66 | {'name': 'slowperiod', 'type': int, 'min': 19, 'max': 36, 'default': 26}, 67 | {'name': 'signalperiod', 'type': int, 'min': 3, 'max': 9, 'default': 9}, 68 | ] 69 | -------------------------------------------------------------------------------- /MAGen/__init__.py: -------------------------------------------------------------------------------- 1 | import jesse.indicators as ta 2 | from jesse import utils 3 | from jesse.strategies import Strategy 4 | 5 | class MAGen(Strategy): 6 | 7 | def should_long(self) -> bool: 8 | return self.longEntry 9 | 10 | def should_short(self) -> bool: 11 | return self.shortEntry 12 | 13 | def go_long(self): 14 | entry = self.price 15 | stop = entry - self.atr * self.hp['stop_loss_atr_rate'] 16 | qty = utils.risk_to_qty(self.balance, 3, entry, stop) 17 | take_profit = entry + self.atr * self.hp['take_profit_atr_rate'] 18 | self.buy = qty, entry 19 | self.stop_loss = qty, stop 20 | self.take_profit = qty, take_profit 21 | 22 | def go_short(self): 23 | entry = self.price 24 | stop = entry + self.atr * self.hp['stop_loss_atr_rate'] 25 | qty = utils.risk_to_qty(self.balance, 3, entry, stop) 26 | take_profit = entry - self.atr * self.hp['take_profit_atr_rate'] 27 | self.sell = qty, entry 28 | self.stop_loss = qty, stop 29 | self.take_profit = qty, take_profit 30 | 31 | def should_cancel_entry(self) -> bool: 32 | return True 33 | 34 | def update_position(self): 35 | if (self.is_short and self.shortExit) or (self.is_long and self.longExit): 36 | self.liquidate() 37 | 38 | ################################################################ 39 | # # # # # # # # # # # # # indicators # # # # # # # # # # # # # # 40 | ################################################################ 41 | 42 | @property 43 | def longEntry(self): 44 | return self.trend_direction_change == 1 and self.adx > self.hp['adx_entry'] 45 | 46 | @property 47 | def shortEntry(self): 48 | return self.trend_direction_change == -1 and self.adx > self.hp['adx_entry'] 49 | 50 | @property 51 | def longExit(self): 52 | return self.ma_fast[-1] < self.ma_slow[-1] and self.adx < self.hp['adx_exit'] 53 | 54 | @property 55 | def shortExit(self): 56 | return self.ma_fast[-1] > self.ma_slow[-1] and self.adx < self.hp['adx_exit'] 57 | 58 | @property 59 | def adx(self): 60 | return ta.adx(self.candles, period=self.hp['adx_period']) 61 | 62 | @property 63 | def trend_direction_change(self): 64 | direction = 0 65 | if self.ma_fast[-1] < self.ma_slow[-1] and self.ma_fast[-2] >= self.ma_slow[-2]: 66 | direction = -1 67 | if self.ma_fast[-1] > self.ma_slow[-1] and self.ma_fast[-2] <= self.ma_slow[-2]: 68 | direction = 1 69 | return direction 70 | 71 | 72 | @property 73 | def ma_slow(self): 74 | if self.hp['ma_source_slow'] == 0: 75 | source = "close" 76 | elif self.hp['ma_source_slow'] == 1: 77 | source = "high" 78 | elif self.hp['ma_source_slow'] == 2: 79 | source = "low" 80 | elif self.hp['ma_source_slow'] == 3: 81 | source = "open" 82 | elif self.hp['ma_source_slow'] == 4: 83 | source = "hl2" 84 | elif self.hp['ma_source_slow'] == 5: 85 | source = "hlc3" 86 | elif self.hp['ma_source_slow'] == 6: 87 | source = "ohlc4" 88 | 89 | return ta.ma(self.candles, matype=self.hp['ma_type_slow'], period=self.hp['ma_period_slow'], source_type=source, sequential=True) 90 | 91 | @property 92 | def ma_fast(self): 93 | 94 | if self.hp['ma_source_fast'] == 0: 95 | source = "close" 96 | elif self.hp['ma_source_fast'] == 1: 97 | source = "high" 98 | elif self.hp['ma_source_fast'] == 2: 99 | source = "low" 100 | elif self.hp['ma_source_fast'] == 3: 101 | source = "open" 102 | elif self.hp['ma_source_fast'] == 4: 103 | source = "hl2" 104 | elif self.hp['ma_source_fast'] == 5: 105 | source = "hlc3" 106 | elif self.hp['ma_source_fast'] == 6: 107 | source = "ohlc4" 108 | 109 | return ta.ma(self.candles, matype=self.hp['ma_type_fast'], period=self.hp['ma_period_fast'], source_type=source, sequential=True) 110 | 111 | @property 112 | def atr(self): 113 | return ta.atr(self.candles, period=self.hp['atr_period']) 114 | 115 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 116 | # # Genetic 117 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 118 | def hyperparameters(self): 119 | return [ 120 | {'name': 'stop_loss_atr_rate', 'type': float, 'min': 1, 'max': 4, 'default': 2}, 121 | {'name': 'take_profit_atr_rate', 'type': float, 'min': 3, 'max': 20, 'default': 5}, 122 | {'name': 'atr_period', 'type': int, 'min': 5, 'max': 40, 'default': 32}, 123 | {'name': 'ma_period_slow', 'type': int, 'min': 3, 'max': 200, 'default': 20}, 124 | {'name': 'ma_source_slow', 'type': int, 'min': 0, 'max': 6, 'default': 0}, 125 | {'name': 'ma_period_fast', 'type': int, 'min': 3, 'max': 100, 'default': 5}, 126 | {'name': 'ma_source_fast', 'type': int, 'min': 0, 'max': 6, 'default': 0}, 127 | {'name': 'ma_type_slow', 'type': int, 'min': 0, 'max': 39, 'default': 11}, 128 | {'name': 'ma_type_fast', 'type': int, 'min': 0, 'max': 39, 'default': 11}, 129 | {'name': 'adx_period', 'type': int, 'min': 3, 'max': 60, 'default': 8}, 130 | {'name': 'adx_exit', 'type': int, 'min': 3, 'max': 40, 'default': 15}, 131 | {'name': 'adx_entry', 'type': int, 'min': 3, 'max': 40, 'default': 13}, 132 | ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Example Jesse Strategies 2 | 3 | The point of this repository is to host examples of different types of strategies that you can implement with Jesse to get you started. 4 | 5 | ## Submission guide 6 | 7 | All submitted strategies must be heavily documented. Try to use lots of comments! 8 | Provide information on symbols and timeframes they work on / were tested. 9 | 10 | 11 | ## Usage Guide 12 | Just copy the folder into your Jesse project's `strategies` directory, and select them in the backtest tab. 13 | 14 | Be aware that these are examples to show different approaches to code strategies, use different indicators and functions of Jesse. 15 | 16 | The aim of this repository is NOT to provide ready-to-go profitable strategies, but code examples. 17 | 18 | The strategies might only work on certain timeframes / symbols. On others, you might face exceptions and need to do adjustments. 19 | 20 | Possible things that can happen for example: 21 | - `Uncaught Exception: InvalidStrategy: qty cannot be 0`: This is related to precision and rounding. Different coins have different price precision leading to necessary adjustments to the qty logic. For example it might be necessary to increase the precision of Jesse's qty utility functions. 22 | - No trades: You might be using a timeframe or symbol where the signals don't work / happen. 23 | - "It's not profitable": That's not the aim of those strategies and besides that this depends on symbol, timeframe, indicators parameters and much more factors. 24 | 25 | 26 | ## Disclaimer 27 | 28 | This code is for educational purposes only. We do NOT guarantee profitable trading results in anyways. USE THE SOFTWARE AT YOUR OWN RISK. THE AUTHORS AND ALL AFFILIATES ASSUME NO RESPONSIBILITY FOR YOUR TRADING RESULTS. Do not risk money which you are afraid to lose. There might be bugs in the code - this strategies DO NOT come with ANY warranty. All investments carry risk! Past performance is no guarantee of future results! Be aware of overfitting! 29 | -------------------------------------------------------------------------------- /RSI2/RSI2-results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 366 days (1.0 years) 5 | starting-ending date | 2019-06-01 => 2020-06-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | BTCUSDT | 3h | RSI2 | 11 | 12 | 13 | Executed backtest simulation in: 15.3 seconds 14 | 15 | 16 | METRICS | 17 | ---------------------------------+---------------------------------------- 18 | Total Closed Trades | 116 19 | Total Net Profit | -4330.43 (-43.3%) 20 | Starting => Finishing Balance | 10000 => 5675.42 21 | Total Open Trades | 0 22 | Open PL | 0 23 | Total Paid Fees | 1772.67 24 | Max Drawdown | -50.11% 25 | Annual Return | -32.22% 26 | Expectancy | -37.33 (-0.37%) 27 | Avg Win | Avg Loss | 86.89 | 248.22 28 | Ratio Avg Win / Avg Loss | 0.35 29 | Percent Profitable | 63% 30 | Longs | Shorts | 51% | 49% 31 | Avg Holding Time | 13.0 hours, 12.0 minutes, 55.0 seconds 32 | Winning Trades Avg Holding Time | 8.0 hours, 35.0 minutes, 20.0 seconds 33 | Losing Trades Avg Holding Time | 21.0 hours, 4.0 minutes, 11.0 seconds 34 | Sharpe Ratio | -0.57 35 | -------------------------------------------------------------------------------- /RSI2/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Connors' RSI2 Strategy 3 | 2020-06-27 original implementation - FengkieJ (fengkiejunis@gmail.com) 4 | 5 | This strategy is a very basic mean reversion strategy, mainly using RSI(2) overbought and oversold levels to determine entry signals. 6 | Originally developed by Larry Connors. 7 | 8 | Reference: - RSI(2) [ChartSchool]. Retrieved 27 June 2020, from https://school.stockcharts.com/doku.php?id=trading_strategies:rsi2 9 | """ 10 | 11 | from jesse.strategies import Strategy 12 | import jesse.indicators as ta 13 | from jesse import utils 14 | 15 | class RSI2(Strategy): 16 | def __init__(self): 17 | super().__init__() 18 | 19 | self.vars["fast_sma_period"] = 5 20 | self.vars["slow_sma_period"] = 200 21 | self.vars["rsi_period"] = 2 22 | self.vars["rsi_ob_threshold"] = 90 23 | self.vars["rsi_os_threshold"] = 10 24 | 25 | @property 26 | def fast_sma(self): 27 | return ta.sma(self.candles, self.vars["fast_sma_period"]) 28 | 29 | @property 30 | def slow_sma(self): 31 | return ta.sma(self.candles, self.vars["slow_sma_period"]) 32 | 33 | @property 34 | def rsi(self): 35 | return ta.rsi(self.candles, self.vars["rsi_period"]) 36 | 37 | def should_long(self) -> bool: 38 | # Enter long if current price is above sma(200) and RSI(2) is below oversold threshold 39 | return self.price > self.slow_sma and self.rsi <= self.vars["rsi_os_threshold"] 40 | 41 | def should_short(self) -> bool: 42 | # Enter long if current price is below sma(200) and RSI(2) is above oversold threshold 43 | return self.price < self.slow_sma and self.rsi >= self.vars["rsi_ob_threshold"] 44 | 45 | def should_cancel_entry(self) -> bool: 46 | return False 47 | 48 | def go_long(self): 49 | # Open long position and use entire balance to buy 50 | qty = utils.size_to_qty(self.balance, self.price, fee_rate=self.fee_rate) 51 | 52 | self.buy = qty, self.price 53 | 54 | def go_short(self): 55 | # Open short position and use entire balance to sell 56 | qty = utils.size_to_qty(self.balance, self.price, fee_rate=self.fee_rate) 57 | 58 | self.sell = qty, self.price 59 | 60 | def update_position(self): 61 | # Exit long position if price is above sma(5) 62 | if self.is_long and self.price > self.fast_sma: 63 | self.liquidate() 64 | 65 | # Exit short position if price is below sma(5) 66 | if self.is_short and self.price < self.fast_sma: 67 | self.liquidate() 68 | -------------------------------------------------------------------------------- /SMACrossover/SMACrossover-results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 366 days (1.0 years) 5 | starting-ending date | 2019-06-01 => 2020-06-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+--------------+------- 10 | Binance | BTCUSDT | 3h | SMACrossover | 11 | 12 | 13 | Executed backtest simulation in: 12.88 seconds 14 | 15 | 16 | METRICS | 17 | ---------------------------------+------------------------------------ 18 | Total Closed Trades | 13 19 | Total Net Profit | 8471.38 (84.71%) 20 | Starting => Finishing Balance | 10000 => 17295.8 21 | Total Open Trades | 1 22 | Open PL | -1134.74 23 | Total Paid Fees | 225.74 24 | Max Drawdown | -53.7% 25 | Annual Return | 45.67% 26 | Expectancy | 651.64 (6.52%) 27 | Avg Win | Avg Loss | 2226.61 | 698.33 28 | Ratio Avg Win / Avg Loss | 3.19 29 | Percent Profitable | 46% 30 | Longs | Shorts | 54% | 46% 31 | Avg Holding Time | 3.0 weeks, 6.0 days, 16.0 hours 32 | Winning Trades Avg Holding Time | 6.0 weeks, 1.0 day, 5.0 hours 33 | Losing Trades Avg Holding Time | 2.0 weeks, 8.0 hours, 34.0 minutes 34 | Sharpe Ratio | 0.81 35 | -------------------------------------------------------------------------------- /SMACrossover/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple Moving Average Crossover Strategy 3 | Author: FengkieJ (fengkiejunis@gmail.com) 4 | Simple moving average crossover strategy is the ''hello world'' of algorithmic trading. 5 | This strategy uses two SMAs to determine '''Golden Cross''' to signal for long position, and '''Death Cross''' to signal for short position. 6 | """ 7 | 8 | from jesse.strategies import Strategy 9 | import jesse.indicators as ta 10 | from jesse import utils 11 | 12 | class SMACrossover(Strategy): 13 | @property 14 | def slow_sma(self): 15 | return ta.sma(self.candles, 200) 16 | 17 | @property 18 | def fast_sma(self): 19 | return ta.sma(self.candles, 50) 20 | 21 | def should_long(self) -> bool: 22 | # Golden Cross (reference: https://www.investopedia.com/terms/g/goldencross.asp) 23 | # Fast SMA above Slow SMA 24 | return self.fast_sma > self.slow_sma 25 | 26 | def should_short(self) -> bool: 27 | # Death Cross (reference: https://www.investopedia.com/terms/d/deathcross.asp) 28 | # Fast SMA below Slow SMA 29 | return self.fast_sma < self.slow_sma 30 | 31 | def should_cancel_entry(self) -> bool: 32 | return False 33 | 34 | def go_long(self): 35 | # Open long position and use entire balance to buy 36 | qty = utils.size_to_qty(self.balance, self.price, fee_rate=self.fee_rate) 37 | 38 | self.buy = qty, self.price 39 | 40 | def go_short(self): 41 | # Open short position and use entire balance to sell 42 | qty = utils.size_to_qty(self.balance, self.price, fee_rate=self.fee_rate) 43 | 44 | self.sell = qty, self.price 45 | 46 | def update_position(self): 47 | # If there exist long position, but the signal shows Death Cross, then close the position, and vice versa. 48 | if self.is_long and self.fast_sma < self.slow_sma: 49 | self.liquidate() 50 | 51 | if self.is_short and self.fast_sma > self.slow_sma: 52 | self.liquidate() 53 | -------------------------------------------------------------------------------- /SimpleBollinger/BTC-USDT_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 731 days (2.0 years) 5 | starting-ending date | 2019-01-01 => 2021-01-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+-----------------+------- 10 | Binance | BTC-USDT | 1h | SimpleBollinger | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 31.17 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+---------------------------------- 19 | Total Closed Trades | 277 20 | Total Net Profit | 27,125.64 (271.26%) 21 | Starting => Finishing Balance | 10,000 => 37,125.64 22 | Total Open Trades | 0 23 | Open PL | 0 24 | Total Paid Fees | 11,139.24 25 | Max Drawdown | -18.72% 26 | Annual Return | 92.34% 27 | Expectancy | 97.93 (0.98%) 28 | Avg Win | Avg Loss | 734.44 | 267.35 29 | Ratio Avg Win / Avg Loss | 2.75 30 | Percent Profitable | 36% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 18 hours, 31 minutes, 11 seconds 33 | Winning Trades Avg Holding Time | 1 day, 7 hours, 39 minutes 34 | Losing Trades Avg Holding Time | 10 hours, 58 minutes, 58 seconds 35 | Sharpe Ratio | 1.8 36 | Market Change | 681.2% -------------------------------------------------------------------------------- /SimpleBollinger/ETH-USDT_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 731 days (2.0 years) 5 | starting-ending date | 2019-01-01 => 2021-01-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+-----------------+------- 10 | Binance | ETH-USDT | 1h | SimpleBollinger | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 31.24 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+---------------------------------- 19 | Total Closed Trades | 282 20 | Total Net Profit | 21,030.4448 (210.3%) 21 | Starting => Finishing Balance | 10,000 => 31,030.44 22 | Total Open Trades | 0 23 | Open PL | 0 24 | Total Paid Fees | 9,730.63 25 | Max Drawdown | -30.29% 26 | Annual Return | 75.88% 27 | Expectancy | 74.58 (0.75%) 28 | Avg Win | Avg Loss | 729.33 | 274.15 29 | Ratio Avg Win / Avg Loss | 2.66 30 | Percent Profitable | 35% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 17 hours, 11 minutes, 29 seconds 33 | Winning Trades Avg Holding Time | 1 day, 4 hours, 52 minutes 34 | Losing Trades Avg Holding Time | 10 hours, 58 minutes, 2 seconds 35 | Sharpe Ratio | 1.41 36 | Market Change | 460.23% -------------------------------------------------------------------------------- /SimpleBollinger/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple Bollinger Bands Strategy 3 | Timeframe: 1h 4 | Repo: https://github.com/gabrielweich/jesse-strategies 5 | When the price closes above the upperband in an uptrend (determined by the ichimoku cloud), 6 | it gives a long signal indicating a possible continuation of the uptrend. 7 | The position can also be closed when the price closes below lowerband, but the best results 8 | were achieved when using the middleband instead. 9 | """ 10 | 11 | from jesse.strategies import Strategy 12 | import jesse.indicators as ta 13 | from jesse import utils 14 | 15 | 16 | class SimpleBollinger(Strategy): 17 | @property 18 | def bb(self): 19 | # Bollinger bands using default parameters and hl2 as source 20 | return ta.bollinger_bands(self.candles, source_type="hl2") 21 | 22 | @property 23 | def ichimoku(self): 24 | return ta.ichimoku_cloud(self.candles) 25 | 26 | def filter_trend(self): 27 | # Only opens a long position when close is above ichimoku cloud 28 | return self.close > self.ichimoku.span_a and self.close > self.ichimoku.span_b 29 | 30 | def filters(self): 31 | return [self.filter_trend] 32 | 33 | def should_long(self) -> bool: 34 | # Go long if candle closes above upperband 35 | return self.close > self.bb[0] 36 | 37 | def should_short(self) -> bool: 38 | return False 39 | 40 | def should_cancel_entry(self) -> bool: 41 | return True 42 | 43 | def go_long(self): 44 | # Open long position using entire balance 45 | qty = utils.size_to_qty(self.balance, self.price, fee_rate=self.fee_rate) 46 | self.buy = qty, self.price 47 | 48 | def go_short(self): 49 | pass 50 | 51 | def update_position(self): 52 | # Close the position when candle closes below middleband 53 | if self.close < self.bb[1]: 54 | self.liquidate() -------------------------------------------------------------------------------- /TradingView_RSI/BTC-USDT-results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 493 days (1.35 years) 5 | starting-ending date | 2020-01-01 => 2021-05-08 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | BTC-USDT | 1h | TV_RSI | 11 | 12 | 13 | Executed backtest simulation in: 27.7 seconds 14 | 15 | 16 | METRICS | 17 | ---------------------------------+------------------------------ 18 | Total Closed Trades | 94 19 | Total Net Profit | 1,792.5069 (179.25%) 20 | Starting => Finishing Balance | 1,000 => 2,792.51 21 | Total Open Trades | 1 22 | Open PL | 131.91 23 | Total Paid Fees | 329.74 24 | Max Drawdown | -27.04% 25 | Annual Return | 113.57% 26 | Expectancy | 19.07 (1.91%) 27 | Avg Win | Avg Loss | 106.56 | 99.05 28 | Ratio Avg Win / Avg Loss | 1.08 29 | Percent Profitable | 57% 30 | Longs | Shorts | 100% | 0% 31 | Avg Holding Time | 2 days, 19 hours, 49 minutes 32 | Winning Trades Avg Holding Time | 3 days, 15 hours, 25 minutes 33 | Losing Trades Avg Holding Time | 1 day, 17 hours, 23 minutes 34 | Sharpe Ratio | 1.8 35 | Market Change | 697.51% 36 | 37 | 38 | -------------------------------------------------------------------------------- /TradingView_RSI/ETH-USDT-results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 493 days (1.35 years) 5 | starting-ending date | 2020-01-01 => 2021-05-08 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | ETH-USDT | 1h | TV_RSI | 11 | 12 | 13 | Executed backtest simulation in: 45.32 seconds 14 | 15 | 16 | METRICS | 17 | ---------------------------------+------------------------------ 18 | Total Closed Trades | 108 19 | Total Net Profit | 1,521.2327 (152.12%) 20 | Starting => Finishing Balance | 1,000 => 2,521.23 21 | Total Open Trades | 0 22 | Open PL | 0 23 | Total Paid Fees | 268.69 24 | Max Drawdown | -31.13% 25 | Annual Return | 98.03% 26 | Expectancy | 14.09 (1.41%) 27 | Avg Win | Avg Loss | 100.49 | 63.22 28 | Ratio Avg Win / Avg Loss | 1.59 29 | Percent Profitable | 47% 30 | Longs | Shorts | 100% | 0% 31 | Avg Holding Time | 2 days, 4 hours, 41 minutes 32 | Winning Trades Avg Holding Time | 2 days, 21 hours, 39 minutes 33 | Losing Trades Avg Holding Time | 1 day, 13 hours, 31 minutes 34 | Sharpe Ratio | 1.45 35 | Market Change | 2596.89% 36 | 37 | 38 | -------------------------------------------------------------------------------- /TradingView_RSI/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Based on https://www.tradingview.com/script/Ru7qOVtp-RSI-Trend-Crypto/ 3 | 4 | Default Trading Rules: 5 | Long: RSI crosses over 35 6 | Close Long: RSI crosses under 75 7 | Emergency Exit: RSI crosses under 10 8 | """ 9 | 10 | from jesse.strategies import Strategy 11 | import jesse.indicators as ta 12 | from jesse import utils 13 | 14 | class TradingView_RSI(Strategy): 15 | 16 | def hyperparameters(self): 17 | return [ 18 | {'name':'rsi', 'type': int, 'min': 10, 'max':30, 'default': 5}, 19 | {'name':'stop_loss', 'type': float, 'min': .5, 'max': .99, 'default': .95}, 20 | {'name':'take_profit', 'type': float, 'min': 1.1, 'max': 1.2, 'default': 1.1}, 21 | {'name':'xparam', 'type':int, 'min': 60, 'max': 90, 'default': 75} 22 | ] 23 | 24 | @property 25 | def rsi(self): 26 | return ta.rsi(self.candles, self.hp['rsi'], sequential=True) 27 | 28 | 29 | def should_long(self): 30 | qty = utils.size_to_qty(self.balance, self.price, 3, fee_rate=self.fee_rate) 31 | 32 | if utils.crossed(self.rsi, 35, direction="above") and qty > 0 and self.available_margin > (qty * self.price): 33 | return True 34 | 35 | def should_short(self): 36 | return False 37 | 38 | def should_cancel_entry(self): 39 | return False 40 | 41 | 42 | def go_long(self): 43 | qty = utils.size_to_qty(self.balance, self.price, 3, fee_rate=self.fee_rate) 44 | self.buy = qty, self.price 45 | self.stop_loss = qty, (self.price * self.hp['stop_loss']) # Willing to lose 5% 46 | self.take_profit = qty, (self.price * self.hp['take_profit']) # Take profits at 10% 47 | 48 | def go_short(self): 49 | pass 50 | 51 | def update_position(self): 52 | if utils.crossed(self.rsi, self.hp['xparam'], direction="below") or utils.crossed(self.rsi, 10, direction="below"): 53 | self.liquidate() 54 | 55 | -------------------------------------------------------------------------------- /TurtleRules/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | The Original Turtle Trading Rules Strategy 3 | 2020-06-20 original implementation - FengkieJ (fengkiejunis@gmail.com) 4 | 2020-06-27 rev 1. add System 1 & System 2 as strategy parameters - FengkieJ (fengkiejunis@gmail.com) 5 | 6 | Turtle strategy is one of classic trend-following system that has clear defined trading rules. The system was taught by Richard Dennis 7 | and William Eckhardt in 1983. This version implements the system described by Curtis Faith, author of bestselling book Way of the Turtle. 8 | 9 | In Turtle system theory, a complete trading system must cover several aspects, i.e.: 10 | - Markets - What to buy or sell 11 | - Position Sizing - How much to buy or sell 12 | - Entries - When to buy or sell 13 | - Stops - When to get out of a losing position 14 | - Exits - When to get out of a winning position 15 | - Tactics - How to buy or sell 16 | 17 | This Python module attempts to implement the system on Jesse framework as described in the pdf by Curtis Faith & Perry J. Kaufman. 18 | 19 | Reference: - Faith, C. (2003). The Original Turtle Rules. Retrieved 14 June 2020, from http://www.tradingblox.com/originalturtles/originalturtlerules.htm 20 | - Kaufman, P. (2013). Trading systems and methods (5th ed., pp. 229-233). Hoboken, N.J.: Wiley. 21 | """ 22 | 23 | from jesse.strategies import Strategy 24 | from jesse.indicators import donchian, atr 25 | from jesse import utils 26 | 27 | class TurtleRules(Strategy): 28 | def __init__(self): 29 | super().__init__() 30 | 31 | self.current_pyramiding_levels = 0 32 | self.last_opened_price = 0 33 | self.last_was_profitable = False 34 | 35 | def before(self): 36 | self.vars["unit_risk_percent"] = 1 37 | self.vars["entry_dc_period"] = 20 38 | self.vars["exit_dc_period"] = 10 39 | self.vars["atr_period"] = 20 40 | self.vars["atr_multiplier"] = 2 41 | self.vars["maximum_pyramiding_levels"] = 4 42 | self.vars["pyramiding_threshold"] = 0.5 43 | self.vars["system_type"] = "S1" 44 | 45 | @property 46 | def entry_donchian(self): 47 | return donchian(self.candles, self.vars["entry_dc_period"]) 48 | 49 | @property 50 | def exit_donchian(self): 51 | return donchian(self.candles, self.vars["exit_dc_period"]) 52 | 53 | @property 54 | def atr(self): 55 | return atr(self.candles, self.vars["atr_period"]) 56 | 57 | def unit_qty(self, unit_risk_percent, dollars_per_point = 1): 58 | # In Original Turtle Rule book, the position sizing formula is defined as: 59 | # Unit = 1% of Account / (N × Dollars per Point) where N is ATR(20) 60 | 61 | return ((unit_risk_percent/100) * self.balance) / (self.atr * dollars_per_point) 62 | 63 | def entry_signal(self): 64 | # "The Turtles used two related system entries, each based on Donchian’s channel breakout system. 65 | # ... 66 | # System 1 – A shorter-term system based on a 20-day breakout (with a filter if previous trade was profitable do not enter the next) 67 | # System 2 – A simpler long-term system based on a 55-day breakout." (Faith, 2003) 68 | signal = None 69 | upperband = self.entry_donchian[0] 70 | lowerband = self.entry_donchian[2] 71 | 72 | if self.high >= upperband: 73 | signal = "entry_long" 74 | elif self.low <= lowerband: 75 | signal = "entry_short" 76 | 77 | return signal 78 | 79 | def exit_signal(self): 80 | # "The System 1 exit was a 10 day low for long positions and a 10 day high for short positions. 81 | # All the Units in the position would be exited if the price went against the position for a 10 day breakout. 82 | # The System 2 exit was a 20 day low for long positions and a 20 day high for short positions. 83 | # All the Units in the position would be exited if the price went against the position for a 20 day breakout." (Faith, 2003) 84 | signal = None 85 | upperband = self.exit_donchian[0] 86 | lowerband = self.exit_donchian[2] 87 | 88 | if self.high >= upperband: 89 | signal = "exit_short" 90 | elif self.low <= lowerband: 91 | signal = "exit_long" 92 | 93 | return signal 94 | 95 | def should_long(self) -> bool: 96 | return self.entry_signal() == "entry_long" 97 | 98 | def should_short(self) -> bool: 99 | return self.entry_signal() == "entry_short" 100 | 101 | def should_cancel_entry(self) -> bool: 102 | pass 103 | 104 | def go_long(self): 105 | qty = self.unit_qty(self.vars["unit_risk_percent"]) 106 | sl = self.price - self.vars["atr_multiplier"] * self.atr 107 | 108 | self.buy = qty, self.price 109 | self.stop_loss = qty, sl 110 | # self.log(f"enter long {qty}") 111 | self.current_pyramiding_levels += 1 # Track the pyramiding level 112 | self.last_opened_price = self.price # Store this value to determine when to add next pyramiding 113 | 114 | def go_short(self): 115 | qty = self.unit_qty(self.vars["unit_risk_percent"]) 116 | sl = self.price + self.vars["atr_multiplier"] * self.atr 117 | 118 | self.sell = qty, self.price 119 | self.stop_loss = qty, sl 120 | # self.log(f"enter short {qty}") 121 | self.current_pyramiding_levels += 1 # Track the pyramiding level 122 | self.last_opened_price = self.price # Store this value to determine when to add next pyramiding 123 | 124 | def update_position(self): 125 | # Handle for pyramiding rules 126 | if self.current_pyramiding_levels < self.vars["maximum_pyramiding_levels"]: 127 | if self.is_long and self.price > self.last_opened_price + (self.vars["pyramiding_threshold"] * self.atr): 128 | qty = self.unit_qty(self.vars["unit_risk_percent"]) 129 | self.buy = qty, self.price 130 | # self.log(f"atr={self.atr}, last price={self.last_opened_price}, cur price={self.price}, action: increase long position {qty}") 131 | 132 | if self.is_short and self.price < self.last_opened_price - (self.vars["pyramiding_threshold"] * self.atr): 133 | qty = self.unit_qty(self.vars["unit_risk_percent"]) 134 | self.sell = qty, self.price 135 | # self.log(f"atr={self.atr}, last price={self.last_opened_price}, cur price={self.price}, action: increase short position {qty}") 136 | 137 | # "Trades are exited on the fi rst occurrence of 138 | # a. The stop-loss 139 | # b. An S1 or S2 reversal 140 | # c. A loss of 2% relative to the portfolio (where 2L is equal to 2% of the portfolio)" (Kaufman, 2013) 141 | if self.is_long and (self.entry_signal() == "entry_short" or self.exit_signal() == "exit_long") \ 142 | or self.is_short and (self.entry_signal() == "entry_long" or self.exit_signal() == "exit_short"): 143 | self.liquidate() 144 | self.current_pyramiding_levels = 0 145 | 146 | def on_increased_position(self, order): 147 | # "In order to keep total position risk at a minimum, if additional units were added, the stops for earlier units were raised by 1⁄2 N. 148 | # This generally meant that all the stops for the entire position would be placed at 2 N from the most recently added unit." (Faith, 2003) 149 | if self.is_long: 150 | self.stop_loss = abs(self.position.qty), self.price - self.vars["atr_multiplier"] * self.atr 151 | # self.log(f"atr={self.atr}, current position sl: {self.average_stop_loss}") 152 | if self.is_short: 153 | self.stop_loss = abs(self.position.qty), self.price + self.vars["atr_multiplier"] * self.atr 154 | # self.log(f"atr={self.atr}, current position sl: {self.average_stop_loss}") 155 | 156 | self.current_pyramiding_levels += 1 157 | self.last_opened_price = self.price 158 | # self.log(f"current pyramiding levels: {self.current_pyramiding_levels}") 159 | 160 | def on_stop_loss(self, order): 161 | # Reset tracked pyramiding levels 162 | self.current_pyramiding_levels = 0 163 | 164 | def on_take_profit(self, order): 165 | self.last_was_profitable = True 166 | 167 | # Reset tracked pyramiding levels 168 | self.current_pyramiding_levels = 0 169 | 170 | def filters(self): 171 | return [ 172 | self.S1_filter 173 | ] 174 | 175 | def S1_filter(self): 176 | if self.vars["system_type"] == "S1" and self.last_was_profitable: 177 | # self.log(f"prev was profitable, do not enter trade") 178 | self.last_was_profitable = False 179 | return False 180 | return True 181 | -------------------------------------------------------------------------------- /example-strategies-master/Donchian/ADA-USDT_6h_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 731 days (2.0 years) 5 | starting-ending date | 2019-01-01 => 2021-01-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | ADA-USDT | 6h | Donchian | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 34.98 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+--------------------------- 19 | Total Closed Trades | 19 20 | Total Net Profit | 55,272.4782 (552.72%) 21 | Starting => Finishing Balance | 10,000 => 65,272.48 22 | Total Open Trades | 1 23 | Open PL | 4,714.81 24 | Total Paid Fees | 1,277.14 25 | Max Drawdown | -36.15% 26 | Annual Return | 154.83% 27 | Expectancy | 2,909.08 (29.09%) 28 | Avg Win | Avg Loss | 8,936.79 | 1,474.71 29 | Ratio Avg Win / Avg Loss | 6.06 30 | Percent Profitable | 42% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 2 weeks, 2 days, 20 hours 33 | Winning Trades Avg Holding Time | 3 weeks, 2 days, 16 hours 34 | Losing Trades Avg Holding Time | 1 week, 4 days, 20 hours 35 | Sharpe Ratio | 1.68 36 | Market Change | 347.53% -------------------------------------------------------------------------------- /example-strategies-master/Donchian/BAT-USDT_6h_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 701 days (1.92 years) 5 | starting-ending date | 2019-05-05 => 2021-04-05 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | BAT-USDT | 6h | Donchian | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 33.2 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+--------------------------- 19 | Total Closed Trades | 17 20 | Total Net Profit | 21,211.9829 (212.12%) 21 | Starting => Finishing Balance | 10,000 => 31,211.98 22 | Total Open Trades | 1 23 | Open PL | 177.78 24 | Total Paid Fees | 409.07 25 | Max Drawdown | -36.55% 26 | Annual Return | 80.73% 27 | Expectancy | 1,247.76 (12.48%) 28 | Avg Win | Avg Loss | 2,755.04 | 905.49 29 | Ratio Avg Win / Avg Loss | 3.04 30 | Percent Profitable | 59% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 2 weeks, 2 days, 22 hours 33 | Winning Trades Avg Holding Time | 2 weeks, 6 days, 18 hours 34 | Losing Trades Avg Holding Time | 1 week, 4 days, 12 hours 35 | Sharpe Ratio | 1.13 36 | Market Change | 224.43% -------------------------------------------------------------------------------- /example-strategies-master/Donchian/BTC-USDT_6h_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 731 days (2.0 years) 5 | starting-ending date | 2019-01-01 => 2021-01-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | BTC-USDT | 6h | Donchian | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 35.53 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+--------------------------- 19 | Total Closed Trades | 18 20 | Total Net Profit | 45,194.3507 (451.94%) 21 | Starting => Finishing Balance | 10,000 => 55,194.35 22 | Total Open Trades | 1 23 | Open PL | 18,320.21 24 | Total Paid Fees | 760.56 25 | Max Drawdown | -46.89% 26 | Annual Return | 134.39% 27 | Expectancy | 2,510.8 (25.11%) 28 | Avg Win | Avg Loss | 5,824.01 | 1,630.71 29 | Ratio Avg Win / Avg Loss | 3.57 30 | Percent Profitable | 56% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 2 weeks, 6 days, 14 hours 33 | Winning Trades Avg Holding Time | 3 weeks, 6 days, 21 hours 34 | Losing Trades Avg Holding Time | 1 week, 4 days, 12 hours 35 | Sharpe Ratio | 2.03 36 | Market Change | 681.2% -------------------------------------------------------------------------------- /example-strategies-master/Donchian/ETH-USDT_6h_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 731 days (2.0 years) 5 | starting-ending date | 2019-01-01 => 2021-01-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | ETH-USDT | 6h | Donchian | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 36.79 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+-------------------------- 19 | Total Closed Trades | 24 20 | Total Net Profit | 33,528.8877 (335.29%) 21 | Starting => Finishing Balance | 10,000 => 43,528.89 22 | Total Open Trades | 1 23 | Open PL | 5,487.64 24 | Total Paid Fees | 1,027.9 25 | Max Drawdown | -34.14% 26 | Annual Return | 108.22% 27 | Expectancy | 1,397.04 (13.97%) 28 | Avg Win | Avg Loss | 4,302.78 | 1,508.71 29 | Ratio Avg Win / Avg Loss | 2.85 30 | Percent Profitable | 50% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 2 weeks, 1 day, 4 hours 33 | Winning Trades Avg Holding Time | 3 weeks, 2 days, 8 hours 34 | Losing Trades Avg Holding Time | 1 week 35 | Sharpe Ratio | 1.53 36 | Market Change | 460.23% -------------------------------------------------------------------------------- /example-strategies-master/Donchian/NEO-USDT_6h_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 731 days (2.0 years) 5 | starting-ending date | 2019-01-01 => 2021-01-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | NEO-USDT | 6h | Donchian | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 33.83 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+------------------------------ 19 | Total Closed Trades | 18 20 | Total Net Profit | 20,801.5637 (208.02%) 21 | Starting => Finishing Balance | 10,000 => 30,801.56 22 | Total Open Trades | 0 23 | Open PL | 0 24 | Total Paid Fees | 626.48 25 | Max Drawdown | -32.92% 26 | Annual Return | 75.23% 27 | Expectancy | 1,155.64 (11.56%) 28 | Avg Win | Avg Loss | 2,924.3 | 1,623.68 29 | Ratio Avg Win / Avg Loss | 1.8 30 | Percent Profitable | 61% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 2 weeks, 4 days, 2 hours 33 | Winning Trades Avg Holding Time | 3 weeks, 3 days, 14 hours 34 | Losing Trades Avg Holding Time | 1 week, 22 hours, 17 minutes 35 | Sharpe Ratio | 1.14 36 | Market Change | 92.41% 37 | -------------------------------------------------------------------------------- /example-strategies-master/Donchian/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple Donchian Strategy 3 | Repo: https://github.com/gabrielweich/jesse-strategies 4 | Uses Donchian Channels to capture long term uptrends, the idea is to buy 5 | when price closes above the upperband and only exit the position when 6 | prices closes below the lowerband. 7 | """ 8 | 9 | from jesse.strategies import Strategy 10 | import jesse.indicators as ta 11 | from jesse import utils 12 | 13 | 14 | class Donchian(Strategy): 15 | @property 16 | def donchian(self): 17 | # Previous Donchian Channels with default parameters 18 | return ta.donchian(self.candles[:-1]) 19 | 20 | @property 21 | def ma_trend(self): 22 | return ta.sma(self.candles, period=200) 23 | 24 | def filter_trend(self): 25 | # Only opens a long position when close is above 200 SMA 26 | return self.close > self.ma_trend 27 | 28 | def filters(self): 29 | return [self.filter_trend] 30 | 31 | def should_long(self) -> bool: 32 | # Go long if candle closes above upperband 33 | return self.close > self.donchian.upperband 34 | 35 | def should_short(self) -> bool: 36 | return False 37 | 38 | def should_cancel(self) -> bool: 39 | return True 40 | 41 | def go_long(self): 42 | # Open long position using entire balance 43 | qty = utils.size_to_qty(self.capital, self.price, fee_rate=self.fee_rate) 44 | self.buy = qty, self.price 45 | 46 | def go_short(self): 47 | pass 48 | 49 | def update_position(self): 50 | # Close the position when candle closes below lowerband 51 | if self.close < self.donchian.lowerband: 52 | self.liquidate() 53 | -------------------------------------------------------------------------------- /example-strategies-master/IFR2/ADA-USDT_1h_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 812 days (2.22 years) 5 | starting-ending date | 2019-01-10 => 2021-04-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | ADA-USDT | 1h | IFR2 | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 39.22 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+--------------------------------- 19 | Total Closed Trades | 260 20 | Total Net Profit | 14,122.7117 (141.23%) 21 | Starting => Finishing Balance | 10,000 => 24,122.71 22 | Total Open Trades | 0 23 | Open PL | 0 24 | Total Paid Fees | 6,313.33 25 | Max Drawdown | -26.61% 26 | Annual Return | 48.49% 27 | Expectancy | 54.32 (0.54%) 28 | Avg Win | Avg Loss | 224.07 | 320.81 29 | Ratio Avg Win / Avg Loss | 0.7 30 | Percent Profitable | 69% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 7 hours, 12 minutes 33 | Winning Trades Avg Holding Time | 5 hours, 7 minutes, 2 seconds 34 | Losing Trades Avg Holding Time | 11 hours, 48 minutes, 8 seconds 35 | Sharpe Ratio | 1.38 36 | Market Change | 2218.44% -------------------------------------------------------------------------------- /example-strategies-master/IFR2/BAT-USDT_1h_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 731 days (2.0 years) 5 | starting-ending date | 2019-04-01 => 2021-04-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | BAT-USDT | 1h | IFR2 | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 31.54 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+---------------------------------- 19 | Total Closed Trades | 249 20 | Total Net Profit | 30,380.8971 (303.81%) 21 | Starting => Finishing Balance | 10,000 => 40,380.9 22 | Total Open Trades | 1 23 | Open PL | 1,482.82 24 | Total Paid Fees | 8,669.0 25 | Max Drawdown | -20.59% 26 | Annual Return | 100.57% 27 | Expectancy | 122.01 (1.22%) 28 | Avg Win | Avg Loss | 366.49 | 434.51 29 | Ratio Avg Win / Avg Loss | 0.84 30 | Percent Profitable | 69% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 7 hours, 8 minutes, 26 seconds 33 | Winning Trades Avg Holding Time | 4 hours, 53 minutes, 24 seconds 34 | Losing Trades Avg Holding Time | 12 hours, 15 minutes, 47 seconds 35 | Sharpe Ratio | 1.98 36 | Market Change | 296.08% -------------------------------------------------------------------------------- /example-strategies-master/IFR2/NEO-USDT_1h_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 821 days (2.25 years) 5 | starting-ending date | 2019-01-01 => 2021-04-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | NEO-USDT | 1h | IFR2 | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 36.23 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+---------------------------------- 19 | Total Closed Trades | 279 20 | Total Net Profit | 17,204.8247 (172.05%) 21 | Starting => Finishing Balance | 10,000 => 27,204.82 22 | Total Open Trades | 0 23 | Open PL | 0 24 | Total Paid Fees | 7,434.63 25 | Max Drawdown | -21.44% 26 | Annual Return | 55.96% 27 | Expectancy | 61.67 (0.62%) 28 | Avg Win | Avg Loss | 228.66 | 256.66 29 | Ratio Avg Win / Avg Loss | 0.89 30 | Percent Profitable | 66% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 6 hours, 55 minutes, 41 seconds 33 | Winning Trades Avg Holding Time | 4 hours, 58 minutes, 41 seconds 34 | Losing Trades Avg Holding Time | 10 hours, 38 minutes, 45 seconds 35 | Sharpe Ratio | 1.65 36 | Market Change | 585.36% -------------------------------------------------------------------------------- /example-strategies-master/IFR2/REEF-USDT_1h_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 81 days (2.7 months) 5 | starting-ending date | 2021-01-10 => 2021-04-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+-----------+-------------+------------+------- 10 | Binance | REEF-USDT | 1h | IFR2 | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 3.98 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+--------------------------------- 19 | Total Closed Trades | 32 20 | Total Net Profit | 20,562.7827 (205.63%) 21 | Starting => Finishing Balance | 10,000 => 30,562.78 22 | Total Open Trades | 0 23 | Open PL | 0 24 | Total Paid Fees | 1,546.58 25 | Max Drawdown | -22.51% 26 | Annual Return | 14344.42% 27 | Expectancy | 642.59 (6.43%) 28 | Avg Win | Avg Loss | 1,109.73 | 2,627.41 29 | Ratio Avg Win / Avg Loss | 0.42 30 | Percent Profitable | 88% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 6 hours, 13 minutes, 7 seconds 33 | Winning Trades Avg Holding Time | 5 hours, 34 minutes, 17 seconds 34 | Losing Trades Avg Holding Time | 10 hours, 45 minutes 35 | Sharpe Ratio | 5.71 36 | Market Change | 299.82% -------------------------------------------------------------------------------- /example-strategies-master/IFR2/UNI-USDT_1h_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 182 days (6.07 months) 5 | starting-ending date | 2020-10-01 => 2021-04-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | UNI-USDT | 1h | IFR2 | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 9.07 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+---------------------------------- 19 | Total Closed Trades | 83 20 | Total Net Profit | 16,466.1687 (164.66%) 21 | Starting => Finishing Balance | 10,000 => 26,466.17 22 | Total Open Trades | 0 23 | Open PL | 0 24 | Total Paid Fees | 2,558.48 25 | Max Drawdown | -20.7% 26 | Annual Return | 596.74% 27 | Expectancy | 198.39 (1.98%) 28 | Avg Win | Avg Loss | 431.57 | 448.16 29 | Ratio Avg Win / Avg Loss | 0.96 30 | Percent Profitable | 73% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 7 hours, 21 minutes, 41 seconds 33 | Winning Trades Avg Holding Time | 5 hours, 3 minutes, 56 seconds 34 | Losing Trades Avg Holding Time | 13 hours, 43 minutes, 38 seconds 35 | Sharpe Ratio | 3.75 36 | Market Change | 575.41% 37 | -------------------------------------------------------------------------------- /example-strategies-master/IFR2/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | IFR2 (portuguese translation of RSI2) 3 | Repo: https://github.com/gabrielweich/jesse-strategies 4 | Simillar to RSI2 described by Larry Connors but with some modifications. 5 | Uses Ichimoku Cloud with parameters adapted to crypto market and also 6 | Hilbert Transform Trendmode as filters to only buy in an uptrend. 7 | The exit occurs when the close is above the highest high of the two 8 | previous cadles. 9 | References: 10 | - RSI2: https://school.stockcharts.com/doku.php?id=trading_strategies:rsi2 11 | - Ichimoku Crypto Parameters: https://www.altcointrading.net/ichimoku-cloud/ 12 | """ 13 | 14 | from jesse.strategies import Strategy 15 | import jesse.indicators as ta 16 | from jesse import utils 17 | 18 | 19 | class IFR2(Strategy): 20 | @property 21 | def rsi(self): 22 | return ta.rsi(self.candles, period=2) 23 | 24 | @property 25 | def trend_mode(self): 26 | return ta.ht_trendmode(self.candles) 27 | 28 | @property 29 | def ichimoku(self): 30 | # Ichimoku cloud using parameters adapted to crypto market 31 | return ta.ichimoku_cloud( 32 | self.candles, 33 | conversion_line_period=20, 34 | base_line_period=30, 35 | lagging_line_period=120, 36 | displacement=60, 37 | ) 38 | 39 | def filter_trend_ichimoku(self): 40 | # Only opens a long position when close is above ichimoku cloud 41 | return self.close > self.ichimoku.span_a and self.close > self.ichimoku.span_b 42 | 43 | def filter_trend_mode(self): 44 | # Only opens a long position when trend_mode indicates a trend 45 | return self.trend_mode == 1 46 | 47 | def filters(self): 48 | return [self.filter_trend_ichimoku, self.filter_trend_mode] 49 | 50 | def should_long(self) -> bool: 51 | # Go long if candle RSI2 is below 10 52 | return self.rsi < 10 53 | 54 | def should_short(self) -> bool: 55 | return False 56 | 57 | def should_cancel(self) -> bool: 58 | return True 59 | 60 | def go_long(self): 61 | # Open long position using entire balance 62 | qty = utils.size_to_qty(self.capital, self.price, fee_rate=self.fee_rate) 63 | self.buy = qty, self.price 64 | 65 | def go_short(self): 66 | pass 67 | 68 | def update_position(self): 69 | # Close the position when candle is above the highest high of the two previous cadles 70 | if ( 71 | self.close > self.candles[-2:, 3][-2] 72 | and self.close > self.candles[-3:, 3][-3] 73 | ): 74 | self.liquidate() -------------------------------------------------------------------------------- /example-strategies-master/KDJ/BTC-USDT_1D_results.txt: -------------------------------------------------------------------------------- 1 | CANDLES | 2 | ----------------------+-------------------------- 3 | period | 731 days (2.0 years) 4 | starting-ending date | 2019-01-01 => 2021-01-01 5 | 6 | 7 | exchange | symbol | timeframe | strategy | DNA 8 | ------------+----------+-------------+-----------------+------- 9 | Binance | BTC-USDT | 1D | AwesomeStrategy | 10 | 11 | 12 | Executing simulation... [####################################] 100% 13 | Executed backtest simulation in: 29.89 seconds 14 | 15 | 16 | METRICS | 17 | ---------------------------------+----------------------------- 18 | Total Closed Trades | 155 19 | Total Net Profit | 897.7349 (89.77%) 20 | Starting => Finishing Balance | 1,000 => 1,897.73 21 | Total Open Trades | 1 22 | Open PL | 1.59 23 | Total Paid Fees | 252.25 24 | Max Drawdown | -22.4% 25 | Annual Return | 37.64% 26 | Expectancy | 5.79 (0.58%) 27 | Avg Win | Avg Loss | 34.2 | 25.3 28 | Ratio Avg Win / Avg Loss | 1.35 29 | Percent Profitable | 52% 30 | Longs | Shorts | 100% | 0% 31 | Avg Holding Time | 2 days, 9 hours, 8 minutes 32 | Winning Trades Avg Holding Time | 2 days, 18 hours, 4 minutes 33 | Losing Trades Avg Holding Time | 1 day, 23 hours, 21 minutes 34 | Sharpe Ratio | 1.31 35 | Market Change | 681.2% 36 | -------------------------------------------------------------------------------- /example-strategies-master/KDJ/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple KDJ Indicator Strategy 3 | 1D Timeframe 4 | Repo: https://github.com/matty5690/example-strategies 5 | Opens position when J value is above K and D values and closes when J value is below K and D values 6 | 7 | 8 | 9 | """ 10 | 11 | from jesse.strategies import Strategy 12 | import jesse.indicators as ta 13 | from jesse import utils 14 | 15 | 16 | class AwesomeStrategy(Strategy): 17 | 18 | @property 19 | def KDJIndicator(self): #KDJ Indicator 20 | return ta.kdj(self.candles) #returns namedtuple KDJ(k,d,j) with default parameters 21 | 22 | @property 23 | def LastKDJ(self): 24 | return ta.kdj(self.candles[:-1]) #returns the previous candles KDJ values 25 | 26 | @property 27 | def atr(self): 28 | return ta.atr(self.candles, period = 14) #14 period ATR used for calculating stop loss 29 | 30 | def should_long(self) -> bool: 31 | #long signal if J value crosses above K and D values 32 | return self.KDJIndicator[2] > (self.KDJIndicator[0] and self.KDJIndicator[1]) 33 | 34 | def should_short(self) -> bool: 35 | return False 36 | 37 | def should_cancel(self) -> bool: 38 | return True 39 | 40 | def go_long(self): 41 | # Open long position and risk 5% of available capital 42 | risk_perc = 5 43 | entry = self.price 44 | stop = entry - 2 * self.atr #trailing stop loss of 2x ATR 45 | qty = utils.risk_to_qty(self.capital, risk_perc, entry, stop) 46 | self.buy = qty, self.price 47 | self.stop_loss = qty, stop 48 | 49 | def go_short(self): 50 | return False 51 | 52 | def update_position(self): 53 | #if position is in profit it will close if the J value at close is less than the previous J value 54 | if self.is_long and self.position.pnl_percentage > 0 and self.LastKDJ[2] > self.KDJIndicator[2]: 55 | self.liquidate() 56 | #if position is not in profit and the J value crosses under the K and D values then the position will close 57 | elif self.is_long and self.KDJIndicator[2] < (self.KDJIndicator[0] and self.KDJIndicator[1]): 58 | self.liquidate() 59 | -------------------------------------------------------------------------------- /example-strategies-master/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 jesse-ai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /example-strategies-master/README.md: -------------------------------------------------------------------------------- 1 | # Example Jesse Strategies 2 | 3 | The point of this repository is to host examples of different types of strategies that you can implement with Jesse to get you started. 4 | 5 | ## Submission guide 6 | 7 | All submitted strategies must be heavily documented. Try to use lots of comments! 8 | 9 | 10 | ## Usage Guide 11 | Just copy the folder into your Jesse project's `strategies` directory, and update your `routes.py` file to point to the strategy. -------------------------------------------------------------------------------- /example-strategies-master/RSI2/RSI2-results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 366 days (1.0 years) 5 | starting-ending date | 2019-06-01 => 2020-06-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | BTCUSDT | 3h | RSI2 | 11 | 12 | 13 | Executed backtest simulation in: 15.3 seconds 14 | 15 | 16 | METRICS | 17 | ---------------------------------+---------------------------------------- 18 | Total Closed Trades | 116 19 | Total Net Profit | -4330.43 (-43.3%) 20 | Starting => Finishing Balance | 10000 => 5675.42 21 | Total Open Trades | 0 22 | Open PL | 0 23 | Total Paid Fees | 1772.67 24 | Max Drawdown | -50.11% 25 | Annual Return | -32.22% 26 | Expectancy | -37.33 (-0.37%) 27 | Avg Win | Avg Loss | 86.89 | 248.22 28 | Ratio Avg Win / Avg Loss | 0.35 29 | Percent Profitable | 63% 30 | Longs | Shorts | 51% | 49% 31 | Avg Holding Time | 13.0 hours, 12.0 minutes, 55.0 seconds 32 | Winning Trades Avg Holding Time | 8.0 hours, 35.0 minutes, 20.0 seconds 33 | Losing Trades Avg Holding Time | 21.0 hours, 4.0 minutes, 11.0 seconds 34 | Sharpe Ratio | -0.57 35 | -------------------------------------------------------------------------------- /example-strategies-master/RSI2/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Connors' RSI2 Strategy 3 | 2020-06-27 original implementation - FengkieJ (fengkiejunis@gmail.com) 4 | 5 | This strategy is a very basic mean reversion strategy, mainly using RSI(2) overbought and oversold levels to determine entry signals. 6 | Originally developed by Larry Connors. 7 | 8 | Reference: - RSI(2) [ChartSchool]. Retrieved 27 June 2020, from https://school.stockcharts.com/doku.php?id=trading_strategies:rsi2 9 | """ 10 | 11 | from jesse.strategies import Strategy 12 | import jesse.indicators as ta 13 | from jesse import utils 14 | 15 | class RSI2(Strategy): 16 | def __init__(self): 17 | super().__init__() 18 | 19 | self.vars["fast_sma_period"] = 5 20 | self.vars["slow_sma_period"] = 200 21 | self.vars["rsi_period"] = 2 22 | self.vars["rsi_ob_threshold"] = 90 23 | self.vars["rsi_os_threshold"] = 10 24 | 25 | @property 26 | def fast_sma(self): 27 | return ta.sma(self.candles, self.vars["fast_sma_period"]) 28 | 29 | @property 30 | def slow_sma(self): 31 | return ta.sma(self.candles, self.vars["slow_sma_period"]) 32 | 33 | @property 34 | def rsi(self): 35 | return ta.rsi(self.candles, self.vars["rsi_period"]) 36 | 37 | def should_long(self) -> bool: 38 | # Enter long if current price is above sma(200) and RSI(2) is below oversold threshold 39 | return self.price > self.slow_sma and self.rsi <= self.vars["rsi_os_threshold"] 40 | 41 | def should_short(self) -> bool: 42 | # Enter long if current price is below sma(200) and RSI(2) is above oversold threshold 43 | return self.price < self.slow_sma and self.rsi >= self.vars["rsi_ob_threshold"] 44 | 45 | def should_cancel(self) -> bool: 46 | return False 47 | 48 | def go_long(self): 49 | # Open long position and use entire balance to buy 50 | qty = utils.size_to_qty(self.capital, self.price, fee_rate=self.fee_rate) 51 | 52 | self.buy = qty, self.price 53 | 54 | def go_short(self): 55 | # Open short position and use entire balance to sell 56 | qty = utils.size_to_qty(self.capital, self.price, fee_rate=self.fee_rate) 57 | 58 | self.sell = qty, self.price 59 | 60 | def update_position(self): 61 | # Exit long position if price is above sma(5) 62 | if self.is_long and self.price > self.fast_sma: 63 | self.liquidate() 64 | 65 | # Exit short position if price is below sma(5) 66 | if self.is_short and self.price < self.fast_sma: 67 | self.liquidate() 68 | -------------------------------------------------------------------------------- /example-strategies-master/SMACrossover/SMACrossover-results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 366 days (1.0 years) 5 | starting-ending date | 2019-06-01 => 2020-06-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+--------------+------- 10 | Binance | BTCUSDT | 3h | SMACrossover | 11 | 12 | 13 | Executed backtest simulation in: 12.88 seconds 14 | 15 | 16 | METRICS | 17 | ---------------------------------+------------------------------------ 18 | Total Closed Trades | 13 19 | Total Net Profit | 8471.38 (84.71%) 20 | Starting => Finishing Balance | 10000 => 17295.8 21 | Total Open Trades | 1 22 | Open PL | -1134.74 23 | Total Paid Fees | 225.74 24 | Max Drawdown | -53.7% 25 | Annual Return | 45.67% 26 | Expectancy | 651.64 (6.52%) 27 | Avg Win | Avg Loss | 2226.61 | 698.33 28 | Ratio Avg Win / Avg Loss | 3.19 29 | Percent Profitable | 46% 30 | Longs | Shorts | 54% | 46% 31 | Avg Holding Time | 3.0 weeks, 6.0 days, 16.0 hours 32 | Winning Trades Avg Holding Time | 6.0 weeks, 1.0 day, 5.0 hours 33 | Losing Trades Avg Holding Time | 2.0 weeks, 8.0 hours, 34.0 minutes 34 | Sharpe Ratio | 0.81 35 | -------------------------------------------------------------------------------- /example-strategies-master/SMACrossover/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple Moving Average Crossover Strategy 3 | Author: FengkieJ (fengkiejunis@gmail.com) 4 | Simple moving average crossover strategy is the ''hello world'' of algorithmic trading. 5 | This strategy uses two SMAs to determine '''Golden Cross''' to signal for long position, and '''Death Cross''' to signal for short position. 6 | """ 7 | 8 | from jesse.strategies import Strategy 9 | import jesse.indicators as ta 10 | from jesse import utils 11 | 12 | class SMACrossover(Strategy): 13 | @property 14 | def slow_sma(self): 15 | return ta.sma(self.candles, 200) 16 | 17 | @property 18 | def fast_sma(self): 19 | return ta.sma(self.candles, 50) 20 | 21 | def should_long(self) -> bool: 22 | # Golden Cross (reference: https://www.investopedia.com/terms/g/goldencross.asp) 23 | # Fast SMA above Slow SMA 24 | return self.fast_sma > self.slow_sma 25 | 26 | def should_short(self) -> bool: 27 | # Death Cross (reference: https://www.investopedia.com/terms/d/deathcross.asp) 28 | # Fast SMA below Slow SMA 29 | return self.fast_sma < self.slow_sma 30 | 31 | def should_cancel(self) -> bool: 32 | return False 33 | 34 | def go_long(self): 35 | # Open long position and use entire balance to buy 36 | qty = utils.size_to_qty(self.capital, self.price, fee_rate=self.fee_rate) 37 | 38 | self.buy = qty, self.price 39 | 40 | def go_short(self): 41 | # Open short position and use entire balance to sell 42 | qty = utils.size_to_qty(self.capital, self.price, fee_rate=self.fee_rate) 43 | 44 | self.sell = qty, self.price 45 | 46 | def update_position(self): 47 | # If there exist long position, but the signal shows Death Cross, then close the position, and vice versa. 48 | if self.is_long and self.fast_sma < self.slow_sma: 49 | self.liquidate() 50 | 51 | if self.is_short and self.fast_sma > self.slow_sma: 52 | self.liquidate() 53 | -------------------------------------------------------------------------------- /example-strategies-master/SimpleBollinger/BTC-USDT_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 731 days (2.0 years) 5 | starting-ending date | 2019-01-01 => 2021-01-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+-----------------+------- 10 | Binance | BTC-USDT | 1h | SimpleBollinger | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 31.17 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+---------------------------------- 19 | Total Closed Trades | 277 20 | Total Net Profit | 27,125.64 (271.26%) 21 | Starting => Finishing Balance | 10,000 => 37,125.64 22 | Total Open Trades | 0 23 | Open PL | 0 24 | Total Paid Fees | 11,139.24 25 | Max Drawdown | -18.72% 26 | Annual Return | 92.34% 27 | Expectancy | 97.93 (0.98%) 28 | Avg Win | Avg Loss | 734.44 | 267.35 29 | Ratio Avg Win / Avg Loss | 2.75 30 | Percent Profitable | 36% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 18 hours, 31 minutes, 11 seconds 33 | Winning Trades Avg Holding Time | 1 day, 7 hours, 39 minutes 34 | Losing Trades Avg Holding Time | 10 hours, 58 minutes, 58 seconds 35 | Sharpe Ratio | 1.8 36 | Market Change | 681.2% -------------------------------------------------------------------------------- /example-strategies-master/SimpleBollinger/ETH-USDT_results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 731 days (2.0 years) 5 | starting-ending date | 2019-01-01 => 2021-01-01 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+-----------------+------- 10 | Binance | ETH-USDT | 1h | SimpleBollinger | 11 | 12 | 13 | Executing simulation... [####################################] 100% 14 | Executed backtest simulation in: 31.24 seconds 15 | 16 | 17 | METRICS | 18 | ---------------------------------+---------------------------------- 19 | Total Closed Trades | 282 20 | Total Net Profit | 21,030.4448 (210.3%) 21 | Starting => Finishing Balance | 10,000 => 31,030.44 22 | Total Open Trades | 0 23 | Open PL | 0 24 | Total Paid Fees | 9,730.63 25 | Max Drawdown | -30.29% 26 | Annual Return | 75.88% 27 | Expectancy | 74.58 (0.75%) 28 | Avg Win | Avg Loss | 729.33 | 274.15 29 | Ratio Avg Win / Avg Loss | 2.66 30 | Percent Profitable | 35% 31 | Longs | Shorts | 100% | 0% 32 | Avg Holding Time | 17 hours, 11 minutes, 29 seconds 33 | Winning Trades Avg Holding Time | 1 day, 4 hours, 52 minutes 34 | Losing Trades Avg Holding Time | 10 hours, 58 minutes, 2 seconds 35 | Sharpe Ratio | 1.41 36 | Market Change | 460.23% -------------------------------------------------------------------------------- /example-strategies-master/SimpleBollinger/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple Bollinger Bands Strategy 3 | Timeframe: 1h 4 | Repo: https://github.com/gabrielweich/jesse-strategies 5 | When the price closes above the upperband in an uptrend (determined by the ichimoku cloud), 6 | it gives a long signal indicating a possible continuation of the uptrend. 7 | The position can also be closed when the price closes below lowerband, but the best results 8 | were achieved when using the middleband instead. 9 | """ 10 | 11 | from jesse.strategies import Strategy 12 | import jesse.indicators as ta 13 | from jesse import utils 14 | 15 | 16 | class SimpleBollinger(Strategy): 17 | @property 18 | def bb(self): 19 | # Bollinger bands using default parameters and hl2 as source 20 | return ta.bollinger_bands(self.candles, source_type="hl2") 21 | 22 | @property 23 | def ichimoku(self): 24 | return ta.ichimoku_cloud(self.candles) 25 | 26 | def filter_trend(self): 27 | # Only opens a long position when close is above ichimoku cloud 28 | return self.close > self.ichimoku.span_a and self.close > self.ichimoku.span_b 29 | 30 | def filters(self): 31 | return [self.filter_trend] 32 | 33 | def should_long(self) -> bool: 34 | # Go long if candle closes above upperband 35 | return self.close > self.bb[0] 36 | 37 | def should_short(self) -> bool: 38 | return False 39 | 40 | def should_cancel(self) -> bool: 41 | return True 42 | 43 | def go_long(self): 44 | # Open long position using entire balance 45 | qty = utils.size_to_qty(self.capital, self.price, fee_rate=self.fee_rate) 46 | self.buy = qty, self.price 47 | 48 | def go_short(self): 49 | pass 50 | 51 | def update_position(self): 52 | # Close the position when candle closes below middleband 53 | if self.close < self.bb[1]: 54 | self.liquidate() -------------------------------------------------------------------------------- /example-strategies-master/TradingView_RSI/BTC-USDT-results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 493 days (1.35 years) 5 | starting-ending date | 2020-01-01 => 2021-05-08 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | BTC-USDT | 1h | TV_RSI | 11 | 12 | 13 | Executed backtest simulation in: 27.7 seconds 14 | 15 | 16 | METRICS | 17 | ---------------------------------+------------------------------ 18 | Total Closed Trades | 94 19 | Total Net Profit | 1,792.5069 (179.25%) 20 | Starting => Finishing Balance | 1,000 => 2,792.51 21 | Total Open Trades | 1 22 | Open PL | 131.91 23 | Total Paid Fees | 329.74 24 | Max Drawdown | -27.04% 25 | Annual Return | 113.57% 26 | Expectancy | 19.07 (1.91%) 27 | Avg Win | Avg Loss | 106.56 | 99.05 28 | Ratio Avg Win / Avg Loss | 1.08 29 | Percent Profitable | 57% 30 | Longs | Shorts | 100% | 0% 31 | Avg Holding Time | 2 days, 19 hours, 49 minutes 32 | Winning Trades Avg Holding Time | 3 days, 15 hours, 25 minutes 33 | Losing Trades Avg Holding Time | 1 day, 17 hours, 23 minutes 34 | Sharpe Ratio | 1.8 35 | Market Change | 697.51% 36 | 37 | 38 | -------------------------------------------------------------------------------- /example-strategies-master/TradingView_RSI/ETH-USDT-results.txt: -------------------------------------------------------------------------------- 1 | loading candles... 2 | CANDLES | 3 | ----------------------+-------------------------- 4 | period | 493 days (1.35 years) 5 | starting-ending date | 2020-01-01 => 2021-05-08 6 | 7 | 8 | exchange | symbol | timeframe | strategy | DNA 9 | ------------+----------+-------------+------------+------- 10 | Binance | ETH-USDT | 1h | TV_RSI | 11 | 12 | 13 | Executed backtest simulation in: 45.32 seconds 14 | 15 | 16 | METRICS | 17 | ---------------------------------+------------------------------ 18 | Total Closed Trades | 108 19 | Total Net Profit | 1,521.2327 (152.12%) 20 | Starting => Finishing Balance | 1,000 => 2,521.23 21 | Total Open Trades | 0 22 | Open PL | 0 23 | Total Paid Fees | 268.69 24 | Max Drawdown | -31.13% 25 | Annual Return | 98.03% 26 | Expectancy | 14.09 (1.41%) 27 | Avg Win | Avg Loss | 100.49 | 63.22 28 | Ratio Avg Win / Avg Loss | 1.59 29 | Percent Profitable | 47% 30 | Longs | Shorts | 100% | 0% 31 | Avg Holding Time | 2 days, 4 hours, 41 minutes 32 | Winning Trades Avg Holding Time | 2 days, 21 hours, 39 minutes 33 | Losing Trades Avg Holding Time | 1 day, 13 hours, 31 minutes 34 | Sharpe Ratio | 1.45 35 | Market Change | 2596.89% 36 | 37 | 38 | -------------------------------------------------------------------------------- /example-strategies-master/TradingView_RSI/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Based on https://www.tradingview.com/script/Ru7qOVtp-RSI-Trend-Crypto/ 3 | 4 | Default Trading Rules: 5 | Long: RSI crosses over 35 6 | Close Long: RSI crosses under 75 7 | Emergency Exit: RSI crosses under 10 8 | """ 9 | 10 | from jesse.strategies import Strategy 11 | import jesse.indicators as ta 12 | from jesse import utils 13 | 14 | class TV_RSI(Strategy): 15 | 16 | def hyperparameters(self): 17 | return [ 18 | {'name':'rsi', 'type': int, 'min': 10, 'max':30, 'default': 5}, 19 | {'name':'stop_loss', 'type': float, 'min': .5, 'max': .99, 'default': .95}, 20 | {'name':'take_profit', 'type': float, 'min': 1.1, 'max': 1.2, 'default': 1.1}, 21 | {'name':'xparam', 'type':int, 'min': 60, 'max': 90, 'default': 75} 22 | ] 23 | 24 | @property 25 | def rsi(self): 26 | return ta.rsi(self.candles, self.hp['rsi'], sequential=True) 27 | 28 | 29 | def should_long(self): 30 | qty = utils.size_to_qty(self.capital, self.price, 3, fee_rate=self.fee_rate) 31 | 32 | if utils.crossed(self.rsi, 35, direction="above") and qty > 0 and self.available_margin > (qty * self.price): 33 | return True 34 | 35 | def should_short(self): 36 | return False 37 | 38 | def should_cancel(self): 39 | return False 40 | 41 | 42 | def go_long(self): 43 | qty = utils.size_to_qty(self.capital, self.price, 3, fee_rate=self.fee_rate) 44 | self.buy = qty, self.price 45 | self.stop_loss = qty, (self.price * self.hp['stop_loss']) # Willing to lose 5% 46 | self.take_profit = qty, (self.price * self.hp['take_profit']) # Take profits at 10% 47 | 48 | def go_short(self): 49 | pass 50 | 51 | def update_position(self): 52 | if utils.crossed(self.rsi, self.hp['xparam'], direction="below") or utils.crossed(self.rsi, 10, direction="below"): 53 | self.liquidate() 54 | 55 | -------------------------------------------------------------------------------- /example-strategies-master/TurtleRules/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | The Original Turtle Trading Rules Strategy 3 | 2020-06-20 original implementation - FengkieJ (fengkiejunis@gmail.com) 4 | 2020-06-27 rev 1. add System 1 & System 2 as strategy parameters - FengkieJ (fengkiejunis@gmail.com) 5 | 6 | Turtle strategy is one of classic trend-following system that has clear defined trading rules. The system was taught by Richard Dennis 7 | and William Eckhardt in 1983. This version implements the system described by Curtis Faith, author of bestselling book Way of the Turtle. 8 | 9 | In Turtle system theory, a complete trading system must cover several aspects, i.e.: 10 | - Markets - What to buy or sell 11 | ƒ - Position Sizing - How much to buy or sell 12 | ƒ - Entries - When to buy or sell 13 | ƒ - Stops - When to get out of a losing position 14 | ƒ - Exits - When to get out of a winning position 15 | ƒ - Tactics - How to buy or sell 16 | 17 | This Python module attempts to implement the system on Jesse framework as described in the pdf by Curtis Faith & Perry J. Kaufman. 18 | 19 | Reference: - Faith, C. (2003). The Original Turtle Rules. Retrieved 14 June 2020, from http://www.tradingblox.com/originalturtles/originalturtlerules.htm 20 | - Kaufman, P. (2013). Trading systems and methods (5th ed., pp. 229-233). Hoboken, N.J.: Wiley. 21 | """ 22 | 23 | from jesse.strategies import Strategy 24 | from jesse.indicators import donchian, atr 25 | from jesse import utils 26 | 27 | class TurtleRules(Strategy): 28 | def __init__(self): 29 | super().__init__() 30 | 31 | self.current_pyramiding_levels = 0 32 | self.last_opened_price = 0 33 | self.last_was_profitable = False 34 | 35 | def before(self): 36 | self.vars["unit_risk_percent"] = 1 37 | self.vars["entry_dc_period"] = 20 38 | self.vars["exit_dc_period"] = 10 39 | self.vars["atr_period"] = 20 40 | self.vars["atr_multiplier"] = 2 41 | self.vars["maximum_pyramiding_levels"] = 4 42 | self.vars["pyramiding_threshold"] = 0.5 43 | self.vars["system_type"] = "S1" 44 | 45 | @property 46 | def entry_donchian(self): 47 | return donchian(self.candles, self.vars["entry_dc_period"]) 48 | 49 | @property 50 | def exit_donchian(self): 51 | return donchian(self.candles, self.vars["exit_dc_period"]) 52 | 53 | @property 54 | def atr(self): 55 | return atr(self.candles, self.vars["atr_period"]) 56 | 57 | def unit_qty(self, unit_risk_percent, dollars_per_point = 1): 58 | # In Original Turtle Rule book, the position sizing formula is defined as: 59 | # Unit = 1% of Account / (N × Dollars per Point) where N is ATR(20) 60 | 61 | return ((unit_risk_percent/100) * self.capital) / (self.atr * dollars_per_point) 62 | 63 | def entry_signal(self): 64 | # "The Turtles used two related system entries, each based on Donchian’s channel breakout system. 65 | # ... 66 | # System 1 – A shorter-term system based on a 20-day breakout (with a filter if previous trade was profitable do not enter the next) 67 | # System 2 – A simpler long-term system based on a 55-day breakout." (Faith, 2003) 68 | signal = None 69 | upperband = self.entry_donchian[0] 70 | lowerband = self.entry_donchian[2] 71 | 72 | if self.high >= upperband: 73 | signal = "entry_long" 74 | elif self.low <= lowerband: 75 | signal = "entry_short" 76 | 77 | return signal 78 | 79 | def exit_signal(self): 80 | # "The System 1 exit was a 10 day low for long positions and a 10 day high for short positions. 81 | # All the Units in the position would be exited if the price went against the position for a 10 day breakout. 82 | # The System 2 exit was a 20 day low for long positions and a 20 day high for short positions. 83 | # All the Units in the position would be exited if the price went against the position for a 20 day breakout." (Faith, 2003) 84 | signal = None 85 | upperband = self.exit_donchian[0] 86 | lowerband = self.exit_donchian[2] 87 | 88 | if self.high >= upperband: 89 | signal = "exit_short" 90 | elif self.low <= lowerband: 91 | signal = "exit_long" 92 | 93 | return signal 94 | 95 | def should_long(self) -> bool: 96 | return self.entry_signal() == "entry_long" 97 | 98 | def should_short(self) -> bool: 99 | return self.entry_signal() == "entry_short" 100 | 101 | def should_cancel(self) -> bool: 102 | pass 103 | 104 | def go_long(self): 105 | qty = self.unit_qty(self.vars["unit_risk_percent"]) 106 | sl = self.price - self.vars["atr_multiplier"] * self.atr 107 | 108 | self.buy = qty, self.price 109 | self.stop_loss = qty, sl 110 | # print(f"enter long {qty}") 111 | self.current_pyramiding_levels += 1 # Track the pyramiding level 112 | self.last_opened_price = self.price # Store this value to determine when to add next pyramiding 113 | 114 | def go_short(self): 115 | qty = self.unit_qty(self.vars["unit_risk_percent"]) 116 | sl = self.price + self.vars["atr_multiplier"] * self.atr 117 | 118 | self.sell = qty, self.price 119 | self.stop_loss = qty, sl 120 | # print(f"enter short {qty}") 121 | self.current_pyramiding_levels += 1 # Track the pyramiding level 122 | self.last_opened_price = self.price # Store this value to determine when to add next pyramiding 123 | 124 | def update_position(self): 125 | # Handle for pyramiding rules 126 | if self.current_pyramiding_levels < self.vars["maximum_pyramiding_levels"]: 127 | if self.is_long and self.price > self.last_opened_price + (self.vars["pyramiding_threshold"] * self.atr): 128 | qty = self.unit_qty(self.vars["unit_risk_percent"]) 129 | self.buy = qty, self.price 130 | # print(f"atr={self.atr}, last price={self.last_opened_price}, cur price={self.price}, action: increase long position {qty}") 131 | 132 | if self.is_short and self.price < self.last_opened_price - (self.vars["pyramiding_threshold"] * self.atr): 133 | qty = self.unit_qty(self.vars["unit_risk_percent"]) 134 | self.sell = qty, self.price 135 | # print(f"atr={self.atr}, last price={self.last_opened_price}, cur price={self.price}, action: increase short position {qty}") 136 | 137 | # "Trades are exited on the fi rst occurrence of 138 | # a. The stop-loss 139 | # b. An S1 or S2 reversal 140 | # c. A loss of 2% relative to the portfolio (where 2L is equal to 2% of the portfolio)" (Kaufman, 2013) 141 | if self.is_long and (self.entry_signal() == "entry_short" or self.exit_signal() == "exit_long") \ 142 | or self.is_short and (self.entry_signal() == "entry_long" or self.exit_signal() == "exit_short"): 143 | self.liquidate() 144 | self.current_pyramiding_levels = 0 145 | 146 | def on_increased_position(self, order): 147 | # "In order to keep total position risk at a minimum, if additional units were added, the stops for earlier units were raised by 1⁄2 N. 148 | # This generally meant that all the stops for the entire position would be placed at 2 N from the most recently added unit." (Faith, 2003) 149 | if self.is_long: 150 | self.stop_loss = abs(self.position.qty), self.price - self.vars["atr_multiplier"] * self.atr 151 | # print(f"atr={self.atr}, current position sl: {self.average_stop_loss}") 152 | if self.is_short: 153 | self.stop_loss = abs(self.position.qty), self.price + self.vars["atr_multiplier"] * self.atr 154 | # print(f"atr={self.atr}, current position sl: {self.average_stop_loss}") 155 | 156 | self.current_pyramiding_levels += 1 157 | self.last_opened_price = self.price 158 | # print(f"current pyramiding levels: {self.current_pyramiding_levels}") 159 | 160 | def on_stop_loss(self, order): 161 | # Reset tracked pyramiding levels 162 | self.current_pyramiding_levels = 0 163 | 164 | def on_take_profit(self, order): 165 | self.last_was_profitable = True 166 | 167 | # Reset tracked pyramiding levels 168 | self.current_pyramiding_levels = 0 169 | 170 | def filters(self): 171 | return [ 172 | self.S1_filter 173 | ] 174 | 175 | def S1_filter(self): 176 | if self.vars["system_type"] == "S1" and self.last_was_profitable: 177 | # print(f"prev was profitable, do not enter trade") 178 | self.last_was_profitable = False 179 | return False 180 | return True 181 | --------------------------------------------------------------------------------