mirror of
https://github.com/aljazceru/hummingbot-dashboard.git
synced 2026-01-31 11:04:21 +01:00
(feat) adapt strategy analysis to new structure
This commit is contained in:
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user