added plots; fixes
This commit is contained in:
parent
d9d0ba9da8
commit
469d681c4b
43
crypto/gogo2/.vscode/launch.json
vendored
43
crypto/gogo2/.vscode/launch.json
vendored
@ -24,18 +24,53 @@
|
|||||||
"type": "python",
|
"type": "python",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "main.py",
|
"program": "main.py",
|
||||||
"args": ["--mode", "live", "--demo"],
|
"args": [
|
||||||
|
"--mode", "live",
|
||||||
|
"--demo", "true",
|
||||||
|
"--symbol", "ETH/USDT",
|
||||||
|
"--timeframe", "1m"
|
||||||
|
],
|
||||||
"console": "integratedTerminal",
|
"console": "integratedTerminal",
|
||||||
"justMyCode": true
|
"justMyCode": true,
|
||||||
|
"env": {
|
||||||
|
"PYTHONUNBUFFERED": "1"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Live Trading (Real)",
|
"name": "Live Trading (Real)",
|
||||||
"type": "python",
|
"type": "python",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "main.py",
|
"program": "main.py",
|
||||||
"args": ["--mode", "live"],
|
"args": [
|
||||||
|
"--mode", "live",
|
||||||
|
"--demo", "false",
|
||||||
|
"--symbol", "ETH/USDT",
|
||||||
|
"--timeframe", "1m",
|
||||||
|
"--leverage", "50"
|
||||||
|
],
|
||||||
"console": "integratedTerminal",
|
"console": "integratedTerminal",
|
||||||
"justMyCode": true
|
"justMyCode": true,
|
||||||
|
"env": {
|
||||||
|
"PYTHONUNBUFFERED": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Live Trading (BTC Futures)",
|
||||||
|
"type": "python",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "main.py",
|
||||||
|
"args": [
|
||||||
|
"--mode", "live",
|
||||||
|
"--demo", "false",
|
||||||
|
"--symbol", "BTC/USDT",
|
||||||
|
"--timeframe", "5m",
|
||||||
|
"--leverage", "20"
|
||||||
|
],
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"justMyCode": true,
|
||||||
|
"env": {
|
||||||
|
"PYTHONUNBUFFERED": "1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -26,6 +26,8 @@ import io
|
|||||||
import matplotlib.dates as mdates
|
import matplotlib.dates as mdates
|
||||||
from matplotlib.figure import Figure
|
from matplotlib.figure import Figure
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
import matplotlib.pyplot as mpf
|
||||||
|
import matplotlib.gridspec as gridspec
|
||||||
|
|
||||||
# Configure logging
|
# Configure logging
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
@ -1782,35 +1784,34 @@ class Agent:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def add_chart_to_tensorboard(self, env, global_step):
|
def add_chart_to_tensorboard(self, env, global_step):
|
||||||
"""Add enhanced trading chart to TensorBoard"""
|
"""Add trading chart to TensorBoard"""
|
||||||
if len(env.data) < 10: # Minimum data to show
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Create chart with annotations
|
# Create chart image
|
||||||
chart_img = create_candlestick_figure(
|
chart_img = create_candlestick_figure(
|
||||||
env.data,
|
env.data,
|
||||||
env.trade_signals,
|
env.trade_signals,
|
||||||
window_size=100,
|
window_size=100,
|
||||||
title=f"Trading Chart (Step {global_step})"
|
title=f"Trading Chart - Step {global_step}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add to TensorBoard
|
if chart_img is not None:
|
||||||
self.writer.add_image('Trading Chart', np.array(chart_img).transpose(2, 0, 1), global_step)
|
# Convert PIL image to numpy array for TensorBoard
|
||||||
self.chart_step = global_step
|
chart_array = np.array(chart_img)
|
||||||
|
# TensorBoard expects [C, H, W] format
|
||||||
# Also log position information
|
chart_array = np.transpose(chart_array, (2, 0, 1))
|
||||||
if env.position != 'flat':
|
self.writer.add_image('Trading Chart', chart_array, global_step)
|
||||||
position_info = {
|
|
||||||
'position_type': env.position,
|
|
||||||
'entry_price': env.entry_price,
|
|
||||||
'position_size': env.position_size,
|
|
||||||
'unrealized_pnl': env.total_pnl
|
|
||||||
}
|
|
||||||
self.writer.add_text('Position', str(position_info), global_step)
|
|
||||||
|
|
||||||
|
# Add position information as text
|
||||||
|
position_info = f"""
|
||||||
|
**Current Position**: {env.position.upper()}
|
||||||
|
**Entry Price**: ${env.entry_price:.2f if env.entry_price else 0:.2f}
|
||||||
|
**Current Price**: ${env.data[-1]['close']:.2f}
|
||||||
|
**Position Size**: ${env.position_size:.2f}
|
||||||
|
**Unrealized PnL**: ${env.unrealized_pnl:.2f}
|
||||||
|
"""
|
||||||
|
self.writer.add_text('Position', position_info, global_step)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error creating chart: {e}")
|
logger.error(f"Error adding chart to TensorBoard: {str(e)}")
|
||||||
|
|
||||||
async def get_live_prices(symbol="ETH/USDT", timeframe="1m"):
|
async def get_live_prices(symbol="ETH/USDT", timeframe="1m"):
|
||||||
"""Get live price data using websockets"""
|
"""Get live price data using websockets"""
|
||||||
@ -2225,122 +2226,155 @@ async def get_historical_data(exchange, symbol="ETH/USDT", timeframe="1m", limit
|
|||||||
logger.error(f"Failed to fetch historical data: {e}")
|
logger.error(f"Failed to fetch historical data: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
async def live_trading(agent, env, exchange, demo=True):
|
async def live_trading(agent, env, exchange, symbol="ETH/USDT", timeframe="1m", demo=True, leverage=50):
|
||||||
"""Run live trading with the trained agent"""
|
"""
|
||||||
logger.info(f"Starting live trading (demo mode: {demo})")
|
Run the trading bot in live mode with enhanced error handling and monitoring
|
||||||
|
|
||||||
try:
|
Args:
|
||||||
# Subscribe to websocket for real-time data
|
agent: Trained trading agent
|
||||||
symbol = "ETH/USDT"
|
env: Trading environment
|
||||||
timeframe = "1m"
|
exchange: CCXT exchange instance
|
||||||
|
symbol: Trading pair (default: ETH/USDT)
|
||||||
|
timeframe: Candle timeframe (default: 1m)
|
||||||
|
demo: If True, simulate trades without executing (default: True)
|
||||||
|
leverage: Leverage for futures trading (default: 50x)
|
||||||
|
"""
|
||||||
|
logger.info(f"Starting live trading for {symbol} on {timeframe} timeframe")
|
||||||
|
logger.info(f"Mode: {'DEMO (paper trading)' if demo else 'LIVE TRADING'}")
|
||||||
|
|
||||||
# Initialize with historical data
|
if not demo:
|
||||||
success = await env.fetch_initial_data(exchange, symbol, timeframe, 100)
|
# Confirm with user before starting live trading
|
||||||
if not success:
|
confirmation = input(f"⚠️ WARNING: You are about to start LIVE TRADING with real funds on {symbol}. Type 'CONFIRM' to continue: ")
|
||||||
logger.error("Failed to initialize with historical data")
|
if confirmation != "CONFIRM":
|
||||||
|
logger.info("Live trading canceled by user")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Main trading loop
|
# Initialize futures trading if not in demo mode
|
||||||
|
try:
|
||||||
|
await env.initialize_futures(exchange)
|
||||||
|
logger.info(f"Futures trading initialized with {leverage}x leverage")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to initialize futures trading: {str(e)}")
|
||||||
|
logger.info("Falling back to demo mode for safety")
|
||||||
|
demo = True
|
||||||
|
|
||||||
|
# Initialize TensorBoard for monitoring
|
||||||
|
if not hasattr(agent, 'writer') or agent.writer is None:
|
||||||
|
agent.writer = SummaryWriter(f'runs/live_{symbol.replace("/", "_")}_{datetime.now().strftime("%Y%m%d_%H%M%S")}')
|
||||||
|
|
||||||
|
# Track performance metrics
|
||||||
|
trades_count = 0
|
||||||
|
winning_trades = 0
|
||||||
|
total_profit = 0
|
||||||
|
max_drawdown = 0
|
||||||
|
peak_balance = env.balance
|
||||||
step_counter = 0
|
step_counter = 0
|
||||||
|
prev_position = 'flat'
|
||||||
|
|
||||||
# For online learning
|
# Create directory for trade logs
|
||||||
states = []
|
os.makedirs('trade_logs', exist_ok=True)
|
||||||
actions = []
|
trade_log_path = f'trade_logs/trades_{datetime.now().strftime("%Y%m%d_%H%M%S")}.csv'
|
||||||
rewards = []
|
with open(trade_log_path, 'w') as f:
|
||||||
next_states = []
|
f.write("timestamp,action,price,position_size,balance,pnl\n")
|
||||||
dones = []
|
|
||||||
|
|
||||||
|
try:
|
||||||
while True:
|
while True:
|
||||||
# Wait for the next candle (1 minute)
|
try:
|
||||||
await asyncio.sleep(5) # Check every 5 seconds
|
# Fetch latest candle data
|
||||||
|
candle = await get_latest_candle(exchange, symbol)
|
||||||
# Fetch latest candle
|
if candle is None:
|
||||||
latest_candle = await get_latest_candle(exchange, symbol)
|
logger.warning("Failed to fetch latest candle, retrying in 5 seconds...")
|
||||||
|
await asyncio.sleep(5)
|
||||||
if not latest_candle:
|
|
||||||
logger.warning("No latest candle received, skipping update")
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Update environment with new data
|
# Add new data to environment
|
||||||
env.add_data(latest_candle)
|
env.add_data(candle)
|
||||||
|
|
||||||
# Get current state
|
# Get current state and select action
|
||||||
state = env.get_state()
|
state = env.get_state()
|
||||||
|
|
||||||
# Select action (no exploration in live trading)
|
|
||||||
action = agent.select_action(state, training=False)
|
action = agent.select_action(state, training=False)
|
||||||
|
|
||||||
# Take action
|
# Execute action
|
||||||
next_state, reward, done = env.step(action)
|
if not demo:
|
||||||
|
# Execute real trade on exchange
|
||||||
|
current_price = env.data[-1]['close']
|
||||||
|
trade_result = await env.execute_real_trade(exchange, action, current_price)
|
||||||
|
if not trade_result['success']:
|
||||||
|
logger.error(f"Trade execution failed: {trade_result['error']}")
|
||||||
|
# Continue with simulated trade for tracking purposes
|
||||||
|
|
||||||
# Store experience for online learning
|
# Update environment with action (simulated in demo mode)
|
||||||
states.append(state)
|
next_state, reward, done, info = env.step(action)
|
||||||
actions.append(action)
|
|
||||||
rewards.append(reward)
|
|
||||||
next_states.append(next_state)
|
|
||||||
dones.append(done)
|
|
||||||
|
|
||||||
# Online learning - update the model with new experiences
|
# Log trade if position changed
|
||||||
if len(states) >= 10: # Batch size for online learning
|
if env.position != prev_position:
|
||||||
# Store experiences in replay memory
|
trades_count += 1
|
||||||
for i in range(len(states)):
|
if env.last_trade_profit > 0:
|
||||||
agent.memory.push(states[i], actions[i], rewards[i], next_states[i], dones[i])
|
winning_trades += 1
|
||||||
|
total_profit += env.last_trade_profit
|
||||||
|
|
||||||
# Learn from experiences if we have enough samples
|
# Log trade details
|
||||||
if len(agent.memory) > 32:
|
with open(trade_log_path, 'a') as f:
|
||||||
loss = agent.learn()
|
f.write(f"{datetime.now().isoformat()},{info['action']},{env.data[-1]['close']},{env.position_size},{env.balance},{env.last_trade_profit}\n")
|
||||||
if loss is not None:
|
|
||||||
agent.writer.add_scalar('Live/Loss', loss, step_counter)
|
|
||||||
|
|
||||||
# Clear the temporary storage
|
logger.info(f"Trade executed: {info['action']} at ${env.data[-1]['close']:.2f}, PnL: ${env.last_trade_profit:.2f}")
|
||||||
states = []
|
|
||||||
actions = []
|
|
||||||
rewards = []
|
|
||||||
next_states = []
|
|
||||||
dones = []
|
|
||||||
|
|
||||||
# Save the updated model periodically
|
# Update performance metrics
|
||||||
if step_counter % 100 == 0:
|
if env.balance > peak_balance:
|
||||||
agent.save("models/trading_agent_live_updated.pt")
|
peak_balance = env.balance
|
||||||
logger.info("Updated model saved during live trading")
|
current_drawdown = (peak_balance - env.balance) / peak_balance if peak_balance > 0 else 0
|
||||||
|
if current_drawdown > max_drawdown:
|
||||||
|
max_drawdown = current_drawdown
|
||||||
|
|
||||||
# Log trading activity
|
# Update TensorBoard metrics
|
||||||
action_names = ["HOLD", "BUY", "SELL", "CLOSE"]
|
|
||||||
logger.info(f"Price: ${latest_candle['close']:.2f} | Action: {action_names[action]}")
|
|
||||||
|
|
||||||
# Log performance metrics
|
|
||||||
if env.trades:
|
|
||||||
wins = sum(1 for t in env.trades if t.get('pnl_percent', 0) > 0)
|
|
||||||
win_rate = wins / len(env.trades) * 100
|
|
||||||
total_pnl = sum(t.get('pnl_dollar', 0) for t in env.trades)
|
|
||||||
|
|
||||||
logger.info(f"Balance: ${env.balance:.2f} | Trades: {len(env.trades)} | "
|
|
||||||
f"Win Rate: {win_rate:.1f}% | Total PnL: ${total_pnl:.2f}")
|
|
||||||
|
|
||||||
# Analyze recent trades
|
|
||||||
trade_analysis = env.analyze_trades()
|
|
||||||
if trade_analysis:
|
|
||||||
logger.info(f"Recent Performance: Win Rate={trade_analysis.get('uptrend_win_rate', 0):.1f}% in uptrends, "
|
|
||||||
f"{trade_analysis.get('downtrend_win_rate', 0):.1f}% in downtrends")
|
|
||||||
|
|
||||||
# Add chart to TensorBoard periodically
|
|
||||||
step_counter += 1
|
step_counter += 1
|
||||||
if step_counter % 10 == 0: # Update chart every 10 steps
|
agent.writer.add_scalar('Live/Balance', env.balance, step_counter)
|
||||||
|
agent.writer.add_scalar('Live/PnL', env.total_pnl, step_counter)
|
||||||
|
agent.writer.add_scalar('Live/Drawdown', current_drawdown * 100, step_counter)
|
||||||
|
|
||||||
|
# Update chart visualization
|
||||||
|
if step_counter % 5 == 0 or env.position != prev_position:
|
||||||
agent.add_chart_to_tensorboard(env, step_counter)
|
agent.add_chart_to_tensorboard(env, step_counter)
|
||||||
|
|
||||||
# Also log current PnL and balance
|
# Log performance summary
|
||||||
agent.writer.add_scalar('Live/Balance', env.balance, step_counter)
|
if trades_count > 0:
|
||||||
agent.writer.add_scalar('Live/TotalPnL', env.total_pnl, step_counter)
|
win_rate = (winning_trades / trades_count) * 100
|
||||||
agent.writer.add_scalar('Live/WinRate',
|
agent.writer.add_scalar('Live/WinRate', win_rate, step_counter)
|
||||||
(env.win_count / (env.win_count + env.loss_count) * 100)
|
|
||||||
if (env.win_count + env.loss_count) > 0 else 0,
|
performance_text = f"""
|
||||||
step_counter)
|
**Live Trading Performance**
|
||||||
|
Balance: ${env.balance:.2f}
|
||||||
|
Total PnL: ${env.total_pnl:.2f}
|
||||||
|
Trades: {trades_count}
|
||||||
|
Win Rate: {win_rate:.1f}%
|
||||||
|
Max Drawdown: {max_drawdown*100:.1f}%
|
||||||
|
"""
|
||||||
|
agent.writer.add_text('Performance', performance_text, step_counter)
|
||||||
|
|
||||||
|
prev_position = env.position
|
||||||
|
|
||||||
|
# Wait for next candle
|
||||||
|
await asyncio.sleep(10) # Check every 10 seconds
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in live trading loop: {str(e)}")
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
logger.info("Continuing after error...")
|
||||||
|
await asyncio.sleep(30) # Wait longer after an error
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
logger.info("Live trading stopped by user")
|
logger.info("Live trading stopped by user")
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error in live trading: {e}")
|
# Final performance report
|
||||||
raise
|
if trades_count > 0:
|
||||||
|
win_rate = (winning_trades / trades_count) * 100
|
||||||
|
logger.info(f"Trading session summary:")
|
||||||
|
logger.info(f"Total trades: {trades_count}")
|
||||||
|
logger.info(f"Win rate: {win_rate:.1f}%")
|
||||||
|
logger.info(f"Final balance: ${env.balance:.2f}")
|
||||||
|
logger.info(f"Total profit: ${total_profit:.2f}")
|
||||||
|
logger.info(f"Maximum drawdown: {max_drawdown*100:.1f}%")
|
||||||
|
logger.info(f"Trade log saved to: {trade_log_path}")
|
||||||
|
|
||||||
async def get_latest_candle(exchange, symbol):
|
async def get_latest_candle(exchange, symbol):
|
||||||
"""Get the latest candle data"""
|
"""Get the latest candle data"""
|
||||||
@ -2404,6 +2438,12 @@ async def main():
|
|||||||
help='Mode to run the bot in')
|
help='Mode to run the bot in')
|
||||||
parser.add_argument('--episodes', type=int, default=1000, help='Number of episodes to train')
|
parser.add_argument('--episodes', type=int, default=1000, help='Number of episodes to train')
|
||||||
parser.add_argument('--demo', action='store_true', help='Run in demo mode (no real trades)')
|
parser.add_argument('--demo', action='store_true', help='Run in demo mode (no real trades)')
|
||||||
|
parser.add_argument('--live', action='store_true', help='Run in live trading mode')
|
||||||
|
parser.add_argument('--real', action='store_true', help='Execute real trades (default is demo/paper trading)')
|
||||||
|
parser.add_argument('--symbol', type=str, default='ETH/USDT', help='Trading pair symbol')
|
||||||
|
parser.add_argument('--timeframe', type=str, default='1m', help='Candle timeframe')
|
||||||
|
parser.add_argument('--leverage', type=int, default=50, help='Leverage for futures trading')
|
||||||
|
parser.add_argument('--model', type=str, help='Path to model file')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# Get device (GPU or CPU)
|
# Get device (GPU or CPU)
|
||||||
@ -2438,12 +2478,46 @@ async def main():
|
|||||||
avg_reward, avg_profit, win_rate = evaluate_agent(agent, env)
|
avg_reward, avg_profit, win_rate = evaluate_agent(agent, env)
|
||||||
|
|
||||||
elif args.mode == 'live':
|
elif args.mode == 'live':
|
||||||
# Load trained model
|
# Add these arguments to the parser
|
||||||
agent.load("models/trading_agent_best_pnl.pt")
|
parser.add_argument('--live', action='store_true', help='Run in live trading mode')
|
||||||
|
parser.add_argument('--real', action='store_true', help='Execute real trades (default is demo/paper trading)')
|
||||||
|
parser.add_argument('--symbol', type=str, default='ETH/USDT', help='Trading pair symbol')
|
||||||
|
parser.add_argument('--timeframe', type=str, default='1m', help='Candle timeframe')
|
||||||
|
parser.add_argument('--leverage', type=int, default=50, help='Leverage for futures trading')
|
||||||
|
parser.add_argument('--model', type=str, help='Path to model file')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
# Run live trading
|
# In the main function, add this section to handle live trading
|
||||||
logger.info("Starting live trading...")
|
if args.live:
|
||||||
await live_trading(agent, env, exchange, demo=args.demo)
|
# Initialize exchange
|
||||||
|
exchange = await initialize_exchange()
|
||||||
|
|
||||||
|
# Load the trained agent
|
||||||
|
model_path = args.model if args.model else "models/trading_agent.pt"
|
||||||
|
if not os.path.exists(model_path):
|
||||||
|
logger.error(f"Model file not found: {model_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Initialize environment with historical data
|
||||||
|
env = TradingEnvironment(initial_balance=INITIAL_BALANCE, window_size=WINDOW_SIZE, demo=not args.real)
|
||||||
|
await env.fetch_initial_data(exchange, symbol=args.symbol, timeframe=args.timeframe)
|
||||||
|
|
||||||
|
# Initialize agent
|
||||||
|
state_size = env.get_state().shape[0]
|
||||||
|
agent = Agent(state_size=state_size, action_size=3)
|
||||||
|
agent.load(model_path)
|
||||||
|
logger.info(f"Loaded model from {model_path}")
|
||||||
|
|
||||||
|
# Start live trading
|
||||||
|
await live_trading(
|
||||||
|
agent=agent,
|
||||||
|
env=env,
|
||||||
|
exchange=exchange,
|
||||||
|
symbol=args.symbol,
|
||||||
|
timeframe=args.timeframe,
|
||||||
|
demo=not args.real,
|
||||||
|
leverage=args.leverage
|
||||||
|
)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# Clean up exchange connection - safely close if possible
|
# Clean up exchange connection - safely close if possible
|
||||||
@ -2458,6 +2532,73 @@ async def main():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Could not properly close exchange connection: {e}")
|
logger.warning(f"Could not properly close exchange connection: {e}")
|
||||||
|
|
||||||
|
# Add this function near the top with other utility functions
|
||||||
|
def create_candlestick_figure(data, trade_signals, window_size=100, title=""):
|
||||||
|
"""Create a candlestick chart with trade signals for TensorBoard visualization"""
|
||||||
|
if len(data) < 10:
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Create figure
|
||||||
|
fig = plt.figure(figsize=(12, 8))
|
||||||
|
|
||||||
|
# Prepare data for plotting
|
||||||
|
df = pd.DataFrame(data[-window_size:])
|
||||||
|
df['date'] = pd.to_datetime(df['timestamp'], unit='ms')
|
||||||
|
df.set_index('date', inplace=True)
|
||||||
|
|
||||||
|
# Create subplot grid
|
||||||
|
gs = gridspec.GridSpec(2, 1, height_ratios=[3, 1])
|
||||||
|
price_ax = plt.subplot(gs[0])
|
||||||
|
volume_ax = plt.subplot(gs[1], sharex=price_ax)
|
||||||
|
|
||||||
|
# Plot candlesticks
|
||||||
|
mpf.plot(df, type='candle', style='yahoo', ax=price_ax, volume=volume_ax)
|
||||||
|
|
||||||
|
# Add trade signals
|
||||||
|
for signal in trade_signals:
|
||||||
|
if signal['timestamp'] not in df.index:
|
||||||
|
continue
|
||||||
|
|
||||||
|
timestamp = pd.to_datetime(signal['timestamp'], unit='ms')
|
||||||
|
price = signal['price']
|
||||||
|
|
||||||
|
if signal['type'] == 'buy':
|
||||||
|
price_ax.plot(timestamp, price, '^', color='green', markersize=10, label='Buy')
|
||||||
|
elif signal['type'] == 'sell':
|
||||||
|
price_ax.plot(timestamp, price, 'v', color='red', markersize=10, label='Sell')
|
||||||
|
elif signal['type'] == 'close_long':
|
||||||
|
price_ax.plot(timestamp, price, 'x', color='gold', markersize=10, label='Close Long')
|
||||||
|
elif signal['type'] == 'close_short':
|
||||||
|
price_ax.plot(timestamp, price, 'x', color='black', markersize=10, label='Close Short')
|
||||||
|
elif 'stop_loss' in signal['type']:
|
||||||
|
price_ax.plot(timestamp, price, 'X', color='purple', markersize=10, label='Stop Loss')
|
||||||
|
elif 'take_profit' in signal['type']:
|
||||||
|
price_ax.plot(timestamp, price, '*', color='cyan', markersize=10, label='Take Profit')
|
||||||
|
|
||||||
|
# Add balance and PnL annotation
|
||||||
|
if trade_signals and 'balance' in trade_signals[-1] and 'pnl' in trade_signals[-1]:
|
||||||
|
balance = trade_signals[-1]['balance']
|
||||||
|
pnl = trade_signals[-1]['pnl']
|
||||||
|
price_ax.annotate(f"Balance: ${balance:.2f}\nPnL: ${pnl:.2f}",
|
||||||
|
xy=(0.02, 0.95), xycoords='axes fraction',
|
||||||
|
bbox=dict(boxstyle="round,pad=0.3", fc="white", ec="gray", alpha=0.8))
|
||||||
|
|
||||||
|
# Set title and format
|
||||||
|
price_ax.set_title(title)
|
||||||
|
fig.tight_layout()
|
||||||
|
|
||||||
|
# Convert to image
|
||||||
|
buf = io.BytesIO()
|
||||||
|
fig.savefig(buf, format='png')
|
||||||
|
buf.seek(0)
|
||||||
|
img = Image.open(buf)
|
||||||
|
return img
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error creating chart: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
try:
|
try:
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user