wip
This commit is contained in:
@@ -2579,8 +2579,9 @@ class RealTrainingAdapter:
|
|||||||
for tf in ['1s', '1m', '1h', '1d']:
|
for tf in ['1s', '1m', '1h', '1d']:
|
||||||
# Get historical data (raw)
|
# Get historical data (raw)
|
||||||
# Force refresh for 1s/1m to ensure we have the very latest candle for prediction
|
# Force refresh for 1s/1m to ensure we have the very latest candle for prediction
|
||||||
|
# But set persist=False to avoid locking the database with high-frequency writes
|
||||||
refresh = tf in ['1s', '1m']
|
refresh = tf in ['1s', '1m']
|
||||||
df = data_provider.get_historical_data(symbol, tf, limit=600, refresh=refresh)
|
df = data_provider.get_historical_data(symbol, tf, limit=600, refresh=refresh, persist=False)
|
||||||
if df is not None and not df.empty:
|
if df is not None and not df.empty:
|
||||||
# Extract raw arrays
|
# Extract raw arrays
|
||||||
opens = df['open'].values.astype(np.float32)
|
opens = df['open'].values.astype(np.float32)
|
||||||
|
|||||||
@@ -1902,10 +1902,11 @@ class ChartManager {
|
|||||||
|
|
||||||
// Add prediction traces (ghost candles)
|
// Add prediction traces (ghost candles)
|
||||||
if (predictionTraces.length > 0) {
|
if (predictionTraces.length > 0) {
|
||||||
// Remove existing ghost traces first (heuristic: traces with name 'Ghost Prediction')
|
// Remove existing ghost traces safely
|
||||||
|
// We iterate backwards to avoid index shifting issues when deleting
|
||||||
const currentTraces = plotElement.data.length;
|
const currentTraces = plotElement.data.length;
|
||||||
const indicesToRemove = [];
|
const indicesToRemove = [];
|
||||||
for (let i = 0; i < currentTraces; i++) {
|
for (let i = currentTraces - 1; i >= 0; i--) {
|
||||||
if (plotElement.data[i].name === 'Ghost Prediction') {
|
if (plotElement.data[i].name === 'Ghost Prediction') {
|
||||||
indicesToRemove.push(i);
|
indicesToRemove.push(i);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -849,8 +849,8 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display last 5 predictions (most recent first)
|
// Display last 15 predictions (most recent first)
|
||||||
const html = predictionHistory.slice(0, 5).map(pred => {
|
const html = predictionHistory.slice(0, 15).map(pred => {
|
||||||
// Safely parse timestamp
|
// Safely parse timestamp
|
||||||
let timeStr = '--:--:--';
|
let timeStr = '--:--:--';
|
||||||
try {
|
try {
|
||||||
@@ -868,12 +868,14 @@
|
|||||||
pred.action === 'SELL' ? 'text-danger' : 'text-secondary';
|
pred.action === 'SELL' ? 'text-danger' : 'text-secondary';
|
||||||
const confidence = (pred.confidence * 100).toFixed(1);
|
const confidence = (pred.confidence * 100).toFixed(1);
|
||||||
const price = (pred.predicted_price && !isNaN(pred.predicted_price)) ? pred.predicted_price.toFixed(2) : '--';
|
const price = (pred.predicted_price && !isNaN(pred.predicted_price)) ? pred.predicted_price.toFixed(2) : '--';
|
||||||
|
const timeframe = pred.timeframe || '1m';
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="d-flex justify-content-between align-items-center mb-1 pb-1 border-bottom">
|
<div class="d-flex justify-content-between align-items-center mb-1 pb-1 border-bottom">
|
||||||
<div>
|
<div>
|
||||||
|
<span class="badge bg-dark text-light me-1" style="font-size: 0.6rem;">${timeframe}</span>
|
||||||
<span class="${actionColor} fw-bold">${pred.action}</span>
|
<span class="${actionColor} fw-bold">${pred.action}</span>
|
||||||
<span class="text-muted ms-1">${timeStr}</span>
|
<span class="text-muted ms-1" style="font-size: 0.75rem;">${timeStr}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-end">
|
<div class="text-end">
|
||||||
<div>${confidence}%</div>
|
<div>${confidence}%</div>
|
||||||
@@ -930,17 +932,23 @@
|
|||||||
timestamp = latest.timestamp;
|
timestamp = latest.timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to prediction history (keep last 5)
|
// Add to prediction history (keep last 15)
|
||||||
predictionHistory.unshift({
|
const newPrediction = {
|
||||||
timestamp: timestamp,
|
timestamp: timestamp,
|
||||||
action: latest.action,
|
action: latest.action,
|
||||||
confidence: latest.confidence,
|
confidence: latest.confidence,
|
||||||
predicted_price: latest.predicted_price
|
predicted_price: latest.predicted_price,
|
||||||
});
|
timeframe: appState.currentTimeframes ? appState.currentTimeframes[0] : '1m'
|
||||||
if (predictionHistory.length > 5) {
|
};
|
||||||
predictionHistory = predictionHistory.slice(0, 5);
|
|
||||||
|
// Filter out undefined/invalid predictions before adding
|
||||||
|
if (latest.action && !isNaN(latest.confidence)) {
|
||||||
|
predictionHistory.unshift(newPrediction);
|
||||||
|
if (predictionHistory.length > 15) {
|
||||||
|
predictionHistory = predictionHistory.slice(0, 15);
|
||||||
|
}
|
||||||
|
updatePredictionHistory();
|
||||||
}
|
}
|
||||||
updatePredictionHistory();
|
|
||||||
|
|
||||||
// Update chart with signal markers and predictions
|
// Update chart with signal markers and predictions
|
||||||
if (window.appState && window.appState.chartManager) {
|
if (window.appState && window.appState.chartManager) {
|
||||||
|
|||||||
@@ -1623,7 +1623,7 @@ class DataProvider:
|
|||||||
logger.error(f"Error getting market state at time: {e}")
|
logger.error(f"Error getting market state at time: {e}")
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def get_historical_data(self, symbol: str, timeframe: str, limit: int = 1000, refresh: bool = False, allow_stale_cache: bool = False) -> Optional[pd.DataFrame]:
|
def get_historical_data(self, symbol: str, timeframe: str, limit: int = 1000, refresh: bool = False, allow_stale_cache: bool = False, persist: bool = True) -> Optional[pd.DataFrame]:
|
||||||
"""Get historical OHLCV data.
|
"""Get historical OHLCV data.
|
||||||
- Prefer cached data for low latency.
|
- Prefer cached data for low latency.
|
||||||
- If cache is empty or refresh=True, fetch real data from exchanges.
|
- If cache is empty or refresh=True, fetch real data from exchanges.
|
||||||
@@ -1635,6 +1635,7 @@ class DataProvider:
|
|||||||
limit: Number of candles to return
|
limit: Number of candles to return
|
||||||
refresh: Force refresh from exchange
|
refresh: Force refresh from exchange
|
||||||
allow_stale_cache: Allow loading stale cache (for startup performance)
|
allow_stale_cache: Allow loading stale cache (for startup performance)
|
||||||
|
persist: Whether to save fetched data to DuckDB (default: True). Set False for high-frequency polling.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Serve from cache when available
|
# Serve from cache when available
|
||||||
@@ -1644,7 +1645,7 @@ class DataProvider:
|
|||||||
return cached_df.tail(limit)
|
return cached_df.tail(limit)
|
||||||
|
|
||||||
# Try loading from DuckDB first (fast Parquet queries)
|
# Try loading from DuckDB first (fast Parquet queries)
|
||||||
if allow_stale_cache:
|
if allow_stale_cache and not refresh:
|
||||||
cached_df = self._load_from_duckdb(symbol, timeframe, limit=1500)
|
cached_df = self._load_from_duckdb(symbol, timeframe, limit=1500)
|
||||||
if cached_df is not None and not cached_df.empty:
|
if cached_df is not None and not cached_df.empty:
|
||||||
logger.info(f"Loaded {len(cached_df)} candles from DuckDB for {symbol} {timeframe} (startup mode)")
|
logger.info(f"Loaded {len(cached_df)} candles from DuckDB for {symbol} {timeframe} (startup mode)")
|
||||||
@@ -1662,8 +1663,8 @@ class DataProvider:
|
|||||||
if df is not None and not df.empty:
|
if df is not None and not df.empty:
|
||||||
df = self._ensure_datetime_index(df)
|
df = self._ensure_datetime_index(df)
|
||||||
|
|
||||||
# Store in DuckDB (Parquet + SQL in one)
|
# Store in DuckDB (Parquet + SQL in one) - Only if persist is True
|
||||||
if self.duckdb_storage:
|
if self.duckdb_storage and persist:
|
||||||
try:
|
try:
|
||||||
self.duckdb_storage.store_ohlcv_data(symbol, timeframe, df)
|
self.duckdb_storage.store_ohlcv_data(symbol, timeframe, df)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -1680,7 +1681,12 @@ class DataProvider:
|
|||||||
combined_df = combined_df.sort_index()
|
combined_df = combined_df.sort_index()
|
||||||
self.cached_data[symbol][timeframe] = combined_df.tail(1500)
|
self.cached_data[symbol][timeframe] = combined_df.tail(1500)
|
||||||
|
|
||||||
logger.info(f"Stored {len(df)} candles for {symbol} {timeframe} (DuckDB + memory cache)")
|
if persist:
|
||||||
|
logger.info(f"Stored {len(df)} candles for {symbol} {timeframe} (DuckDB + memory cache)")
|
||||||
|
else:
|
||||||
|
# Less verbose for high-frequency polling
|
||||||
|
logger.debug(f"Updated memory cache with {len(df)} candles for {symbol} {timeframe}")
|
||||||
|
|
||||||
return self.cached_data[symbol][timeframe].tail(limit)
|
return self.cached_data[symbol][timeframe].tail(limit)
|
||||||
|
|
||||||
logger.warning(f"No real data available for {symbol} {timeframe} at request time")
|
logger.warning(f"No real data available for {symbol} {timeframe} at request time")
|
||||||
|
|||||||
Reference in New Issue
Block a user