├── .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 |
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 |
--------------------------------------------------------------------------------