diff --git a/_notes.md b/_notes.md
index 6d71aa8..8c51da0 100644
--- a/_notes.md
+++ b/_notes.md
@@ -55,4 +55,6 @@ python test_model.py
python train_with_realtime_ticks.py
python NN/train_rl.py
-python train_rl_with_realtime.py
\ No newline at end of file
+python train_rl_with_realtime.py
+
+python train_rl_with_realtime.py --episodes 2 --no-train --visualize-only
\ No newline at end of file
diff --git a/realtime.py b/realtime.py
index 7323f3d..8d8aa34 100644
--- a/realtime.py
+++ b/realtime.py
@@ -1,7 +1,7 @@
import asyncio
import json
import logging
-import datetime
+
from typing import Dict, List, Optional
import websockets
import plotly.graph_objects as go
@@ -1694,13 +1694,13 @@ class RealTimeChart:
else:
df_1d = self.tick_storage.get_candles(interval_seconds=86400)
- # Limit the number of candles to display
+ # Limit the number of candles to display but show more for context
if df_1m is not None and not df_1m.empty:
- df_1m = df_1m.tail(100)
+ df_1m = df_1m.tail(200) # Show more 1m candles (3+ hours)
if df_1h is not None and not df_1h.empty:
- df_1h = df_1h.tail(48) # Last 2 days
+ df_1h = df_1h.tail(72) # Show 3 days of hourly data
if df_1d is not None and not df_1d.empty:
- df_1d = df_1d.tail(30) # Last month
+ df_1d = df_1d.tail(60) # Show 2 months of daily data
except Exception as e:
logger.error(f"Error getting additional timeframes: {str(e)}")
@@ -1757,6 +1757,155 @@ class RealTimeChart:
),
row=2, col=1
)
+
+ # Add buy/sell markers and PnL annotations to the main chart
+ if hasattr(self, 'trades') and self.trades:
+ buy_times = []
+ buy_prices = []
+ buy_markers = []
+ sell_times = []
+ sell_prices = []
+ sell_markers = []
+
+ # Filter trades to only include recent ones (last 100)
+ recent_trades = self.trades[-100:]
+
+ for trade in recent_trades:
+ # Convert timestamp to datetime if it's not already
+ trade_time = trade.get('timestamp')
+ if isinstance(trade_time, (int, float)):
+ trade_time = pd.to_datetime(trade_time, unit='ms')
+
+ price = trade.get('price', 0)
+ pnl = trade.get('pnl', None)
+ action = trade.get('action', 'SELL') # Default to SELL
+
+ if action == 'BUY':
+ buy_times.append(trade_time)
+ buy_prices.append(price)
+ buy_markers.append("")
+ elif action == 'SELL':
+ sell_times.append(trade_time)
+ sell_prices.append(price)
+ # Add PnL as marker text if available
+ if pnl is not None:
+ pnl_text = f"{pnl:.4f}" if abs(pnl) < 0.01 else f"{pnl:.2f}"
+ sell_markers.append(pnl_text)
+ else:
+ sell_markers.append("")
+
+ # Add buy markers
+ if buy_times:
+ fig.add_trace(
+ go.Scatter(
+ x=buy_times,
+ y=buy_prices,
+ mode='markers',
+ name='Buy',
+ marker=dict(
+ symbol='triangle-up',
+ size=12,
+ color='rgba(0,255,0,0.8)',
+ line=dict(width=1, color='darkgreen')
+ ),
+ text=buy_markers,
+ hoverinfo='x+y+text'
+ ),
+ row=1, col=1
+ )
+
+ # Add vertical and horizontal connecting lines for buys
+ for i, (btime, bprice) in enumerate(zip(buy_times, buy_prices)):
+ # Add vertical dashed line to time axis
+ fig.add_shape(
+ type="line",
+ x0=btime, x1=btime,
+ y0=y_min, y1=bprice,
+ line=dict(color="rgba(0,255,0,0.5)", width=1, dash="dash"),
+ row=1, col=1
+ )
+ # Add horizontal dashed line showing the price level
+ fig.add_shape(
+ type="line",
+ x0=df.index.min(), x1=btime,
+ y0=bprice, y1=bprice,
+ line=dict(color="rgba(0,255,0,0.5)", width=1, dash="dash"),
+ row=1, col=1
+ )
+
+ # Add sell markers with PnL annotations
+ if sell_times:
+ fig.add_trace(
+ go.Scatter(
+ x=sell_times,
+ y=sell_prices,
+ mode='markers+text',
+ name='Sell',
+ marker=dict(
+ symbol='triangle-down',
+ size=12,
+ color='rgba(255,0,0,0.8)',
+ line=dict(width=1, color='darkred')
+ ),
+ text=sell_markers,
+ textposition='top center',
+ textfont=dict(size=10),
+ hoverinfo='x+y+text'
+ ),
+ row=1, col=1
+ )
+
+ # Add vertical and horizontal connecting lines for sells
+ for i, (stime, sprice) in enumerate(zip(sell_times, sell_prices)):
+ # Add vertical dashed line to time axis
+ fig.add_shape(
+ type="line",
+ x0=stime, x1=stime,
+ y0=y_min, y1=sprice,
+ line=dict(color="rgba(255,0,0,0.5)", width=1, dash="dash"),
+ row=1, col=1
+ )
+ # Add horizontal dashed line showing the price level
+ fig.add_shape(
+ type="line",
+ x0=df.index.min(), x1=stime,
+ y0=sprice, y1=sprice,
+ line=dict(color="rgba(255,0,0,0.5)", width=1, dash="dash"),
+ row=1, col=1
+ )
+
+ # Add connecting lines between consecutive buy-sell pairs
+ if len(buy_times) > 0 and len(sell_times) > 0:
+ # Create pairs of buy-sell trades based on timestamps
+ pairs = []
+ buys_copy = list(zip(buy_times, buy_prices))
+
+ for i, (stime, sprice) in enumerate(zip(sell_times, sell_prices)):
+ # Find the most recent buy before this sell
+ matching_buy = None
+ for j, (btime, bprice) in enumerate(buys_copy):
+ if btime < stime:
+ matching_buy = (btime, bprice)
+ buys_copy.pop(j) # Remove this buy to prevent reuse
+ break
+
+ if matching_buy:
+ pairs.append((matching_buy, (stime, sprice)))
+
+ # Add connecting lines for each pair
+ for (btime, bprice), (stime, sprice) in pairs:
+ # Draw line connecting the buy and sell points
+ fig.add_shape(
+ type="line",
+ x0=btime, x1=stime,
+ y0=bprice, y1=sprice,
+ line=dict(
+ color="rgba(255,255,255,0.5)",
+ width=1,
+ dash="dot"
+ ),
+ row=1, col=1
+ )
# Add 1m chart
if df_1m is not None and not df_1m.empty and 'open' in df_1m.columns:
@@ -1772,7 +1921,100 @@ class RealTimeChart:
),
row=3, col=1
)
- fig.update_xaxes(title_text="", row=3, col=1)
+
+ # Set appropriate date format for 1m chart
+ fig.update_xaxes(
+ title_text="",
+ row=3,
+ col=1,
+ tickformat="%H:%M",
+ tickmode="auto",
+ nticks=12
+ )
+
+ # Add buy/sell markers to 1m chart if they fall within the visible timeframe
+ if hasattr(self, 'trades') and self.trades:
+ # Filter trades visible in 1m timeframe
+ min_time = df_1m.index.min()
+ max_time = df_1m.index.max()
+
+ # Ensure min_time and max_time are pandas.Timestamp objects
+ if isinstance(min_time, (int, float)):
+ min_time = pd.to_datetime(min_time, unit='ms')
+ if isinstance(max_time, (int, float)):
+ max_time = pd.to_datetime(max_time, unit='ms')
+
+ # Collect only trades within this timeframe
+ minute_buy_times = []
+ minute_buy_prices = []
+ minute_sell_times = []
+ minute_sell_prices = []
+
+ for trade in self.trades[-100:]:
+ trade_time = trade.get('timestamp')
+ if isinstance(trade_time, (int, float)):
+ # Convert numeric timestamp to datetime
+ trade_time = pd.to_datetime(trade_time, unit='ms')
+ elif not isinstance(trade_time, pd.Timestamp) and not isinstance(trade_time, datetime):
+ # Skip trades with invalid timestamp format
+ continue
+
+ # Check if trade falls within 1m chart timeframe
+ try:
+ if min_time <= trade_time <= max_time:
+ price = trade.get('price', 0)
+ action = trade.get('action', 'SELL')
+
+ if action == 'BUY':
+ minute_buy_times.append(trade_time)
+ minute_buy_prices.append(price)
+ elif action == 'SELL':
+ minute_sell_times.append(trade_time)
+ minute_sell_prices.append(price)
+ except TypeError:
+ # If comparison fails due to type mismatch, log the error and skip this trade
+ logger.warning(f"Type mismatch in timestamp comparison: min_time={type(min_time)}, trade_time={type(trade_time)}")
+ continue
+
+ # Add buy markers to 1m chart
+ if minute_buy_times:
+ fig.add_trace(
+ go.Scatter(
+ x=minute_buy_times,
+ y=minute_buy_prices,
+ mode='markers',
+ name='Buy (1m)',
+ marker=dict(
+ symbol='triangle-up',
+ size=8,
+ color='rgba(0,255,0,0.8)',
+ line=dict(width=1, color='darkgreen')
+ ),
+ showlegend=False,
+ hoverinfo='x+y'
+ ),
+ row=3, col=1
+ )
+
+ # Add sell markers to 1m chart
+ if minute_sell_times:
+ fig.add_trace(
+ go.Scatter(
+ x=minute_sell_times,
+ y=minute_sell_prices,
+ mode='markers',
+ name='Sell (1m)',
+ marker=dict(
+ symbol='triangle-down',
+ size=8,
+ color='rgba(255,0,0,0.8)',
+ line=dict(width=1, color='darkred')
+ ),
+ showlegend=False,
+ hoverinfo='x+y'
+ ),
+ row=3, col=1
+ )
# Add 1h chart
if df_1h is not None and not df_1h.empty and 'open' in df_1h.columns:
@@ -1788,8 +2030,100 @@ class RealTimeChart:
),
row=4, col=1
)
- fig.update_xaxes(title_text="", row=4, col=1)
+ # Set appropriate date format for 1h chart
+ fig.update_xaxes(
+ title_text="",
+ row=4,
+ col=1,
+ tickformat="%m-%d %H:%M",
+ tickmode="auto",
+ nticks=8
+ )
+
+ # Add buy/sell markers to 1h chart if they fall within the visible timeframe
+ if hasattr(self, 'trades') and self.trades:
+ # Filter trades visible in 1h timeframe
+ min_time = df_1h.index.min()
+ max_time = df_1h.index.max()
+
+ # Ensure min_time and max_time are pandas.Timestamp objects
+ if isinstance(min_time, (int, float)):
+ min_time = pd.to_datetime(min_time, unit='ms')
+ if isinstance(max_time, (int, float)):
+ max_time = pd.to_datetime(max_time, unit='ms')
+
+ # Collect only trades within this timeframe
+ hour_buy_times = []
+ hour_buy_prices = []
+ hour_sell_times = []
+ hour_sell_prices = []
+
+ for trade in self.trades[-200:]: # Check more trades for longer timeframe
+ trade_time = trade.get('timestamp')
+ if isinstance(trade_time, (int, float)):
+ # Convert numeric timestamp to datetime
+ trade_time = pd.to_datetime(trade_time, unit='ms')
+ elif not isinstance(trade_time, pd.Timestamp) and not isinstance(trade_time, datetime):
+ # Skip trades with invalid timestamp format
+ continue
+
+ # Check if trade falls within 1h chart timeframe
+ try:
+ if min_time <= trade_time <= max_time:
+ price = trade.get('price', 0)
+ action = trade.get('action', 'SELL')
+
+ if action == 'BUY':
+ hour_buy_times.append(trade_time)
+ hour_buy_prices.append(price)
+ elif action == 'SELL':
+ hour_sell_times.append(trade_time)
+ hour_sell_prices.append(price)
+ except TypeError:
+ # If comparison fails due to type mismatch, log the error and skip this trade
+ logger.warning(f"Type mismatch in timestamp comparison: min_time={type(min_time)}, trade_time={type(trade_time)}")
+ continue
+
+ # Add buy markers to 1h chart
+ if hour_buy_times:
+ fig.add_trace(
+ go.Scatter(
+ x=hour_buy_times,
+ y=hour_buy_prices,
+ mode='markers',
+ name='Buy (1h)',
+ marker=dict(
+ symbol='triangle-up',
+ size=6,
+ color='rgba(0,255,0,0.8)',
+ line=dict(width=1, color='darkgreen')
+ ),
+ showlegend=False,
+ hoverinfo='x+y'
+ ),
+ row=4, col=1
+ )
+
+ # Add sell markers to 1h chart
+ if hour_sell_times:
+ fig.add_trace(
+ go.Scatter(
+ x=hour_sell_times,
+ y=hour_sell_prices,
+ mode='markers',
+ name='Sell (1h)',
+ marker=dict(
+ symbol='triangle-down',
+ size=6,
+ color='rgba(255,0,0,0.8)',
+ line=dict(width=1, color='darkred')
+ ),
+ showlegend=False,
+ hoverinfo='x+y'
+ ),
+ row=4, col=1
+ )
# Add 1d chart
if df_1d is not None and not df_1d.empty and 'open' in df_1d.columns:
fig.add_trace(
@@ -1804,7 +2138,100 @@ class RealTimeChart:
),
row=5, col=1
)
- fig.update_xaxes(title_text="", row=5, col=1)
+
+ # Set appropriate date format for 1d chart
+ fig.update_xaxes(
+ title_text="",
+ row=5,
+ col=1,
+ tickformat="%Y-%m-%d",
+ tickmode="auto",
+ nticks=10
+ )
+
+ # Add buy/sell markers to 1d chart if they fall within the visible timeframe
+ if hasattr(self, 'trades') and self.trades:
+ # Filter trades visible in 1d timeframe
+ min_time = df_1d.index.min()
+ max_time = df_1d.index.max()
+
+ # Ensure min_time and max_time are pandas.Timestamp objects
+ if isinstance(min_time, (int, float)):
+ min_time = pd.to_datetime(min_time, unit='ms')
+ if isinstance(max_time, (int, float)):
+ max_time = pd.to_datetime(max_time, unit='ms')
+
+ # Collect only trades within this timeframe
+ day_buy_times = []
+ day_buy_prices = []
+ day_sell_times = []
+ day_sell_prices = []
+
+ for trade in self.trades[-300:]: # Check more trades for daily timeframe
+ trade_time = trade.get('timestamp')
+ if isinstance(trade_time, (int, float)):
+ # Convert numeric timestamp to datetime
+ trade_time = pd.to_datetime(trade_time, unit='ms')
+ elif not isinstance(trade_time, pd.Timestamp) and not isinstance(trade_time, datetime):
+ # Skip trades with invalid timestamp format
+ continue
+
+ # Check if trade falls within 1d chart timeframe
+ try:
+ if min_time <= trade_time <= max_time:
+ price = trade.get('price', 0)
+ action = trade.get('action', 'SELL')
+
+ if action == 'BUY':
+ day_buy_times.append(trade_time)
+ day_buy_prices.append(price)
+ elif action == 'SELL':
+ day_sell_times.append(trade_time)
+ day_sell_prices.append(price)
+ except TypeError:
+ # If comparison fails due to type mismatch, log the error and skip this trade
+ logger.warning(f"Type mismatch in timestamp comparison: min_time={type(min_time)}, trade_time={type(trade_time)}")
+ continue
+
+ # Add buy markers to 1d chart
+ if day_buy_times:
+ fig.add_trace(
+ go.Scatter(
+ x=day_buy_times,
+ y=day_buy_prices,
+ mode='markers',
+ name='Buy (1d)',
+ marker=dict(
+ symbol='triangle-up',
+ size=5,
+ color='rgba(0,255,0,0.8)',
+ line=dict(width=1, color='darkgreen')
+ ),
+ showlegend=False,
+ hoverinfo='x+y'
+ ),
+ row=5, col=1
+ )
+
+ # Add sell markers to 1d chart
+ if day_sell_times:
+ fig.add_trace(
+ go.Scatter(
+ x=day_sell_times,
+ y=day_sell_prices,
+ mode='markers',
+ name='Sell (1d)',
+ marker=dict(
+ symbol='triangle-down',
+ size=5,
+ color='rgba(255,0,0,0.8)',
+ line=dict(width=1, color='darkred')
+ ),
+ showlegend=False,
+ hoverinfo='x+y'
+ ),
+ row=5, col=1
+ )
# Add trading info annotation if available
if hasattr(self, 'current_signal') and self.current_signal:
@@ -1813,12 +2240,16 @@ class RealTimeChart:
# Format position value
position_text = f"{self.current_position:.4f}" if self.current_position < 0.01 else f"{self.current_position:.2f}"
+ # Format PnL with color based on value
+ pnl_color = "#33DD33" if self.session_pnl >= 0 else "#FF4444"
+ pnl_text = f"{self.session_pnl:.4f}"
+
# Create trading info text
info_text = (
f"Signal: {self.current_signal} | "
f"Position: {position_text} | "
f"Balance: ${self.session_balance:.2f} | "
- f"PnL: {self.session_pnl:.4f}"
+ f"PnL: {pnl_text}"
)
# Add annotation
@@ -1858,10 +2289,22 @@ class RealTimeChart:
xaxis5_rangeslider_visible=False
)
+ # Improve date formatting for the main chart
+ fig.update_xaxes(
+ title_text="",
+ row=1,
+ col=1,
+ tickformat="%H:%M:%S",
+ tickmode="auto",
+ nticks=15
+ )
+
return fig
except Exception as e:
logger.error(f"Error updating chart: {str(e)}")
+ import traceback
+ logger.error(traceback.format_exc())
fig = go.Figure()
fig.add_annotation(
x=0.5, y=0.5,
@@ -1958,6 +2401,7 @@ class RealTimeChart:
tick_count += 1
# Also update the old candlestick data for backward compatibility
+ # Add check to ensure the candlestick_data attribute exists before using it
if hasattr(self, 'candlestick_data'):
self.candlestick_data.update_from_trade(trade_data)
@@ -2403,4 +2847,4 @@ if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
- logger.info("Application terminated by user")
\ No newline at end of file
+ logger.info("Application terminated by user")
diff --git a/train_rl_with_realtime.py b/train_rl_with_realtime.py
index ecb8945..5d67ddb 100644
--- a/train_rl_with_realtime.py
+++ b/train_rl_with_realtime.py
@@ -534,7 +534,7 @@ def run_training_thread(chart):
def training_thread_func():
try:
# Use a small number of episodes to test termination handling
- integrator.start_training(num_episodes=2, max_steps=500)
+ integrator.start_training(num_episodes=100, max_steps=2000)
except Exception as e:
logger.error(f"Error in training thread: {str(e)}")