We have added a built-in stop loss order support to the Trading Strategy execution engine.
What are stop loss orders?
In centralised exchanges, a stop loss order is an order type that closes your position if the position goes too much into red .
- You open ETH buy position at $1500 in the expectation it goes up
- You set the stop loss to $1400, or 6.3%
- Overnight there is a volatile market drop and ETH falls to $1000, because of some event, like forced cascading liquidations
- A stop loss trigger closes the position at $1400
- The actual executed position sell price might be somewhere below $1400, because usually the stop loss needs to do a forced sell in down spiraling market
- You lost ~7% instead of ~30% of what you would have lost without a stop loss
Why are stop loss orders important?
Cryptocurrency markets tend to be more volatile than traditional finance markets. Furthermore, cryptocurrency markets trade 24/7. It is very difficult to manually trade with alerts and such, so it is better to use automation to protect against losses.
How to implement stop loss orders on decentralised markets?
Decentralised exchanges, especially automated market makers, do not natively support stop loss orders, and many do not support even limit orders.
Much of this stems from the fact that in blockchains someone always needs to cover transaction fees, or more accurately the cost of a block space where the transaction lands in. Deferred transactions or scheduled transactions are still largely unsolved problems - EOS blockchain tried, Croncat is making progress. The transaction fees cannot be paid upfront as this would greatly complicate block production and pricing - someone could just reserve all the blockspace upfront.
Unlike centralised exchanges which may slow down or crash when executing stop losses and liquidations, block production may never slow down in a fully functional blockchain. In the case of there are too many transactions and blocks are full, the transactions pile up in a mempool and are then included in the next free block based on priority fees.
However, in a simple form, stop loss is usually just a market buy/market sell in the opposite direction, executed by the exchange on the behalf of the trader. Thus, you can execute stop loss trades yourself, assuming you have
- Automated your trading logic using a programmatically hot wallet or smart contract
- You have a trigger system that watches the price movement and executes stop loss buy/sell when the loss condition is met
How does Trading Strategy have stop loss orders?
Trading Strategy offers a few different components needed for automated and systematic trading
- Market data feeds
- Strategy development and backtesting framework
- Trade execution engine for DeFi markets
As the strategy execution needs high-quality and real-time data feeds to monitor and revalue the current positions for net asset value calculations, adding the stop loss functionality to the trade execution itself is quite straightforward.
Take profit orders
The Trading Strategy framework also supports profit orders, the opposite of stop loss, which will automatically close the position when a profit goal is reached. This ensures you can close the profit in the case you aim to trade against wicks.
Opening a position with a stop loss trigger
This function takes argument stop_loss_pct. This argument is the per cent value of the initial position value, under which the position will be market closed if the value falls below the %.
Because cryptocurrency market movements may be violent and the price of the asset change radically in a short period of time. To have a realistic reflection on the stop loss benefit, you need to use time-granular market data fees to simulate the stop loss.
- Your backtest uses daily (24h) OHLCV candles, or calls decide_trades() function for every 24h time tick
- Stop loss signal uses more granular 1h candle data and automatically processes any position closes without calling decide_trades()
This split between the main strategy function and stop loss trigger function speeds up the running of the backtest, as the trade execution engine calls the heavier full backtest functionality for every daily candle only, whereas the 1h candles are only used in the optimised stop loss test code path.
The backtesting stop loss data feed set up is done in the create_trading_universe() function of a strategy, where you can pass the stop loss market price feed granularity with
stop_loss_time_bucket parameter. Then the market data loader will also load and set up special data feeds for stop loss simulation.
def create_trading_universe( ts: datetime.datetime, client: Client, execution_context: ExecutionContext, candle_time_frame_override: Optional[TimeBucket]=None, ) -> TradingStrategyUniverse: # Load all datas we can get for our candle time bucket dataset = load_pair_data_for_single_exchange( client, execution_context, TimeBucket.d1, chain_id, exchange_slug, [trading_pair_ticker], stop_loss_time_bucket=TimeBucket.h1, ) # Filter down to the single pair we are interested in universe = TradingStrategyUniverse.create_single_pair_universe( dataset, chain_id, exchange_slug, trading_pair_ticker, trading_pair_ticker, ) return universe
The trade chart shows one asset price and when the trades for this asset happened.
The trade visualisation differentiates “sell” and “stop loss sell” with different markers to perceive easily when stop losses happened.
The trade timeline shows individual position entries and exits.
In this timeline table of all positions, we mark positions that were closed due to stop loss with “SL”.
Live trading support
The Trading Strategy execution engine needs automatic execution of orders in any case to rebalance the trading positions. Adding stop loss checks and trigger position closures are just special cases for this logic.
To have the maximum speed and market price accuracy, the trade execution engine directly connects to blockchain nodes to get the real-time market price from decentralised exchanges. This architecture bypasses any backend and database to ensure stop losses are likely to be triggered even if any other infrastructure is overloaded or down, as is usually the case during volatile cryptocurrency price movement.
However, to avoid any noise in the real-time price feed, like unexpected high/low prices caused by flashloan attacks, a time-weighted average oracle is used. You can customize this oracle time window preferred for your strategy, making compromise with the real-time latest price vs. unnoisy and clear price.
def time_weighted_average_price(events: List[PriceEntry]) -> Decimal: """Calculate TWAP price over all entries in the buffer. Calculates the price using :py:func:`statistics.mean`. Further reading: - https://blog.quantinsti.com/twap/ - https://analyzingalpha.com/twap """ prices = [e.price for e in events] return statistics.mean(prices)
Example strategies using stop loss
About Trading Strategy
Trading Strategy is an algorithmic trading protocol for decentralised markets, enabling automated trading directly on blockchains. Learn more about Trading Strategy in the introduction post.
Join Trading Strategy community Discord to discuss decentralised finance, technical analysis and algorithmic trading.