fixed showing all moves

This commit is contained in:
Dobromir Popov 2025-04-01 21:22:02 +03:00
parent 44b02b4e7d
commit b0a57c5330
2 changed files with 108 additions and 24 deletions

View File

@ -30,6 +30,9 @@ class BinanceHistoricalData:
""" """
def __init__(self): def __init__(self):
self.base_url = "https://api.binance.com/api/v3" self.base_url = "https://api.binance.com/api/v3"
self.cache_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'cache')
if not os.path.exists(self.cache_dir):
os.makedirs(self.cache_dir)
def get_historical_candles(self, symbol, interval_seconds=3600, limit=1000): def get_historical_candles(self, symbol, interval_seconds=3600, limit=1000):
""" """
@ -60,6 +63,14 @@ class BinanceHistoricalData:
# Format symbol for Binance API (remove slash) # Format symbol for Binance API (remove slash)
formatted_symbol = symbol.replace("/", "") formatted_symbol = symbol.replace("/", "")
# Check if we have cached data first
cache_file = self._get_cache_filename(formatted_symbol, interval)
cached_data = self._load_from_cache(formatted_symbol, interval)
if cached_data is not None and len(cached_data) >= limit:
logger.info(f"Using cached historical data for {symbol} ({interval})")
return cached_data
try: try:
# Build URL for klines endpoint # Build URL for klines endpoint
url = f"{self.base_url}/klines" url = f"{self.base_url}/klines"
@ -93,14 +104,48 @@ class BinanceHistoricalData:
# Sort by timestamp # Sort by timestamp
df = df.sort_values("timestamp") df = df.sort_values("timestamp")
# Save to cache for future use
self._save_to_cache(df, formatted_symbol, interval)
logger.info(f"Fetched {len(df)} candles for {symbol} ({interval})") logger.info(f"Fetched {len(df)} candles for {symbol} ({interval})")
return df return df
except Exception as e: except Exception as e:
logger.error(f"Error fetching historical data from Binance: {str(e)}") logger.error(f"Error fetching historical data from Binance: {str(e)}")
# Return cached data if we have it, even if it's not enough
if cached_data is not None:
logger.warning(f"Using cached data instead (may be incomplete)")
return cached_data
# Return empty dataframe on error # Return empty dataframe on error
return pd.DataFrame() return pd.DataFrame()
def _get_cache_filename(self, symbol, interval):
"""Get filename for cache file"""
return os.path.join(self.cache_dir, f"{symbol}_{interval}_candles.csv")
def _load_from_cache(self, symbol, interval):
"""Load candles from cache file"""
try:
cache_file = self._get_cache_filename(symbol, interval)
if os.path.exists(cache_file):
df = pd.read_csv(cache_file)
df["timestamp"] = pd.to_datetime(df["timestamp"])
return df
except Exception as e:
logger.error(f"Error loading cached data: {str(e)}")
return None
def _save_to_cache(self, df, symbol, interval):
"""Save candles to cache file"""
try:
cache_file = self._get_cache_filename(symbol, interval)
df.to_csv(cache_file, index=False)
logger.info(f"Saved {len(df)} candles to cache: {cache_file}")
return True
except Exception as e:
logger.error(f"Error saving to cache: {str(e)}")
return False
def get_recent_trades(self, symbol, limit=1000): def get_recent_trades(self, symbol, limit=1000):
"""Get recent trades for a symbol""" """Get recent trades for a symbol"""
formatted_symbol = symbol.replace("/", "") formatted_symbol = symbol.replace("/", "")
@ -553,6 +598,7 @@ class TickStorage:
try: try:
# Load data for different timeframes # Load data for different timeframes
timeframes = [ timeframes = [
(1, '1s'), # 1 second
(60, '1m'), # 1 minute (60, '1m'), # 1 minute
(300, '5m'), # 5 minutes (300, '5m'), # 5 minutes
(900, '15m'), # 15 minutes (900, '15m'), # 15 minutes
@ -562,7 +608,22 @@ class TickStorage:
] ]
for interval_seconds, interval_key in timeframes: for interval_seconds, interval_key in timeframes:
df = historical_data.get_historical_candles(symbol, interval_seconds) # Set appropriate limits based on timeframe
limit = 1000 # Default
if interval_seconds == 1:
limit = 500 # 1s is too much data, limit to 500
elif interval_seconds < 60:
limit = 750 # For seconds-level data
elif interval_seconds < 300:
limit = 1000 # 1m
elif interval_seconds < 900:
limit = 500 # 5m
elif interval_seconds < 3600:
limit = 300 # 15m
else:
limit = 200 # hourly/daily data
df = historical_data.get_historical_candles(symbol, interval_seconds, limit)
if df is not None and not df.empty: if df is not None and not df.empty:
logger.info(f"Loaded {len(df)} historical candles for {symbol} ({interval_key})") logger.info(f"Loaded {len(df)} historical candles for {symbol} ({interval_key})")
@ -578,12 +639,14 @@ class TickStorage:
} }
self.candles[interval_key].append(candle) self.candles[interval_key].append(candle)
# Also use the close price to simulate ticks # For 1m and above, also use the close price to simulate ticks
self.add_tick( # but don't do this for seconds-level data as it creates too many ticks
price=row['close'], if interval_seconds >= 60 and interval_key == '1m':
volume=row['volume'], self.add_tick(
timestamp=row['timestamp'] price=row['close'],
) volume=row['volume'],
timestamp=row['timestamp']
)
# Update latest price from most recent candle # Update latest price from most recent candle
if len(df) > 0: if len(df) > 0:
@ -904,14 +967,19 @@ class RealTimeChart:
sell_times = [] sell_times = []
sell_prices = [] sell_prices = []
# Use only last 20 positions for clarity # Use all positions for chart display
for position in self.positions[-20:]: # Filter recent ones based on visible time range
if position.action == "BUY": now = datetime.now()
buy_times.append(position.entry_timestamp) time_limit = now - timedelta(hours=24) # Show at most 24h of trades
buy_prices.append(position.entry_price)
elif position.action == "SELL" and position.exit_timestamp: for position in self.positions:
sell_times.append(position.exit_timestamp) if position.entry_timestamp > time_limit:
sell_prices.append(position.exit_price) if position.action == "BUY":
buy_times.append(position.entry_timestamp)
buy_prices.append(position.entry_price)
elif position.action == "SELL" and position.exit_timestamp:
sell_times.append(position.exit_timestamp)
sell_prices.append(position.exit_price)
# Add buy markers (green triangles pointing up) # Add buy markers (green triangles pointing up)
if buy_times: if buy_times:
@ -1112,14 +1180,17 @@ class RealTimeChart:
winning_trades = sum(1 for p in self.positions if p.pnl and p.pnl > 0) winning_trades = sum(1 for p in self.positions if p.pnl and p.pnl > 0)
win_rate = winning_trades / total_trades * 100 if total_trades > 0 else 0 win_rate = winning_trades / total_trades * 100 if total_trades > 0 else 0
# Format display colors for PnL
pnl_color = "green" if self.accumulative_pnl >= 0 else "red"
summary_row = html.Tr([ summary_row = html.Tr([
html.Td("SUMMARY", colSpan=2, style={"fontWeight": "bold"}), html.Td("SUMMARY", colSpan=2, style={"fontWeight": "bold"}),
html.Td(f"Trades: {total_trades}"), html.Td(f"Trades: {total_trades}"),
html.Td(f"Win Rate: {win_rate:.1f}%"), html.Td(f"Win Rate: {win_rate:.1f}%"),
html.Td("Total PnL:", style={"fontWeight": "bold"}), html.Td("Total PnL:", style={"fontWeight": "bold"}),
html.Td(f"${self.accumulative_pnl:.2f}", html.Td(f"${self.accumulative_pnl:.2f}",
style={"color": "green" if self.accumulative_pnl >= 0 else "red", "fontWeight": "bold"}), style={"color": pnl_color, "fontWeight": "bold"}),
html.Td("") html.Td(f"Balance: ${self.current_balance:.2f}")
], style={"backgroundColor": "rgba(80, 80, 80, 0.3)"}) ], style={"backgroundColor": "rgba(80, 80, 80, 0.3)"})
# Create the table with improved styling # Create the table with improved styling

View File

@ -516,17 +516,27 @@ class RLTrainingIntegrator:
if len(self.price_history) > self.price_history_max_len: if len(self.price_history) > self.price_history_max_len:
self.price_history = self.price_history[-self.price_history_max_len:] self.price_history = self.price_history[-self.price_history_max_len:]
# Normalize rewards to be realistic for crypto trading (smaller values)
normalized_reward = reward * 0.1 # Scale down rewards
if abs(normalized_reward) > 5.0: # Cap maximum reward value
normalized_reward = 5.0 if normalized_reward > 0 else -5.0
# Update session PnL and balance # Update session PnL and balance
self.session_step += 1 self.session_step += 1
self.session_pnl += reward self.session_pnl += normalized_reward
# Increase balance based on reward # Increase balance based on reward - cap to reasonable values
self.session_balance += reward self.session_balance += normalized_reward
self.session_balance = min(self.session_balance, 1000.0) # Cap maximum balance
self.session_balance = max(self.session_balance, 0.0) # Prevent negative balance
# Update chart's accumulativePnL and balance if available # Update chart's accumulativePnL and balance if available
if self.chart: if self.chart:
if hasattr(self.chart, 'accumulative_pnl'): if hasattr(self.chart, 'accumulative_pnl'):
self.chart.accumulative_pnl = self.session_pnl self.chart.accumulative_pnl = self.session_pnl
# Cap accumulated PnL to reasonable values
self.chart.accumulative_pnl = min(self.chart.accumulative_pnl, 500.0)
self.chart.accumulative_pnl = max(self.chart.accumulative_pnl, -100.0)
if hasattr(self.chart, 'current_balance'): if hasattr(self.chart, 'current_balance'):
self.chart.current_balance = self.session_balance self.chart.current_balance = self.session_balance
@ -751,7 +761,7 @@ def _add_trade_compat(chart, price, timestamp, amount, pnl=0.0, action="BUY"):
position.close(price, timestamp) position.close(price, timestamp)
# Use realistic PnL values rather than the enormous ones from the model # Use realistic PnL values rather than the enormous ones from the model
# Cap PnL to reasonable values based on position size and price # Cap PnL to reasonable values based on position size and price
max_reasonable_pnl = price * amount * 0.10 # Max 10% profit max_reasonable_pnl = price * amount * 0.05 # Max 5% profit per trade
if abs(pnl) > max_reasonable_pnl: if abs(pnl) > max_reasonable_pnl:
if pnl > 0: if pnl > 0:
pnl = max_reasonable_pnl * 0.8 # Positive but reasonable pnl = max_reasonable_pnl * 0.8 # Positive but reasonable
@ -762,11 +772,14 @@ def _add_trade_compat(chart, price, timestamp, amount, pnl=0.0, action="BUY"):
# Update chart's accumulated PnL if available # Update chart's accumulated PnL if available
if hasattr(chart, 'accumulative_pnl'): if hasattr(chart, 'accumulative_pnl'):
chart.accumulative_pnl += pnl chart.accumulative_pnl += pnl
# Cap accumulated PnL to reasonable values
chart.accumulative_pnl = min(chart.accumulative_pnl, 500.0)
chart.accumulative_pnl = max(chart.accumulative_pnl, -100.0)
# Add to positions list, keeping only the last 10 if we have more # Add to positions list, keeping only the last 200 for chart display
chart.positions.append(position) chart.positions.append(position)
if len(chart.positions) > 10: if len(chart.positions) > 200:
chart.positions = chart.positions[-10:] chart.positions = chart.positions[-200:]
logger.info(f"Added {action} trade: price={price:.2f}, amount={amount}, pnl={pnl:.2f}") logger.info(f"Added {action} trade: price={price:.2f}, amount={amount}, pnl={pnl:.2f}")
return True return True