fix template again
This commit is contained in:
@@ -480,6 +480,51 @@ class AnnotationDashboard:
|
||||
</html>
|
||||
"""
|
||||
|
||||
@self.server.route('/api/recalculate-pivots', methods=['POST'])
|
||||
def recalculate_pivots():
|
||||
"""Recalculate pivot points for merged data"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
symbol = data.get('symbol', 'ETH/USDT')
|
||||
timeframe = data.get('timeframe')
|
||||
timestamps = data.get('timestamps', [])
|
||||
ohlcv_data = data.get('ohlcv', {})
|
||||
|
||||
if not timeframe or not timestamps:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {'code': 'INVALID_REQUEST', 'message': 'Missing timeframe or timestamps'}
|
||||
})
|
||||
|
||||
logger.info(f"🔄 Recalculating pivots for {symbol} {timeframe} with {len(timestamps)} candles")
|
||||
|
||||
# Convert to DataFrame
|
||||
df = pd.DataFrame({
|
||||
'open': ohlcv_data.get('open', []),
|
||||
'high': ohlcv_data.get('high', []),
|
||||
'low': ohlcv_data.get('low', []),
|
||||
'close': ohlcv_data.get('close', []),
|
||||
'volume': ohlcv_data.get('volume', [])
|
||||
})
|
||||
df.index = pd.to_datetime(timestamps)
|
||||
|
||||
# Recalculate pivot markers
|
||||
pivot_markers = self._get_pivot_markers_for_timeframe(symbol, timeframe, df)
|
||||
|
||||
logger.info(f" ✅ Recalculated {len(pivot_markers)} pivot candles")
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'pivot_markers': pivot_markers
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error recalculating pivots: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': {'code': 'RECALC_ERROR', 'message': str(e)}
|
||||
})
|
||||
|
||||
@self.server.route('/api/chart-data', methods=['POST'])
|
||||
def get_chart_data():
|
||||
"""Get chart data for specified symbol and timeframes with infinite scroll support"""
|
||||
@@ -489,9 +534,15 @@ class AnnotationDashboard:
|
||||
timeframes = data.get('timeframes', ['1s', '1m', '1h', '1d'])
|
||||
start_time_str = data.get('start_time')
|
||||
end_time_str = data.get('end_time')
|
||||
limit = data.get('limit', 500) # Allow client to request more data
|
||||
limit = data.get('limit', 2000) # Default 2000 candles for training
|
||||
direction = data.get('direction', 'latest') # 'latest', 'before', or 'after'
|
||||
|
||||
logger.info(f"📊 Chart data request: {symbol} {timeframes} direction={direction} limit={limit}")
|
||||
if start_time_str:
|
||||
logger.info(f" start_time: {start_time_str}")
|
||||
if end_time_str:
|
||||
logger.info(f" end_time: {end_time_str}")
|
||||
|
||||
if not self.data_loader:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
@@ -522,6 +573,8 @@ class AnnotationDashboard:
|
||||
)
|
||||
|
||||
if df is not None and not df.empty:
|
||||
logger.info(f" ✅ {timeframe}: {len(df)} candles ({df.index[0]} to {df.index[-1]})")
|
||||
|
||||
# Get pivot points for this timeframe
|
||||
pivot_markers = self._get_pivot_markers_for_timeframe(symbol, timeframe, df)
|
||||
|
||||
@@ -535,6 +588,8 @@ class AnnotationDashboard:
|
||||
'volume': df['volume'].tolist(),
|
||||
'pivot_markers': pivot_markers # Optional: only present if pivots exist
|
||||
}
|
||||
else:
|
||||
logger.warning(f" ❌ {timeframe}: No data returned")
|
||||
|
||||
# Get pivot bounds for the symbol
|
||||
pivot_bounds = None
|
||||
|
||||
@@ -1190,6 +1190,14 @@ class ChartManager {
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
console.log(`📊 API Response for ${timeframe} ${direction}:`, {
|
||||
success: result.success,
|
||||
hasChartData: !!result.chart_data,
|
||||
hasTimeframeData: result.chart_data ? !!result.chart_data[timeframe] : false,
|
||||
dataLength: result.chart_data && result.chart_data[timeframe] ? result.chart_data[timeframe].timestamps.length : 0,
|
||||
error: result.error
|
||||
});
|
||||
|
||||
if (result.success && result.chart_data && result.chart_data[timeframe]) {
|
||||
const newData = result.chart_data[timeframe];
|
||||
|
||||
@@ -1200,13 +1208,18 @@ class ChartManager {
|
||||
return;
|
||||
}
|
||||
|
||||
// Log data ranges for debugging
|
||||
console.log(`📥 New data: ${newData.timestamps[0]} to ${newData.timestamps[newData.timestamps.length - 1]}`);
|
||||
console.log(`📦 Existing: ${chart.data.timestamps[0]} to ${chart.data.timestamps[chart.data.timestamps.length - 1]}`);
|
||||
|
||||
// Merge with existing data
|
||||
this.mergeChartData(timeframe, newData, direction);
|
||||
|
||||
console.log(`Loaded ${newData.timestamps.length} new candles for ${timeframe}`);
|
||||
console.log(`✅ Loaded ${newData.timestamps.length} new candles for ${timeframe}`);
|
||||
window.showSuccess(`Loaded ${newData.timestamps.length} more candles`);
|
||||
} else {
|
||||
console.warn(`No more data available for ${timeframe} ${direction}`);
|
||||
console.warn(`❌ No more data available for ${timeframe} ${direction}`);
|
||||
console.warn('Full result:', result);
|
||||
window.showWarning('No more historical data available');
|
||||
}
|
||||
|
||||
@@ -1287,10 +1300,173 @@ class ChartManager {
|
||||
// Update stored data
|
||||
chart.data = mergedData;
|
||||
|
||||
// Recalculate pivot points for the merged data
|
||||
this.recalculatePivots(timeframe, mergedData);
|
||||
|
||||
// Update the chart with merged data
|
||||
this.updateSingleChart(timeframe, mergedData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculate pivot points for merged data
|
||||
*/
|
||||
async recalculatePivots(timeframe, data) {
|
||||
try {
|
||||
console.log(`🔄 Recalculating pivots for ${timeframe} with ${data.timestamps.length} candles...`);
|
||||
|
||||
const response = await fetch('/api/recalculate-pivots', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
symbol: window.appState?.currentSymbol || 'ETH/USDT',
|
||||
timeframe: timeframe,
|
||||
timestamps: data.timestamps,
|
||||
ohlcv: {
|
||||
open: data.open,
|
||||
high: data.high,
|
||||
low: data.low,
|
||||
close: data.close,
|
||||
volume: data.volume
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success && result.pivot_markers) {
|
||||
// Update pivot markers in chart data
|
||||
const chart = this.charts[timeframe];
|
||||
if (chart && chart.data) {
|
||||
chart.data.pivot_markers = result.pivot_markers;
|
||||
console.log(`✅ Pivots recalculated: ${Object.keys(result.pivot_markers).length} pivot candles`);
|
||||
|
||||
// Redraw the chart with updated pivots
|
||||
this.redrawChartWithPivots(timeframe, chart.data);
|
||||
}
|
||||
} else {
|
||||
console.warn('Failed to recalculate pivots:', result.error);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Error recalculating pivots for ${timeframe}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redraw chart with updated pivot markers
|
||||
*/
|
||||
redrawChartWithPivots(timeframe, data) {
|
||||
const chart = this.charts[timeframe];
|
||||
if (!chart) return;
|
||||
|
||||
// Build pivot shapes and annotations
|
||||
const shapes = [];
|
||||
const annotations = [];
|
||||
const pivotDots = {
|
||||
x: [], y: [], text: [],
|
||||
marker: { color: [], size: [], symbol: [] },
|
||||
mode: 'markers',
|
||||
hoverinfo: 'text',
|
||||
showlegend: false
|
||||
};
|
||||
|
||||
if (data.pivot_markers && Object.keys(data.pivot_markers).length > 0) {
|
||||
const xMin = data.timestamps[0];
|
||||
const xMax = data.timestamps[data.timestamps.length - 1];
|
||||
|
||||
// Process each timestamp that has pivot markers
|
||||
Object.entries(data.pivot_markers).forEach(([timestamp, pivots]) => {
|
||||
// Process high pivots
|
||||
if (pivots.highs && pivots.highs.length > 0) {
|
||||
pivots.highs.forEach(pivot => {
|
||||
const color = this._getPivotColor(pivot.level, 'high');
|
||||
|
||||
pivotDots.x.push(timestamp);
|
||||
pivotDots.y.push(pivot.price);
|
||||
pivotDots.text.push(`L${pivot.level} High Pivot<br>Price: ${pivot.price.toFixed(2)}<br>Strength: ${(pivot.strength * 100).toFixed(0)}%`);
|
||||
pivotDots.marker.color.push(color);
|
||||
pivotDots.marker.size.push(this._getPivotMarkerSize(pivot.level));
|
||||
pivotDots.marker.symbol.push('triangle-down');
|
||||
|
||||
if (pivot.is_last) {
|
||||
shapes.push({
|
||||
type: 'line',
|
||||
x0: xMin, y0: pivot.price,
|
||||
x1: xMax, y1: pivot.price,
|
||||
line: { color: color, width: 1, dash: 'dash' },
|
||||
layer: 'below'
|
||||
});
|
||||
|
||||
annotations.push({
|
||||
x: xMax, y: pivot.price,
|
||||
text: `L${pivot.level}H`,
|
||||
showarrow: false,
|
||||
xanchor: 'left',
|
||||
font: { size: 9, color: color },
|
||||
bgcolor: '#1f2937',
|
||||
borderpad: 2
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Process low pivots
|
||||
if (pivots.lows && pivots.lows.length > 0) {
|
||||
pivots.lows.forEach(pivot => {
|
||||
const color = this._getPivotColor(pivot.level, 'low');
|
||||
|
||||
pivotDots.x.push(timestamp);
|
||||
pivotDots.y.push(pivot.price);
|
||||
pivotDots.text.push(`L${pivot.level} Low Pivot<br>Price: ${pivot.price.toFixed(2)}<br>Strength: ${(pivot.strength * 100).toFixed(0)}%`);
|
||||
pivotDots.marker.color.push(color);
|
||||
pivotDots.marker.size.push(this._getPivotMarkerSize(pivot.level));
|
||||
pivotDots.marker.symbol.push('triangle-up');
|
||||
|
||||
if (pivot.is_last) {
|
||||
shapes.push({
|
||||
type: 'line',
|
||||
x0: xMin, y0: pivot.price,
|
||||
x1: xMax, y1: pivot.price,
|
||||
line: { color: color, width: 1, dash: 'dash' },
|
||||
layer: 'below'
|
||||
});
|
||||
|
||||
annotations.push({
|
||||
x: xMax, y: pivot.price,
|
||||
text: `L${pivot.level}L`,
|
||||
showarrow: false,
|
||||
xanchor: 'left',
|
||||
font: { size: 9, color: color },
|
||||
bgcolor: '#1f2937',
|
||||
borderpad: 2
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update chart layout with new pivots
|
||||
Plotly.relayout(chart.plotId, {
|
||||
shapes: shapes,
|
||||
annotations: annotations
|
||||
});
|
||||
|
||||
// Update pivot dots trace
|
||||
if (pivotDots.x.length > 0) {
|
||||
Plotly.restyle(chart.plotId, {
|
||||
x: [pivotDots.x],
|
||||
y: [pivotDots.y],
|
||||
text: [pivotDots.text],
|
||||
'marker.color': [pivotDots.marker.color],
|
||||
'marker.size': [pivotDots.marker.size],
|
||||
'marker.symbol': [pivotDots.marker.symbol]
|
||||
}, [2]); // Trace index 2 is pivot dots
|
||||
}
|
||||
|
||||
console.log(`🎨 Redrawn ${timeframe} chart with updated pivots`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a single chart with new data
|
||||
*/
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
// Initialize application state
|
||||
// IMPORTANT!!! DO NOT CHANGE {{ x }} to { { x } }
|
||||
window.appState = {
|
||||
currentSymbol: '{{ current_symbol }}',
|
||||
currentTimeframes: {{ timeframes | tojson }},
|
||||
@@ -97,7 +98,7 @@
|
||||
function loadInitialData() {
|
||||
console.log('Loading initial chart data...');
|
||||
|
||||
// Fetch initial chart data
|
||||
// Fetch initial chart data with 2000 candles for training
|
||||
fetch('/api/chart-data', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -105,7 +106,8 @@
|
||||
symbol: appState.currentSymbol,
|
||||
timeframes: appState.currentTimeframes,
|
||||
start_time: null,
|
||||
end_time: null
|
||||
end_time: null,
|
||||
limit: 2000 // Load 2000 candles initially for training
|
||||
})
|
||||
})
|
||||
.then(response => {
|
||||
|
||||
Reference in New Issue
Block a user