position history with fees
This commit is contained in:
@ -49,18 +49,23 @@ class TradingSession:
|
||||
self.starting_balance = 100.0 # $100 USD starting balance
|
||||
self.current_balance = self.starting_balance
|
||||
self.total_pnl = 0.0
|
||||
self.total_fees = 0.0 # Track total fees paid (opening + closing)
|
||||
self.total_trades = 0
|
||||
self.winning_trades = 0
|
||||
self.losing_trades = 0
|
||||
self.positions = {} # symbol -> {'size': float, 'entry_price': float, 'side': str}
|
||||
self.positions = {} # symbol -> {'size': float, 'entry_price': float, 'side': str, 'fees': float}
|
||||
self.trade_history = []
|
||||
self.last_action = None
|
||||
self.trading_executor = trading_executor
|
||||
|
||||
# Fee configuration - MEXC spot trading fees
|
||||
self.fee_rate = 0.001 # 0.1% trading fee (typical for MEXC spot)
|
||||
|
||||
logger.info(f"NEW TRADING SESSION STARTED WITH MEXC INTEGRATION")
|
||||
logger.info(f"Session ID: {self.session_id}")
|
||||
logger.info(f"Starting Balance: ${self.starting_balance:.2f}")
|
||||
logger.info(f"MEXC Trading: {'ENABLED' if trading_executor and trading_executor.trading_enabled else 'DISABLED'}")
|
||||
logger.info(f"Trading Fee Rate: {self.fee_rate*100:.1f}%")
|
||||
logger.info(f"Start Time: {self.start_time.strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
|
||||
def execute_trade(self, action: TradingAction, current_price: float):
|
||||
@ -105,14 +110,20 @@ class TradingSession:
|
||||
if action.action == 'BUY':
|
||||
# Close any existing short position
|
||||
if symbol in self.positions and self.positions[symbol]['side'] == 'SHORT':
|
||||
self._close_position(symbol, current_price, 'BUY')
|
||||
pnl = self._close_position(symbol, current_price, 'BUY')
|
||||
trade_info['pnl'] = pnl
|
||||
|
||||
# Open new long position with opening fee
|
||||
opening_fee = current_price * position_size * self.fee_rate
|
||||
self.total_fees += opening_fee
|
||||
|
||||
# Open new long position
|
||||
self.positions[symbol] = {
|
||||
'size': position_size,
|
||||
'entry_price': current_price,
|
||||
'side': 'LONG'
|
||||
'side': 'LONG',
|
||||
'fees': opening_fee # Track opening fee
|
||||
}
|
||||
trade_info['opening_fee'] = opening_fee
|
||||
trade_info['pnl'] = 0 # No immediate P&L on entry
|
||||
|
||||
elif action.action == 'SELL':
|
||||
@ -121,12 +132,17 @@ class TradingSession:
|
||||
pnl = self._close_position(symbol, current_price, 'SELL')
|
||||
trade_info['pnl'] = pnl
|
||||
else:
|
||||
# Open new short position
|
||||
# Open new short position with opening fee
|
||||
opening_fee = current_price * position_size * self.fee_rate
|
||||
self.total_fees += opening_fee
|
||||
|
||||
self.positions[symbol] = {
|
||||
'size': position_size,
|
||||
'entry_price': current_price,
|
||||
'side': 'SHORT'
|
||||
'side': 'SHORT',
|
||||
'fees': opening_fee # Track opening fee
|
||||
}
|
||||
trade_info['opening_fee'] = opening_fee
|
||||
trade_info['pnl'] = 0
|
||||
|
||||
elif action.action == 'HOLD':
|
||||
@ -154,7 +170,7 @@ class TradingSession:
|
||||
return None
|
||||
|
||||
def _close_position(self, symbol: str, exit_price: float, close_action: str) -> float:
|
||||
"""Close an existing position and calculate P&L"""
|
||||
"""Close an existing position and calculate P&L with fees"""
|
||||
if symbol not in self.positions:
|
||||
return 0.0
|
||||
|
||||
@ -162,18 +178,27 @@ class TradingSession:
|
||||
entry_price = position['entry_price']
|
||||
size = position['size']
|
||||
side = position['side']
|
||||
opening_fee = position.get('fees', 0.0)
|
||||
|
||||
# Calculate P&L
|
||||
# Calculate closing fee
|
||||
closing_fee = exit_price * size * self.fee_rate
|
||||
total_fees = opening_fee + closing_fee
|
||||
self.total_fees += closing_fee
|
||||
|
||||
# Calculate gross P&L
|
||||
if side == 'LONG':
|
||||
pnl = (exit_price - entry_price) * size
|
||||
gross_pnl = (exit_price - entry_price) * size
|
||||
else: # SHORT
|
||||
pnl = (entry_price - exit_price) * size
|
||||
gross_pnl = (entry_price - exit_price) * size
|
||||
|
||||
# Calculate net P&L (after fees)
|
||||
net_pnl = gross_pnl - total_fees
|
||||
|
||||
# Update session P&L
|
||||
self.total_pnl += pnl
|
||||
self.total_pnl += net_pnl
|
||||
|
||||
# Track win/loss
|
||||
if pnl > 0:
|
||||
# Track win/loss based on net P&L
|
||||
if net_pnl > 0:
|
||||
self.winning_trades += 1
|
||||
else:
|
||||
self.losing_trades += 1
|
||||
@ -183,9 +208,10 @@ class TradingSession:
|
||||
|
||||
logger.info(f"CHART: POSITION CLOSED: {side} {symbol}")
|
||||
logger.info(f"CHART: Entry: ${entry_price:.2f} | Exit: ${exit_price:.2f}")
|
||||
logger.info(f"MONEY: Trade P&L: ${pnl:+.2f}")
|
||||
logger.info(f"FEES: Opening: ${opening_fee:.4f} | Closing: ${closing_fee:.4f} | Total: ${total_fees:.4f}")
|
||||
logger.info(f"MONEY: Gross P&L: ${gross_pnl:+.2f} | Net P&L: ${net_pnl:+.2f}")
|
||||
|
||||
return pnl
|
||||
return net_pnl
|
||||
|
||||
def get_win_rate(self) -> float:
|
||||
"""Calculate current win rate"""
|
||||
@ -203,6 +229,7 @@ class TradingSession:
|
||||
'starting_balance': self.starting_balance,
|
||||
'current_balance': self.current_balance,
|
||||
'total_pnl': self.total_pnl,
|
||||
'total_fees': self.total_fees,
|
||||
'total_trades': self.total_trades,
|
||||
'winning_trades': self.winning_trades,
|
||||
'losing_trades': self.losing_trades,
|
||||
@ -605,22 +632,27 @@ class RealTimeScalpingDashboard:
|
||||
html.Div([
|
||||
html.H3(id="live-pnl", className="text-success"),
|
||||
html.P("Session P&L", className="text-white")
|
||||
], className="col-md-3 text-center"),
|
||||
], className="col-md-2 text-center"),
|
||||
|
||||
html.Div([
|
||||
html.H3(id="total-fees", className="text-warning"),
|
||||
html.P("Total Fees", className="text-white")
|
||||
], className="col-md-2 text-center"),
|
||||
|
||||
html.Div([
|
||||
html.H3(id="win-rate", className="text-info"),
|
||||
html.P("Win Rate", className="text-white")
|
||||
], className="col-md-3 text-center"),
|
||||
], className="col-md-2 text-center"),
|
||||
|
||||
html.Div([
|
||||
html.H3(id="total-trades", className="text-primary"),
|
||||
html.P("Total Trades", className="text-white")
|
||||
], className="col-md-3 text-center"),
|
||||
], className="col-md-2 text-center"),
|
||||
|
||||
html.Div([
|
||||
html.H3(id="last-action", className="text-warning"),
|
||||
html.P("Last Action", className="text-white")
|
||||
], className="col-md-3 text-center")
|
||||
], className="col-md-4 text-center")
|
||||
], className="col-md-4"),
|
||||
|
||||
# Middle - Price displays (2 columns, 2/12 width)
|
||||
@ -732,6 +764,7 @@ class RealTimeScalpingDashboard:
|
||||
Output('session-duration', 'children'),
|
||||
Output('open-positions', 'children'),
|
||||
Output('live-pnl', 'children'),
|
||||
Output('total-fees', 'children'),
|
||||
Output('win-rate', 'children'),
|
||||
Output('total-trades', 'children'),
|
||||
Output('last-action', 'children'),
|
||||
@ -823,8 +856,8 @@ class RealTimeScalpingDashboard:
|
||||
# MEXC status
|
||||
if dashboard_instance.trading_executor and dashboard_instance.trading_executor.trading_enabled:
|
||||
mexc_status = "LIVE"
|
||||
elif dashboard_instance.trading_executor and dashboard_instance.trading_executor.dry_run:
|
||||
mexc_status = "DRY RUN"
|
||||
elif dashboard_instance.trading_executor and dashboard_instance.trading_executor.simulation_mode:
|
||||
mexc_status = f"{dashboard_instance.trading_executor.trading_mode.upper()} MODE"
|
||||
else:
|
||||
mexc_status = "OFFLINE"
|
||||
|
||||
@ -2579,4 +2612,4 @@ def create_scalping_dashboard(data_provider=None, orchestrator=None, trading_exe
|
||||
return RealTimeScalpingDashboard(data_provider, orchestrator, trading_executor)
|
||||
|
||||
# For backward compatibility
|
||||
ScalpingDashboard = RealTimeScalpingDashboard
|
||||
ScalpingDashboard = RealTimeScalpingDashboard
|
||||
|
Reference in New Issue
Block a user