recent trades in list
This commit is contained in:
parent
989e755264
commit
3eff11fd78
46
access_app.py
Normal file
46
access_app.py
Normal file
@ -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.")
|
53
add_test_trades.py
Normal file
53
add_test_trades.py
Normal file
@ -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!")
|
11
generate_trades.py
Normal file
11
generate_trades.py
Normal file
@ -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')
|
189
realtime.py
189
realtime.py
@ -1519,7 +1519,7 @@ class RealTimeChart:
|
|||||||
'color': '#FFFFFF',
|
'color': '#FFFFFF',
|
||||||
'backgroundColor': '#222'
|
'backgroundColor': '#222'
|
||||||
})
|
})
|
||||||
], style={'marginTop': '20px', 'marginBottom': '20px', 'overflowX': 'auto'})
|
], style={'marginTop': '20px', 'marginBottom': '20px', 'overflowX': 'auto'}),
|
||||||
|
|
||||||
# Chart acknowledgment
|
# Chart acknowledgment
|
||||||
html.Div("Real-time trading chart with ML signals", style={
|
html.Div("Real-time trading chart with ML signals", style={
|
||||||
@ -2344,42 +2344,177 @@ class RealTimeChart:
|
|||||||
def _setup_position_list_callback(self):
|
def _setup_position_list_callback(self):
|
||||||
"""Callback to update the position list with the latest 5 trades"""
|
"""Callback to update the position list with the latest 5 trades"""
|
||||||
@self.app.callback(
|
@self.app.callback(
|
||||||
Output('position-list', 'children'),
|
Output('position-table-body', 'children'),
|
||||||
[Input('interval-component', 'n_intervals')]
|
[Input('interval-component', 'n_intervals')]
|
||||||
)
|
)
|
||||||
def update_position_list(n):
|
def update_position_list(n):
|
||||||
try:
|
try:
|
||||||
# Get the last 5 positions
|
# Get the last 5 positions
|
||||||
if not hasattr(self, 'trades') or not self.trades:
|
if not hasattr(self, 'trades') or not self.trades:
|
||||||
return [html.Li("No trades yet", style={'color': '#AAAAAA'})]
|
return [html.Tr([html.Td("No trades yet", colSpan=6, style={'textAlign': 'center', 'color': '#AAAAAA', 'padding': '10px'})])]
|
||||||
|
|
||||||
last_positions = self.trades[-5:]
|
|
||||||
position_items = []
|
|
||||||
|
|
||||||
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')
|
action = trade.get('action', 'UNKNOWN')
|
||||||
price = trade.get('price', 'N/A')
|
if action == 'BUY':
|
||||||
timestamp = trade.get('timestamp', 'N/A')
|
# Store buy trades by timestamp to match with sells later
|
||||||
pnl = trade.get('pnl', None)
|
buy_trades[trade.get('timestamp')] = trade
|
||||||
|
elif action == 'SELL':
|
||||||
# Format the display
|
# Store sell trades
|
||||||
pnl_str = f", PnL: {pnl:.4f}" if pnl is not None else ""
|
sell_trades[trade.get('timestamp')] = trade
|
||||||
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'}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
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:
|
except Exception as e:
|
||||||
logger.error(f"Error updating position list: {str(e)}")
|
logger.error(f"Error updating position table: {str(e)}")
|
||||||
return [html.Li("Error loading positions", style={'color': '#FF0000'})]
|
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:
|
def _interval_to_seconds(self, interval_key: str) -> int:
|
||||||
"""Convert interval key to seconds"""
|
"""Convert interval key to seconds"""
|
||||||
|
50
test_timestamps.py
Normal file
50
test_timestamps.py
Normal file
@ -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!')
|
Loading…
x
Reference in New Issue
Block a user