├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── strategy-request.md ├── .gitignore ├── README.md ├── config ├── ema_crossover.yaml ├── long_ema_crossover.yaml ├── macd.yaml └── supertrend.yaml ├── indiview.py ├── manual.py ├── run.py └── strategies ├── ema_crossover.py ├── long_ema_crossover.py ├── macd.py └── supertrend.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | 27 | **Additional context** 28 | Add any other context about the problem here. 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/strategy-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Strategy request 3 | about: Request a strategy to be added to this repository 4 | title: "[STRATEGY REQUEST]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the strategy you'd like to see added** 11 | A clear and concise description of the strategy, including entry and exits conditions, position sizing, indicators, etc. 12 | 13 | **Is your strategy request related to one posted online somewhere? Please provide any links.** 14 | A clear and concise description of where you found the strategy. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | candlestick.html 3 | price_data/ 4 | config/GLOBAL.yaml 5 | 6 | # Byte-compiled / optimized / DLL files 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | 11 | # C extensions 12 | *.so 13 | 14 | # Distribution / packaging 15 | .Python 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | downloads/ 20 | eggs/ 21 | .eggs/ 22 | lib/ 23 | lib64/ 24 | parts/ 25 | sdist/ 26 | var/ 27 | wheels/ 28 | pip-wheel-metadata/ 29 | share/python-wheels/ 30 | *.egg-info/ 31 | .installed.cfg 32 | *.egg 33 | MANIFEST 34 | 35 | # PyInstaller 36 | # Usually these files are written by a python script from a template 37 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 38 | *.manifest 39 | *.spec 40 | 41 | # Installer logs 42 | pip-log.txt 43 | pip-delete-this-directory.txt 44 | 45 | # Unit test / coverage reports 46 | htmlcov/ 47 | .tox/ 48 | .nox/ 49 | .coverage 50 | .coverage.* 51 | .cache 52 | nosetests.xml 53 | coverage.xml 54 | *.cover 55 | *.py,cover 56 | .hypothesis/ 57 | .pytest_cache/ 58 | 59 | # Translations 60 | *.mo 61 | *.pot 62 | 63 | # Django stuff: 64 | *.log 65 | local_settings.py 66 | db.sqlite3 67 | db.sqlite3-journal 68 | 69 | # Flask stuff: 70 | instance/ 71 | .webassets-cache 72 | 73 | # Scrapy stuff: 74 | .scrapy 75 | 76 | # Sphinx documentation 77 | docs/_build/ 78 | 79 | # PyBuilder 80 | target/ 81 | 82 | # Jupyter Notebook 83 | .ipynb_checkpoints 84 | 85 | # IPython 86 | profile_default/ 87 | ipython_config.py 88 | 89 | # pyenv 90 | .python-version 91 | 92 | # pipenv 93 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 94 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 95 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 96 | # install all needed dependencies. 97 | #Pipfile.lock 98 | 99 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 100 | __pypackages__/ 101 | 102 | # Celery stuff 103 | celerybeat-schedule 104 | celerybeat.pid 105 | 106 | # SageMath parsed files 107 | *.sage.py 108 | 109 | # Environments 110 | .env 111 | .venv 112 | env/ 113 | venv/ 114 | ENV/ 115 | env.bak/ 116 | venv.bak/ 117 | 118 | # Spyder project settings 119 | .spyderproject 120 | .spyproject 121 | 122 | # Rope project settings 123 | .ropeproject 124 | 125 | # mkdocs documentation 126 | /site 127 | 128 | # mypy 129 | .mypy_cache/ 130 | .dmypy.json 131 | dmypy.json 132 | 133 | # Pyre type checker 134 | .pyre/ 135 | *.html 136 | keys.yaml 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | AutoTrader Logo 4 | 5 |

6 | 7 |

Welcome to the AutoTrader Demo Repository

8 | 9 | This repository contains example strategies to help you get started with [AutoTrader](https://github.com/kieran-mackle/AutoTrader). Please note that the strategies here are for the latest release of AutoTrader. 10 | 11 | **Are you looking to trade crypto?** Then you may be more interested in my other project, [cryptobots](https://github.com/kieran-mackle/cryptobots). 12 | 13 | ## Getting Started 14 | ### Install AutoTrader 15 | To run the strategies in this repository, you must first have AutoTrader installed. The easiest way to do so is by using pip: 16 | ``` 17 | pip install autotrader 18 | ``` 19 | 20 | ### Download this Repository 21 | Clone this repository by your preffered method. If you are new to git, simply click the green 'code' button at the top right of this page and download the zip. 22 | Alternatively, click [here](https://github.com/kieran-mackle/autotrader-demo/archive/refs/heads/main.zip). You can also clone the repository on the command line using: 23 | 24 | ``` 25 | git clone https://github.com/kieran-mackle/autotrader-demo/ 26 | ``` 27 | 28 | ### Backtest a Strategy 29 | After cloning this repo, you are ready to begin backtesting any of the strategies. Simply run `runfile.py`, after specifying the name of the strategy's configuration file (located in the `config/` directory). For example, to run the MACD Crossover Trend Strategy, the runfile will look as so: 30 | 31 | ```py 32 | from autotrader.autotrader import AutoTrader # Import AutoTrader 33 | 34 | at = AutoTrader() # Create AutoTrader instance 35 | at.configure(show_plot=True, verbosity=1) # Configure the settings of AutoTrader 36 | at.add_strategy('macd') # Provide the name of the strategy configuration file 37 | at.backtest(start = '1/1/2020', # Configure the backtest 38 | end = '1/1/2021', 39 | initial_balance=1000, 40 | leverage = 30) 41 | at.run() # Run AutoTrader 42 | ``` 43 | 44 | ### Tutorials 45 | If you would like a detailed explanation of how to construct a strategy with AutoTrader, refer to the tutorials on the AutoTrader website, by clicking 46 | [here](https://kieran-mackle.github.io/AutoTrader/tutorials). 47 | 48 | ## Demo Strategies 49 | The following is a list of demo strategies provided in this repository. The configuration file names of each is provided. 50 | 51 | - `macd.yaml` MACD Crossover Trend Strategy (from the website [tutorials](https://kieran-mackle.github.io/AutoTrader/tutorials/strategy)) 52 | - `ema_crossover.yaml`/`long_ema_crossover.yaml` EMA Crossover Strategy (long only example plus long/short Forex example) 53 | - `supertrend.yaml` SuperTrend Trend Detector ([AutoScan demo](https://kieran-mackle.github.io/AutoTrader/2021/09/27/developing-scanner.html)) 54 | -------------------------------------------------------------------------------- /config/ema_crossover.yaml: -------------------------------------------------------------------------------- 1 | NAME: 'EMA Crossover' 2 | MODULE: 'ema_crossover' 3 | CLASS: 'EMAcrossOver' 4 | INTERVAL: '1h' 5 | PARAMETERS: 6 | slow_ema: 50 7 | fast_ema: 21 8 | RR: 2.0 9 | 10 | # Define pairs to monitor 11 | WATCHLIST: ['AUDJPY=X'] -------------------------------------------------------------------------------- /config/long_ema_crossover.yaml: -------------------------------------------------------------------------------- 1 | NAME: 'EMA Crossover' 2 | MODULE: 'long_ema_crossover' 3 | CLASS: 'LongEMAcrossOver' 4 | INTERVAL: '1h' 5 | PARAMETERS: 6 | slow_ema: 20 7 | fast_ema: 10 8 | RR: 1 9 | 10 | # Define pairs to monitor 11 | WATCHLIST: ['AAPL'] -------------------------------------------------------------------------------- /config/macd.yaml: -------------------------------------------------------------------------------- 1 | NAME: 'MACD Trend Strategy' 2 | MODULE: 'macd' 3 | CLASS: 'SimpleMACD' 4 | INTERVAL: '1h' 5 | PARAMETERS: 6 | ema_period: 200 7 | MACD_fast: 12 8 | MACD_slow: 26 9 | MACD_smoothing: 9 10 | 11 | # Exit level parameters 12 | RR: 1.5 13 | 14 | WATCHLIST: ['ETH-USD',] 15 | # 'EURJPY=X', 16 | # 'EURAUD=X', 17 | # 'AUDJPY=X'] 18 | -------------------------------------------------------------------------------- /config/supertrend.yaml: -------------------------------------------------------------------------------- 1 | NAME: 'SuperTrend Scanner' 2 | MODULE: 'supertrend' 3 | CLASS: 'SuperTrendScan' 4 | INTERVAL: '1d' 5 | PARAMETERS: 6 | ema_period: 200 7 | candle_tol: 10 8 | RR: 2.0 9 | WATCHLIST: ['EURUSD=X'] -------------------------------------------------------------------------------- /indiview.py: -------------------------------------------------------------------------------- 1 | # """This is an example of using AutoTraders plotting module.""" 2 | from datetime import datetime 3 | from autotrader import AutoTrader, AutoPlot, indicators 4 | 5 | 6 | # Instantiate AutoTrader and configure the exchange to trade on/immitate 7 | at = AutoTrader() 8 | at.configure(broker="ccxt:bybit") 9 | 10 | # Run AutoTrader to get the broker connection(s) 11 | broker = at.run() 12 | 13 | # Get candles 14 | instrument = "ETH/USDT" 15 | data = broker.get_candles( 16 | instrument=instrument, 17 | granularity="1d", 18 | start_time=datetime(2023, 1, 1), 19 | end_time=datetime(2024, 2, 1), 20 | ) 21 | 22 | # Construct indicators dictionary 23 | halftrend_df = indicators.halftrend(data) 24 | indicator_dict = {"HalfTrend": {"type": "HalfTrend", "data": halftrend_df}} 25 | 26 | # Instantiate AutoPlot and plot 27 | ap = AutoPlot(data) 28 | ap.plot(indicators=indicator_dict, instrument=instrument) 29 | -------------------------------------------------------------------------------- /manual.py: -------------------------------------------------------------------------------- 1 | """Example script to use AutoTrader for manual trading via API. 2 | 3 | It is recommeded that you run this in an interactive environment, such 4 | as IPython. Otherwise, you must copy and paste these commands in a 5 | Python terminal, rather than just running the script. 6 | 7 | This is a good way to play around and test things before automating them 8 | in a strategy. 9 | """ 10 | 11 | from autotrader import AutoTrader, Order 12 | 13 | 14 | # Instantiate AutoTrader and configure the exchange to trade on/immitate 15 | at = AutoTrader() 16 | at.configure(broker="ccxt:bybit") 17 | 18 | # If you want to simulate trading on the exchange (but use real data from it), 19 | # configure a virtual account: 20 | at.virtual_account_config(verbosity=1, exchange="ccxt:bybit", leverage=10) 21 | 22 | # Note that if you exclude the line above, you will connect to the real 23 | # exchange specified. 24 | 25 | # Run AutoTrader to get the broker connection(s) 26 | broker = at.run() 27 | 28 | # If you configured a virtual account above, the broker here will be an instance 29 | # of the Virtual broker. Otherwise, it will be a real exchange connection (eg. 30 | # bybit above), assuming you have provided valid keys. 31 | 32 | # Now you can interact with the broker 33 | book = broker.get_orderbook("BTC/USDT:USDT") 34 | print(book.midprice) 35 | 36 | # Create an order: 37 | # o = Order("MAGIC/USDT:USDT", direction=1, size=1) 38 | # broker.place_order(o) 39 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | from autotrader import AutoTrader 2 | 3 | # Create AutoTrader instance, configure it, and run backtest 4 | at = AutoTrader() 5 | at.configure(verbosity=1, show_plot=True, feed="yahoo") 6 | # at.add_strategy("long_ema_crossover") 7 | # at.add_strategy("ema_crossover") 8 | at.add_strategy("macd") 9 | at.backtest(start="1/6/2023", end="1/2/2024", localize_to_utc=True) 10 | at.virtual_account_config(initial_balance=1000, leverage=30) 11 | at.run() 12 | -------------------------------------------------------------------------------- /strategies/ema_crossover.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from finta import TA 3 | from autotrader import Order 4 | from datetime import datetime 5 | from autotrader.strategy import Strategy 6 | from autotrader.indicators import crossover 7 | from autotrader.brokers.broker import Broker 8 | 9 | 10 | class EMAcrossOver(Strategy): 11 | """EMA Crossover example strategy. 12 | 13 | Entry signals are on crosses of two EMA's, with a stop-loss 14 | set using the ATR. 15 | """ 16 | 17 | def __init__( 18 | self, parameters: dict, instrument: str, broker: Broker, *args, **kwargs 19 | ) -> None: 20 | """Define all indicators used in the strategy.""" 21 | self.name = "EMA Crossover Strategy" 22 | self.instrument = instrument 23 | self.parameters = parameters 24 | self.broker = broker 25 | 26 | def create_plotting_indicators(self, data: pd.DataFrame): 27 | # Construct indicators dict for plotting 28 | self.indicators = { 29 | "Fast EMA": { 30 | "type": "MA", 31 | "data": TA.EMA(data, self.parameters["fast_ema"]), 32 | }, 33 | "Slow EMA": { 34 | "type": "MA", 35 | "data": TA.EMA(data, self.parameters["slow_ema"]), 36 | }, 37 | } 38 | 39 | def generate_features(self, data: pd.DataFrame): 40 | """Calculates the indicators required to run the strategy.""" 41 | # EMA's 42 | slow_ema = TA.EMA(data, self.parameters["slow_ema"]) 43 | fast_ema = TA.EMA(data, self.parameters["fast_ema"]) 44 | 45 | crossovers = crossover(fast_ema, slow_ema) 46 | 47 | # ATR for stops 48 | atr = TA.ATR(data, 14) 49 | 50 | return crossovers, atr 51 | 52 | def generate_signal(self, dt: datetime): 53 | """Define strategy to determine entry signals.""" 54 | # Get OHLCV data 55 | data = self.broker.get_candles(self.instrument, granularity="1h", count=300) 56 | if len(data) < 300: 57 | # This was previously a check in AT 58 | return None 59 | 60 | # Calculate indicators 61 | crossovers, atr = self.generate_features(data) 62 | 63 | RR = self.parameters["RR"] 64 | if crossovers.iloc[-1] > 0: 65 | # Fast EMA has crossed above slow EMA, go long 66 | stop = data["Close"].iloc[-1] - 2 * atr.iloc[-1] 67 | take = data["Close"].iloc[-1] + RR * (data["Close"].iloc[-1] - stop) 68 | 69 | order = Order( 70 | instrument=self.instrument, 71 | direction=1, 72 | size=10, 73 | stop_loss=stop, 74 | take_profit=take, 75 | ) 76 | 77 | elif crossovers.iloc[-1] < 0: 78 | # Fast EMA has crossed below slow EMA, go short 79 | stop = data["Close"].iloc[-1] + 2 * atr.iloc[-1] 80 | take = data["Close"].iloc[-1] + RR * (data["Close"].iloc[-1] - stop) 81 | 82 | order = Order( 83 | instrument=self.instrument, 84 | direction=-1, 85 | size=10, 86 | stop_loss=stop, 87 | take_profit=take, 88 | ) 89 | 90 | else: 91 | # No signal 92 | order = None 93 | 94 | return order 95 | -------------------------------------------------------------------------------- /strategies/long_ema_crossover.py: -------------------------------------------------------------------------------- 1 | from finta import TA 2 | from autotrader import Order 3 | from pandas import DataFrame 4 | from datetime import datetime 5 | from autotrader.strategy import Strategy 6 | from autotrader.indicators import crossover 7 | from autotrader.brokers.broker import Broker 8 | 9 | 10 | class LongEMAcrossOver(Strategy): 11 | """EMA Crossover example strategy.""" 12 | 13 | def __init__( 14 | self, 15 | parameters: dict[str, any], 16 | instrument: str, 17 | broker: Broker, 18 | *args, 19 | **kwargs 20 | ) -> None: 21 | """Define all indicators used in the strategy.""" 22 | self.name = "Long EMA crossover" 23 | self.params = parameters 24 | self.instrument = instrument 25 | self.broker = broker 26 | 27 | def create_plotting_indicators(self, data: DataFrame): 28 | # Construct indicators dict for plotting 29 | slow_ema = TA.EMA(data, self.params["slow_ema"]) 30 | fast_ema = TA.EMA(data, self.params["fast_ema"]) 31 | self.indicators = { 32 | "Fast EMA": {"type": "MA", "data": fast_ema}, 33 | "Slow EMA": {"type": "MA", "data": slow_ema}, 34 | } 35 | 36 | def calculate_crossovers(self, data: DataFrame): 37 | """Calculates the indicators required to run the strategy.""" 38 | # EMA's 39 | slow_ema = TA.EMA(data, self.params["slow_ema"]) 40 | fast_ema = TA.EMA(data, self.params["fast_ema"]) 41 | crossovers = crossover(fast_ema, slow_ema) 42 | return crossovers 43 | 44 | def generate_signal(self, dt: datetime): 45 | """Define strategy to determine entry signals.""" 46 | orders = [] 47 | 48 | # Get data and generate crossovers 49 | data = self.broker.get_candles(self.instrument, granularity="1h", count=300) 50 | if len(data) < 300: 51 | # Not ready to trade yet 52 | return None 53 | crossovers = self.calculate_crossovers(data) 54 | 55 | # Get current position 56 | current_position = self.broker.get_positions(self.instrument) 57 | 58 | # Put entry strategy here 59 | if len(current_position) == 0: 60 | # Not currently in any position, okay to enter long 61 | if crossovers.iloc[-1] == 1: 62 | # Fast EMA has crossed above slow EMA, enter long 63 | order = Order( 64 | direction=1, 65 | size=1, 66 | ) 67 | orders.append(order) 68 | else: 69 | # Already in a position, only look for long exits 70 | if crossovers.iloc[-1] == -1: 71 | net_position = current_position[self.instrument].net_position 72 | order = Order(direction=-1, size=-net_position) 73 | orders.append(order) 74 | 75 | return orders 76 | -------------------------------------------------------------------------------- /strategies/macd.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from finta import TA 3 | from datetime import datetime 4 | from autotrader.strategy import Strategy 5 | import autotrader.indicators as indicators 6 | from autotrader.brokers.trading import Order 7 | from autotrader.brokers.broker import Broker 8 | 9 | 10 | class SimpleMACD(Strategy): 11 | """Simple MACD Strategy 12 | 13 | Rules 14 | ------ 15 | 1. Trade in direction of trend, as per 200EMA. 16 | 2. Entry signal on MACD cross below/above zero line. 17 | 3. Set stop loss at recent price swing. 18 | 4. Target 1.5 take profit. 19 | """ 20 | 21 | def __init__( 22 | self, parameters: dict, instrument: str, broker: Broker, *args, **kwargs 23 | ) -> None: 24 | """Define all indicators used in the strategy.""" 25 | self.name = "MACD Trend Strategy" 26 | self.params = parameters 27 | self.broker = broker 28 | self.instrument = instrument 29 | 30 | def create_plotting_indicators(self, data: pd.DataFrame): 31 | # Construct indicators dict for plotting 32 | ema, MACD, MACD_CO, MACD_CO_vals, swings = self.generate_features(data) 33 | self.indicators = { 34 | "MACD (12/26/9)": { 35 | "type": "MACD", 36 | "macd": MACD.MACD, 37 | "signal": MACD.SIGNAL, 38 | "histogram": MACD.MACD - MACD.SIGNAL, 39 | }, 40 | "EMA (200)": {"type": "MA", "data": ema}, 41 | } 42 | 43 | def generate_features(self, data: pd.DataFrame): 44 | # 200EMA 45 | ema = TA.EMA(data, self.params["ema_period"]) 46 | 47 | # MACD 48 | MACD = TA.MACD( 49 | data, 50 | self.params["MACD_fast"], 51 | self.params["MACD_slow"], 52 | self.params["MACD_smoothing"], 53 | ) 54 | MACD_CO = indicators.crossover(MACD.MACD, MACD.SIGNAL) 55 | MACD_CO_vals = indicators.cross_values(MACD.MACD, MACD.SIGNAL, MACD_CO) 56 | 57 | # Price swings 58 | swings = indicators.find_swings(data) 59 | 60 | return ema, MACD, MACD_CO, MACD_CO_vals, swings 61 | 62 | def generate_signal(self, dt: datetime): 63 | """Define strategy to determine entry signals.""" 64 | # Get OHLCV data 65 | data = self.broker.get_candles(self.instrument, granularity="1h", count=300) 66 | if len(data) < 300: 67 | # This was previously a check in AT 68 | return None 69 | 70 | # Generate indicators 71 | ema, MACD, MACD_CO, MACD_CO_vals, swings = self.generate_features(data) 72 | 73 | # Create orders 74 | if ( 75 | data["Close"].values[-1] > ema.iloc[-1] 76 | and MACD_CO.iloc[-1] == 1 77 | and MACD_CO_vals.iloc[-1] < 0 78 | ): 79 | exit_dict = self.generate_exit_levels(signal=1, data=data, swings=swings) 80 | new_order = Order( 81 | direction=1, 82 | size=1, 83 | stop_loss=exit_dict["stop_loss"], 84 | take_profit=exit_dict["take_profit"], 85 | ) 86 | 87 | elif ( 88 | data["Close"].values[-1] < ema.iloc[-1] 89 | and MACD_CO.iloc[-1] == -1 90 | and MACD_CO_vals.iloc[-1] > 0 91 | ): 92 | exit_dict = self.generate_exit_levels(signal=-1, data=data, swings=swings) 93 | new_order = Order( 94 | direction=-1, 95 | size=1, 96 | stop_loss=exit_dict["stop_loss"], 97 | take_profit=exit_dict["take_profit"], 98 | ) 99 | 100 | else: 101 | new_order = None 102 | 103 | return new_order 104 | 105 | def generate_exit_levels( 106 | self, signal: int, data: pd.DataFrame, swings: pd.DataFrame 107 | ): 108 | """Function to determine stop loss and take profit levels.""" 109 | stop_type = "limit" 110 | RR = self.params["RR"] 111 | 112 | if signal == 0: 113 | stop = None 114 | take = None 115 | else: 116 | if signal == 1: 117 | stop = swings["Lows"].iloc[-1] 118 | take = data["Close"].iloc[-1] + RR * (data["Close"].iloc[-1] - stop) 119 | else: 120 | stop = swings["Highs"].iloc[-1] 121 | take = data["Close"].iloc[-1] - RR * (stop - data["Close"].iloc[-1]) 122 | 123 | exit_dict = {"stop_loss": stop, "stop_type": stop_type, "take_profit": take} 124 | 125 | return exit_dict 126 | -------------------------------------------------------------------------------- /strategies/supertrend.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from finta import TA 3 | from autotrader import Order 4 | from datetime import datetime 5 | from autotrader.strategy import Strategy 6 | from autotrader.indicators import supertrend 7 | from autotrader.brokers.broker import Broker 8 | 9 | 10 | class SuperTrendScan(Strategy): 11 | """ 12 | Supertrend Signal Generator 13 | ----------------------------- 14 | The code below was developed for detecting trends using the SuperTrend 15 | indicator. You can read more about it at: 16 | https://kieran-mackle.github.io/AutoTrader/blog 17 | """ 18 | 19 | def __init__( 20 | self, parameters: dict, instrument: str, broker: Broker, *args, **kwargs 21 | ): 22 | """Initialise strategy indicators""" 23 | self.name = "SuperTrend" 24 | self.params = parameters 25 | self.broker = broker 26 | self.instrument = instrument 27 | 28 | def caculate_indicators(self, data: pd.DataFrame): 29 | ema200 = TA.EMA(data, 200) 30 | st_df = supertrend(data, period=12, ATR_multiplier=2) 31 | 32 | # ATR for stops 33 | atr = TA.ATR(data, 14) 34 | 35 | return ema200, st_df, atr 36 | 37 | def generate_signal(self, dt: datetime): 38 | """Generate long and short signals based on SuperTrend Indicator""" 39 | orders = [] 40 | 41 | # Get data and generate indicators 42 | data = self.broker.get_candles(self.instrument, granularity="1h", count=300) 43 | if len(data) < 200: 44 | # Not ready to trade yet 45 | return None 46 | ema200, st_df, atr = self.caculate_indicators(data) 47 | 48 | # Create orders 49 | RR = self.params["RR"] 50 | if ( 51 | data["Close"].iloc[-1] > ema200.iloc[-1] 52 | and st_df["trend"].iloc[-1] == 1 53 | and st_df["trend"].iloc[-2] == -1 54 | ): 55 | # Start of uptrend; buy 56 | stop = data["Close"].iloc[-1] - 2 * atr.iloc[-1] 57 | take = data["Close"].iloc[-1] + RR * (data["Close"].iloc[-1] - stop) 58 | order = Order( 59 | instrument=self.instrument, 60 | direction=1, 61 | size=10, 62 | stop_loss=stop, 63 | take_profit=take, 64 | ) 65 | orders.append(order) 66 | 67 | elif ( 68 | data["Close"].iloc[-1] < ema200.iloc[-1] 69 | and st_df["trend"].iloc[-1] == -1 70 | and st_df["trend"].iloc[-2] == 1 71 | ): 72 | # Start of downtrend; sell 73 | stop = data["Close"].iloc[-1] + 2 * atr.iloc[-1] 74 | take = data["Close"].iloc[-1] + RR * (data["Close"].iloc[-1] - stop) 75 | order = Order( 76 | instrument=self.instrument, 77 | direction=-1, 78 | size=10, 79 | stop_loss=stop, 80 | take_profit=take, 81 | ) 82 | orders.append(order) 83 | 84 | return orders 85 | --------------------------------------------------------------------------------