diff --git a/frontend/visualization/backtesting.py b/frontend/visualization/backtesting.py new file mode 100644 index 0000000..6efdc1e --- /dev/null +++ b/frontend/visualization/backtesting.py @@ -0,0 +1,33 @@ +from plotly.subplots import make_subplots +from frontend.visualization.candles import get_candlestick_trace +from frontend.visualization.executors import add_executors_trace +from frontend.visualization.pnl import get_pnl_trace +from frontend.visualization.theme import get_default_layout + + +def create_backtesting_figure(df, executors, config): + # Create subplots + fig = make_subplots(rows=2, cols=1, shared_xaxes=True, + vertical_spacing=0.02, subplot_titles=('Candlestick', 'PNL Quote'), + row_heights=[0.7, 0.3]) + + # Add candlestick trace + fig.add_trace(get_candlestick_trace(df), row=1, col=1) + + # Add executors trace + fig = add_executors_trace(fig, executors, row=1, col=1) + + # Add PNL trace + fig.add_trace(get_pnl_trace(executors), row=2, col=1) + + # Apply the theme layout + layout_settings = get_default_layout(f"Trading Pair: {config['trading_pair']}") + layout_settings["showlegend"] = False + fig.update_layout(**layout_settings) + + # Update axis properties + fig.update_xaxes(rangeslider_visible=False, row=1, col=1) + fig.update_xaxes(row=2, col=1) + fig.update_yaxes(title_text="Price", row=1, col=1) + fig.update_yaxes(title_text="PNL", row=2, col=1) + return fig diff --git a/frontend/visualization/candles.py b/frontend/visualization/candles.py new file mode 100644 index 0000000..9d0b050 --- /dev/null +++ b/frontend/visualization/candles.py @@ -0,0 +1,12 @@ +import plotly.graph_objects as go +import pandas as pd + +def get_candlestick_trace(df): + df.index = pd.to_datetime(df.timestamp, unit='ms') + return go.Candlestick(x=df.index, + open=df['open'], + high=df['high'], + low=df['low'], + close=df['close'], + name="Candlesticks", + increasing_line_color='#2ECC71', decreasing_line_color='#E74C3C') diff --git a/frontend/visualization/executors.py b/frontend/visualization/executors.py new file mode 100644 index 0000000..ef00a2d --- /dev/null +++ b/frontend/visualization/executors.py @@ -0,0 +1,23 @@ +import plotly.graph_objects as go +import pandas as pd +from decimal import Decimal + + +def add_executors_trace(fig, executors, row, col): + for executor in executors: + entry_time = pd.to_datetime(executor.timestamp, unit='ms') + entry_price = executor.custom_info["current_position_average_price"] + exit_time = pd.to_datetime(executor.close_timestamp, unit='ms') + exit_price = executor.custom_info["close_price"] + + if executor.filled_amount_quote == 0: + fig.add_trace(go.Scatter(x=[entry_time, exit_time], y=[entry_price, exit_price], mode='lines', + line=dict(color='blue', width=2, dash="dash"), name='Buy Entry/Exit'), row=row, col=col) + else: + if executor.net_pnl_quote > Decimal(0): + fig.add_trace(go.Scatter(x=[entry_time, exit_time], y=[entry_price, exit_price], mode='lines', + line=dict(color='green', width=2), name='Buy Entry/Exit'), row=row, col=col) + else: + fig.add_trace(go.Scatter(x=[entry_time, exit_time], y=[entry_price, exit_price], mode='lines', + line=dict(color='red', width=2), name='Sell Entry/Exit'), row=row, col=col) + return fig diff --git a/frontend/visualization/pnl.py b/frontend/visualization/pnl.py new file mode 100644 index 0000000..643c610 --- /dev/null +++ b/frontend/visualization/pnl.py @@ -0,0 +1,15 @@ +import plotly.graph_objects as go +import numpy as np +import pandas as pd + + +def get_pnl_trace(executors): + pnl = [e.net_pnl_quote for e in executors] + cum_pnl = np.cumsum(pnl) + return go.Scatter( + x=pd.to_datetime([e.close_timestamp for e in executors], unit="ms"), + y=cum_pnl, + mode='lines', + line=dict(color='blue', width=2, dash="dash"), + name='Cumulative PNL' + )