fix 1s 1m chart less candles ;
fix vertical zoom
This commit is contained in:
@@ -2430,13 +2430,14 @@ class RealTrainingAdapter:
|
||||
if not hasattr(self, 'inference_sessions'):
|
||||
self.inference_sessions = {}
|
||||
|
||||
# Create inference session
|
||||
# Create inference session with position tracking
|
||||
self.inference_sessions[inference_id] = {
|
||||
'model_name': model_name,
|
||||
'symbol': symbol,
|
||||
'status': 'running',
|
||||
'start_time': time.time(),
|
||||
'signals': [],
|
||||
'signals': [], # All signals (including rejected ones)
|
||||
'executed_trades': [], # Only executed trades (open/close positions)
|
||||
'stop_flag': False,
|
||||
'live_training_enabled': enable_live_training,
|
||||
'train_every_candle': train_every_candle,
|
||||
@@ -2447,7 +2448,13 @@ class RealTrainingAdapter:
|
||||
'loss': 0.0,
|
||||
'steps': 0
|
||||
},
|
||||
'last_candle_time': None
|
||||
'last_candle_time': None,
|
||||
# Position tracking
|
||||
'position': None, # {'type': 'long/short', 'entry_price': float, 'entry_time': str, 'entry_id': str}
|
||||
'total_pnl': 0.0,
|
||||
'win_count': 0,
|
||||
'loss_count': 0,
|
||||
'total_trades': 0
|
||||
}
|
||||
|
||||
training_mode = "per-candle" if train_every_candle else ("pivot-based" if enable_live_training else "inference-only")
|
||||
@@ -3211,13 +3218,39 @@ class RealTrainingAdapter:
|
||||
'predicted_candle': prediction.get('predicted_candle')
|
||||
}
|
||||
|
||||
# Store signal (all signals, including rejected ones)
|
||||
session['signals'].append(signal)
|
||||
|
||||
# Keep only last 100 signals
|
||||
if len(session['signals']) > 100:
|
||||
session['signals'] = session['signals'][-100:]
|
||||
|
||||
logger.info(f"Live Signal: {signal['action']} @ {signal['price']:.2f} (conf: {signal['confidence']:.2f})")
|
||||
# Execute trade logic (only if confidence is high enough and position logic allows)
|
||||
executed_trade = self._execute_realtime_trade(session, signal, current_price)
|
||||
|
||||
if executed_trade:
|
||||
logger.info(f"Live Trade EXECUTED: {executed_trade['action']} @ {executed_trade['price']:.2f} (conf: {signal['confidence']:.2f})")
|
||||
|
||||
# Send executed trade to frontend via WebSocket
|
||||
if hasattr(self, 'socketio') and self.socketio:
|
||||
self.socketio.emit('executed_trade', {
|
||||
'trade': executed_trade,
|
||||
'position_state': {
|
||||
'has_position': session['position'] is not None,
|
||||
'position_type': session['position']['type'] if session['position'] else None,
|
||||
'entry_price': session['position']['entry_price'] if session['position'] else None,
|
||||
'unrealized_pnl': self._calculate_unrealized_pnl(session, current_price) if session['position'] else 0.0
|
||||
},
|
||||
'session_metrics': {
|
||||
'total_pnl': session['total_pnl'],
|
||||
'total_trades': session['total_trades'],
|
||||
'win_count': session['win_count'],
|
||||
'loss_count': session['loss_count'],
|
||||
'win_rate': (session['win_count'] / session['total_trades'] * 100) if session['total_trades'] > 0 else 0
|
||||
}
|
||||
})
|
||||
else:
|
||||
logger.info(f"Live Signal (NOT executed): {signal['action']} @ {signal['price']:.2f} (conf: {signal['confidence']:.2f}) - {self._get_rejection_reason(session, signal)}")
|
||||
|
||||
# Store prediction for visualization
|
||||
if self.orchestrator and hasattr(self.orchestrator, 'store_transformer_prediction'):
|
||||
@@ -3250,3 +3283,173 @@ class RealTrainingAdapter:
|
||||
logger.error(f"Fatal error in inference loop: {e}")
|
||||
session['status'] = 'error'
|
||||
session['error'] = str(e)
|
||||
|
||||
def _execute_realtime_trade(self, session: Dict, signal: Dict, current_price: float) -> Optional[Dict]:
|
||||
"""
|
||||
Execute trade based on signal, respecting position management rules
|
||||
|
||||
Rules:
|
||||
1. Only execute if confidence >= 0.6
|
||||
2. Only open new position if no position is currently open
|
||||
3. Close position on opposite signal
|
||||
4. Track all executed trades for visualization
|
||||
|
||||
Returns:
|
||||
Dict with executed trade info, or None if signal was rejected
|
||||
"""
|
||||
action = signal['action']
|
||||
confidence = signal['confidence']
|
||||
timestamp = signal['timestamp']
|
||||
|
||||
# Rule 1: Confidence threshold
|
||||
if confidence < 0.6:
|
||||
return None # Rejected: low confidence
|
||||
|
||||
# Rule 2 & 3: Position management
|
||||
position = session.get('position')
|
||||
|
||||
if action == 'BUY':
|
||||
if position is None:
|
||||
# Open long position
|
||||
trade_id = str(uuid.uuid4())[:8]
|
||||
session['position'] = {
|
||||
'type': 'long',
|
||||
'entry_price': current_price,
|
||||
'entry_time': timestamp,
|
||||
'entry_id': trade_id,
|
||||
'signal_confidence': confidence
|
||||
}
|
||||
|
||||
executed_trade = {
|
||||
'trade_id': trade_id,
|
||||
'action': 'OPEN_LONG',
|
||||
'price': current_price,
|
||||
'timestamp': timestamp,
|
||||
'confidence': confidence
|
||||
}
|
||||
|
||||
session['executed_trades'].append(executed_trade)
|
||||
return executed_trade
|
||||
|
||||
elif position['type'] == 'short':
|
||||
# Close short position
|
||||
entry_price = position['entry_price']
|
||||
pnl = entry_price - current_price # Short profit
|
||||
pnl_pct = (pnl / entry_price) * 100
|
||||
|
||||
executed_trade = {
|
||||
'trade_id': position['entry_id'],
|
||||
'action': 'CLOSE_SHORT',
|
||||
'price': current_price,
|
||||
'timestamp': timestamp,
|
||||
'confidence': confidence,
|
||||
'entry_price': entry_price,
|
||||
'entry_time': position['entry_time'],
|
||||
'pnl': pnl,
|
||||
'pnl_pct': pnl_pct
|
||||
}
|
||||
|
||||
# Update session metrics
|
||||
session['total_pnl'] += pnl
|
||||
session['total_trades'] += 1
|
||||
if pnl > 0:
|
||||
session['win_count'] += 1
|
||||
else:
|
||||
session['loss_count'] += 1
|
||||
|
||||
session['position'] = None
|
||||
session['executed_trades'].append(executed_trade)
|
||||
|
||||
logger.info(f"Position CLOSED: SHORT @ {current_price:.2f}, PnL=${pnl:.2f} ({pnl_pct:+.2f}%)")
|
||||
return executed_trade
|
||||
|
||||
elif action == 'SELL':
|
||||
if position is None:
|
||||
# Open short position
|
||||
trade_id = str(uuid.uuid4())[:8]
|
||||
session['position'] = {
|
||||
'type': 'short',
|
||||
'entry_price': current_price,
|
||||
'entry_time': timestamp,
|
||||
'entry_id': trade_id,
|
||||
'signal_confidence': confidence
|
||||
}
|
||||
|
||||
executed_trade = {
|
||||
'trade_id': trade_id,
|
||||
'action': 'OPEN_SHORT',
|
||||
'price': current_price,
|
||||
'timestamp': timestamp,
|
||||
'confidence': confidence
|
||||
}
|
||||
|
||||
session['executed_trades'].append(executed_trade)
|
||||
return executed_trade
|
||||
|
||||
elif position['type'] == 'long':
|
||||
# Close long position
|
||||
entry_price = position['entry_price']
|
||||
pnl = current_price - entry_price # Long profit
|
||||
pnl_pct = (pnl / entry_price) * 100
|
||||
|
||||
executed_trade = {
|
||||
'trade_id': position['entry_id'],
|
||||
'action': 'CLOSE_LONG',
|
||||
'price': current_price,
|
||||
'timestamp': timestamp,
|
||||
'confidence': confidence,
|
||||
'entry_price': entry_price,
|
||||
'entry_time': position['entry_time'],
|
||||
'pnl': pnl,
|
||||
'pnl_pct': pnl_pct
|
||||
}
|
||||
|
||||
# Update session metrics
|
||||
session['total_pnl'] += pnl
|
||||
session['total_trades'] += 1
|
||||
if pnl > 0:
|
||||
session['win_count'] += 1
|
||||
else:
|
||||
session['loss_count'] += 1
|
||||
|
||||
session['position'] = None
|
||||
session['executed_trades'].append(executed_trade)
|
||||
|
||||
logger.info(f"Position CLOSED: LONG @ {current_price:.2f}, PnL=${pnl:.2f} ({pnl_pct:+.2f}%)")
|
||||
return executed_trade
|
||||
|
||||
# HOLD or position already open in same direction
|
||||
return None
|
||||
|
||||
def _get_rejection_reason(self, session: Dict, signal: Dict) -> str:
|
||||
"""Get reason why a signal was not executed"""
|
||||
action = signal['action']
|
||||
confidence = signal['confidence']
|
||||
position = session.get('position')
|
||||
|
||||
if confidence < 0.6:
|
||||
return f"Low confidence ({confidence:.2f} < 0.6)"
|
||||
|
||||
if action == 'HOLD':
|
||||
return "HOLD signal (no trade)"
|
||||
|
||||
if position:
|
||||
if action == 'BUY' and position['type'] == 'long':
|
||||
return "Already in LONG position"
|
||||
elif action == 'SELL' and position['type'] == 'short':
|
||||
return "Already in SHORT position"
|
||||
|
||||
return "Unknown reason"
|
||||
|
||||
def _calculate_unrealized_pnl(self, session: Dict, current_price: float) -> float:
|
||||
"""Calculate unrealized PnL for open position"""
|
||||
position = session.get('position')
|
||||
if not position or not current_price:
|
||||
return 0.0
|
||||
|
||||
entry_price = position['entry_price']
|
||||
|
||||
if position['type'] == 'long':
|
||||
return ((current_price - entry_price) / entry_price) * 100 # Percentage
|
||||
else: # short
|
||||
return ((entry_price - current_price) / entry_price) * 100 # Percentage
|
||||
|
||||
Reference in New Issue
Block a user