#!/usr/bin/env python3 """ DOLPHIN Paper Trading Session ============================== Brief paper trading run using clean architecture. """ import sys sys.path.insert(0, '/mnt/dolphinng5_predict/prod/clean_arch') sys.path.insert(0, '/mnt/dolphinng5_predict') import asyncio import logging from datetime import datetime from typing import Optional logging.basicConfig( level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s' ) logger = logging.getLogger("PaperTrade") from adapters.hazelcast_feed import HazelcastDataFeed, MarketSnapshot class SimplePaperTrader: """Simple paper trader for demonstration.""" def __init__(self, capital: float = 10000.0): self.capital = capital self.position = 0.0 # BTC quantity self.entry_price = 0.0 self.trades = [] self.start_time = datetime.utcnow() def on_snapshot(self, snapshot: MarketSnapshot): """Process market snapshot and decide to trade.""" # Use velocity divergence as signal (non-zero in current data) signal = snapshot.velocity_divergence or 0.0 # Simple mean-reversion strategy on velocity divergence BUY_THRESHOLD = -0.01 # Buy when velocity divergence is very negative SELL_THRESHOLD = 0.01 # Sell when velocity divergence is very positive if signal > BUY_THRESHOLD and self.position == 0: # Buy signal size = 0.001 # 0.001 BTC self.position = size self.entry_price = snapshot.price self.trades.append({ 'time': datetime.utcnow(), 'side': 'BUY', 'size': size, 'price': snapshot.price, 'signal': signal }) logger.info(f"🟢 BUY {size} BTC @ ${snapshot.price:,.2f} (signal: {signal:.4f})") elif signal < SELL_THRESHOLD and self.position > 0: # Sell signal pnl = self.position * (snapshot.price - self.entry_price) self.trades.append({ 'time': datetime.utcnow(), 'side': 'SELL', 'size': self.position, 'price': snapshot.price, 'signal': signal, 'pnl': pnl }) logger.info(f"šŸ”“ SELL {self.position} BTC @ ${snapshot.price:,.2f} (signal: {signal:.4f}, PnL: ${pnl:+.2f})") self.position = 0.0 self.entry_price = 0.0 def get_status(self) -> dict: """Get current trading status.""" current_price = self.trades[-1]['price'] if self.trades else 0 unrealized = self.position * (current_price - self.entry_price) if self.position > 0 else 0 realized = sum(t.get('pnl', 0) for t in self.trades) return { 'trades': len(self.trades), 'position': self.position, 'unrealized_pnl': unrealized, 'realized_pnl': realized, 'total_pnl': unrealized + realized } async def paper_trade(duration_seconds: int = 60): """Run paper trading for specified duration.""" logger.info("=" * 60) logger.info("🐬 DOLPHIN PAPER TRADING SESSION") logger.info("=" * 60) logger.info(f"Duration: {duration_seconds}s") logger.info("") # Setup feed = HazelcastDataFeed({ 'hazelcast': {'cluster': 'dolphin', 'host': 'localhost:5701'} }) trader = SimplePaperTrader(capital=10000.0) # Connect logger.info("Connecting to Hazelcast...") if not await feed.connect(): logger.error("Failed to connect!") return logger.info("āœ“ Connected. Starting trading loop...") logger.info("") # Trading loop start_time = datetime.utcnow() iteration = 0 try: while (datetime.utcnow() - start_time).total_seconds() < duration_seconds: iteration += 1 # Get latest snapshot snapshot = await feed.get_latest_snapshot("BTCUSDT") if snapshot: trader.on_snapshot(snapshot) # Log status every 5 iterations if iteration % 5 == 0: status = trader.get_status() pos_str = f"Position: {status['position']:.4f} BTC" if status['position'] > 0 else "Position: FLAT" pnl_str = f"PnL: ${status['total_pnl']:+.2f}" logger.info(f"[{iteration}] {pos_str} | {pnl_str} | Price: ${snapshot.price:,.2f}") # Wait before next iteration (sub-5 second for faster updates) await asyncio.sleep(1.0) except KeyboardInterrupt: logger.info("\nāš ļø Interrupted by user") # Cleanup await feed.disconnect() # Final report logger.info("") logger.info("=" * 60) logger.info("šŸ“Š FINAL REPORT") logger.info("=" * 60) status = trader.get_status() logger.info(f"Total Trades: {status['trades']}") logger.info(f"Final Position: {status['position']:.6f} BTC") logger.info(f"Realized PnL: ${status['realized_pnl']:+.2f}") logger.info(f"Unrealized PnL: ${status['unrealized_pnl']:+.2f}") logger.info(f"Total PnL: ${status['total_pnl']:+.2f}") if trader.trades: logger.info("") logger.info("Trade History:") for t in trader.trades: pnl_str = f" (${t.get('pnl', 0):+.2f})" if 'pnl' in t else "" logger.info(f" {t['side']:4} {t['size']:.4f} @ ${t['price']:,.2f}{pnl_str}") logger.info("=" * 60) logger.info("āœ… Paper trading session complete") if __name__ == "__main__": import argparse parser = argparse.ArgumentParser() parser.add_argument('--duration', type=int, default=30, help='Trading duration in seconds') args = parser.parse_args() asyncio.run(paper_trade(args.duration))