Selaa lähdekoodia

支持三个所的价格同时看的模板

skyffire 6 kuukautta sitten
vanhempi
commit
a5005b2c07
2 muutettua tiedostoa jossa 58 lisäystä ja 72 poistoa
  1. 2 2
      price_checker.py
  2. 56 70
      templates/index.html

+ 2 - 2
price_checker.py

@@ -9,8 +9,8 @@ import logging
 
 # --- 配置部分 ---
 # OpenOcean
-IN_TOKEN_ADDRESS_BSC = '0x8F0528cE5eF7B51152A59745bEfDD91D97091d2F'  # USDT on BSC
-OUT_TOKEN_ADDRESS_BSC = '0x6894CDe390a3f51155ea41Ed24a33A4827d3063D'  # CAT on BSC (示例)
+IN_TOKEN_ADDRESS_BSC = '0x55d398326f99059ff775485246999027b3197955'  # USDT on BSC
+OUT_TOKEN_ADDRESS_BSC = '0x8F0528cE5eF7B51152A59745bEfDD91D97091d2F'
 AMOUNT_TO_QUERY_HUMAN = decimal.Decimal('1000')  # 查询的USDT数量
 
 # Gate.io 现货交易对

+ 56 - 70
templates/index.html

@@ -9,21 +9,24 @@
     <style>
         body { font-family: Arial, sans-serif; margin: 20px; background-color: #f4f4f4; color: #333; }
         .container { background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); margin-bottom: 20px; }
-        h1, h2 { text-align: center; color: #333; }
-        table { width: 100%; border-collapse: collapse; margin-top: 20px; margin-bottom: 20px; }
-        th, td { border: 1px solid #ddd; padding: 10px; text-align: left; font-size:0.9em; } /* Smaller padding/font for more data */
-        th { background-color: #e9e9e9; }
+        h1, h2 { text-align: center; color: #333; margin-top:10px; margin-bottom:15px; }
+        table { width: 100%; border-collapse: collapse; margin-top: 10px; margin-bottom: 15px; }
+        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; font-size:0.85em; word-break: break-all; }
+        th { background-color: #e9e9e9; white-space: nowrap; }
         .price-up { color: green; }
         .price-down { color: red; }
-        .error-message { color: #c00; font-style: italic; font-size: 0.9em; }
-        .timestamp { font-size: 0.9em; color: #666; text-align: right; margin-top: 15px; }
-        .chart-container { position: relative; height: 40vh; width: 90vw; margin: auto; margin-bottom: 10px; }
-        .controls-container { text-align: center; margin-bottom: 20px; margin-top: 5px; }
+        .error-message { color: #c00; font-style: italic; }
+        .status-cell { min-width: 100px; } /* Give status cells some min width */
+        .timestamp { font-size: 0.9em; color: #666; text-align: right; margin-top: 10px; }
+        .chart-container { position: relative; height: 38vh; width: 95vw; margin: auto; margin-bottom: 10px; } /* Slightly smaller height for more charts if needed */
+        .controls-container { text-align: center; margin-bottom: 15px; margin-top: 5px; }
         .control-button { background-color: #007bff; color: white; border: none; padding: 8px 15px; font-size: 14px; border-radius: 5px; cursor: pointer; margin:0 5px; }
         .control-button:hover { background-color: #0056b3; }
         .pause-button-active { background-color: #ffc107; color: #333; }
         .pause-button-active:hover { background-color: #e0a800; }
         .platform-name {font-weight: bold;}
+        #main-title { font-size: 1.8em; }
+        h2 { font-size: 1.3em; }
     </style>
 </head>
 <body>
@@ -37,7 +40,7 @@
                 <tr>
                     <th>平台</th>
                     <th>价格 (USDT)</th>
-                    <th>状态/错误</th>
+                    <th class="status-cell">状态/错误</th>
                 </tr>
             </thead>
             <tbody>
@@ -97,7 +100,7 @@
     <div class="container">
         <h2 id="diff-chart-title">价差百分比历史曲线</h2>
         <div class="chart-container">
-            <canvas id="diffPercentageChart"></canvas> <!-- Changed ID -->
+            <canvas id="diffPercentageChart"></canvas>
         </div>
         <div class="controls-container">
             <button id="reset-diff-zoom-button" class="control-button">重置缩放</button>
@@ -106,15 +109,14 @@
 
     <script>
         let priceChartInstance = null;
-        let diffChartInstance = null; // For percentage differences
+        let diffChartInstance = null;
 
         let dataUpdateIntervalID = null;
         let isPaused = false;
-        const REFRESH_INTERVAL_MS = 1000;
+        const REFRESH_INTERVAL_MS = 1000; // 1秒刷新一次
         const pauseResumeButton = document.getElementById('pause-resume-button');
         let isSyncingZoomPan = false;
 
-        // Get initial config from Flask if available
         const initialConfig = {
             GATEIO_SPOT_PAIR: "{{ config.GATEIO_SPOT_PAIR if config else 'SPOT_USDT' }}",
             GATEIO_FUTURES_CONTRACT: "{{ config.GATEIO_FUTURES_CONTRACT if config else 'FUTURES_USDT' }}",
@@ -125,31 +127,30 @@
         let currentAssetSymbol = initialConfig.TARGET_ASSET_SYMBOL;
 
         function formatPrice(priceStr) {
-            // (formatPrice function from previous example, can be reused)
-            if (priceStr === null || priceStr === undefined || priceStr === "N/A") return "N/A";
+            if (priceStr === null || priceStr === undefined || priceStr === "N/A" || priceStr.toLowerCase() === "n/a") return "N/A";
             const price = parseFloat(priceStr);
             if (isNaN(price)) return "N/A";
-            if (price === 0) return "0.000000";
-            if (price < 0.000001 && price > 0) return price.toExponential(4);
-            if (price < 0.01) return price.toFixed(8);
-            if (price < 1) return price.toFixed(6);
+            if (price === 0) return "0.00000000"; // More precision for 0
+            if (Math.abs(price) < 0.0000001 && price !== 0) return price.toExponential(3);
+            if (Math.abs(price) < 0.001) return price.toFixed(8);
+            if (Math.abs(price) < 1) return price.toFixed(6);
             return price.toFixed(4);
         }
+
         function formatPercentage(percStr) {
-            if (percStr === null || percStr === undefined || percStr === "N/A") return "N/A";
-            // Assumes percStr is like "+0.1234%" or "-5.6789%"
-            if (String(percStr).includes('%')) return percStr;
+            if (percStr === null || percStr === undefined || percStr === "N/A" || percStr.toLowerCase() === "n/a") return "N/A";
+            if (String(percStr).includes('%')) return percStr; // Already formatted
             const perc = parseFloat(percStr);
             if (isNaN(perc)) return "N/A";
             return `${perc > 0 ? '+' : ''}${perc.toFixed(4)}%`;
         }
 
-        function syncXAxes(sourceChart, targetChart) { /* (Same as before) */
+        function syncXAxes(sourceChart, targetChart) {
             if (isSyncingZoomPan || !sourceChart || !targetChart) return;
             isSyncingZoomPan = true;
             const sourceXScale = sourceChart.scales.x;
             const targetXScale = targetChart.scales.x;
-            if (sourceXScale && targetXScale) {
+            if (sourceXScale && targetXScale && sourceXScale.min !== undefined && sourceXScale.max !== undefined) { // Ensure min/max are defined
                 targetXScale.options.min = sourceXScale.min;
                 targetXScale.options.max = sourceXScale.max;
                 targetChart.update('none');
@@ -162,12 +163,12 @@
                 type: 'line',
                 data: { labels: initialLabels, datasets: datasetsConfig },
                 options: {
-                    responsive: true, maintainAspectRatio: false, animation: { duration: 150 },
+                    responsive: true, maintainAspectRatio: false, animation: { duration: 0 }, // Disable animation for faster updates
                     scales: {
                         x: { title: { display: true, text: '时间' } },
                         y: {
                             title: { display: true, text: yAxisTitle },
-                            beginAtZero: !isPriceChart ? false : undefined, // For diff chart, don't begin at zero
+                            beginAtZero: !isPriceChart ? false : undefined,
                              ticks: {
                                 callback: function(value) {
                                     return isPriceChart ? formatPrice(String(value)) : parseFloat(value).toFixed(2) + '%';
@@ -178,6 +179,7 @@
                     plugins: {
                         legend: { position: 'top' }, title: { display: true, text: chartTitleText },
                         tooltip: {
+                            mode: 'index', intersect: false,
                             callbacks: {
                                 label: function(context) {
                                     let label = context.dataset.label || '';
@@ -189,18 +191,15 @@
                                 }
                             }
                         },
-                        zoom: { /* (Zoom config same as before) */
-                            pan: {
-                                enabled: true, mode: 'x', threshold: 5,
+                        zoom: {
+                            pan: { enabled: true, mode: 'x', threshold: 5,
                                 onPanComplete({chart}) {
                                     if (chartIdentifier === 'price' && diffChartInstance) syncXAxes(chart, diffChartInstance);
                                     else if (chartIdentifier === 'diff' && priceChartInstance) syncXAxes(chart, priceChartInstance);
                                 }
                             },
-                            zoom: {
-                                wheel: { enabled: true, speed: 0.1 }, pinch: { enabled: true },
-                                drag: { enabled: true, backgroundColor: 'rgba(0,123,255,0.25)'},
-                                mode: 'x',
+                            zoom: { wheel: { enabled: true, speed: 0.1 }, pinch: { enabled: true },
+                                drag: { enabled: true, backgroundColor: 'rgba(0,123,255,0.25)'}, mode: 'x',
                                 onZoomComplete({chart}) {
                                     if (chartIdentifier === 'price' && diffChartInstance) syncXAxes(chart, diffChartInstance);
                                     else if (chartIdentifier === 'diff' && priceChartInstance) syncXAxes(chart, priceChartInstance);
@@ -208,13 +207,12 @@
                             }
                         }
                     },
-                    elements: { point:{ radius: 1.5 } } // Smaller points for more lines
+                    elements: { point:{ radius: 1.5, hoverRadius: 3 } }
                 }
             });
         }
 
         function updateChartData(chartInstance, newLabels, newDatasetsDataArray) {
-            // (updateChartData function largely same, ensures it updates all datasets in newDatasetsDataArray)
             if (!chartInstance) return;
             const currentXMin = chartInstance.scales.x.min;
             const currentXMax = chartInstance.scales.x.max;
@@ -225,7 +223,7 @@
 
             chartInstance.data.labels = newLabels;
             newDatasetsDataArray.forEach((datasetData, index) => {
-                if(chartInstance.data.datasets[index]) { // Check if dataset exists
+                if(chartInstance.data.datasets[index]) {
                     chartInstance.data.datasets[index].data = datasetData;
                 }
             });
@@ -242,7 +240,7 @@
                     chartInstance.options.scales.x.max = undefined;
                 }
             }
-            chartInstance.update('quiet');
+            chartInstance.update('none'); // Use 'none' for no animation for faster update
         }
 
         function updateDisplayAndCharts() {
@@ -250,16 +248,13 @@
                 .then(response => response.json())
                 .then(data => {
                     const current = data.current;
-                    // Update global config vars if they changed (though unlikely for these)
                     currentGateSpotPair = current.config_gate_spot_pair || currentGateSpotPair;
                     currentGateFuturesContract = current.config_gate_futures_contract || currentGateFuturesContract;
                     currentAssetSymbol = current.config_target_asset_symbol || currentAssetSymbol;
 
-                    // --- Update Titles and Labels ---
                     document.getElementById('main-title').textContent = `${currentAssetSymbol}/USDT 多平台价格监控`;
                     document.getElementById('gate-spot-label').textContent = `Gate.io 现货 (${currentGateSpotPair})`;
                     document.getElementById('gate-futures-label').textContent = `Gate.io 期货 (${currentGateFuturesContract})`;
-
                     document.getElementById('diff-label-oo-spot').textContent = `OO vs Gate.io 现货 (${currentGateSpotPair})`;
                     document.getElementById('diff-label-oo-futures').textContent = `OO vs Gate.io 期货 (${currentGateFuturesContract})`;
                     document.getElementById('diff-label-spot-futures').textContent = `Gate.io 现货 (${currentGateSpotPair}) vs 期货 (${currentGateFuturesContract})`;
@@ -276,48 +271,39 @@
                         diffChartInstance.options.plugins.title.text = diffChartTitleText;
                     }
 
-                    // --- Update Table Data ---
                     document.getElementById('oo-price').textContent = formatPrice(current.oo_price);
                     document.getElementById('gate-spot-price').textContent = formatPrice(current.gate_spot_price);
                     document.getElementById('gate-futures-price').textContent = formatPrice(current.gate_futures_price);
 
-                    document.getElementById('oo-status').textContent = current.oo_error ? `错误: ${current.oo_error}` : '正常';
-                    document.getElementById('gate-spot-status').textContent = current.gate_spot_error ? `错误: ${current.gate_spot_error}` : '正常';
-                    document.getElementById('gate-futures-status').textContent = current.gate_futures_error ? `错误: ${current.gate_futures_error}` : '正常';
-
-                    // Update error message classes
+                    document.getElementById('oo-status').textContent = current.oo_error ? current.oo_error : '正常';
+                    document.getElementById('gate-spot-status').textContent = current.gate_spot_error ? current.gate_spot_error : '正常';
+                    document.getElementById('gate-futures-status').textContent = current.gate_futures_error ? current.gate_futures_error : '正常';
                     document.getElementById('oo-status').className = current.oo_error ? 'status-cell error-message' : 'status-cell';
                     document.getElementById('gate-spot-status').className = current.gate_spot_error ? 'status-cell error-message' : 'status-cell';
                     document.getElementById('gate-futures-status').className = current.gate_futures_error ? 'status-cell error-message' : 'status-cell';
 
-                    // Update diff percentages
                     const diffOOSpotEl = document.getElementById('diff-oo-vs-spot');
                     const diffOOFuturesEl = document.getElementById('diff-oo-vs-futures');
                     const diffSpotFuturesEl = document.getElementById('diff-spot-vs-futures');
-
                     diffOOSpotEl.textContent = formatPercentage(current.diff_oo_vs_spot_percentage);
                     diffOOFuturesEl.textContent = formatPercentage(current.diff_oo_vs_futures_percentage);
                     diffSpotFuturesEl.textContent = formatPercentage(current.diff_spot_vs_futures_percentage);
-
                     [diffOOSpotEl, diffOOFuturesEl, diffSpotFuturesEl].forEach(el => {
                         const valStr = el.textContent.replace('%','').replace('+','');
                         const val = parseFloat(valStr);
-                        if (!isNaN(val)) {
-                            el.className = val > 0 ? 'price-up' : (val < 0 ? 'price-down' : '');
-                        } else { el.className = ''; }
+                        if (!isNaN(val)) { el.className = val > 0 ? 'price-up' : (val < 0 ? 'price-down' : ''); }
+                        else { el.className = ''; }
                     });
 
                     document.getElementById('last-updated').textContent = current.last_updated || "N/A";
 
-                    // --- Update Charts ---
                     const history = data.history;
 
-                    // Price History Chart
                     const priceCtx = document.getElementById('priceHistoryChart').getContext('2d');
                     const priceDatasets = [
-                        { label: 'OpenOcean', data: history.prices.oo, borderColor: 'rgb(75, 192, 192)', tension: 0.1, fill: false },
-                        { label: `Gate Spot (${currentGateSpotPair})`, data: history.prices.spot, borderColor: 'rgb(255, 99, 132)', tension: 0.1, fill: false },
-                        { label: `Gate Futures (${currentGateFuturesContract})`, data: history.prices.futures, borderColor: 'rgb(54, 162, 235)', tension: 0.1, fill: false }
+                        { label: 'OpenOcean', data: history.prices.oo, borderColor: 'rgb(75, 192, 192)', tension: 0.1, fill: false, borderWidth: 1.5 },
+                        { label: `Gate Spot (${currentGateSpotPair})`, data: history.prices.spot, borderColor: 'rgb(255, 99, 132)', tension: 0.1, fill: false, borderWidth: 1.5 },
+                        { label: `Gate Futures (${currentGateFuturesContract})`, data: history.prices.futures, borderColor: 'rgb(54, 162, 235)', tension: 0.1, fill: false, borderWidth: 1.5 }
                     ];
                     if (!priceChartInstance) {
                         priceChartInstance = initializeChart(priceCtx, 'price', priceDatasets, history.prices.labels, priceChartYLabel, priceChartTitle, true);
@@ -327,12 +313,11 @@
                         updateChartData(priceChartInstance, history.prices.labels, [history.prices.oo, history.prices.spot, history.prices.futures]);
                     }
 
-                    // Diff Percentage Chart
                     const diffCtx = document.getElementById('diffPercentageChart').getContext('2d');
                     const diffDatasets = [
-                        { label: `OO vs Spot (${currentGateSpotPair})`, data: history.diffs.oo_vs_spot, borderColor: 'rgb(255, 159, 64)', tension: 0.1, fill: false },
-                        { label: `OO vs Futures (${currentGateFuturesContract})`, data: history.diffs.oo_vs_futures, borderColor: 'rgb(153, 102, 255)', tension: 0.1, fill: false },
-                        { label: `Spot (${currentGateSpotPair}) vs Futures (${currentGateFuturesContract})`, data: history.diffs.spot_vs_futures, borderColor: 'rgb(75, 192, 75)', tension: 0.1, fill: false }
+                        { label: `OO vs Spot (${currentGateSpotPair})`, data: history.diffs.oo_vs_spot, borderColor: 'rgb(255, 159, 64)', tension: 0.1, fill: false, borderWidth: 1.5 },
+                        { label: `OO vs Futures (${currentGateFuturesContract})`, data: history.diffs.oo_vs_futures, borderColor: 'rgb(153, 102, 255)', tension: 0.1, fill: false, borderWidth: 1.5 },
+                        { label: `Spot (${currentGateSpotPair}) vs Futures (${currentGateFuturesContract})`, data: history.diffs.spot_vs_futures, borderColor: 'rgb(75, 192, 75)', tension: 0.1, fill: false, borderWidth: 1.5 }
                     ];
                     if (!diffChartInstance) {
                         diffChartInstance = initializeChart(diffCtx, 'diff', diffDatasets, history.diffs.labels, '价差 (%)', diffChartTitleText, false);
@@ -342,36 +327,34 @@
                         diffChartInstance.data.datasets[2].label = `Spot (${currentGateSpotPair}) vs Futures (${currentGateFuturesContract})`;
                         updateChartData(diffChartInstance, history.diffs.labels, [history.diffs.oo_vs_spot, history.diffs.oo_vs_futures, history.diffs.spot_vs_futures]);
                     }
-
                 })
                 .catch(error => {
                     console.error('Error fetching data for all platforms:', error);
-                    // Display a general error, or individual errors if available.
                 });
         }
 
-        function togglePauseResume() { /* (Same as before) */
+        function togglePauseResume() {
             isPaused = !isPaused;
             if (isPaused) {
                 clearInterval(dataUpdateIntervalID); dataUpdateIntervalID = null;
                 pauseResumeButton.textContent = '继续刷新'; pauseResumeButton.classList.add('pause-button-active');
             } else {
                 pauseResumeButton.textContent = '暂停刷新'; pauseResumeButton.classList.remove('pause-button-active');
-                updateDisplayAndCharts();
+                updateDisplayAndCharts(); // Refresh immediately on resume
                 dataUpdateIntervalID = setInterval(updateDisplayAndCharts, REFRESH_INTERVAL_MS);
             }
         }
 
-        function resetAllZooms() { /* (Same as before, ensure it updates both charts) */
+        function resetAllZooms() {
             if (priceChartInstance) {
                 priceChartInstance.resetZoom(); priceChartInstance.options.scales.x.min = undefined; priceChartInstance.options.scales.x.max = undefined;
             }
             if (diffChartInstance) {
                 diffChartInstance.resetZoom(); diffChartInstance.options.scales.x.min = undefined; diffChartInstance.options.scales.x.max = undefined;
             }
-            if(isPaused){ updateDisplayAndCharts(); }
-            else {
-                if (priceChartInstance) priceChartInstance.update('none');
+            if(isPaused){ updateDisplayAndCharts(); } // If paused, manually trigger redraw
+            else { // If not paused, next interval will update, or force faster view update
+                if (priceChartInstance) priceChartInstance.update('none'); // 'none' to avoid animation interference
                 if (diffChartInstance) diffChartInstance.update('none');
             }
         }
@@ -380,10 +363,13 @@
         document.getElementById('reset-price-zoom-button').addEventListener('click', resetAllZooms);
         document.getElementById('reset-diff-zoom-button').addEventListener('click', resetAllZooms);
 
+        console.log("Frontend Initializing...");
+        console.log("Initial Config from Flask:", initialConfig);
+
         updateDisplayAndCharts(); // Initial load
         if (!isPaused) {
             dataUpdateIntervalID = setInterval(updateDisplayAndCharts, REFRESH_INTERVAL_MS);
         }
     </script>
 </body>
-</html>
+</html>