Files
DOLPHIN/prod/clean_arch/paper_trade.py
hjnormey 01c19662cb initial: import DOLPHIN baseline 2026-04-21 from dolphinng5_predict working tree
Includes core prod + GREEN/BLUE subsystems:
- prod/ (BLUE harness, configs, scripts, docs)
- nautilus_dolphin/ (GREEN Nautilus-native impl + dvae/ preserved)
- adaptive_exit/ (AEM engine + models/bucket_assignments.pkl)
- Observability/ (EsoF advisor, TUI, dashboards)
- external_factors/ (EsoF producer)
- mc_forewarning_qlabs_fork/ (MC regime/envelope)

Excludes runtime caches, logs, backups, and reproducible artifacts per .gitignore.
2026-04-21 16:58:38 +02:00

174 lines
5.8 KiB
Python
Executable File

#!/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))