diff --git a/access_app.py b/access_app.py new file mode 100644 index 0000000..12dda48 --- /dev/null +++ b/access_app.py @@ -0,0 +1,46 @@ +import sys +import os +import time +from datetime import datetime + +# Add the project root to the path +sys.path.append(os.getcwd()) + +# Try to access the running application's charts +from train_rl_with_realtime import charts + +if not charts: + print("No charts are running. Please start the application first.") + sys.exit(1) + +# Get the first chart +chart = charts[0] +print(f"Accessing chart for {chart.symbol}") + +# Add some test trades +print("Adding trades...") +chart.add_trade(price=64500, timestamp=datetime.now(), pnl=0.5, action='BUY') +print("Added BUY trade 1") +time.sleep(1) +chart.add_trade(price=64800, timestamp=datetime.now(), pnl=0.7, action='SELL') +print("Added SELL trade 1") +time.sleep(1) +chart.add_trade(price=64600, timestamp=datetime.now(), pnl=0.3, action='BUY') +print("Added BUY trade 2") +time.sleep(1) +chart.add_trade(price=64900, timestamp=datetime.now(), pnl=0.6, action='SELL') +print("Added SELL trade 2") + +# Try to get the current trades +print("\nCurrent trades:") +if hasattr(chart, 'trades') and chart.trades: + for i, trade in enumerate(chart.trades[-5:]): + action = trade.get('action', 'UNKNOWN') + price = trade.get('price', 'N/A') + timestamp = trade.get('timestamp', 'N/A') + pnl = trade.get('pnl', None) + close_price = trade.get('close_price', 'N/A') + + print(f"Trade {i+1}: {action} @ {price}, Close: {close_price}, PnL: {pnl}, Time: {timestamp}") +else: + print("No trades found.") \ No newline at end of file diff --git a/add_test_trades.py b/add_test_trades.py new file mode 100644 index 0000000..ad43d5d --- /dev/null +++ b/add_test_trades.py @@ -0,0 +1,53 @@ +from datetime import datetime, timedelta +import random +import time +from realtime import RealTimeChart + +# Create a standalone chart instance +chart = RealTimeChart('BTC/USDT') + +# Base price +base_price = 65000.0 + +# Add 5 pairs of trades (BUY followed by SELL) +for i in range(5): + # Create a buy trade + buy_price = base_price + random.uniform(-200, 200) + buy_time = datetime.now() - timedelta(minutes=5-i) # Older to newer + buy_amount = round(random.uniform(0.05, 0.5), 2) + + # Add the BUY trade + chart.add_trade( + price=buy_price, + timestamp=buy_time, + amount=buy_amount, + pnl=None, # Set to None for buys + action='BUY' + ) + print(f"Added BUY trade {i+1}: Price={buy_price:.2f}, Amount={buy_amount}, Time={buy_time}") + + # Wait a moment + time.sleep(0.5) + + # Create a sell trade (typically at a different price) + price_change = random.uniform(-100, 300) # More likely to be positive for profit + sell_price = buy_price + price_change + sell_time = buy_time + timedelta(minutes=random.uniform(0.5, 1.5)) + + # Calculate PnL + pnl = (sell_price - buy_price) * buy_amount + + # Add the SELL trade + chart.add_trade( + price=sell_price, + timestamp=sell_time, + amount=buy_amount, # Same amount as buy + pnl=pnl, + action='SELL' + ) + print(f"Added SELL trade {i+1}: Price={sell_price:.2f}, PnL={pnl:.2f}, Time={sell_time}") + + # Wait a moment before the next pair + time.sleep(0.5) + +print("\nAll trades added successfully!") \ No newline at end of file diff --git a/generate_trades.py b/generate_trades.py new file mode 100644 index 0000000..0625498 --- /dev/null +++ b/generate_trades.py @@ -0,0 +1,11 @@ +from datetime import datetime +from realtime import RealTimeChart + +chart = RealTimeChart('BTC/USDT') + +for i in range(3): + chart.add_trade(price=65000+i*100, timestamp=datetime.now(), pnl=0.1*i, action='BUY') + chart.add_trade(price=65100+i*100, timestamp=datetime.now(), pnl=0.2*i, action='SELL') + print(f'Added trade pair {i+1}') + +print('All trades added successfully') \ No newline at end of file diff --git a/realtime.py b/realtime.py index ecc840f..0bfa47f 100644 --- a/realtime.py +++ b/realtime.py @@ -1519,7 +1519,7 @@ class RealTimeChart: 'color': '#FFFFFF', 'backgroundColor': '#222' }) - ], style={'marginTop': '20px', 'marginBottom': '20px', 'overflowX': 'auto'}) + ], style={'marginTop': '20px', 'marginBottom': '20px', 'overflowX': 'auto'}), # Chart acknowledgment html.Div("Real-time trading chart with ML signals", style={ @@ -2344,42 +2344,177 @@ class RealTimeChart: def _setup_position_list_callback(self): """Callback to update the position list with the latest 5 trades""" @self.app.callback( - Output('position-list', 'children'), + Output('position-table-body', '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 = [] + return [html.Tr([html.Td("No trades yet", colSpan=6, style={'textAlign': 'center', 'color': '#AAAAAA', 'padding': '10px'})])] - for trade in last_positions: + # Collect BUY and SELL trades to pair them + buy_trades = {} + sell_trades = {} + position_rows = [] + + # Process all trades to match buy and sell pairs + for trade in self.trades: 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'} - ) - ) + if action == 'BUY': + # Store buy trades by timestamp to match with sells later + buy_trades[trade.get('timestamp')] = trade + elif action == 'SELL': + # Store sell trades + sell_trades[trade.get('timestamp')] = trade - return position_items + # Create position entries (matched pairs or single trades) + position_entries = [] + + # First add matched pairs (BUY trades with matching close_price) + for timestamp, buy_trade in buy_trades.items(): + if 'close_price' in buy_trade: + # This is a closed position + entry_price = buy_trade.get('price', 'N/A') + exit_price = buy_trade.get('close_price', 'N/A') + entry_time = buy_trade.get('timestamp') + exit_time = buy_trade.get('close_timestamp') + amount = buy_trade.get('amount', 0.1) + + # Calculate PnL if not provided + pnl = buy_trade.get('pnl') + if pnl is None and isinstance(entry_price, (int, float)) and isinstance(exit_price, (int, float)): + pnl = (exit_price - entry_price) * amount + + position_entries.append({ + 'action': 'CLOSED', + 'entry_price': entry_price, + 'exit_price': exit_price, + 'amount': amount, + 'pnl': pnl, + 'time': entry_time, + 'exit_time': exit_time, + 'status': 'CLOSED' + }) + else: + # This is an open position (BUY without a matching SELL) + current_price = self.tick_storage.get_latest_price() or buy_trade.get('price', 0) + amount = buy_trade.get('amount', 0.1) + entry_price = buy_trade.get('price', 0) + + # Calculate unrealized PnL + unrealized_pnl = (current_price - entry_price) * amount if isinstance(current_price, (int, float)) and isinstance(entry_price, (int, float)) else None + + position_entries.append({ + 'action': 'BUY', + 'entry_price': entry_price, + 'exit_price': current_price, # Use current price as potential exit + 'amount': amount, + 'pnl': unrealized_pnl, + 'time': buy_trade.get('timestamp'), + 'status': 'OPEN' + }) + + # Add standalone SELL trades that don't have a matching BUY + for timestamp, sell_trade in sell_trades.items(): + # Check if this SELL is already accounted for in a closed position + already_matched = False + for entry in position_entries: + if entry.get('status') == 'CLOSED' and entry.get('exit_time') == timestamp: + already_matched = True + break + + if not already_matched: + position_entries.append({ + 'action': 'SELL', + 'entry_price': 'N/A', + 'exit_price': sell_trade.get('price', 'N/A'), + 'amount': sell_trade.get('amount', 0.1), + 'pnl': sell_trade.get('pnl'), + 'time': sell_trade.get('timestamp'), + 'status': 'STANDALONE' + }) + + # Sort by time (most recent first) and take last 5 + position_entries.sort(key=lambda x: x['time'] if isinstance(x['time'], datetime) else datetime.now(), reverse=True) + position_entries = position_entries[:5] + + # Convert to table rows + for entry in position_entries: + action = entry['action'] + entry_price = entry['entry_price'] + exit_price = entry['exit_price'] + amount = entry['amount'] + pnl = entry['pnl'] + time_obj = entry['time'] + status = entry['status'] + + # Format time + if isinstance(time_obj, datetime): + # If trade is from a different day, include the date + today = datetime.now().date() + if time_obj.date() == today: + time_str = time_obj.strftime('%H:%M:%S') + else: + time_str = time_obj.strftime('%m-%d %H:%M:%S') + else: + time_str = str(time_obj) + + # Format prices with proper decimal places + if isinstance(entry_price, (int, float)): + entry_price_str = f"${entry_price:.2f}" + else: + entry_price_str = str(entry_price) + + if isinstance(exit_price, (int, float)): + exit_price_str = f"${exit_price:.2f}" + else: + exit_price_str = str(exit_price) + + # Format PnL + if pnl is not None and isinstance(pnl, (int, float)): + pnl_str = f"${pnl:.2f}" + pnl_color = '#00FF00' if pnl >= 0 else '#FF0000' + else: + pnl_str = 'N/A' + pnl_color = '#FFFFFF' + + # Set action/status color and text + if status == 'OPEN': + status_color = '#00AAFF' # Blue for open positions + status_text = "OPEN (BUY)" + elif status == 'CLOSED': + if pnl is not None and isinstance(pnl, (int, float)): + status_color = '#00FF00' if pnl >= 0 else '#FF0000' # Green/Red based on profit + else: + status_color = '#FFCC00' # Yellow if PnL unknown + status_text = "CLOSED" + elif action == 'BUY': + status_color = '#00FF00' + status_text = "BUY" + elif action == 'SELL': + status_color = '#FF0000' + status_text = "SELL" + else: + status_color = '#FFFFFF' + status_text = action + + # Create table row + position_rows.append(html.Tr([ + html.Td(status_text, style={'color': status_color, 'padding': '8px', 'border': '1px solid #444'}), + html.Td(f"{amount} BTC", style={'padding': '8px', 'border': '1px solid #444'}), + html.Td(entry_price_str, style={'padding': '8px', 'border': '1px solid #444'}), + html.Td(exit_price_str, style={'padding': '8px', 'border': '1px solid #444'}), + html.Td(pnl_str, style={'color': pnl_color, 'padding': '8px', 'border': '1px solid #444'}), + html.Td(time_str, style={'padding': '8px', 'border': '1px solid #444'}) + ])) + + return position_rows except Exception as e: - logger.error(f"Error updating position list: {str(e)}") - return [html.Li("Error loading positions", style={'color': '#FF0000'})] + logger.error(f"Error updating position table: {str(e)}") + import traceback + logger.error(traceback.format_exc()) + return [html.Tr([html.Td(f"Error: {str(e)}", colSpan=6, style={'color': '#FF0000', 'padding': '10px'})])] def _interval_to_seconds(self, interval_key: str) -> int: """Convert interval key to seconds""" diff --git a/test_timestamps.py b/test_timestamps.py new file mode 100644 index 0000000..6996a90 --- /dev/null +++ b/test_timestamps.py @@ -0,0 +1,50 @@ +from datetime import datetime, timedelta +from realtime import RealTimeChart + +# Create a chart instance +chart = RealTimeChart('BTC/USDT') + +# Add a BUY position from yesterday +yesterday = datetime.now() - timedelta(days=1) +chart.add_trade( + price=64950.25, + timestamp=yesterday, + amount=0.5, + pnl=None, + action='BUY' +) +print(f'Added BUY position from {yesterday}') + +# Add a matching SELL position from yesterday (2 hours later) +yesterday_plus_2h = yesterday + timedelta(hours=2) +chart.add_trade( + price=65100.75, + timestamp=yesterday_plus_2h, + amount=0.5, + pnl=75.25, + action='SELL' +) +print(f'Added matching SELL position from {yesterday_plus_2h}') + +# Add a trade from 2 days ago +two_days_ago = datetime.now() - timedelta(days=2) +chart.add_trade( + price=64800.50, + timestamp=two_days_ago, + amount=0.25, + pnl=None, + action='BUY' +) +print(f'Added BUY position from {two_days_ago}') + +two_days_ago_plus_3h = two_days_ago + timedelta(hours=3) +chart.add_trade( + price=65000.75, + timestamp=two_days_ago_plus_3h, + amount=0.25, + pnl=50.06, + action='SELL' +) +print(f'Added matching SELL position from {two_days_ago_plus_3h}') + +print('\nAll test trades added successfully!') \ No newline at end of file