wip
This commit is contained in:
112
web/dashboard.py
112
web/dashboard.py
@ -358,16 +358,26 @@ class TradingDashboard:
|
||||
pnl_text = f"${total_session_pnl:.2f}"
|
||||
pnl_class = "text-success mb-0 small" if total_session_pnl >= 0 else "text-danger mb-0 small"
|
||||
|
||||
# Position info with real-time unrealized PnL
|
||||
# Position info with real-time unrealized PnL and color coding
|
||||
if self.current_position:
|
||||
pos_side = self.current_position['side']
|
||||
pos_size = self.current_position['size']
|
||||
pos_price = self.current_position['price']
|
||||
unrealized_pnl = self._calculate_unrealized_pnl(current_price) if current_price else 0.0
|
||||
pnl_color = "text-success" if unrealized_pnl >= 0 else "text-danger"
|
||||
position_text = f"{pos_side} {pos_size} @ ${pos_price:.2f} | P&L: ${unrealized_pnl:.2f}"
|
||||
|
||||
# Color code the position based on side and P&L (no Unicode for Windows compatibility)
|
||||
if pos_side == 'LONG':
|
||||
side_icon = "[LONG]" # ASCII indicator for long
|
||||
side_color = "success" if unrealized_pnl >= 0 else "warning"
|
||||
else: # SHORT
|
||||
side_icon = "[SHORT]" # ASCII indicator for short
|
||||
side_color = "danger" if unrealized_pnl >= 0 else "info"
|
||||
|
||||
position_text = f"{side_icon} {pos_size} @ ${pos_price:.2f} | P&L: ${unrealized_pnl:.2f}"
|
||||
position_class = f"text-{side_color} fw-bold"
|
||||
else:
|
||||
position_text = "None"
|
||||
position_text = "No Position"
|
||||
position_class = "text-muted"
|
||||
|
||||
# Trade count
|
||||
trade_count_text = f"{len(self.session_trades)}"
|
||||
@ -906,6 +916,26 @@ class TradingDashboard:
|
||||
|
||||
confidence_pct = f"{confidence*100:.1f}%" if confidence else "N/A"
|
||||
|
||||
# Check if this is a trade with PnL information
|
||||
pnl_info = ""
|
||||
if isinstance(decision, dict) and 'pnl' in decision:
|
||||
pnl = decision['pnl']
|
||||
pnl_color = "text-success" if pnl >= 0 else "text-danger"
|
||||
pnl_info = html.Span([
|
||||
" • PnL: ",
|
||||
html.Strong(f"${pnl:.2f}", className=pnl_color)
|
||||
])
|
||||
|
||||
# Check for position action to show entry/exit info
|
||||
position_info = ""
|
||||
if isinstance(decision, dict) and 'position_action' in decision:
|
||||
pos_action = decision['position_action']
|
||||
if 'CLOSE' in pos_action and 'entry_price' in decision:
|
||||
entry_price = decision['entry_price']
|
||||
position_info = html.Small([
|
||||
f" (Entry: ${entry_price:.2f})"
|
||||
], className="text-muted")
|
||||
|
||||
decisions_html.append(
|
||||
html.Div([
|
||||
html.Div([
|
||||
@ -913,11 +943,13 @@ class TradingDashboard:
|
||||
html.Strong(action, className=action_class),
|
||||
html.Span(f" {symbol} ", className="text-muted"),
|
||||
html.Small(f"@${price:.2f}", className="text-muted"),
|
||||
position_info,
|
||||
html.Span(className=f"{badge_class} ms-2", children=badge_text, style={"fontSize": "0.7em"})
|
||||
], className="d-flex align-items-center"),
|
||||
html.Small([
|
||||
html.Span(f"Confidence: {confidence_pct} • ", className="text-info"),
|
||||
html.Span(time_str, className="text-muted")
|
||||
html.Span(time_str, className="text-muted"),
|
||||
pnl_info
|
||||
])
|
||||
], className="border-bottom pb-2 mb-2")
|
||||
)
|
||||
@ -1139,7 +1171,7 @@ class TradingDashboard:
|
||||
return None
|
||||
|
||||
def _process_trading_decision(self, decision: Dict) -> None:
|
||||
"""Process a trading decision and update PnL tracking"""
|
||||
"""Process a trading decision and update PnL tracking with position flipping"""
|
||||
try:
|
||||
if not decision:
|
||||
return
|
||||
@ -1168,6 +1200,49 @@ class TradingDashboard:
|
||||
|
||||
logger.info(f"[TRADE] OPENED LONG: {decision['size']} @ ${decision['price']:.2f}")
|
||||
|
||||
elif self.current_position['side'] == 'SHORT':
|
||||
# Close short position and flip to long
|
||||
entry_price = self.current_position['price']
|
||||
exit_price = decision['price']
|
||||
size = self.current_position['size']
|
||||
|
||||
# Calculate PnL for closing short
|
||||
gross_pnl = (entry_price - exit_price) * size # Short PnL calculation
|
||||
fee = exit_price * size * fee_rate
|
||||
net_pnl = gross_pnl - fee - self.current_position['fees']
|
||||
|
||||
self.total_realized_pnl += net_pnl
|
||||
self.total_fees += fee
|
||||
|
||||
# Record the close trade
|
||||
close_record = decision.copy()
|
||||
close_record['position_action'] = 'CLOSE_SHORT'
|
||||
close_record['entry_price'] = entry_price
|
||||
close_record['pnl'] = net_pnl
|
||||
close_record['fees'] = fee
|
||||
self.session_trades.append(close_record)
|
||||
|
||||
logger.info(f"[TRADE] CLOSED SHORT: {size} @ ${exit_price:.2f} | PnL: ${net_pnl:.2f} | FLIPPING TO LONG")
|
||||
|
||||
# Open new long position
|
||||
new_fee = decision['price'] * decision['size'] * fee_rate
|
||||
self.current_position = {
|
||||
'side': 'LONG',
|
||||
'price': decision['price'],
|
||||
'size': decision['size'],
|
||||
'timestamp': current_time,
|
||||
'fees': new_fee
|
||||
}
|
||||
self.total_fees += new_fee
|
||||
|
||||
# Record the new long position
|
||||
open_record = decision.copy()
|
||||
open_record['position_action'] = 'OPEN_LONG'
|
||||
open_record['fees'] = new_fee
|
||||
self.session_trades.append(open_record)
|
||||
|
||||
logger.info(f"[TRADE] OPENED LONG: {decision['size']} @ ${decision['price']:.2f}")
|
||||
|
||||
elif decision['action'] == 'SELL':
|
||||
if self.current_position and self.current_position['side'] == 'LONG':
|
||||
# Close long position
|
||||
@ -1175,28 +1250,29 @@ class TradingDashboard:
|
||||
exit_price = decision['price']
|
||||
size = self.current_position['size']
|
||||
|
||||
# Calculate PnL
|
||||
gross_pnl = (exit_price - entry_price) * size
|
||||
# Calculate PnL for closing long
|
||||
gross_pnl = (exit_price - entry_price) * size # Long PnL calculation
|
||||
fee = exit_price * size * fee_rate
|
||||
net_pnl = gross_pnl - fee - self.current_position['fees']
|
||||
|
||||
self.total_realized_pnl += net_pnl
|
||||
self.total_fees += fee
|
||||
|
||||
trade_record = decision.copy()
|
||||
trade_record['position_action'] = 'CLOSE_LONG'
|
||||
trade_record['entry_price'] = entry_price
|
||||
trade_record['pnl'] = net_pnl
|
||||
trade_record['fees'] = fee
|
||||
self.session_trades.append(trade_record)
|
||||
# Record the close trade
|
||||
close_record = decision.copy()
|
||||
close_record['position_action'] = 'CLOSE_LONG'
|
||||
close_record['entry_price'] = entry_price
|
||||
close_record['pnl'] = net_pnl
|
||||
close_record['fees'] = fee
|
||||
self.session_trades.append(close_record)
|
||||
|
||||
logger.info(f"[TRADE] CLOSED LONG: {size} @ ${exit_price:.2f} | PnL: ${net_pnl:.2f}")
|
||||
|
||||
# Clear position
|
||||
# Clear position (or could flip to short here if desired)
|
||||
self.current_position = None
|
||||
|
||||
elif self.current_position is None:
|
||||
# Open short position (for demo)
|
||||
# Open short position
|
||||
fee = decision['price'] * decision['size'] * fee_rate
|
||||
self.current_position = {
|
||||
'side': 'SHORT',
|
||||
@ -1213,6 +1289,10 @@ class TradingDashboard:
|
||||
self.session_trades.append(trade_record)
|
||||
|
||||
logger.info(f"[TRADE] OPENED SHORT: {decision['size']} @ ${decision['price']:.2f}")
|
||||
|
||||
elif self.current_position['side'] == 'LONG':
|
||||
# This case is already handled above, but adding for completeness
|
||||
pass
|
||||
|
||||
# Add to recent decisions
|
||||
self.recent_decisions.append(decision)
|
||||
|
Reference in New Issue
Block a user