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.
This commit is contained in:
173
prod/clean_arch/paper_trade.py
Executable file
173
prod/clean_arch/paper_trade.py
Executable file
@@ -0,0 +1,173 @@
|
||||
#!/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))
|
||||
Reference in New Issue
Block a user