skyffire пре 6 месеци
родитељ
комит
7bd983aef5
2 измењених фајлова са 100 додато и 174 уклоњено
  1. 71 99
      price_checker.py
  2. 29 75
      templates/index_plotly_sol_spot_only.html

+ 71 - 99
price_checker.py

@@ -12,16 +12,13 @@ import plotly.graph_objects as go
 from plotly.utils import PlotlyJSONEncoder
 
 # --- 配置部分 ---
-# OpenOcean (假设 'SOL' 是输入, 'RFC' 是输出, 得到 RFC/SOL 价格)
 IN_TOKEN_ADDRESS_SOLANA = 'So11111111111111111111111111111111111111112'  # SOL Mint Address
-OUT_TOKEN_ADDRESS_SOLANA = 'DitHyRMQiSDhn5cnKMJV2CDDt6sVct96YrECiM49pump'  # RFC Mint Address (示例)
-AMOUNT_TO_QUERY_OPENOCEAN_IN_SOL = decimal.Decimal('10')  # 例如用 10 SOL 去查询能买多少 RFC
+OUT_TOKEN_ADDRESS_SOLANA = '38PgzpJYu2HkiYvV8qePFakB8tuobPdGm2FFEn7Dpump'  #OutToken
+AMOUNT_TO_QUERY_OPENOCEAN_IN_SOL = decimal.Decimal('10')
 
-# Gate.io (RFC/USDT)
-GATEIO_SPOT_PAIR_RFC_USDT = 'HOUSE_USDT'
-GATEIO_FUTURES_CONTRACT_RFC_USDT = 'HOUSE_USDT'
+GATEIO_SPOT_PAIR_RFC_USDT = 'GORK_USDT'
+# GATEIO_FUTURES_CONTRACT_RFC_USDT = 'GORK_USDT' # 注释掉期货配置
 
-# Binance (SOL/USDT - 用于转换)
 BINANCE_SOL_USDT_PAIR = 'SOLUSDT'
 
 proxies = None
@@ -29,7 +26,7 @@ decimal.getcontext().prec = 36
 
 
 # --- 价格获取函数 ---
-def get_openocean_price_solana(in_token_addr, out_token_addr, human_amount_in_decimal_for_request):  # Solana gas is low
+def get_openocean_price_solana(in_token_addr, out_token_addr, human_amount_in_decimal_for_request):
     chain = 'solana'
     url = f'https://open-api.openocean.finance/v4/{chain}/quote'
     params = {'inTokenAddress': in_token_addr, 'outTokenAddress': out_token_addr,
@@ -45,12 +42,11 @@ def get_openocean_price_solana(in_token_addr, out_token_addr, human_amount_in_de
                 'decimals') is None: return {"error": "OO API缺少数据"}
             in_dec, out_dec = int(d['inToken']['decimals']), int(d['outToken']['decimals'])
             atomic_in, atomic_out = decimal.Decimal(d['inAmount']), decimal.Decimal(d['outAmount'])
-            h_in_sol = atomic_in / (10 ** in_dec)  # 这是花费的SOL数量
-            h_out_rfc = atomic_out / (10 ** out_dec)  # 这是得到的RFC数量
+            h_in_sol = atomic_in / (10 ** in_dec);
+            h_out_rfc = atomic_out / (10 ** out_dec)
             if h_out_rfc == 0: return {"error": "OO输出RFC为0"}
-            # 价格:1 RFC = X SOL  =>  SOL数量 / RFC数量
             price_rfc_per_sol = h_in_sol / h_out_rfc
-            return {"price_sol_per_rfc": price_rfc_per_sol}  # 更明确的键名
+            return {"price_sol_per_rfc": price_rfc_per_sol}
         else:
             return {
                 "error": f"OO API错误 - Code:{data.get('code', 'N/A')}, Msg:{data.get('msg', data.get('message', 'N/A')) if isinstance(data, dict) else '格式错误'}"}
@@ -58,7 +54,7 @@ def get_openocean_price_solana(in_token_addr, out_token_addr, human_amount_in_de
         return {"error": f"OO(Solana)请求错误: {e}"}
 
 
-def get_gateio_spot_price_usdt(pair_symbol):  # 获取 RFC/USDT
+def get_gateio_spot_price_usdt(pair_symbol):
     url = f'https://api.gateio.ws/api/v4/spot/tickers';
     params = {'currency_pair': pair_symbol}
     try:
@@ -67,7 +63,7 @@ def get_gateio_spot_price_usdt(pair_symbol):  # 获取 RFC/USDT
         data = r.json()
         if isinstance(data, list) and data:
             td = data[0]
-            if td.get('currency_pair') == pair_symbol and td.get('lowest_ask'):
+            if td.get('currency_pair') == pair_symbol and td.get('lowest_ask'):  # 使用 lowest_ask 作为卖价
                 return {"price_rfc_usdt": decimal.Decimal(td['lowest_ask'])}
             else:
                 return {"error": f"Gate现货({pair_symbol})数据不匹配或无价格"}
@@ -79,41 +75,30 @@ def get_gateio_spot_price_usdt(pair_symbol):  # 获取 RFC/USDT
         return {"error": f"Gate现货({pair_symbol})请求错误: {e}"}
 
 
-def get_gateio_futures_price_usdt(contract_symbol, settle_currency='usdt'):  # 获取 RFC/USDT
-    url = f'https://api.gateio.ws/api/v4/futures/{settle_currency}/tickers';
-    params = {'contract': contract_symbol}
+# def get_gateio_futures_price_usdt(contract_symbol, settle_currency='usdt'): # 注释掉期货函数
+#     url = f'https://api.gateio.ws/api/v4/futures/{settle_currency}/tickers'; params = {'contract': contract_symbol}
+#     try:
+#         r = requests.get(url, params=params, proxies=proxies, timeout=10); r.raise_for_status(); data = r.json()
+#         if isinstance(data, list) and data:
+#             td = data[0]
+#             if td.get('contract') == contract_symbol and td.get('highest_bid'): # 使用 highest_bid 作为买价
+#                 return {"price_rfc_usdt": decimal.Decimal(td['highest_bid'])}
+#             else: return {"error": f"Gate期货({contract_symbol})数据不匹配或无价格"}
+#         elif isinstance(data, dict) and data.get('label'): return {"error": f"Gate期货API错误: {data['label']}-{data.get('message','')}"}
+#         else: return {"error": f"Gate期货API({contract_symbol})数据格式错误或未找到"}
+#     except Exception as e: return {"error": f"Gate期货({contract_symbol})请求错误: {e}"}
+
+def get_binance_spot_price_usdt(symbol):
+    url = "https://api.binance.com/api/v3/ticker/price";
+    params = {'symbol': symbol}
     try:
-        r = requests.get(url, params=params, proxies=proxies, timeout=10);
-        r.raise_for_status();
-        data = r.json()
-        if isinstance(data, list) and data:
-            td = data[0]
-            if td.get('contract') == contract_symbol and td.get('highest_bid'):
-
-                return {"price_rfc_usdt": decimal.Decimal(td['highest_bid'])}
-            else:
-                return {"error": f"Gate期货({contract_symbol})数据不匹配或无价格"}
-        elif isinstance(data, dict) and data.get('label'):
-            return {"error": f"Gate期货API错误: {data['label']}-{data.get('message', '')}"}
-        else:
-            return {"error": f"Gate期货API({contract_symbol})数据格式错误或未找到"}
-    except Exception as e:
-        return {"error": f"Gate期货({contract_symbol})请求错误: {e}"}
-
-
-def get_binance_spot_price_usdt(symbol):  # 获取 SOL/USDT
-    # 使用币安现货API: https://binance-docs.github.io/apidocs/spot/en/#ticker-price
-    url = "https://api.binance.com/api/v3/ticker/price"
-    params = {'symbol': symbol}  # e.g., 'SOLUSDT'
-    try:
-        response = requests.get(url, params=params, proxies=proxies, timeout=10)
-        response.raise_for_status()
+        response = requests.get(url, params=params, proxies=proxies, timeout=10);
+        response.raise_for_status();
         data = response.json()
         if 'price' in data and data.get('symbol') == symbol:
             return {"price_sol_usdt": decimal.Decimal(data['price'])}
         else:
-            msg = data.get('msg', '未知错误')
-            return {"error": f"Binance API错误 ({symbol}): {msg}"}
+            msg = data.get('msg', '未知错误'); return {"error": f"Binance API错误 ({symbol}): {msg}"}
     except requests.exceptions.RequestException as e:
         return {"error": f"Binance API ({symbol}) 请求失败: {e}"}
     except Exception as e:
@@ -124,36 +109,37 @@ app = Flask(__name__)
 log = logging.getLogger('werkzeug');
 log.setLevel(logging.ERROR)
 
-MAX_HISTORY_POINTS_PLOTLY = 86400
+MAX_HISTORY_POINTS_PLOTLY = 86400  # 你坚持要这个值,保留它,但请注意性能
 REFRESH_INTERVAL_SECONDS = 1
 
 historical_data_points = deque(maxlen=MAX_HISTORY_POINTS_PLOTLY)
 latest_values_for_table = {
-    "oo_rfc_sol_price": "N/A",  # OpenOcean RFC/SOL
-    "gate_spot_rfc_sol_price": "N/A",  # Gate Spot RFC/USDT converted to RFC/SOL
-    "gate_futures_rfc_sol_price": "N/A",  # Gate Futures RFC/USDT converted to RFC/SOL
-    "sol_usdt_price_binance": "N/A",  # For reference
+    "oo_rfc_sol_price": "N/A",
+    "gate_spot_rfc_sol_price": "N/A",
+    # "gate_futures_rfc_sol_price": "N/A", # 注释
+    "sol_usdt_price_binance": "N/A",
     "diff_oo_vs_spot_rfc_sol_percentage": "N/A",
-    "diff_oo_vs_futures_rfc_sol_percentage": "N/A",
-    "diff_spot_vs_futures_rfc_sol_percentage": "N/A",
-    "oo_error": None, "gate_spot_error": None, "gate_futures_error": None, "binance_sol_error": None,
+    # "diff_oo_vs_futures_rfc_sol_percentage": "N/A", # 注释
+    # "diff_spot_vs_futures_rfc_sol_percentage": "N/A", # 注释
+    "oo_error": None, "gate_spot_error": None,
+    # "gate_futures_error": None, # 注释
+    "binance_sol_error": None,
     "last_updated": "N/A",
     "gate_spot_pair_name_usdt": GATEIO_SPOT_PAIR_RFC_USDT,
-    "gate_futures_contract_name_usdt": GATEIO_FUTURES_CONTRACT_RFC_USDT,
-    "target_asset_symbol": GATEIO_SPOT_PAIR_RFC_USDT.split('_')[0]  # RFC
+    # "gate_futures_contract_name_usdt": GATEIO_FUTURES_CONTRACT_RFC_USDT, # 注释
+    "target_asset_symbol": GATEIO_SPOT_PAIR_RFC_USDT.split('_')[0]
 }
 data_lock = threading.Lock()
 
 
 def calculate_percentage_diff(price_a, price_b):
     if price_a is not None and price_b is not None and isinstance(price_a, decimal.Decimal) and isinstance(price_b,
-                                                                                                           decimal.Decimal) and price_b != 0:  # 基准不能为0
+                                                                                                           decimal.Decimal) and price_b != 0:
         return ((price_a - price_b) / price_b) * 100
     return None
 
 
 def convert_usdt_price_to_sol_price(price_asset_usdt, price_sol_usdt):
-    """Converts Price(Asset/USDT) to Price(Asset/SOL) using Price(SOL/USDT)"""
     if price_asset_usdt is not None and price_sol_usdt is not None and price_sol_usdt > 0:
         return price_asset_usdt / price_sol_usdt
     return None
@@ -161,7 +147,7 @@ def convert_usdt_price_to_sol_price(price_asset_usdt, price_sol_usdt):
 
 def update_data_for_plotly_and_table():
     global historical_data_points, latest_values_for_table
-    print("数据更新线程启动...")
+    print("数据更新线程启动 (仅现货)...")
     while True:
         fetch_time_full = time.strftime("%Y-%m-%d %H:%M:%S")
         fetch_time_chart = time.strftime("%H:%M:%S")
@@ -169,36 +155,34 @@ def update_data_for_plotly_and_table():
         oo_data = get_openocean_price_solana(IN_TOKEN_ADDRESS_SOLANA, OUT_TOKEN_ADDRESS_SOLANA,
                                              AMOUNT_TO_QUERY_OPENOCEAN_IN_SOL)
         spot_usdt_data = get_gateio_spot_price_usdt(GATEIO_SPOT_PAIR_RFC_USDT)
-        futures_usdt_data = get_gateio_futures_price_usdt(GATEIO_FUTURES_CONTRACT_RFC_USDT)
+        # futures_usdt_data = get_gateio_futures_price_usdt(GATEIO_FUTURES_CONTRACT_RFC_USDT) # 注释
         binance_sol_usdt_data = get_binance_spot_price_usdt(BINANCE_SOL_USDT_PAIR)
 
-        oo_rfc_sol_price = oo_data.get("price_sol_per_rfc")  # 1 RFC = X SOL
-        spot_rfc_usdt_price = spot_usdt_data.get("price_rfc_usdt")  # 1 RFC = X USDT
-        futures_rfc_usdt_price = futures_usdt_data.get("price_rfc_usdt")  # 1 RFC = X USDT
-        sol_usdt_price = binance_sol_usdt_data.get("price_sol_usdt")  # 1 SOL = X USDT
+        oo_rfc_sol_price = oo_data.get("price_sol_per_rfc")
+        spot_rfc_usdt_price = spot_usdt_data.get("price_rfc_usdt")
+        # futures_rfc_usdt_price = futures_usdt_data.get("price_rfc_usdt") # 注释
+        sol_usdt_price = binance_sol_usdt_data.get("price_sol_usdt")
 
         oo_err = oo_data.get("error")
         spot_err = spot_usdt_data.get("error")
-        futures_err = futures_usdt_data.get("error")
+        # futures_err = futures_usdt_data.get("error") # 注释
         binance_err = binance_sol_usdt_data.get("error")
 
-        # Convert Gate.io prices to RFC/SOL
         spot_rfc_sol_price = convert_usdt_price_to_sol_price(spot_rfc_usdt_price, sol_usdt_price)
-        futures_rfc_sol_price = convert_usdt_price_to_sol_price(futures_rfc_usdt_price, sol_usdt_price)
+        # futures_rfc_sol_price = convert_usdt_price_to_sol_price(futures_rfc_usdt_price, sol_usdt_price) # 注释
 
-        # Calculate diffs based on RFC/SOL prices
         diff_oo_spot_pct = calculate_percentage_diff(oo_rfc_sol_price, spot_rfc_sol_price)
-        diff_oo_futures_pct = calculate_percentage_diff(oo_rfc_sol_price, futures_rfc_sol_price)
-        diff_spot_futures_pct = calculate_percentage_diff(spot_rfc_sol_price, futures_rfc_sol_price)
+        # diff_oo_futures_pct = calculate_percentage_diff(oo_rfc_sol_price, futures_rfc_sol_price) # 注释
+        # diff_spot_futures_pct = calculate_percentage_diff(spot_rfc_sol_price, futures_rfc_sol_price) # 注释
 
         current_point = {
             "time": fetch_time_chart,
             "oo_rfc_sol": float(oo_rfc_sol_price) if oo_rfc_sol_price else None,
             "spot_rfc_sol": float(spot_rfc_sol_price) if spot_rfc_sol_price else None,
-            "futures_rfc_sol": float(futures_rfc_sol_price) if futures_rfc_sol_price else None,
+            # "futures_rfc_sol": float(futures_rfc_sol_price) if futures_rfc_sol_price else None, # 注释
             "diff_oo_spot_rfc_sol": float(diff_oo_spot_pct) if diff_oo_spot_pct is not None else None,
-            "diff_oo_futures_rfc_sol": float(diff_oo_futures_pct) if diff_oo_futures_pct is not None else None,
-            "diff_spot_futures_rfc_sol": float(diff_spot_futures_pct) if diff_spot_futures_pct is not None else None,
+            # "diff_oo_futures_rfc_sol": float(diff_oo_futures_pct) if diff_oo_futures_pct is not None else None, # 注释
+            # "diff_spot_futures_rfc_sol": float(diff_spot_futures_pct) if diff_spot_futures_pct is not None else None, # 注释
         }
 
         with data_lock:
@@ -207,36 +191,31 @@ def update_data_for_plotly_and_table():
             latest_values_for_table["oo_rfc_sol_price"] = str(oo_rfc_sol_price) if oo_rfc_sol_price else "N/A"
             latest_values_for_table["gate_spot_rfc_sol_price"] = str(
                 spot_rfc_sol_price) if spot_rfc_sol_price else "N/A"
-            latest_values_for_table["gate_futures_rfc_sol_price"] = str(
-                futures_rfc_sol_price) if futures_rfc_sol_price else "N/A"
+            # latest_values_for_table["gate_futures_rfc_sol_price"] = str(futures_rfc_sol_price) if futures_rfc_sol_price else "N/A" # 注释
             latest_values_for_table["sol_usdt_price_binance"] = str(sol_usdt_price) if sol_usdt_price else "N/A"
 
             latest_values_for_table[
                 "diff_oo_vs_spot_rfc_sol_percentage"] = f"{diff_oo_spot_pct:+.4f}%" if diff_oo_spot_pct is not None else "N/A"
-            latest_values_for_table[
-                "diff_oo_vs_futures_rfc_sol_percentage"] = f"{diff_oo_futures_pct:+.4f}%" if diff_oo_futures_pct is not None else "N/A"
-            latest_values_for_table[
-                "diff_spot_vs_futures_rfc_sol_percentage"] = f"{diff_spot_futures_pct:+.4f}%" if diff_spot_futures_pct is not None else "N/A"
+            # latest_values_for_table["diff_oo_vs_futures_rfc_sol_percentage"] = f"{diff_oo_futures_pct:+.4f}%" if diff_oo_futures_pct is not None else "N/A" # 注释
+            # latest_values_for_table["diff_spot_vs_futures_rfc_sol_percentage"] = f"{diff_spot_futures_pct:+.4f}%" if diff_spot_futures_pct is not None else "N/A" # 注释
 
             latest_values_for_table["oo_error"] = oo_err
             latest_values_for_table["gate_spot_error"] = spot_err
-            latest_values_for_table["gate_futures_error"] = futures_err
+            # latest_values_for_table["gate_futures_error"] = futures_err # 注释
             latest_values_for_table["binance_sol_error"] = binance_err
             latest_values_for_table["last_updated"] = fetch_time_full
 
         print(
-            f"{fetch_time_chart} Fetch | OO_RFC/SOL:{'OK' if oo_rfc_sol_price else 'F'} | Spot_RFC/SOL:{'OK' if spot_rfc_sol_price else 'F'} | Fut_RFC/SOL:{'OK' if futures_rfc_sol_price else 'F'} | SOL/USDT:{'OK' if sol_usdt_price else 'F'}")
-
+            f"{fetch_time_chart} Fetch | OO_RFC/SOL:{'OK' if oo_rfc_sol_price else 'F'} | Spot_RFC/SOL:{'OK' if spot_rfc_sol_price else 'F'} | SOL/USDT:{'OK' if sol_usdt_price else 'F'}")
         time.sleep(REFRESH_INTERVAL_SECONDS)
 
 
 @app.route('/')
 def index_plotly():
-    target_asset = latest_values_for_table["target_asset_symbol"]  # RFC
-    return render_template('index_plotly_sol.html',  # Use a new template name to avoid confusion
+    target_asset = latest_values_for_table["target_asset_symbol"]
+    return render_template('index_plotly_sol_spot_only.html',
                            target_asset=target_asset,
                            spot_pair_usdt=GATEIO_SPOT_PAIR_RFC_USDT,
-                           futures_contract_usdt=GATEIO_FUTURES_CONTRACT_RFC_USDT,
                            sol_usdt_pair_binance=BINANCE_SOL_USDT_PAIR,
                            refresh_interval_ms=REFRESH_INTERVAL_SECONDS * 1000)
 
@@ -258,7 +237,7 @@ def get_plotly_chart_data():
                             "diff_chart": json.loads(json.dumps(fig, cls=PlotlyJSONEncoder))})
 
         times = [p['time'] for p in points]
-        target_asset_symbol = latest_values_for_table["target_asset_symbol"]  # RFC
+        target_asset_symbol = latest_values_for_table["target_asset_symbol"]
 
         fig_prices = go.Figure()
         fig_prices.add_trace(
@@ -269,10 +248,7 @@ def get_plotly_chart_data():
                                         name=f'Gate Spot ({GATEIO_SPOT_PAIR_RFC_USDT} → RFC/SOL)',
                                         line=dict(color='rgb(255, 99, 132)'),
                                         hovertemplate=f'<b>Gate Spot (RFC/SOL)</b><br>Time: %{{x}}<br>Price: %{{y:.8f}} SOL<extra></extra>'))
-        fig_prices.add_trace(go.Scatter(x=times, y=[p['futures_rfc_sol'] for p in points], mode='lines',
-                                        name=f'Gate Futures ({GATEIO_FUTURES_CONTRACT_RFC_USDT} → RFC/SOL)',
-                                        line=dict(color='rgb(54, 162, 235)'),
-                                        hovertemplate=f'<b>Gate Futures (RFC/SOL)</b><br>Time: %{{x}}<br>Price: %{{y:.8f}} SOL<extra></extra>'))
+        # fig_prices.add_trace(go.Scatter(x=times, y=[p['futures_rfc_sol'] for p in points], mode='lines', name=f'Gate Futures ({GATEIO_FUTURES_CONTRACT_RFC_USDT} → RFC/SOL)', line=dict(color='rgb(54, 162, 235)'), hovertemplate=f'<b>Gate Futures (RFC/SOL)</b><br>Time: %{{x}}<br>Price: %{{y:.8f}} SOL<extra></extra>')) # 注释
 
         fig_prices.update_layout(title_text=f'{target_asset_symbol}/SOL 价格历史', xaxis_title='时间',
                                  yaxis_title=f'价格 (1 {target_asset_symbol} = X SOL)', legend_title_text='平台',
@@ -282,14 +258,10 @@ def get_plotly_chart_data():
         fig_diffs.add_trace(go.Scatter(x=times, y=[p['diff_oo_spot_rfc_sol'] for p in points], mode='lines',
                                        name=f'OO vs Spot (RFC/SOL)', line=dict(color='rgb(255, 159, 64)'),
                                        hovertemplate=f'<b>OO vs Spot (RFC/SOL)</b><br>Time: %{{x}}<br>Diff: %{{y:+.4f}}%<extra></extra>'))
-        fig_diffs.add_trace(go.Scatter(x=times, y=[p['diff_oo_futures_rfc_sol'] for p in points], mode='lines',
-                                       name=f'OO vs Futures (RFC/SOL)', line=dict(color='rgb(153, 102, 255)'),
-                                       hovertemplate=f'<b>OO vs Futures (RFC/SOL)</b><br>Time: %{{x}}<br>Diff: %{{y:+.4f}}%<extra></extra>'))
-        fig_diffs.add_trace(go.Scatter(x=times, y=[p['diff_spot_futures_rfc_sol'] for p in points], mode='lines',
-                                       name=f'Spot vs Futures (RFC/SOL)', line=dict(color='rgb(75, 192, 75)'),
-                                       hovertemplate=f'<b>Spot vs Futures (RFC/SOL)</b><br>Time: %{{x}}<br>Diff: %{{y:+.4f}}%<extra></extra>'))
-
-        fig_diffs.update_layout(title_text=f'{target_asset_symbol}/SOL 价差百分比历史', xaxis_title='时间',
+        # fig_diffs.add_trace(go.Scatter(x=times, y=[p['diff_oo_futures_rfc_sol'] for p in points], mode='lines', name=f'OO vs Futures (RFC/SOL)', line=dict(color='rgb(153, 102, 255)'), hovertemplate=f'<b>OO vs Futures (RFC/SOL)</b><br>Time: %{{x}}<br>Diff: %{{y:+.4f}}%<extra></extra>')) # 注释
+        # fig_diffs.add_trace(go.Scatter(x=times, y=[p['diff_spot_futures_rfc_sol'] for p in points], mode='lines', name=f'Spot vs Futures (RFC/SOL)', line=dict(color='rgb(75, 192, 75)'), hovertemplate=f'<b>Spot vs Futures (RFC/SOL)</b><br>Time: %{{x}}<br>Diff: %{{y:+.4f}}%<extra></extra>')) # 注释
+
+        fig_diffs.update_layout(title_text=f'{target_asset_symbol}/SOL 价差百分比历史 (OO vs Spot)', xaxis_title='时间',
                                 yaxis_title='价差 (%)', legend_title_text='对比', yaxis_zeroline=True,
                                 hovermode='x unified')
 
@@ -302,15 +274,15 @@ def get_plotly_chart_data():
 
 if __name__ == "__main__":
     print("应用启动...")
-    asset_symbol = GATEIO_SPOT_PAIR_RFC_USDT.split('_')[0]  # RFC
+    asset_symbol = GATEIO_SPOT_PAIR_RFC_USDT.split('_')[0]
     print(f"目标资产: {asset_symbol}")
     print(
         f"OpenOcean: {asset_symbol}/SOL (通过 {IN_TOKEN_ADDRESS_SOLANA[-6:]}...SOL / {OUT_TOKEN_ADDRESS_SOLANA[-6:]}...{asset_symbol})")
     print(f"Gate.io 现货: {GATEIO_SPOT_PAIR_RFC_USDT} (将转换为 {asset_symbol}/SOL)")
-    print(f"Gate.io 期货: {GATEIO_FUTURES_CONTRACT_RFC_USDT} (将转换为 {asset_symbol}/SOL)")
+    # print(f"Gate.io 期货: {GATEIO_FUTURES_CONTRACT_RFC_USDT} (将转换为 {asset_symbol}/SOL)") # 注释
     print(f"币安转换汇率: {BINANCE_SOL_USDT_PAIR}")
 
     data_thread = threading.Thread(target=update_data_for_plotly_and_table, daemon=True)
     data_thread.start()
     print(f"Flask 服务将在 http://0.0.0.0:5000 上运行 (刷新间隔: {REFRESH_INTERVAL_SECONDS}s)")
-    app.run(debug=False, host='0.0.0.0', port=5000, use_reloader=False)
+    app.run(debug=False, host='0.0.0.0', port=5000, use_reloader=False)

+ 29 - 75
templates/index_plotly_sol.html → templates/index_plotly_sol_spot_only.html

@@ -3,10 +3,10 @@
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>{{ target_asset }}/SOL 多平台价格监控 (Plotly)</title>
+    <title>{{ target_asset }}/SOL 价格监控 (Plotly - 现货Only)</title>
     <script src='https://cdn.plot.ly/plotly-latest.min.js'></script>
     <style>
-        /* CSS样式与之前相同,此处省略以保持简洁 */
+        /* CSS样式保持不变 */
         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; margin-top:10px; margin-bottom:15px; }
@@ -30,9 +30,8 @@
     </style>
 </head>
 <body>
-    <!-- HTML 结构与之前相同,此处省略 -->
     <div class="container">
-        <h1 id="main-title">{{ target_asset }}/SOL 多平台价格监控</h1>
+        <h1 id="main-title">{{ target_asset }}/SOL 价格监控 (现货 vs OO)</h1>
         <div class="controls-container">
             <button id="pause-resume-button" class="control-button">暂停刷新</button>
         </div>
@@ -55,11 +54,7 @@
                     <td id="gate-spot-rfc-sol-price">加载中...</td>
                     <td id="gate-spot-status" class="status-cell"></td>
                 </tr>
-                <tr>
-                    <td class="platform-name" id="gate-futures-label">Gate.io 期货 ({{ futures_contract_usdt }} → {{ target_asset }}/SOL)</td>
-                    <td id="gate-futures-rfc-sol-price">加载中...</td>
-                    <td id="gate-futures-status" class="status-cell"></td>
-                </tr>
+                <!-- Gate.io 期货行被移除 -->
                 <tr>
                     <td class="platform-name">参考汇率 (Binance)</td>
                     <td id="sol-usdt-price-binance" title="{{ sol_usdt_pair_binance }}">加载中... (USDT)</td>
@@ -80,14 +75,7 @@
                     <td id="diff-label-oo-spot">OO vs Gate.io 现货 (转换后)</td>
                     <td id="diff-oo-vs-spot-rfc-sol">计算中...</td>
                 </tr>
-                <tr>
-                    <td id="diff-label-oo-futures">OO vs Gate.io 期货 (转换后)</td>
-                    <td id="diff-oo-vs-futures-rfc-sol">计算中...</td>
-                </tr>
-                <tr>
-                    <td id="diff-label-spot-futures">Gate.io 现货 vs 期货 (转换后)</td>
-                    <td id="diff-spot-vs-futures-rfc-sol">计算中...</td>
-                </tr>
+                <!-- 其他价差行被移除 -->
             </tbody>
         </table>
         <div class="timestamp">最后更新: <span id="last-updated">N/A</span></div>
@@ -100,7 +88,7 @@
     </div>
 
     <div class="container">
-        <h2 id="diff-chart-title">{{ target_asset }}/SOL 价差百分比历史曲线</h2>
+        <h2 id="diff-chart-title">{{ target_asset }}/SOL 价差百分比历史曲线 (OO vs Spot)</h2>
         <div id='diffPercentageChartPlotly' class="chart-container"></div>
         <div id="diff-chart-status" class="status-line">加载价差图表...</div>
     </div>
@@ -118,10 +106,9 @@
 
         let pricePlotInitialized = false;
         let diffPlotInitialized = false;
-        let isSyncingLayout = false; // Flag to prevent event loops
+        let isSyncingLayout = false;
 
-        // --- Helper functions (formatPriceForTable, formatPercentageForTable) ---
-        // (与之前版本相同)
+        // --- Helper functions ---
         function formatPriceForTable(priceStr, precision = 8) {
             if (priceStr === null || priceStr === undefined || String(priceStr).toLowerCase() === "n/a") return "N/A";
             const price = parseFloat(priceStr);
@@ -138,45 +125,29 @@
             return `${perc > 0 ? '+' : ''}${perc.toFixed(4)}%`;
         }
 
-        // --- Function to sync X-axis between two Plotly charts ---
+        // --- Function to sync X-axis ---
         function syncPlotlyXAxes(sourceDiv, targetDiv, eventData) {
-            if (isSyncingLayout) return; // Prevent infinite loop
+            if (isSyncingLayout) return;
             isSyncingLayout = true;
-
             const update = {};
             if (eventData && eventData['xaxis.range[0]'] !== undefined) {
-                // Zoom/Pan event
                 update['xaxis.range[0]'] = eventData['xaxis.range[0]'];
                 update['xaxis.range[1]'] = eventData['xaxis.range[1]'];
             } else if (eventData && eventData['xaxis.autorange']) {
-                // Double-click to reset zoom (autorange)
                 update['xaxis.autorange'] = true;
             } else {
-                // Fallback: try to get range from source layout if eventData is not specific enough
-                // This might not always be reliable if eventData is empty or non-standard
                 if (sourceDiv.layout && sourceDiv.layout.xaxis && sourceDiv.layout.xaxis.range) {
                      update['xaxis.range[0]'] = sourceDiv.layout.xaxis.range[0];
                      update['xaxis.range[1]'] = sourceDiv.layout.xaxis.range[1];
-                } else {
-                    isSyncingLayout = false;
-                    return; // Not enough info to sync
-                }
+                } else { isSyncingLayout = false; return; }
             }
-
-            // Only apply if a valid update is constructed
             if (Object.keys(update).length > 0) {
-                Plotly.relayout(targetDiv, update).then(() => {
-                    isSyncingLayout = false;
-                }).catch(e => {
-                    console.error("Error syncing layout:", e);
-                    isSyncingLayout = false;
-                });
-            } else {
-                 isSyncingLayout = false;
-            }
+                Plotly.relayout(targetDiv, update).then(() => { isSyncingLayout = false; }).catch(e => { console.error("Error syncing layout:", e); isSyncingLayout = false;});
+            } else { isSyncingLayout = false; }
         }
 
-        async function updateTableData() { /* (与之前版本相同) */
+        // --- Update Table Data ---
+        async function updateTableData() {
             if (isPaused) return;
             try {
                 const response = await fetch('/table-data');
@@ -185,28 +156,28 @@
 
                 document.getElementById('oo-rfc-sol-price').textContent = formatPriceForTable(data.oo_rfc_sol_price);
                 document.getElementById('gate-spot-rfc-sol-price').textContent = formatPriceForTable(data.gate_spot_rfc_sol_price);
-                document.getElementById('gate-futures-rfc-sol-price').textContent = formatPriceForTable(data.gate_futures_rfc_sol_price);
+                // document.getElementById('gate-futures-rfc-sol-price').textContent = formatPriceForTable(data.gate_futures_rfc_sol_price); // Removed
                 document.getElementById('sol-usdt-price-binance').textContent = formatPriceForTable(data.sol_usdt_price_binance, 4);
 
                 document.getElementById('oo-status').textContent = data.oo_error || '正常';
                 document.getElementById('gate-spot-status').textContent = data.gate_spot_error || '正常';
-                document.getElementById('gate-futures-status').textContent = data.gate_futures_error || '正常';
+                // document.getElementById('gate-futures-status').textContent = data.gate_futures_error || '正常'; // Removed
                 document.getElementById('binance-sol-status').textContent = data.binance_sol_error || '正常';
 
                 document.getElementById('oo-status').className = data.oo_error ? 'status-cell error-message' : 'status-cell';
                 document.getElementById('gate-spot-status').className = data.gate_spot_error ? 'status-cell error-message' : 'status-cell';
-                document.getElementById('gate-futures-status').className = data.gate_futures_error ? 'status-cell error-message' : 'status-cell';
+                // document.getElementById('gate-futures-status').className = data.gate_futures_error ? 'status-cell error-message' : 'status-cell'; // Removed
                 document.getElementById('binance-sol-status').className = data.binance_sol_error ? 'status-cell error-message' : 'status-cell';
 
                 const diffOOSpotEl = document.getElementById('diff-oo-vs-spot-rfc-sol');
-                const diffOOFuturesEl = document.getElementById('diff-oo-vs-futures-rfc-sol');
-                const diffSpotFuturesEl = document.getElementById('diff-spot-vs-futures-rfc-sol');
+                // const diffOOFuturesEl = document.getElementById('diff-oo-vs-futures-rfc-sol'); // Removed
+                // const diffSpotFuturesEl = document.getElementById('diff-spot-vs-futures-rfc-sol'); // Removed
 
                 diffOOSpotEl.textContent = formatPercentageForTable(data.diff_oo_vs_spot_rfc_sol_percentage);
-                diffOOFuturesEl.textContent = formatPercentageForTable(data.diff_oo_vs_futures_rfc_sol_percentage);
-                diffSpotFuturesEl.textContent = formatPercentageForTable(data.diff_spot_vs_futures_rfc_sol_percentage);
+                // diffOOFuturesEl.textContent = formatPercentageForTable(data.diff_oo_vs_futures_rfc_sol_percentage); // Removed
+                // diffSpotFuturesEl.textContent = formatPercentageForTable(data.diff_spot_vs_futures_rfc_sol_percentage); // Removed
 
-                [diffOOSpotEl, diffOOFuturesEl, diffSpotFuturesEl].forEach(el => {
+                [diffOOSpotEl/*, diffOOFuturesEl, diffSpotFuturesEl*/].forEach(el => { // Adjusted array
                     const valStr = el.textContent.replace('%','').replace('+','');
                     const val = parseFloat(valStr);
                     if (!isNaN(val)) { el.className = val > 0 ? 'price-up' : (val < 0 ? 'price-down' : ''); }
@@ -216,6 +187,7 @@
             } catch (error) { console.error('Error fetching table data:', error); }
         }
 
+        // --- Update Plotly Charts ---
         async function updatePlotlyCharts() {
             if(isPaused && (pricePlotInitialized || diffPlotInitialized) ) return;
             try {
@@ -223,39 +195,20 @@
                 if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
                 const chartData = await response.json();
 
-                const currentPriceXRange = priceChartDiv.layout ? priceChartDiv.layout.xaxis.range : undefined;
-                const currentDiffXRange = diffChartDiv.layout ? diffChartDiv.layout.xaxis.range : undefined;
-
                 if (chartData.price_chart && chartData.price_chart.data && chartData.price_chart.layout) {
-                    // Preserve X-axis range if user has zoomed/panned, unless paused and it's first draw
-                    if (pricePlotInitialized && !isPaused && currentPriceXRange) {
-                        // chartData.price_chart.layout.xaxis.range = currentPriceXRange; // This might reset if autorange is true
-                        // Let Plotly.react handle it, it preserves zoom by default if data length doesn't change drastically or x values change
-                    }
                     Plotly.react(priceChartDiv, chartData.price_chart.data, chartData.price_chart.layout, {responsive: true});
                     if (!pricePlotInitialized) {
                         pricePlotInitialized = true;
-                        // Add event listener AFTER first plot
-                        priceChartDiv.on('plotly_relayout', (eventData) => {
-                             console.log('Price chart relayout:', eventData);
-                             syncPlotlyXAxes(priceChartDiv, diffChartDiv, eventData);
-                        });
+                        priceChartDiv.on('plotly_relayout', (eventData) => { syncPlotlyXAxes(priceChartDiv, diffChartDiv, eventData); });
                     }
                     priceChartStatusDiv.textContent = `价格图表更新于: ${new Date().toLocaleTimeString()}`;
                 } else { priceChartStatusDiv.textContent = "错误: 价格图表数据无效。"; }
 
                 if (chartData.diff_chart && chartData.diff_chart.data && chartData.diff_chart.layout) {
-                    if (diffPlotInitialized && !isPaused && currentDiffXRange) {
-                        // chartData.diff_chart.layout.xaxis.range = currentDiffXRange;
-                    }
                     Plotly.react(diffChartDiv, chartData.diff_chart.data, chartData.diff_chart.layout, {responsive: true});
                      if (!diffPlotInitialized) {
                         diffPlotInitialized = true;
-                        // Add event listener AFTER first plot
-                        diffChartDiv.on('plotly_relayout', (eventData) => {
-                            console.log('Diff chart relayout:', eventData);
-                            syncPlotlyXAxes(diffChartDiv, priceChartDiv, eventData);
-                        });
+                        diffChartDiv.on('plotly_relayout', (eventData) => { syncPlotlyXAxes(diffChartDiv, priceChartDiv, eventData); });
                     }
                     diffChartStatusDiv.textContent = `价差图表更新于: ${new Date().toLocaleTimeString()}`;
                 } else { diffChartStatusDiv.textContent = "错误: 价差图表数据无效。"; }
@@ -267,7 +220,8 @@
             }
         }
 
-        function togglePauseResume() { /* (与之前版本相同) */
+        // --- Pause/Resume ---
+        function togglePauseResume() {
             isPaused = !isPaused;
             if (isPaused) {
                 if (dataUpdateIntervalID) clearInterval(dataUpdateIntervalID); dataUpdateIntervalID = null;
@@ -287,4 +241,4 @@
         }
     </script>
 </body>
-</html>
+</html>