diff --git a/ANNOTATE/core/real_training_adapter.py b/ANNOTATE/core/real_training_adapter.py index 1bf33de..1594be6 100644 --- a/ANNOTATE/core/real_training_adapter.py +++ b/ANNOTATE/core/real_training_adapter.py @@ -785,42 +785,94 @@ class RealTrainingAdapter: logger.warning("No price data in any timeframe") return None - # Concatenate all timeframes along sequence dimension - # This gives the model multi-timeframe context - price_data = np.concatenate(all_price_data, axis=0) + # Use only the primary timeframe (1m) for transformer training + # The transformer expects a fixed sequence length of 150 + primary_tf = '1m' if '1m' in timeframes else timeframe_order[0] - # Add batch dimension [1, total_seq_len, 5] + if primary_tf not in timeframes: + logger.warning(f"Primary timeframe {primary_tf} not available") + return None + + # Get primary timeframe data + primary_data = timeframes[primary_tf] + closes = np.array(primary_data.get('close', []), dtype=np.float32) + + if len(closes) == 0: + logger.warning("No data in primary timeframe") + return None + + # Use the last 150 candles (or pad/truncate to exactly 150) + target_seq_len = 150 # Transformer expects exactly 150 sequence length + + if len(closes) >= target_seq_len: + # Take the last 150 candles + price_data = np.stack([ + np.array(primary_data.get('open', [])[-target_seq_len:], dtype=np.float32), + np.array(primary_data.get('high', [])[-target_seq_len:], dtype=np.float32), + np.array(primary_data.get('low', [])[-target_seq_len:], dtype=np.float32), + np.array(primary_data.get('close', [])[-target_seq_len:], dtype=np.float32), + np.array(primary_data.get('volume', [])[-target_seq_len:], dtype=np.float32) + ], axis=-1) + else: + # Pad with the last available candle + last_open = primary_data.get('open', [0])[-1] if primary_data.get('open') else 0 + last_high = primary_data.get('high', [0])[-1] if primary_data.get('high') else 0 + last_low = primary_data.get('low', [0])[-1] if primary_data.get('low') else 0 + last_close = primary_data.get('close', [0])[-1] if primary_data.get('close') else 0 + last_volume = primary_data.get('volume', [0])[-1] if primary_data.get('volume') else 0 + + # Pad arrays to target length + opens = np.array(primary_data.get('open', []), dtype=np.float32) + highs = np.array(primary_data.get('high', []), dtype=np.float32) + lows = np.array(primary_data.get('low', []), dtype=np.float32) + closes = np.array(primary_data.get('close', []), dtype=np.float32) + volumes = np.array(primary_data.get('volume', []), dtype=np.float32) + + # Pad with last values + while len(opens) < target_seq_len: + opens = np.append(opens, last_open) + highs = np.append(highs, last_high) + lows = np.append(lows, last_low) + closes = np.append(closes, last_close) + volumes = np.append(volumes, last_volume) + + price_data = np.stack([opens, highs, lows, closes, volumes], axis=-1) + + # Add batch dimension [1, 150, 5] price_data = torch.tensor(price_data, dtype=torch.float32).unsqueeze(0) - # Get primary timeframe for reference - primary_tf = '1m' if '1m' in timeframes else timeframe_order[0] - closes = np.array(timeframes[primary_tf].get('close', []), dtype=np.float32) + # Sequence length is now exactly 150 + total_seq_len = 150 # Create placeholder COB data (zeros if not available) - # COB data shape: [1, seq_len, cob_features] + # COB data shape: [1, 150, cob_features] + # MUST match the total sequence length from price_data (150) # Transformer expects 100 COB features (as defined in TransformerConfig) - cob_data = torch.zeros(1, len(closes), 100, dtype=torch.float32) # 100 COB features + cob_data = torch.zeros(1, 150, 100, dtype=torch.float32) # Match price seq_len (150) # Create technical indicators (simple ones for now) # tech_data shape: [1, features] tech_features = [] + # Use the closes data from the price_data we just created + closes_for_tech = price_data[0, :, 3].numpy() # Close prices from OHLCV data + # Add simple technical indicators - if len(closes) >= 20: - sma_20 = np.mean(closes[-20:]) - tech_features.append(closes[-1] / sma_20 - 1.0) # Price vs SMA + if len(closes_for_tech) >= 20: + sma_20 = np.mean(closes_for_tech[-20:]) + tech_features.append(closes_for_tech[-1] / sma_20 - 1.0) # Price vs SMA else: tech_features.append(0.0) - if len(closes) >= 2: - returns = (closes[-1] - closes[-2]) / closes[-2] + if len(closes_for_tech) >= 2: + returns = (closes_for_tech[-1] - closes_for_tech[-2]) / closes_for_tech[-2] tech_features.append(returns) # Recent return else: tech_features.append(0.0) # Add volatility - if len(closes) >= 20: - volatility = np.std(closes[-20:]) / np.mean(closes[-20:]) + if len(closes_for_tech) >= 20: + volatility = np.std(closes_for_tech[-20:]) / np.mean(closes_for_tech[-20:]) tech_features.append(volatility) else: tech_features.append(0.0) @@ -836,36 +888,36 @@ class RealTrainingAdapter: market_features = [] # Add volume profile - primary_volumes = np.array(timeframes[primary_tf].get('volume', []), dtype=np.float32) - if len(primary_volumes) >= 20: - vol_ratio = primary_volumes[-1] / np.mean(primary_volumes[-20:]) + volumes_for_tech = price_data[0, :, 4].numpy() # Volume from OHLCV data + if len(volumes_for_tech) >= 20: + vol_ratio = volumes_for_tech[-1] / np.mean(volumes_for_tech[-20:]) market_features.append(vol_ratio) else: market_features.append(1.0) # Add price range - primary_highs = np.array(timeframes[primary_tf].get('high', []), dtype=np.float32) - primary_lows = np.array(timeframes[primary_tf].get('low', []), dtype=np.float32) - if len(primary_highs) >= 20 and len(primary_lows) >= 20: - price_range = (np.max(primary_highs[-20:]) - np.min(primary_lows[-20:])) / closes[-1] + highs_for_tech = price_data[0, :, 1].numpy() # High from OHLCV data + lows_for_tech = price_data[0, :, 2].numpy() # Low from OHLCV data + if len(highs_for_tech) >= 20 and len(lows_for_tech) >= 20: + price_range = (np.max(highs_for_tech[-20:]) - np.min(lows_for_tech[-20:])) / closes_for_tech[-1] market_features.append(price_range) else: market_features.append(0.0) # Add pivot point features # Calculate simple pivot points from recent price action - if len(primary_highs) >= 5 and len(primary_lows) >= 5: + if len(highs_for_tech) >= 5 and len(lows_for_tech) >= 5: # Pivot Point = (High + Low + Close) / 3 - pivot = (primary_highs[-1] + primary_lows[-1] + closes[-1]) / 3.0 + pivot = (highs_for_tech[-1] + lows_for_tech[-1] + closes_for_tech[-1]) / 3.0 # Support and Resistance levels - r1 = 2 * pivot - primary_lows[-1] # Resistance 1 - s1 = 2 * pivot - primary_highs[-1] # Support 1 + r1 = 2 * pivot - lows_for_tech[-1] # Resistance 1 + s1 = 2 * pivot - highs_for_tech[-1] # Support 1 # Normalize relative to current price - pivot_distance = (closes[-1] - pivot) / closes[-1] - r1_distance = (closes[-1] - r1) / closes[-1] - s1_distance = (closes[-1] - s1) / closes[-1] + pivot_distance = (closes_for_tech[-1] - pivot) / closes_for_tech[-1] + r1_distance = (closes_for_tech[-1] - r1) / closes_for_tech[-1] + s1_distance = (closes_for_tech[-1] - s1) / closes_for_tech[-1] market_features.extend([pivot_distance, r1_distance, s1_distance]) else: diff --git a/ANNOTATE/data/annotations/annotations_db.json b/ANNOTATE/data/annotations/annotations_db.json index f8aadd4..abf4326 100644 --- a/ANNOTATE/data/annotations/annotations_db.json +++ b/ANNOTATE/data/annotations/annotations_db.json @@ -45,10 +45,33 @@ "entry_state": {}, "exit_state": {} } + }, + { + "annotation_id": "91847a37-6315-4546-b5a0-573118311322", + "symbol": "ETH/USDT", + "timeframe": "1s", + "entry": { + "timestamp": "2025-10-25 13:08:04", + "price": 3940.24, + "index": 25 + }, + "exit": { + "timestamp": "2025-10-25 13:15:12", + "price": 3942.59, + "index": 57 + }, + "direction": "LONG", + "profit_loss_pct": 0.05964103709419639, + "notes": "", + "created_at": "2025-10-25T16:17:02.931920", + "market_context": { + "entry_state": {}, + "exit_state": {} + } } ], "metadata": { - "total_annotations": 2, - "last_updated": "2025-10-24T23:35:14.216759" + "total_annotations": 3, + "last_updated": "2025-10-25T16:17:02.931920" } } \ No newline at end of file diff --git a/ANNOTATE/web/app.py b/ANNOTATE/web/app.py index a615372..5b7ea9c 100644 --- a/ANNOTATE/web/app.py +++ b/ANNOTATE/web/app.py @@ -75,12 +75,23 @@ except ImportError: HistoricalDataLoader = data_module.HistoricalDataLoader TimeRangeManager = data_module.TimeRangeManager -# Setup logging +# Setup logging - configure before any logging occurs +log_dir = Path(__file__).parent.parent / 'logs' +log_dir.mkdir(exist_ok=True) +log_file = log_dir / 'annotate_app.log' + +# Configure logging to both file and console +# File mode 'w' truncates the file on each run logging.basicConfig( level=logging.INFO, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler(log_file, mode='w'), # Truncate on each run + logging.StreamHandler(sys.stdout) # Also print to console + ] ) logger = logging.getLogger(__name__) +logger.info(f"Logging to: {log_file}") class AnnotationDashboard: """Main annotation dashboard application""" @@ -1261,6 +1272,11 @@ class AnnotationDashboard: def main(): """Main entry point""" + logger.info("=" * 80) + logger.info("ANNOTATE Application Starting") + logger.info(f"Timestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") + logger.info("=" * 80) + dashboard = AnnotationDashboard() dashboard.run(debug=True) diff --git a/NN/models/advanced_transformer_trading.py b/NN/models/advanced_transformer_trading.py index 932f96a..fc649ed 100644 --- a/NN/models/advanced_transformer_trading.py +++ b/NN/models/advanced_transformer_trading.py @@ -243,10 +243,11 @@ class MarketRegimeDetector(nn.Module): # Weighted combination based on regime probabilities regime_stack = torch.stack(regime_outputs, dim=0) # (n_regimes, batch, seq_len, d_model) - regime_weights = regime_probs.unsqueeze(1).unsqueeze(3) # (batch, 1, 1, n_regimes) + regime_weights = regime_probs.unsqueeze(0).unsqueeze(2).unsqueeze(3) # (1, batch, 1, 1, n_regimes) + regime_weights = regime_weights.permute(4, 1, 2, 3, 0).squeeze(-1) # (n_regimes, batch, 1, 1) # Weighted sum across regimes - adapted_output = torch.sum(regime_stack * regime_weights.transpose(0, 3), dim=0) + adapted_output = torch.sum(regime_stack * regime_weights, dim=0) return adapted_output, regime_probs