better charts
This commit is contained in:
parent
cb3ec0602c
commit
fff5e8cfb7
@ -56,3 +56,5 @@ python test_model.py
|
||||
python train_with_realtime_ticks.py
|
||||
python NN/train_rl.py
|
||||
python train_rl_with_realtime.py
|
||||
|
||||
python train_rl_with_realtime.py --episodes 2 --no-train --visualize-only
|
462
realtime.py
462
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)}")
|
||||
@ -1758,6 +1758,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:
|
||||
fig.add_trace(
|
||||
@ -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"<b style='color:{pnl_color}'>{self.session_pnl:.4f}</b>"
|
||||
|
||||
# Create trading info text
|
||||
info_text = (
|
||||
f"Signal: <b style='color:{signal_color}'>{self.current_signal}</b> | "
|
||||
f"Position: <b>{position_text}</b> | "
|
||||
f"Balance: <b>${self.session_balance:.2f}</b> | "
|
||||
f"PnL: <b>{self.session_pnl:.4f}</b>"
|
||||
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)
|
||||
|
||||
|
@ -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)}")
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user