(feat) adapt strategy analysis to new structure

This commit is contained in:
cardosofede
2023-09-07 17:39:07 +08:00
parent 78b548286a
commit 25fc352948

View File

@@ -11,6 +11,9 @@ class StrategyAnalysis:
def __init__(self, positions: pd.DataFrame, candles_df: Optional[pd.DataFrame] = None):
self.candles_df = candles_df
self.positions = positions
self.candles_df["timestamp"] = pd.to_datetime(self.candles_df["timestamp"], unit="ms")
self.positions["timestamp"] = pd.to_datetime(self.positions["timestamp"], unit="ms")
self.positions["close_time"] = pd.to_datetime(self.positions["close_time"], unit="ms")
self.base_figure = None
def create_base_figure(self, candlestick=True, volume=True, positions=False, trade_pnl=False, extra_rows=0):
@@ -32,18 +35,18 @@ class StrategyAnalysis:
def add_positions(self):
# Add long and short positions
active_signals = self.positions.copy()
active_signals.loc[active_signals['side'] == -1, 'symbol'] = 'triangle-down'
active_signals.loc[active_signals['side'] == 1, 'symbol'] = 'triangle-up'
active_signals.loc[active_signals['real_class'] == 1, 'color'] = 'lightgreen'
active_signals.loc[active_signals['real_class'] == -1, 'color'] = 'red'
self.base_figure.add_trace(go.Scatter(x=active_signals.loc[(active_signals['side'] != 0), 'timestamp'],
y=active_signals.loc[active_signals['side'] != 0, 'close'],
name='Entry Price: $',
mode='markers',
marker_color=active_signals.loc[(active_signals['side'] != 0), 'color'],
marker_symbol=active_signals.loc[(active_signals['side'] != 0), 'symbol'],
active_signals.loc[active_signals["signal"] == -1, "symbol"] = "triangle-down"
active_signals.loc[active_signals["signal"] == 1, "symbol"] = "triangle-up"
active_signals.loc[active_signals["profitable"] == 1, "color"] = "lightgreen"
active_signals.loc[active_signals["profitable"] == -1, "color"] = "red"
self.base_figure.add_trace(go.Scatter(x=active_signals.loc[(active_signals["side"] != 0), "timestamp"],
y=active_signals.loc[active_signals["side"] != 0, "close"],
name="Entry Price: $",
mode="markers",
marker_color=active_signals.loc[(active_signals["side"] != 0), "color"],
marker_symbol=active_signals.loc[(active_signals["side"] != 0), "symbol"],
marker_size=20,
marker_line={'color': 'black', 'width': 0.7}),
marker_line={"color": "black", "width": 0.7}),
row=1, col=1)
for index, row in active_signals.iterrows():
@@ -53,7 +56,7 @@ class StrategyAnalysis:
x0=row.timestamp,
y0=row.close,
x1=row.close_time,
y1=row.tp,
y1=row.take_profit_price,
line=dict(color="green"),
row=1, col=1)
# Add SL
@@ -63,7 +66,7 @@ class StrategyAnalysis:
x0=row.timestamp,
y0=row.close,
x1=row.close_time,
y1=row.sl,
y1=row.stop_loss_price,
line=dict(color="red"),
row=1, col=1)
@@ -81,11 +84,11 @@ class StrategyAnalysis:
def add_candles_graph(self):
self.base_figure.add_trace(
go.Candlestick(
x=self.candles_df['timestamp'],
open=self.candles_df['open'],
high=self.candles_df['high'],
low=self.candles_df['low'],
close=self.candles_df['close'],
x=self.candles_df["timestamp"],
open=self.candles_df["open"],
high=self.candles_df["high"],
low=self.candles_df["low"],
close=self.candles_df["close"],
name="OHLC"
),
row=1, col=1,
@@ -94,11 +97,11 @@ class StrategyAnalysis:
def add_volume(self):
self.base_figure.add_trace(
go.Bar(
x=self.candles_df['timestamp'],
y=self.candles_df['volume'],
x=self.candles_df["timestamp"],
y=self.candles_df["volume"],
name="Volume",
opacity=0.5,
marker=dict(color='lightgreen')
marker=dict(color="lightgreen")
),
row=2, col=1,
)
@@ -106,23 +109,23 @@ class StrategyAnalysis:
def add_trade_pnl(self, row=2):
self.base_figure.add_trace(
go.Scatter(
x=self.positions['timestamp'],
y=self.positions['ret_usd'].cumsum(),
x=self.positions["timestamp"],
y=self.positions["net_pnl_quote"].cumsum(),
name="Cumulative Trade PnL",
mode='lines',
line=dict(color='chocolate', width=2)),
mode="lines",
line=dict(color="chocolate", width=2)),
row=row, col=1
)
self.base_figure.update_yaxes(title_text='Cum Trade PnL', row=row, col=1)
self.base_figure.update_yaxes(title_text="Cum Trade PnL", row=row, col=1)
def update_layout(self, volume=True):
self.base_figure.update_layout(
title={
'text': "Backtesting Analysis",
'y': 0.95,
'x': 0.5,
'xanchor': 'center',
'yanchor': 'top'
"text": "Backtesting Analysis",
"y": 0.95,
"x": 0.5,
"xanchor": "center",
"yanchor": "top"
},
legend=dict(
orientation="h",
@@ -133,7 +136,7 @@ class StrategyAnalysis:
),
height=1000,
xaxis_rangeslider_visible=False,
hovermode='x unified'
hovermode="x unified"
)
self.base_figure.update_yaxes(title_text="Price", row=1, col=1)
if volume:
@@ -141,10 +144,10 @@ class StrategyAnalysis:
self.base_figure.update_xaxes(title_text="Time", row=self.rows, col=1)
def initial_portfolio(self):
return self.positions['current_portfolio'].dropna().values[0]
return self.positions["inventory"].dropna().values[0]
def final_portfolio(self):
return self.positions['current_portfolio'].dropna().values[-1]
return self.positions["inventory"].dropna().values[-1]
def net_profit_usd(self):
return self.final_portfolio() - self.initial_portfolio()
@@ -153,22 +156,22 @@ class StrategyAnalysis:
return self.net_profit_usd() / self.initial_portfolio()
def returns(self):
return self.positions['ret_usd'] / self.initial_portfolio()
return self.positions["net_pnl_quote"] / self.initial_portfolio()
def total_positions(self):
return self.positions.shape[0] - 1
def win_signals(self):
return self.positions.loc[(self.positions['real_class'] > 0) & (self.positions["side"] != 0)]
return self.positions.loc[(self.positions["profitable"] > 0) & (self.positions["side"] != 0)]
def loss_signals(self):
return self.positions.loc[(self.positions['real_class'] < 0) & (self.positions["side"] != 0)]
return self.positions.loc[(self.positions["profitable"] < 0) & (self.positions["side"] != 0)]
def accuracy(self):
return self.win_signals().shape[0] / self.total_positions()
def max_drawdown_usd(self):
cumulative_returns = self.positions["ret_usd"].cumsum()
cumulative_returns = self.positions["net_pnl_quote"].cumsum()
peak = np.maximum.accumulate(cumulative_returns)
drawdown = (cumulative_returns - peak)
max_draw_down = np.min(drawdown)
@@ -182,25 +185,25 @@ class StrategyAnalysis:
return returns.mean() / returns.std()
def profit_factor(self):
total_won = self.win_signals().loc[:, 'ret_usd'].sum()
total_loss = - self.loss_signals().loc[:, 'ret_usd'].sum()
total_won = self.win_signals().loc[:, "net_pnl_quote"].sum()
total_loss = - self.loss_signals().loc[:, "net_pnl_quote"].sum()
return total_won / total_loss
def duration_in_minutes(self):
return (self.positions['timestamp'].iloc[-1] - self.positions['timestamp'].iloc[0]).total_seconds() / 60
return (self.positions["timestamp"].iloc[-1] - self.positions["timestamp"].iloc[0]).total_seconds() / 60
def avg_trading_time_in_minutes(self):
time_diff_minutes = (pd.to_datetime(self.positions['close_time']) - self.positions['timestamp']).dt.total_seconds() / 60
time_diff_minutes = (self.positions["close_time"] - self.positions["timestamp"]).dt.total_seconds() / 60
return time_diff_minutes.mean()
def start_date(self):
return self.candles_df.timestamp.min()
return pd.to_datetime(self.candles_df.timestamp.min(), unit="ms")
def end_date(self):
return self.candles_df.timestamp.max()
return pd.to_datetime(self.candles_df.timestamp.max(), unit="ms")
def avg_profit(self):
return self.positions.ret_usd.mean()
return self.positions.net_pnl_quote.mean()
def text_report(self):
return f"""
@@ -221,7 +224,7 @@ Strategy Performance Report:
fig = go.Figure()
fig.add_trace(go.Scatter(name="PnL Over Time",
x=self.positions.index,
y=self.positions.ret_usd.cumsum()))
y=self.positions.net_pnl_quote.cumsum()))
# Update layout with the required attributes
fig.update_layout(
title="PnL Over Time",