Long & Short direction mean reversion strategy with Bollinger Bands, Uniswap v3 on Polygon#
This strategy introduces the new shorting capabilities of the TradingStrategy framework. Shorting allows us to profit on downturns in the market, so now, if we trade correctly, it is possible to earn profit whether the market is going up or down.
Shorting#
For those of you who aren’t familiar with shorting, here is a quick explanation:
Short selling, often referred to as “shorting,” is an investment strategy where an investor borrows shares of a stock or other asset that they believe will decrease in value by a set future date. The investor sells these borrowed shares to buyers willing to pay the current market price. Later, the investor aims to buy back the shares at a lower price.
However, short selling comes with significant risks. If the stock price increases instead of decreasing, the investor will have to buy back the shares at a higher price, leading to a loss. This loss can be substantial, as theoretically, a stock’s price can rise indefinitely. That’s why is important to always use a stop loss with shorting.
Short selling is often used as a way to hedge against the downside risk of a long position in the same or similar stocks. It can also be used by those who believe the market or a particular stock is overvalued and due for a downturn.
Leverage#
Leverage refers to the use of various financial instruments or borrowed capital (like debt) to increase the potential return of an investment. It allows investors to increase their market exposure beyond what would be possible with their own capital alone. Basically, leverage amplifies investment power; however, leverage also amplifies losses if the investment does not perform as expected. Since the investor must repay the borrowed funds regardless of the investment’s success, losses can exceed the initial amount of capital. So again, remember your stop losses!
Set up#
Set up the parameters used in in this strategy backtest study.
Backtested blockchain, exchange and trading pair
Backtesting period
Strategy parameters
[1]:
import datetime
import pandas as pd
from tradingstrategy.chain import ChainId
from tradingstrategy.timebucket import TimeBucket
from tradingstrategy.lending import LendingProtocolType # NEW
from tradeexecutor.strategy.cycle import CycleDuration
from tradeexecutor.strategy.strategy_module import StrategyType, TradeRouting, ReserveCurrency
# Tell what trade execution engine version this strategy needs to use
# NOTE: this setting has currently no effect
TRADING_STRATEGY_TYPE_ENGINE_VERSION = "0.1"
# What kind of strategy we are running.
# This tells we are going to use
# NOTE: this setting has currently no effect
TRADING_STRATEGY_TYPE = StrategyType.managed_positions
# How our trades are routed.
TRADE_ROUTING = TradeRouting.uniswap_v3_usdc
# How often the strategy performs the decide_trades cycle.
# We do it for every 4h.
TRADING_STRATEGY_CYCLE = CycleDuration.cycle_4h
# Strategy keeps its cash in USDC
RESERVE_CURRENCY = ReserveCurrency.usdc
# Time bucket for our candles
CANDLE_TIME_BUCKET = TimeBucket.h4
# Which trading pair we are backtesting on
TRADING_PAIR = (ChainId.polygon, "uniswap-v3", "WETH", "USDC", 0.0005)
# Which lending reserves we are using for supplying/borrowing assets
# NEW
LENDING_RESERVES = [
(ChainId.polygon, LendingProtocolType.aave_v3, "WETH"),
(ChainId.polygon, LendingProtocolType.aave_v3, "USDC"),
]
# How much % of the cash to put on a single trade
POSITION_SIZE = 0.8
# Start with this amount of USD
INITIAL_DEPOSIT = 50_000
# Candle time granularity we use to trigger stop loss checks
STOP_LOSS_TIME_BUCKET = TimeBucket.h1
#
# Strategy thinking specific parameter
#
# How many candles we load in the decide_trades() function for calculating indicators
LOOKBACK_WINDOW = 50
# Moving average
# How many candles to smooth out for Bollinger band's middle line
EMA_CANDLE_COUNT = 20
# How many candles we use to calculate the Relative Strength Indicator
RSI_LENGTH = 14
# RSI must be above this value to open a new Long position
RSI_THRESHOLD = 35
# RSI must be above this value to open a new Short position
RSI_THRESHOLD_SHORT = 70
# Backtest time range: start date
START_AT = datetime.datetime(2022, 7, 20)
# Backtest time range: end date
END_AT = datetime.datetime(2023, 9, 27)
# A fixed Stop Loss for Long positions relative to the mid price during the time when the position is opened
# If the price candle closes below this level in a Long position, stop loss gets triggered
STOP_LOSS_PCT = 0.97
# A fixed Stop Loss for Short positions relative to the mid price during the time when the position is opened
# If the price candle closes above this level in a Short position, stop loss gets triggered
STOP_LOSS_SHORT_PCT = 0.97
# Take profit percentage for Long positions
TAKE_PROFIT_PCT = 1.035
# Take profit percentage for Short positions
TAKE_PROFIT_SHORT_PCT = 1.035
# Leverage ratio for short positions. We use leverage that is hogher than 1, because lending pools require more collateral than the amount that you want to borrow
LEVERAGE = 2
Strategy logic and trade decisions#
In this example, we calculate two exponential moving averages (EMAs) and make decisions based on those:
enter Long positions when price candle closes below the lower BB line
enter Short position when price candle closes above the higher BB line
use RSI value as threshold and additional confluence when deciding trade entries
4 hour price candle time frame for price action analysis and trade entries
use a fixed % based stop loss with 1 hour price candle time frame
enable Short positions by using Aave v3 lending pools for borrowing the token that you want to short sell
Indicators#
Note how we also make use of detached and overlayed technical indicators, so that the price chart is not overcrowded
[2]:
from typing import List, Dict
from pandas_ta import bbands
from pandas_ta.overlap import ema
from pandas_ta.momentum import rsi
from tradingstrategy.universe import Universe
from tradeexecutor.strategy.trading_strategy_universe import TradingStrategyUniverse
from tradeexecutor.state.visualisation import PlotKind
from tradeexecutor.state.trade import TradeExecution
from tradeexecutor.strategy.pricing_model import PricingModel
from tradeexecutor.strategy.pandas_trader.position_manager import PositionManager
from tradeexecutor.state.state import State
from tradeexecutor.strategy.pandas_trader.position_manager import PositionManager
def decide_trades(
timestamp: pd.Timestamp,
strategy_universe: TradingStrategyUniverse,
state: State,
pricing_model: PricingModel,
cycle_debug_data: Dict) -> List[TradeExecution]:
universe = strategy_universe.universe
# We have only a single trading pair for this strategy.
pair = universe.pairs.get_single()
# How much cash we have in a hand
cash = state.portfolio.get_current_cash()
# Get OHLCV candles for our trading pair as Pandas Dataframe.
# We could have candles for multiple trading pairs in a different strategy,
# but this strategy only operates on single pair candle.
# We also limit our sample size to N latest candles to speed up calculations.
candles: pd.DataFrame = universe.candles.get_single_pair_data(timestamp, sample_count=LOOKBACK_WINDOW)
# We have data for open, high, close, etc.
# We only operate using candle close values in this strategy.
close_prices = candles["close"]
# Calculate exponential moving for candle close
# https://tradingstrategy.ai/docs/programming/api/technical-analysis/overlap/help/pandas_ta.overlap.ema.html#ema
moving_average = ema(close_prices, length=EMA_CANDLE_COUNT)
# Calculate RSI for candle close
# https://tradingstrategy.ai/docs/programming/api/technical-analysis/momentum/help/pandas_ta.momentum.rsi.html#rsi
current_rsi = rsi(close_prices, length=RSI_LENGTH)[-1]
trades = []
if moving_average is None:
# Cannot calculate EMA, in case there are not enough data points in backtesting buffer yet.
return trades
# Price at the most recent candle close
price_close = close_prices.iloc[-1]
# Create a position manager helper class that allows us easily to create
# opening/closing trades for different positions
position_manager = PositionManager(timestamp, strategy_universe, state, pricing_model)
# Calculate wider Bollinger Bands using the typical 20-day MA and 2 standard deviations
bollinger_bands_wide = bbands(close_prices, length=20, std=2)
wide_bb_upper = bollinger_bands_wide["BBU_20_2.0"]
wide_bb_lower = bollinger_bands_wide["BBL_20_2.0"]
### LONGING
if not position_manager.is_any_long_position_open():
# We open long if the latest candle close is under the lower BB line, the previous candle close was above the lower BB line, and RSI is above the RSI threshold value
if price_close < wide_bb_lower.iloc[-1] and candles["open"].iloc[-1] > wide_bb_lower.iloc[-1] and close_prices.iloc[-2] > wide_bb_lower.iloc[-2] and current_rsi > RSI_THRESHOLD:
amount = cash * POSITION_SIZE
new_trades = position_manager.open_1x_long(pair, amount, stop_loss_pct=STOP_LOSS_PCT, take_profit_pct=TAKE_PROFIT_PCT)
trades.extend(new_trades)
else:
# LONGING: We close the position when the price closes above the moving average line.
if price_close > moving_average.iloc[-1] and close_prices.iloc[-2] < moving_average.iloc[-2]:
current_position = position_manager.get_current_long_position()
new_trades = position_manager.close_position(current_position)
trades.extend(new_trades)
### SHORTING
if not position_manager.is_any_short_position_open():
# We open Short position if the latest candle close is above the higher BB line, the previous candle close was below the higher BB line, and RSI is below the RSI threshold value
if price_close > wide_bb_upper.iloc[-1] and close_prices.iloc[-2] < wide_bb_upper.iloc[-2] and current_rsi < RSI_THRESHOLD_SHORT:
amount = cash * POSITION_SIZE
new_trades = position_manager.open_short(pair, amount, leverage=LEVERAGE, stop_loss_pct=STOP_LOSS_SHORT_PCT, take_profit_pct=TAKE_PROFIT_SHORT_PCT)
trades.extend(new_trades)
else:
# We close the position when the price closes below the moving average line.
if price_close < moving_average.iloc[-1] and close_prices.iloc[-2] > moving_average.iloc[-2]:
current_position = position_manager.get_current_short_position()
new_trades = position_manager.close_position(current_position)
trades.extend(new_trades)
# Visualise our technical indicators
visualisation = state.visualisation
visualisation.plot_indicator(timestamp, "Wide BB upper", PlotKind.technical_indicator_on_price, wide_bb_upper.iloc[-1], colour="red")
visualisation.plot_indicator(timestamp, "Wide BB lower", PlotKind.technical_indicator_on_price, wide_bb_lower.iloc[-1], colour="red")
visualisation.plot_indicator(timestamp, "EMA", PlotKind.technical_indicator_on_price, moving_average.iloc[-1], colour="black")
visualisation.plot_indicator(timestamp, "RSI", PlotKind.technical_indicator_detached, current_rsi)
return trades
Defining the trading universe#
We create a trading universe with a single blockchain, single exchange and a single trading pair.
Trading Strategy framework supports complex strategies, spanning thousands of pairs and lending pools, but we are not interested in this example.
[3]:
import datetime
from tradingstrategy.client import Client
from tradeexecutor.strategy.trading_strategy_universe import TradingStrategyUniverse, load_partial_data
from tradeexecutor.strategy.execution_context import ExecutionContext
from tradeexecutor.strategy.universe_model import UniverseOptions
def create_single_pair_trading_universe(
ts: datetime.datetime,
client: Client,
execution_context: ExecutionContext,
universe_options: UniverseOptions,
) -> TradingStrategyUniverse:
dataset = load_partial_data(
client,
execution_context=execution_context,
time_bucket=CANDLE_TIME_BUCKET,
pairs=[TRADING_PAIR],
universe_options=universe_options,
start_at=universe_options.start_at,
end_at=universe_options.end_at,
lending_reserves=LENDING_RESERVES, # NEW
stop_loss_time_bucket=STOP_LOSS_TIME_BUCKET,
)
# Filter down to the single pair we are interested in
strategy_universe = TradingStrategyUniverse.create_single_pair_universe(dataset)
return strategy_universe
Set up the market data client#
The Trading Strategy market data client is the Python library responsible for managing the data feeds needed to run the backtest.None
We set up the market data client with an API key.
If you do not have an API key yet, you can register one.
[4]:
from tradingstrategy.client import Client
client = Client.create_jupyter_client()
Started Trading Strategy in Jupyter notebook environment, configuration is stored in /home/alex/.tradingstrategy
Load data#
[5]:
from datetime import timedelta
from tradeexecutor.strategy.execution_context import ExecutionMode
from tradeexecutor.strategy.universe_model import UniverseOptions
universe = create_single_pair_trading_universe(
END_AT,
client,
ExecutionContext(mode=ExecutionMode.data_preload),
UniverseOptions(
# NOTE: quick hack to get enough data for look back period
start_at=START_AT - timedelta(days=50),
end_at=END_AT,
)
)
print(f"We loaded {universe.universe.candles.get_candle_count():,} candles.")
We loaded 2,681 candles.
Run backtest#
Run backtest using giving trading universe and strategy function.
Running the backtest outputs
state
object that contains all the information on the backtesting position and trades.The trade execution engine will download the necessary datasets to run the backtest. The datasets may be large, several gigabytes.
[6]:
from tradeexecutor.backtest.backtest_runner import run_backtest_inline
state, universe, debug_dump = run_backtest_inline(
name="BB short example",
start_at=START_AT,
end_at=END_AT,
client=client,
cycle_duration=TRADING_STRATEGY_CYCLE,
decide_trades=decide_trades,
universe=universe,
initial_deposit=INITIAL_DEPOSIT,
reserve_currency=RESERVE_CURRENCY,
trade_routing=TRADE_ROUTING,
engine_version="0.3",
)
trade_count = len(list(state.portfolio.get_all_trades()))
print(f"Backtesting completed, backtested strategy made {trade_count} trades")
Backtesting completed, backtested strategy made 136 trades
Examine backtest results#
Examine state
that contains all actions the trade executor took.
We plot out a chart that shows - The price action - When the strategy made buys or sells
[7]:
print(f"Positions taken: {len(list(state.portfolio.get_all_positions()))}")
print(f"Trades made: {len(list(state.portfolio.get_all_trades()))}")
Positions taken: 68
Trades made: 136
[8]:
from tradeexecutor.visual.single_pair import visualise_single_pair, visualise_single_pair_positions_with_duration_and_slippage
from tradingstrategy.charting.candle_chart import VolumeBarMode
figure = visualise_single_pair(
state,
universe.universe.candles,
start_at=START_AT,
end_at=END_AT,
volume_bar_mode=VolumeBarMode.separate,
volume_axis_name="Volume (USD)",
height = 1200,
)
figure.show()
[9]:
from tradeexecutor.backtest.notebook import setup_charting_and_output
setup_charting_and_output()
# Needed to improve the resolution of matplotlib chart used here
%config InlineBackend.figure_format = 'svg'
from tradeexecutor.visual.equity_curve import calculate_equity_curve, calculate_returns
from tradeexecutor.visual.equity_curve import visualise_equity_curve
curve = calculate_equity_curve(state)
returns = calculate_returns(curve)
visualise_equity_curve(returns)
/tmp/ipykernel_61373/746198727.py:1: DeprecationWarning:
This module is deprecated. Import tradeexecutor.utils.notebook instead
[9]:
Benchmarking the strategy performance#
Here we benchmark the strategy performance against some baseline scenarios.
Buy and hold US dollar
Buy and hold the underlying trading pair base asset
[10]:
from tradeexecutor.visual.benchmark import visualise_benchmark
traded_pair = universe.universe.pairs.get_single()
fig = visualise_benchmark(
"Example long & short mean reversion strategy",
portfolio_statistics=state.stats.portfolio,
all_cash=state.portfolio.get_initial_deposit(),
buy_and_hold_asset_name=traded_pair.base_token_symbol,
buy_and_hold_price_series=universe.universe.candles.get_single_pair_data()["close"],
start_at=START_AT,
end_at=END_AT,
height=1200
)
fig.show()
Analysing the strategy success#
Here we calculate statistics on how well the strategy performed.
Won/lost trades
Timeline of taken positions with color coding of trade performance
[11]:
from tradeexecutor.analysis.trade_analyser import build_trade_analysis
analysis = build_trade_analysis(state.portfolio)
Strategy summary#
Overview of strategy performance
[12]:
from IPython.core.display_functions import display
summary = analysis.calculate_summary_statistics(state=state, time_bucket = CANDLE_TIME_BUCKET)
# with pd.option_context("display.max_row", None):
# display(summary.to_dataframe())
summary.display()
Returns | |
---|---|
Annualised return % | 41.62% |
Lifetime return % | 48.43% |
Realised PnL | $27,864.04 |
Unrealised PnL | $0.00 |
Trade period | 424 days 16 hours |
Holdings | |
---|---|
Total assets | $74,213.29 |
Cash left | $74,213.29 |
Open position value | $0.00 |
Open positions | 0 |
Winning | Losing | Total | |
---|---|---|---|
Closed Positions | |||
Number of positions | 49 | 19 | 68 |
% of total | 72.06% | 27.94% | 100.00% |
Average PnL % | 2.05% | -3.29% | 0.56% |
Median PnL % | 1.74% | -3.36% | 1.35% |
Biggest PnL % | 4.88% | -5.44% | - |
Average duration | 1 days 1 hours | 1 days 10 hours | 1 days 4 hours |
Max consecutive streak | 13 | 4 | - |
Max runup / drawdown | 78.75% | -16.56% | - |
Stop losses | Take profits | |
---|---|---|
Position Exits | ||
Triggered exits | 17 | 8 |
Percent winning | 0.00% | 100.00% |
Percent losing | 100.00% | 0.00% |
Percent of total | 25.00% | 11.76% |
Risk Analysis | |
---|---|
Biggest realized risk | 2.44% |
Average realized risk | -3.98% |
Max pullback of capital | -19.36% |
Sharpe Ratio | 1.33 |
Sortino Ratio | 2.03 |
Profit Factor | 1.25 |
[13]:
from tradeexecutor.analysis.multipair import analyse_multipair
from tradeexecutor.analysis.multipair import format_multipair_summary
multipair_summary = analyse_multipair(state)
display(format_multipair_summary(multipair_summary))
Trading pair | Positions | Trades | Total PnL USD | Best | Worst | Avg | Median | Volume | Wins | Losses | Take profits | Stop losses | Trailing stop losses | Volatility | Total return % | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | WETH-USDC spot | 31 | 62 | 4,677.21 | 4.71% | -5.44% | 0.29% | 1.19% | 3,112,457.48 | 23 | 23 | 1 | 8 | 0 | 2.73% | 9.03% |
0 | WETH-USDC short | 37 | 74 | 23,186.84 | 4.30% | -4.89% | -0.79% | -1.41% | 7,298,158.03 | 26 | 26 | 7 | 9 | 0 | 2.63% | -29.38% |
[14]:
from tradeexecutor.visual.equity_curve import visualise_returns_over_time
visualise_returns_over_time(returns)
[14]:
[15]:
from IPython.core.display_functions import display
summary = analysis.calculate_all_summary_stats_by_side(state=state, time_bucket=CANDLE_TIME_BUCKET)
with pd.option_context("display.max_row", None):
display(summary)
All | Long | Short | |
---|---|---|---|
Trading period length | 424 days 16 hours | - | - |
Return % | 48.43% | 9.38% | 34.12% |
Annualised return % | 41.62% | 8.06% | 29.32% |
Cash at start | $50,000.00 | - | - |
Value at end | $74,213.29 | - | - |
Trade volume | $10,410,615.50 | $3,112,457.48 | $7,298,158.03 |
Position win percent | 72.06% | 74.19% | 70.27% |
Total positions | 68 | 31 | 37 |
Won positions | 49 | 23 | 26 |
Lost positions | 19 | 8 | 11 |
Stop losses triggered | 17 | 8 | 9 |
Stop loss % of all | 25.00% | 25.81% | 24.32% |
Stop loss % of lost | 89.47% | 100.00% | 81.82% |
Winning stop losses | 0 | 0 | 0 |
Winning stop losses percent | 0.00% | 0.00% | 0.00% |
Losing stop losses | 17 | 8 | 9 |
Losing stop losses percent | 100.00% | 100.00% | 100.00% |
Take profits triggered | 8 | 1 | 7 |
Take profit % of all | 11.76% | 3.23% | 18.92% |
Take profit % of won | 16.33% | 4.35% | 26.92% |
Zero profit positions | 0 | 0 | 0 |
Positions open at the end | 0 | 0 | 0 |
Realised profit and loss | $27,864.04 | $4,677.21 | $23,186.84 |
Unrealised profit and loss | $0.00 | $0.00 | $0.00 |
Portfolio unrealised value | $0.00 | $0.00 | $0.00 |
Extra returns on lending pool interest | $0.00 | $0.00 | $0.00 |
Cash left at the end | $74,213.29 | - | - |
Average winning position profit % | 2.05% | 1.79% | 2.29% |
Average losing position loss % | -3.29% | -4.02% | -2.76% |
Biggest winning position % | 4.88% | 4.71% | 4.88% |
Biggest losing position % | -5.44% | -5.44% | -4.31% |
Average duration of winning positions | 1 days 1 hours | 1 days 2 hours | 1 days 1 hours |
Average duration of losing positions | 1 days 10 hours | 1 days 7 hours | 1 days 12 hours |
Average bars of winning positions | 6 bars | 6 bars | 6 bars |
Average bars of losing positions | 8 bars | 7 bars | 9 bars |
LP fees paid | $5,206.61 | $1,556.62 | $3,649.99 |
LP fees paid % of volume | 0.05% | 0.05% | 0.05% |
Average position | 0.56% | 0.29% | 0.79% |
Median position | 1.35% | 1.19% | 1.40% |
Most consecutive wins | 13 | 10 | 5 |
Most consecutive losses | 4 | 3 | 2 |
Biggest realised risk | -7.19% | -4.35% | -7.19% |
Avg realised risk | -3.98% | -3.22% | -4.54% |
Max pullback of total capital | -19.36% | - | - |
Max loss risk at opening of position | 2.44% | 2.44% | -4.88% |
Max drawdown | -16.56% | - | - |
[16]:
from tradeexecutor.visual.equity_curve import calculate_equity_curve, calculate_returns
from tradeexecutor.analysis.advanced_metrics import visualise_advanced_metrics, AdvancedMetricsMode
equity = calculate_equity_curve(state)
returns = calculate_returns(equity)
metrics = visualise_advanced_metrics(returns, mode=AdvancedMetricsMode.full)
with pd.option_context("display.max_row", None):
display(metrics)
Strategy | |
---|---|
Start Period | 2022-07-20 |
End Period | 2023-09-26 |
Risk-Free Rate | 0.0% |
Time in Market | 22.0% |
Cumulative Return | 48.43% |
CAGR﹪ | 39.5% |
Sharpe | 0.55 |
Prob. Sharpe Ratio | 93.46% |
Smart Sharpe | 0.51 |
Sortino | 0.86 |
Smart Sortino | 0.8 |
Sortino/√2 | 0.61 |
Smart Sortino/√2 | 0.57 |
Omega | 1.25 |
Max Drawdown | -16.56% |
Longest DD Days | 148 |
Volatility (ann.) | 11.26% |
Calmar | 2.39 |
Skew | 2.76 |
Kurtosis | 72.57 |
Expected Daily | 0.02% |
Expected Monthly | 2.67% |
Expected Yearly | 21.83% |
Kelly Criterion | 9.72% |
Risk of Ruin | 0.0% |
Daily Value-at-Risk | -0.95% |
Expected Shortfall (cVaR) | -0.95% |
Max Consecutive Wins | 8 |
Max Consecutive Losses | 6 |
Gain/Pain Ratio | 0.42 |
Gain/Pain (1M) | 2.42 |
Payoff Ratio | 1.29 |
Profit Factor | 1.25 |
Common Sense Ratio | 1.92 |
CPC Index | 0.79 |
Tail Ratio | 1.54 |
Outlier Win Ratio | 18.49 |
Outlier Loss Ratio | 2.78 |
MTD | 3.5% |
3M | 2.23% |
6M | 0.58% |
YTD | 14.68% |
1Y | 53.54% |
3Y (ann.) | 39.5% |
5Y (ann.) | 39.5% |
10Y (ann.) | 39.5% |
All-time (ann.) | 39.5% |
Best Day | 10.52% |
Worst Day | -5.07% |
Best Month | 14.16% |
Worst Month | -8.23% |
Best Year | 29.42% |
Worst Year | 14.68% |
Avg. Drawdown | -2.25% |
Avg. Drawdown Days | 9 |
Recovery Factor | 2.92 |
Ulcer Index | 0.07 |
Serenity Index | 0.35 |
Avg. Up Month | 6.2% |
Avg. Down Month | -3.89% |
Win Days | 49.09% |
Win Month | 66.67% |
Win Quarter | 60.0% |
Win Year | 100.0% |
Position and trade timeline#
Display all positions and how much profit they made.
[17]:
from tradeexecutor.analysis.trade_analyser import expand_timeline
timeline = analysis.create_timeline()
expanded_timeline, apply_styles = expand_timeline(
universe.universe.exchanges,
universe.universe.pairs,
timeline)
# Do not truncate the row output
with pd.option_context("display.max_row", None):
display(apply_styles(expanded_timeline))
Remarks | Type | Opened at | Duration | Exchange | Base asset | Quote asset | Position max value | PnL USD | PnL % | Open mid price USD | Close mid price USD | Trade count | LP fees |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
TP | Short | 2022-07-22 | 4 hours | Uniswap v3 | WETH | USDC | $80,000.00 | $3,113.63 | 4.05% | $1,638.329540 | $1,574.565143 | 2 | $78.46 |
SL | Long | 2022-07-26 | 13 hours | Uniswap v3 | WETH | USDC | $42,428.13 | $-1,422.34 | -3.35% | $1,438.475258 | $1,390.252463 | 2 | $41.73 |
SL | Long | 2022-08-01 | 14 hours | Uniswap v3 | WETH | USDC | $41,290.26 | $-1,599.10 | -3.87% | $1,669.419998 | $1,604.766374 | 2 | $40.50 |
SL | Short | 2022-08-05 | 2 days 21 hours | Uniswap v3 | WETH | USDC | $80,021.97 | $-2,801.92 | -3.39% | $1,712.877877 | $1,772.823024 | 2 | $81.45 |
SL | Short | 2022-08-10 | 10 hours | Uniswap v3 | WETH | USDC | $75,408.94 | $-2,764.20 | -3.54% | $1,838.539276 | $1,905.929060 | 2 | $76.81 |
Short | 2022-08-24 | 4 hours | Uniswap v3 | WETH | USDC | $70,863.37 | $2,120.92 | 3.08% | $1,663.753808 | $1,613.939201 | 2 | $69.82 | |
Short | 2022-08-24 | 1 days 16 hours | Uniswap v3 | WETH | USDC | $74,145.08 | $1,079.69 | 1.47% | $1,683.333064 | $1,658.604414 | 2 | $73.62 | |
SL | Long | 2022-08-26 | 8 hours | Uniswap v3 | WETH | USDC | $37,877.25 | $-2,059.15 | -5.44% | $1,594.369818 | $1,507.693959 | 2 | $36.86 |
TP | Short | 2022-08-30 | 7 hours | Uniswap v3 | WETH | USDC | $72,459.86 | $2,945.05 | 4.24% | $1,586.259556 | $1,521.780208 | 2 | $71.00 |
TP | Short | 2022-09-02 | 3 hours | Uniswap v3 | WETH | USDC | $77,058.29 | $2,813.77 | 3.79% | $1,636.692111 | $1,576.928645 | 2 | $75.67 |
SL | Short | 2022-09-06 | 7 hours | Uniswap v3 | WETH | USDC | $81,439.25 | $-2,482.09 | -2.96% | $1,616.200185 | $1,665.418311 | 2 | $82.70 |
TP | Short | 2022-09-27 | 13 hours | Uniswap v3 | WETH | USDC | $77,335.69 | $2,979.28 | 4.01% | $1,385.256802 | $1,331.874707 | 2 | $75.87 |
Long | 2022-10-01 | 1 days 20 hours | Uniswap v3 | WETH | USDC | $40,990.51 | $156.05 | 0.38% | $1,312.965434 | $1,317.963878 | 2 | $41.08 | |
Short | 2022-10-04 | 1 days 8 hours | Uniswap v3 | WETH | USDC | $82,230.71 | $1,208.06 | 1.48% | $1,349.166317 | $1,329.346674 | 2 | $81.65 | |
SL | Long | 2022-10-07 | 3 days 5 hours | Uniswap v3 | WETH | USDC | $42,016.41 | $-1,635.30 | -3.89% | $1,328.815135 | $1,277.097090 | 2 | $41.21 |
Short | 2022-10-14 | 16 hours | Uniswap v3 | WETH | USDC | $81,416.35 | $1,906.86 | 2.39% | $1,328.549345 | $1,297.434449 | 2 | $80.48 | |
Short | 2022-10-17 | 1 days | Uniswap v3 | WETH | USDC | $84,338.44 | $1,397.10 | 1.68% | $1,329.080844 | $1,307.070592 | 2 | $83.66 | |
SL | Short | 2022-10-22 | 1 days 7 hours | Uniswap v3 | WETH | USDC | $86,439.84 | $-3,149.85 | -3.52% | $1,316.251723 | $1,364.224730 | 2 | $88.04 |
Long | 2022-11-02 | 1 days 12 hours | Uniswap v3 | WETH | USDC | $40,629.73 | $1,270.80 | 3.13% | $1,538.303752 | $1,586.418182 | 2 | $41.28 | |
Short | 2022-11-04 | 2 days 4 hours | Uniswap v3 | WETH | USDC | $83,292.75 | $801.44 | 0.96% | $1,624.463446 | $1,608.783108 | 2 | $82.92 | |
Long | 2022-11-07 | 12 hours | Uniswap v3 | WETH | USDC | $42,221.13 | $1,324.93 | 3.14% | $1,551.279275 | $1,599.959529 | 2 | $42.89 | |
Long | 2022-11-16 | 3 days 8 hours | Uniswap v3 | WETH | USDC | $43,281.07 | $838.98 | 1.94% | $1,193.026145 | $1,216.152332 | 2 | $43.71 | |
Short | 2022-11-24 | 3 days 20 hours | Uniswap v3 | WETH | USDC | $87,904.51 | $597.57 | 0.68% | $1,199.605477 | $1,191.357101 | 2 | $87.63 | |
Long | 2022-11-28 | 1 days 4 hours | Uniswap v3 | WETH | USDC | $44,360.14 | $1,236.77 | 2.79% | $1,172.213613 | $1,204.895105 | 2 | $44.99 | |
Long | 2022-12-04 | 20 hours | Uniswap v3 | WETH | USDC | $45,349.55 | $1,310.99 | 2.89% | $1,242.084534 | $1,277.991326 | 2 | $46.02 | |
Long | 2022-12-07 | 1 days 8 hours | Uniswap v3 | WETH | USDC | $46,398.34 | $899.41 | 1.94% | $1,225.797563 | $1,249.559006 | 2 | $46.86 | |
Short | 2022-12-08 | 1 days 4 hours | Uniswap v3 | WETH | USDC | $94,235.74 | $1,614.84 | 1.74% | $1,284.011721 | $1,261.990452 | 2 | $93.45 | |
Long | 2022-12-12 | 20 hours | Uniswap v3 | WETH | USDC | $48,334.91 | $1,169.06 | 2.42% | $1,244.446619 | $1,274.545575 | 2 | $48.93 | |
Short | 2022-12-20 | 2 days 8 hours | Uniswap v3 | WETH | USDC | $98,540.31 | $1,761.93 | 1.81% | $1,207.790181 | $1,186.126906 | 2 | $97.69 | |
Short | 2022-12-27 | 16 hours | Uniswap v3 | WETH | USDC | $101,202.76 | $1,395.83 | 1.40% | $1,227.269267 | $1,210.329079 | 2 | $100.53 | |
Long | 2022-12-28 | 3 days 12 hours | Uniswap v3 | WETH | USDC | $51,637.58 | $165.49 | 0.32% | $1,198.286762 | $1,202.127169 | 2 | $51.73 | |
SL | Short | 2023-01-02 | 1 days 20 hours | Uniswap v3 | WETH | USDC | $103,539.96 | $-3,168.63 | -2.97% | $1,211.903451 | $1,248.934414 | 2 | $105.15 |
Long | 2023-01-19 | 20 hours | Uniswap v3 | WETH | USDC | $49,151.18 | $1,269.41 | 2.58% | $1,512.828662 | $1,551.899802 | 2 | $49.80 | |
TP | Short | 2023-01-29 | 14 hours | Uniswap v3 | WETH | USDC | $100,333.41 | $4,009.81 | 4.16% | $1,653.140314 | $1,587.052845 | 2 | $98.35 |
Short | 2023-02-02 | 3 days 12 hours | Uniswap v3 | WETH | USDC | $106,591.54 | $1,758.09 | 1.66% | $1,670.588859 | $1,642.923061 | 2 | $105.75 | |
Long | 2023-02-05 | 20 hours | Uniswap v3 | WETH | USDC | $54,617.34 | $842.03 | 1.54% | $1,621.704424 | $1,646.705943 | 2 | $55.05 | |
Short | 2023-02-08 | 16 hours | Uniswap v3 | WETH | USDC | $110,581.92 | $2,251.62 | 2.08% | $1,681.314377 | $1,647.035301 | 2 | $109.48 | |
SL | Long | 2023-02-21 | 1 days 4 hours | Uniswap v3 | WETH | USDC | $57,004.57 | $-1,916.50 | -3.36% | $1,671.424404 | $1,615.230804 | 2 | $56.06 |
Short | 2023-03-02 | 16 hours | Uniswap v3 | WETH | USDC | $110,942.74 | $2,424.05 | 2.23% | $1,664.086575 | $1,627.715462 | 2 | $109.76 | |
Short | 2023-03-06 | 4 hours | Uniswap v3 | WETH | USDC | $114,645.43 | $867.95 | 0.76% | $1,577.401770 | $1,565.459552 | 2 | $114.24 | |
SL | Long | 2023-03-07 | 2 days | Uniswap v3 | WETH | USDC | $57,925.67 | $-2,437.89 | -4.21% | $1,552.365497 | $1,487.031713 | 2 | $56.72 |
SL | Short | 2023-03-17 | 13 hours | Uniswap v3 | WETH | USDC | $111,950.72 | $-3,583.55 | -3.10% | $1,764.334287 | $1,820.792544 | 2 | $113.77 |
TP | Long | 2023-03-22 | 19 hours | Uniswap v3 | WETH | USDC | $53,017.60 | $2,495.64 | 4.71% | $1,721.979859 | $1,803.036810 | 2 | $54.28 |
TP | Short | 2023-03-23 | 20 hours | Uniswap v3 | WETH | USDC | $110,028.22 | $5,125.42 | 4.88% | $1,840.378643 | $1,754.657568 | 2 | $107.49 |
Long | 2023-03-27 | 20 hours | Uniswap v3 | WETH | USDC | $59,028.28 | $1,102.13 | 1.87% | $1,714.077337 | $1,746.081204 | 2 | $59.59 | |
Long | 2023-04-03 | 8 hours | Uniswap v3 | WETH | USDC | $59,909.99 | $917.54 | 1.53% | $1,779.750276 | $1,807.007658 | 2 | $60.38 | |
SL | Short | 2023-04-04 | 1 days 1 hours | Uniswap v3 | WETH | USDC | $121,288.03 | $-3,675.44 | -2.95% | $1,863.526592 | $1,920.084537 | 2 | $123.16 |
Long | 2023-04-09 | 12 hours | Uniswap v3 | WETH | USDC | $57,605.25 | $573.09 | 0.99% | $1,837.987917 | $1,856.273354 | 2 | $57.91 | |
Short | 2023-04-10 | 1 days 8 hours | Uniswap v3 | WETH | USDC | $116,127.44 | $1,609.20 | 1.39% | $1,890.176101 | $1,864.085706 | 2 | $115.36 | |
TP | Short | 2023-04-26 | 8 hours | Uniswap v3 | WETH | USDC | $118,517.44 | $5,148.12 | 4.54% | $1,912.802425 | $1,829.735873 | 2 | $115.97 |
SL | Long | 2023-05-01 | 20 hours | Uniswap v3 | WETH | USDC | $63,284.41 | $-1,974.56 | -3.12% | $1,871.931031 | $1,813.524283 | 2 | $62.31 |
SL | Short | 2023-05-04 | 1 days 17 hours | Uniswap v3 | WETH | USDC | $123,409.53 | $-5,542.58 | -4.31% | $1,903.643431 | $1,989.269419 | 2 | $126.22 |
Long | 2023-05-17 | 8 hours | Uniswap v3 | WETH | USDC | $57,170.09 | $1,131.53 | 1.98% | $1,792.251606 | $1,827.724371 | 2 | $57.75 | |
Long | 2023-06-26 | 16 hours | Uniswap v3 | WETH | USDC | $58,075.31 | $753.98 | 1.30% | $1,856.273447 | $1,880.373185 | 2 | $58.47 | |
Long | 2023-06-28 | 16 hours | Uniswap v3 | WETH | USDC | $58,678.50 | $1,101.57 | 1.88% | $1,833.765613 | $1,868.191004 | 2 | $59.24 | |
Short | 2023-06-30 | 4 days | Uniswap v3 | WETH | USDC | $119,119.51 | $-513.44 | -0.47% | $1,925.468038 | $1,934.151734 | 2 | $119.43 | |
Short | 2023-07-10 | 16 hours | Uniswap v3 | WETH | USDC | $118,107.10 | $1,878.76 | 1.61% | $1,898.320950 | $1,868.191004 | 2 | $117.20 | |
SL | Short | 2023-07-13 | 2 hours | Uniswap v3 | WETH | USDC | $120,925.49 | $-3,769.79 | -3.02% | $1,927.201653 | $1,987.281243 | 2 | $122.84 |
Long | 2023-07-17 | 1 days 8 hours | Uniswap v3 | WETH | USDC | $57,348.64 | $680.69 | 1.19% | $1,889.042487 | $1,911.463999 | 2 | $57.70 | |
Long | 2023-07-31 | 1 days 8 hours | Uniswap v3 | WETH | USDC | $57,893.18 | $389.16 | 0.67% | $1,859.059807 | $1,871.556607 | 2 | $58.10 | |
Long | 2023-08-07 | 20 hours | Uniswap v3 | WETH | USDC | $58,204.52 | $590.81 | 1.02% | $1,816.609833 | $1,835.049543 | 2 | $58.51 | |
Short | 2023-08-08 | 2 days 12 hours | Uniswap v3 | WETH | USDC | $117,354.33 | $-42.33 | -0.09% | $1,841.851462 | $1,843.325459 | 2 | $117.44 | |
Short | 2023-08-13 | 4 hours | Uniswap v3 | WETH | USDC | $117,098.72 | $1,133.59 | 0.97% | $1,856.830292 | $1,838.907003 | 2 | $116.56 | |
Long | 2023-08-14 | 8 hours | Uniswap v3 | WETH | USDC | $11,709.87 | $37.53 | 0.32% | $1,840.746830 | $1,846.646266 | 2 | $11.73 | |
SL | Long | 2023-08-15 | 1 days 20 hours | Uniswap v3 | WETH | USDC | $59,393.00 | $-2,919.09 | -4.91% | $1,827.907235 | $1,738.068075 | 2 | $57.95 |
Long | 2023-08-28 | 16 hours | Uniswap v3 | WETH | USDC | $57,057.73 | $383.55 | 0.67% | $1,641.117012 | $1,652.148777 | 2 | $57.26 | |
Short | 2023-09-08 | 12 hours | Uniswap v3 | WETH | USDC | $114,729.13 | $1,472.81 | 1.30% | $1,645.718266 | $1,624.625893 | 2 | $114.02 | |
Short | 2023-09-18 | 1 days 16 hours | Uniswap v3 | WETH | USDC | $116,903.12 | $1,265.28 | 1.08% | $1,654.297859 | $1,636.528458 | 2 | $116.31 |
Finishing notes#
Print out a line to signal the notebook finished the execution successfully.
[18]:
print("All ok")
All ok