list last 5 positions
This commit is contained in:
parent
e434b96860
commit
989e755264
91
realtime.py
91
realtime.py
@ -1457,6 +1457,7 @@ class RealTimeChart:
|
|||||||
# Set up callbacks
|
# Set up callbacks
|
||||||
self._setup_interval_callback(button_style, active_button_style)
|
self._setup_interval_callback(button_style, active_button_style)
|
||||||
self._setup_chart_callback()
|
self._setup_chart_callback()
|
||||||
|
self._setup_position_list_callback()
|
||||||
# We've removed the ticks callback, so don't call it
|
# We've removed the ticks callback, so don't call it
|
||||||
# self._setup_ticks_callback()
|
# self._setup_ticks_callback()
|
||||||
|
|
||||||
@ -1497,6 +1498,29 @@ class RealTimeChart:
|
|||||||
# Main chart
|
# Main chart
|
||||||
dcc.Graph(id='live-chart', style={'height': '75vh'}),
|
dcc.Graph(id='live-chart', style={'height': '75vh'}),
|
||||||
|
|
||||||
|
# Last 5 Positions component
|
||||||
|
html.Div([
|
||||||
|
html.H4("Last 5 Positions", style={'textAlign': 'center', 'color': '#FFFFFF'}),
|
||||||
|
html.Table([
|
||||||
|
html.Thead(
|
||||||
|
html.Tr([
|
||||||
|
html.Th("Action", style={'padding': '8px', 'border': '1px solid #444', 'backgroundColor': '#333'}),
|
||||||
|
html.Th("Size", style={'padding': '8px', 'border': '1px solid #444', 'backgroundColor': '#333'}),
|
||||||
|
html.Th("Entry Price", style={'padding': '8px', 'border': '1px solid #444', 'backgroundColor': '#333'}),
|
||||||
|
html.Th("Exit Price", style={'padding': '8px', 'border': '1px solid #444', 'backgroundColor': '#333'}),
|
||||||
|
html.Th("PnL", style={'padding': '8px', 'border': '1px solid #444', 'backgroundColor': '#333'}),
|
||||||
|
html.Th("Time", style={'padding': '8px', 'border': '1px solid #444', 'backgroundColor': '#333'})
|
||||||
|
])
|
||||||
|
),
|
||||||
|
html.Tbody(id='position-table-body')
|
||||||
|
], style={
|
||||||
|
'width': '100%',
|
||||||
|
'borderCollapse': 'collapse',
|
||||||
|
'color': '#FFFFFF',
|
||||||
|
'backgroundColor': '#222'
|
||||||
|
})
|
||||||
|
], style={'marginTop': '20px', 'marginBottom': '20px', 'overflowX': 'auto'})
|
||||||
|
|
||||||
# Chart acknowledgment
|
# Chart acknowledgment
|
||||||
html.Div("Real-time trading chart with ML signals", style={
|
html.Div("Real-time trading chart with ML signals", style={
|
||||||
'textAlign': 'center',
|
'textAlign': 'center',
|
||||||
@ -2317,6 +2341,46 @@ class RealTimeChart:
|
|||||||
)
|
)
|
||||||
return fig
|
return fig
|
||||||
|
|
||||||
|
def _setup_position_list_callback(self):
|
||||||
|
"""Callback to update the position list with the latest 5 trades"""
|
||||||
|
@self.app.callback(
|
||||||
|
Output('position-list', 'children'),
|
||||||
|
[Input('interval-component', 'n_intervals')]
|
||||||
|
)
|
||||||
|
def update_position_list(n):
|
||||||
|
try:
|
||||||
|
# Get the last 5 positions
|
||||||
|
if not hasattr(self, 'trades') or not self.trades:
|
||||||
|
return [html.Li("No trades yet", style={'color': '#AAAAAA'})]
|
||||||
|
|
||||||
|
last_positions = self.trades[-5:]
|
||||||
|
position_items = []
|
||||||
|
|
||||||
|
for trade in last_positions:
|
||||||
|
action = trade.get('action', 'UNKNOWN')
|
||||||
|
price = trade.get('price', 'N/A')
|
||||||
|
timestamp = trade.get('timestamp', 'N/A')
|
||||||
|
pnl = trade.get('pnl', None)
|
||||||
|
|
||||||
|
# Format the display
|
||||||
|
pnl_str = f", PnL: {pnl:.4f}" if pnl is not None else ""
|
||||||
|
time_str = timestamp.strftime('%H:%M:%S') if isinstance(timestamp, datetime) else str(timestamp)
|
||||||
|
|
||||||
|
# Set color based on action
|
||||||
|
color = '#00FF00' if action == 'BUY' else '#FF0000'
|
||||||
|
|
||||||
|
position_items.append(
|
||||||
|
html.Li(
|
||||||
|
f"{action} @ {price} ({time_str}){pnl_str}",
|
||||||
|
style={'color': color, 'padding': '5px', 'borderBottom': '1px solid #333'}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return position_items
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error updating position list: {str(e)}")
|
||||||
|
return [html.Li("Error loading positions", style={'color': '#FF0000'})]
|
||||||
|
|
||||||
def _interval_to_seconds(self, interval_key: str) -> int:
|
def _interval_to_seconds(self, interval_key: str) -> int:
|
||||||
"""Convert interval key to seconds"""
|
"""Convert interval key to seconds"""
|
||||||
mapping = {
|
mapping = {
|
||||||
@ -2761,6 +2825,18 @@ class RealTimeChart:
|
|||||||
# Add to our trades list
|
# Add to our trades list
|
||||||
if not hasattr(self, 'trades'):
|
if not hasattr(self, 'trades'):
|
||||||
self.trades = []
|
self.trades = []
|
||||||
|
|
||||||
|
# If this is a SELL trade, try to find the corresponding BUY trade and update it with close_price
|
||||||
|
if trade_type == 'SELL' and len(self.trades) > 0:
|
||||||
|
for i in range(len(self.trades) - 1, -1, -1):
|
||||||
|
prev_trade = self.trades[i]
|
||||||
|
if prev_trade.get('action') == 'BUY' and 'close_price' not in prev_trade:
|
||||||
|
# Found a BUY trade without a close_price, consider it the matching trade
|
||||||
|
prev_trade['close_price'] = price
|
||||||
|
prev_trade['close_timestamp'] = timestamp
|
||||||
|
logger.info(f"Updated BUY trade at {prev_trade['timestamp']} with close price {price}")
|
||||||
|
break
|
||||||
|
|
||||||
self.trades.append(trade)
|
self.trades.append(trade)
|
||||||
|
|
||||||
# Log the trade for debugging
|
# Log the trade for debugging
|
||||||
@ -2773,11 +2849,17 @@ class RealTimeChart:
|
|||||||
try:
|
try:
|
||||||
# Only update if we have a dash app running
|
# Only update if we have a dash app running
|
||||||
# This is a workaround to make trades appear immediately
|
# This is a workaround to make trades appear immediately
|
||||||
interval_component = self.app.get_asset_url('interval-component')
|
callback_context = dash.callback_context
|
||||||
if interval_component:
|
# Force an update by triggering the callback
|
||||||
self.app.callback_map[interval_component]['callback']()
|
for callback_id, callback_info in self.app.callback_map.items():
|
||||||
except:
|
if 'live-chart' in callback_id:
|
||||||
|
# Found the chart callback, try to trigger it
|
||||||
|
logger.debug(f"Triggering chart update callback after trade")
|
||||||
|
callback_info['callback']()
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
# If callback triggering fails, it's not critical
|
# If callback triggering fails, it's not critical
|
||||||
|
logger.debug(f"Failed to trigger chart update: {str(e)}")
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return trade
|
return trade
|
||||||
@ -2863,3 +2945,4 @@ if __name__ == "__main__":
|
|||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
logger.info("Application terminated by user")
|
logger.info("Application terminated by user")
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user