瀏覽代碼

第一版:监听

skyfffire 4 月之前
父節點
當前提交
1bce4f35a5

+ 18 - 18
checker/erc20_to_mexc_checker.py

@@ -140,8 +140,8 @@ def get_mexc_spot_price_target_usdt_bid(pair_symbol):
 
 latest_values_for_table = {
     f"oo_price_usdt_per_target": "N/A",
-    f"mexc_price_usdt_per_target_bid1": "N/A", # MEXC Bid1 (converted to USDT/TARGET)
-    f"diff_oo_vs_mexc_bid1_percentage": "N/A",
+    f"mexc_price_target_per_base": "N/A", # MEXC Bid1 (converted to USDT/TARGET)
+    f"diff_oo_vs_mexc_percentage": "N/A",
     "profit_value_for_table": "N/A", # 新增:用于表格的利润值
     "oo_error": None, "mexc_error": None,
     "last_updated": "N/A",
@@ -230,10 +230,10 @@ def update_data_for_plotly_and_table():
         mexc_err = mexc_data.get("error")
 
         # price_target_per_usdt_bid1: 这是1个目标币能卖多少USDT, 即 USDT/TARGET
-        # 所以可以直接用,不需要转换,变量名应为 mexc_price_usdt_per_target_bid1
-        mexc_price_usdt_per_target_bid1_for_calc = None
+        # 所以可以直接用,不需要转换,变量名应为 mexc_price_target_per_base
+        mexc_price_target_per_base = None
         if mexc_price_target_per_usdt_bid1 is not None and mexc_price_target_per_usdt_bid1 > 0:
-            mexc_price_usdt_per_target_bid1_for_calc = mexc_price_target_per_usdt_bid1 # RATO/USDT => USDT/TARGET (命名约定)
+            mexc_price_target_per_base = mexc_price_target_per_usdt_bid1 # RATO/USDT => USDT/TARGET (命名约定)
         elif not mexc_err and mexc_price_target_per_usdt_bid1 is not None:
              mexc_err = mexc_err or "MEXC价格为0或无效"
         
@@ -271,8 +271,8 @@ def update_data_for_plotly_and_table():
 
         # 4. 计算百分比差异
         # diff = (MEXC卖价 - 链上买价) / 链上买价
-        diff_oo_vs_mexc_bid1_pct = calculate_percentage_diff(
-            mexc_price_usdt_per_target_bid1_for_calc, # MEXC卖价 (USDT/TARGET)
+        diff_oo_vs_mexc_pct = calculate_percentage_diff(
+            mexc_price_target_per_base, # MEXC卖价 (USDT/TARGET)
             oo_price_usdt_per_target                  # 链上买价 (USDT/TARGET)
         )
 
@@ -281,9 +281,9 @@ def update_data_for_plotly_and_table():
         # 链上买入的目标币数量 = in_amount_to_query_human / oo_price_usdt_per_target
         # 简化:利润百分比 * 投入的USDT金额
         actual_profit_usdt = None
-        if diff_oo_vs_mexc_bid1_pct is not None and oo_price_usdt_per_target is not None and oo_price_usdt_per_target > 0:
+        if diff_oo_vs_mexc_pct is not None and oo_price_usdt_per_target is not None and oo_price_usdt_per_target > 0:
             # 方案A: 基于百分比和投入金额
-            actual_profit_usdt = diff_oo_vs_mexc_bid1_pct * in_amount_to_query_human
+            actual_profit_usdt = diff_oo_vs_mexc_pct * in_amount_to_query_human
             # # 方案B: 基于单价差和数量 (更精确,如果chain_swap_full_response可用)
             # if chain_swap_full_response and chain_swap_full_response.get('data'):
             #     router_result = chain_swap_full_response['data'][0]['routerResult']
@@ -294,7 +294,7 @@ def update_data_for_plotly_and_table():
             #     # 能在CEX卖出的目标币数量,取链上换到的数量和CEX预设卖出量的较小值,因为CEX订单深度是按EXCHANGE_OUT_AMOUNT查的
             #     effective_target_to_sell_on_mexc = min(human_out_target_onchain, EXCHANGE_OUT_AMOUNT)
 
-            #     revenue_on_mexc = effective_target_to_sell_on_mexc * mexc_price_usdt_per_target_bid1_for_calc
+            #     revenue_on_mexc = effective_target_to_sell_on_mexc * mexc_price_target_per_base
             #     cost_on_chain = in_amount_to_query_human # 这是我们实际在链上花的USDT
             #     actual_profit_usdt_v2 = revenue_on_mexc - cost_on_chain
             #     actual_profit_usdt = actual_profit_usdt_v2 # 使用更精确的V2版本
@@ -303,30 +303,30 @@ def update_data_for_plotly_and_table():
         global mode
         if actual_profit_usdt is not None and actual_profit_usdt > PROFIT_LIMIT + 3 and mode == 'trade':
             if chain_swap_full_response: # 确保有完整的链上数据
-                send_arb_msg(actual_profit_usdt, chain_swap_full_response, mexc_price_usdt_per_target_bid1_for_calc, in_amount_to_query_human)
+                send_arb_msg(actual_profit_usdt, chain_swap_full_response, mexc_price_target_per_base, in_amount_to_query_human)
             else:
                 logger.warning("利润满足但链上数据不完整,无法发送套利消息。")
 
         current_point = {
             "time": fetch_time_chart,
             "oo_price_usdt_per_target": float(oo_price_usdt_per_target) if oo_price_usdt_per_target else None,
-            "mexc_price_usdt_per_target_bid1": float(mexc_price_usdt_per_target_bid1_for_calc) if mexc_price_usdt_per_target_bid1_for_calc else None,
-            "diff_oo_vs_mexc_bid1": float(diff_oo_vs_mexc_bid1_pct) if diff_oo_vs_mexc_bid1_pct is not None else None,
+            "mexc_price_target_per_base": float(mexc_price_target_per_base) if mexc_price_target_per_base else None,
+            "diff_oo_vs_mexc": float(diff_oo_vs_mexc_pct) if diff_oo_vs_mexc_pct is not None else None,
             "profit_value": float(actual_profit_usdt) if actual_profit_usdt is not None else None, # 新增:用于图表的实际利润额
         }
 
         with data_lock:
             historical_data_points.append(current_point)
             latest_values_for_table["oo_price_usdt_per_target"] = f"{oo_price_usdt_per_target:.8f}" if oo_price_usdt_per_target else "N/A"
-            latest_values_for_table["mexc_price_usdt_per_target_bid1"] = f"{mexc_price_usdt_per_target_bid1_for_calc:.8f}" if mexc_price_usdt_per_target_bid1_for_calc else "N/A"
-            latest_values_for_table["diff_oo_vs_mexc_bid1_percentage"] = f"{diff_oo_vs_mexc_bid1_pct:+.4%}" if diff_oo_vs_mexc_bid1_pct is not None else "N/A" # 显示为百分比
+            latest_values_for_table["mexc_price_target_per_base"] = f"{mexc_price_target_per_base:.8f}" if mexc_price_target_per_base else "N/A"
+            latest_values_for_table["diff_oo_vs_mexc_percentage"] = f"{diff_oo_vs_mexc_pct:+.4%}" if diff_oo_vs_mexc_pct is not None else "N/A" # 显示为百分比
             latest_values_for_table["profit_value_for_table"] = f"{actual_profit_usdt:.2f} {BASE_CURRENCY_SYMBOL}" if actual_profit_usdt is not None else "N/A" # 新增
             latest_values_for_table["oo_error"] = oo_err
             latest_values_for_table["mexc_error"] = mexc_err
             latest_values_for_table["last_updated"] = fetch_time_full
             latest_values_for_table["in_amount_for_query_display"] = f"{in_amount_to_query_human:.2f} {BASE_CURRENCY_SYMBOL}" if in_amount_to_query_human > 0 else "N/A"
 
-        # logger.info(f"{fetch_time_chart} Price Query: Chain Input {in_amount_to_query_human:.2f} {BASE_CURRENCY_SYMBOL} | OKX Price: {oo_price_usdt_per_target_display} | MEXC Price: {mexc_price_usdt_per_target_bid1_display} | Diff: {diff_display} | Profit: {profit_display}")
+        # logger.info(f"{fetch_time_chart} Price Query: Chain Input {in_amount_to_query_human:.2f} {BASE_CURRENCY_SYMBOL} | OKX Price: {oo_price_usdt_per_target_display} | MEXC Price: {mexc_price_target_per_base_display} | Diff: {diff_display} | Profit: {profit_display}")
         if oo_err or mexc_err :
              logger.warning(f"{fetch_time_chart} Errors: OO:{oo_err}, MEXC:{mexc_err}")
 
@@ -379,7 +379,7 @@ def get_plotly_chart_data():
                                         line=dict(color='rgb(75, 192, 192)'),
                                         hovertemplate=f'<b>Okx链上价</b><br>价格: %{{y:.8f}} {display_base_asset}<extra></extra>',
                                         connectgaps=True)) # 处理None值不画线
-        fig_prices.add_trace(go.Scatter(x=times, y=[p['mexc_price_usdt_per_target_bid1'] for p in points], mode='lines',
+        fig_prices.add_trace(go.Scatter(x=times, y=[p['mexc_price_target_per_base'] for p in points], mode='lines',
                                         name=f'MEXC卖1价 ({display_base_asset}/{display_target_asset})',
                                         line=dict(color='rgb(255, 99, 132)', dash='dash'),
                                         hovertemplate=f'<b>MEXC卖出价</b><br>价格: %{{y:.8f}} {display_base_asset}<extra></extra>',
@@ -394,7 +394,7 @@ def get_plotly_chart_data():
         # Percentage Difference Chart
         fig_diffs = go.Figure()
         fig_diffs.add_trace(
-            go.Scatter(x=times, y=[p['diff_oo_vs_mexc_bid1'] for p in points], mode='lines', name=f'价差百分比 (MEXC卖价 vs Okx买价)',
+            go.Scatter(x=times, y=[p['diff_oo_vs_mexc'] for p in points], mode='lines', name=f'价差百分比 (MEXC卖价 vs Okx买价)',
                        line=dict(color='rgb(255, 159, 64)'),
                        hovertemplate=f'<b>(MEXC卖价-Okx买价)/Okx买价</b><br>百分比: %{{y:+.4%}}<extra></extra>', # 显示为百分比
                        connectgaps=True))

+ 9 - 9
checker/erc20_to_mexc_checker_ws.py

@@ -91,7 +91,7 @@ chain_data_lock = threading.Lock()
 # 圖表數據
 latest_values_for_table = {
     f"oo_price_usdt_per_target": "N/A",
-    f"mexc_price_usdt_per_target_bid1": "N/A", # MEXC Bid1 (converted to USDT/TARGET)
+    f"mexc_price_target_per_base": "N/A", # MEXC Bid1 (converted to USDT/TARGET)
     f"diff_oo_vs_mexc_bid1_percentage": "N/A",
     "profit_value_for_table": "N/A", # 新增:用于表格的利润值
     "oo_error": None, "mexc_error": None,
@@ -462,10 +462,10 @@ def update_data_for_plotly_and_table():
     mexc_err = mexc_data.get("error")
 
     # price_target_per_usdt_bid1: 这是1个目标币能卖多少USDT, 即 USDT/TARGET
-    # 所以可以直接用,不需要转换,变量名应为 mexc_price_usdt_per_target_bid1
-    mexc_price_usdt_per_target_bid1_for_calc = None
+    # 所以可以直接用,不需要转换,变量名应为 mexc_price_target_per_base
+    mexc_price_target_per_base = None
     if mexc_price_target_per_usdt_bid is not None and mexc_price_target_per_usdt_bid > 0:
-        mexc_price_usdt_per_target_bid1_for_calc = mexc_price_target_per_usdt_bid # RATO/USDT => USDT/TARGET (命名约定)
+        mexc_price_target_per_base = mexc_price_target_per_usdt_bid # RATO/USDT => USDT/TARGET (命名约定)
 
     elif not mexc_err and mexc_price_target_per_usdt_bid is not None:
             mexc_err = mexc_err or "MEXC价格为0或无效"
@@ -501,7 +501,7 @@ def update_data_for_plotly_and_table():
     # 4. 计算百分比差异
     # diff = (MEXC卖价 - 链上买价) / 链上买价
     diff_oo_vs_mexc_bid1_pct = calculate_percentage_diff(
-        mexc_price_usdt_per_target_bid1_for_calc, # MEXC卖价 (USDT/TARGET)
+        mexc_price_target_per_base, # MEXC卖价 (USDT/TARGET)
         oo_price_usdt_per_target                  # 链上买价 (USDT/TARGET)
     )
 
@@ -518,7 +518,7 @@ def update_data_for_plotly_and_table():
     global mode
     if actual_profit_usdt is not None and actual_profit_usdt > PROFIT_LIMIT + 3 and mode == 'trade':
         if chain_swap_full_response: # 确保有完整的链上数据
-            send_arb_msg(actual_profit_usdt, chain_swap_full_response, mexc_price_usdt_per_target_bid1_for_calc, in_amount_copy)
+            send_arb_msg(actual_profit_usdt, chain_swap_full_response, mexc_price_target_per_base, in_amount_copy)
         else:
             logger.warning("利润满足但链上数据不完整,无法发送套利消息。")
 
@@ -530,7 +530,7 @@ def update_data_for_plotly_and_table():
             current_point = {
                 "time": fetch_time_chart,
                 "oo_price_usdt_per_target": float(oo_price_usdt_per_target) if oo_price_usdt_per_target else None,
-                "mexc_price_usdt_per_target_bid1": float(mexc_price_usdt_per_target_bid1_for_calc) if mexc_price_usdt_per_target_bid1_for_calc else None,
+                "mexc_price_target_per_base": float(mexc_price_target_per_base) if mexc_price_target_per_base else None,
                 "diff_oo_vs_mexc_bid1": float(diff_oo_vs_mexc_bid1_pct) if diff_oo_vs_mexc_bid1_pct is not None else None,
                 "profit_value": float(actual_profit_usdt) if actual_profit_usdt is not None else None, # 新增:用于图表的实际利润额
             }
@@ -538,7 +538,7 @@ def update_data_for_plotly_and_table():
             with data_lock:
                 historical_data_points.append(current_point)
                 latest_values_for_table["oo_price_usdt_per_target"] = f"{oo_price_usdt_per_target:.8f}" if oo_price_usdt_per_target else "N/A"
-                latest_values_for_table["mexc_price_usdt_per_target_bid1"] = f"{mexc_price_usdt_per_target_bid1_for_calc:.8f}" if mexc_price_usdt_per_target_bid1_for_calc else "N/A"
+                latest_values_for_table["mexc_price_target_per_base"] = f"{mexc_price_target_per_base:.8f}" if mexc_price_target_per_base else "N/A"
                 latest_values_for_table["diff_oo_vs_mexc_bid1_percentage"] = f"{diff_oo_vs_mexc_bid1_pct:+.4%}" if diff_oo_vs_mexc_bid1_pct is not None else "N/A" # 显示为百分比
                 latest_values_for_table["profit_value_for_table"] = f"{actual_profit_usdt:.2f} {BASE_CURRENCY_SYMBOL}" if actual_profit_usdt is not None else "N/A" # 新增
                 latest_values_for_table["oo_error"] = oo_err
@@ -596,7 +596,7 @@ def get_plotly_chart_data():
                                         line=dict(color='rgb(75, 192, 192)'),
                                         hovertemplate=f'<b>Okx链上价</b><br>价格: %{{y:.8f}} {display_base_asset}<extra></extra>',
                                         connectgaps=True)) # 处理None值不画线
-        fig_prices.add_trace(go.Scatter(x=times, y=[p['mexc_price_usdt_per_target_bid1'] for p in points], mode='lines',
+        fig_prices.add_trace(go.Scatter(x=times, y=[p['mexc_price_target_per_base'] for p in points], mode='lines',
                                         name=f'MEXC卖1价 ({display_base_asset}/{display_target_asset})',
                                         line=dict(color='rgb(255, 99, 132)', dash='dash'),
                                         hovertemplate=f'<b>MEXC卖出价</b><br>价格: %{{y:.8f}} {display_base_asset}<extra></extra>',

+ 95 - 132
checker/mexc_to_erc20_checker.py

@@ -29,11 +29,12 @@ ok_chain_client.api_config = okchain_api # 假设ok_chain_client有此配置方
 ARB_EXECUTOR_URL = arb["ARB_EXECUTOR_URL"]
 
 # --- 配置部分 ---
-# IN_AMOUNT_TO_QUERY 将在循环中动态确定
+# EXCHANGE_OUT_AMOUNT 将在循环中动态确定
 EXCHANGE_OUT_AMOUNT = decimal.Decimal(str(arb["EXCHANGE_OUT_AMOUNT"])) # 确保是Decimal
 PROFIT_LIMIT = decimal.Decimal(str(arb["PROFIT_LIMIT"])) # 确保是Decimal
 IN_TOKEN_ADDRESS = arb["IN_TOKEN_ADDRESS"]
 IN_TOKEN_DECIMALS = arb["IN_TOKEN_DECIMALS"]
+IN_AMOUNT_TO_QUERY = arb["IN_AMOUNT_TO_QUERY"]
 OUT_TOKEN_ADDRESS = arb["OUT_TOKEN_ADDRESS"]
 SLIPPAGE = arb["SLIPPAGE"]
 MEXC_TARGET_PAIR_USDT = arb["MEXC_TARGET_PAIR_USDT"]
@@ -62,7 +63,7 @@ historical_data_points = deque(maxlen=MAX_HISTORY_POINTS_PLOTLY)
 TARGET_ASSET_SYMBOL = MEXC_TARGET_PAIR_USDT.split('_')[0] # e.g., RATO
 BASE_CURRENCY_SYMBOL = MEXC_TARGET_PAIR_USDT.split('_')[1] # e.g., USDT (assumed to be consistent with IN_TOKEN_ADDRESS)
 
-# --- 链上价格获取函数 (Okx) ---
+# --- 链上价格获取函数 (链上) ---
 # 返回: price_base_per_target (例如 USDT per RATO)
 def get_chain_price_vs_target_currency(chain_id, in_token_addr, out_token_addr, amount_in_base_human, in_token_decimals, slippage, user_wallet_addr, user_exchange_wallet_addr):
     try:
@@ -76,72 +77,78 @@ def get_chain_price_vs_target_currency(chain_id, in_token_addr, out_token_addr,
             in_dec, out_dec = int(router_result['fromToken']['decimal']), int(router_result['toToken']['decimal'])
             atomic_in_base, atomic_out_target = decimal.Decimal(router_result['fromTokenAmount']), decimal.Decimal(router_result['toTokenAmount'])
 
-            human_in_base = atomic_in_base / (10 ** in_dec)
-            human_out_target = atomic_out_target / (10 ** out_dec)
-            if human_out_target == 0: return {"error": f"OO输出目标代币为0 ({CHAIN_ID})"}, data # data 也返回
-            return {"price_base_per_target": human_in_base / human_out_target}, data
+            human_in_target = atomic_in_base / (10 ** in_dec)
+            human_out_base = atomic_out_target / (10 ** out_dec)
+            if human_out_base == 0: return {"error": f"OO输出目标代币为0 ({CHAIN_ID})"}, data # data 也返回
+
+            # 1target = x usdt,这个价格
+            return {"price_base_per_target": human_out_base / human_in_target }, data, human_out_base
         else:
             pprint.pprint(data)
             return {
-                "error": f"Okx API错误({chain_id}) - Code:{data.get('code', 'N/A')}, Msg:{data.get('msg', data.get('message', 'N/A')) if isinstance(data, dict) else '格式错误'}"}, None
+                "error": f"链上 API错误({chain_id}) - Code:{data.get('code', 'N/A')}, Msg:{data.get('msg', data.get('message', 'N/A')) if isinstance(data, dict) else '格式错误'}"}, None, 0
     except Exception as e:
-        logger.error(f"Okx ({chain_id})请求错误详情: ", exc_info=True)
-        return {"error": f"Okx ({chain_id})请求错误: {e}"}, None
+        logger.error(f"链上 ({chain_id})请求错误详情: ", exc_info=True)
+        return {"error": f"链上 ({chain_id})请求错误: {e}"}, None
 
 # MEXC 现货 (获取 目标代币/USDT 的 bid 价格)
-# 返回: price_target_per_usdt_bid1 (例如 RATO per USDT)
-def get_mexc_spot_price_target_usdt_bid(pair_symbol):
+# human_out_base: 需要买入大概多少价值的target
+def get_mexc_spot_price_target_usdt_ask(human_out_base):
     url = "https://api.mexc.com/api/v3/depth"
-    params = {'symbol': pair_symbol.replace('_', ''), 'limit': 1000} # 减少limit,5000可能过大且非必要
+    params = {'symbol': MEXC_TARGET_PAIR_USDT.replace('_', ''), 'limit': 1000} 
+
     try:
         r = requests.get(url, params=params, proxies=proxies, timeout=5) # 减少超时
         r.raise_for_status()
         data = r.json()
-        if 'bids' in data and data['bids']: # 确保bids存在且不为空
-            bids = data['bids']
-            trade_volume_remaining = EXCHANGE_OUT_AMOUNT # 还需要卖出的数量 (Decimal)
+        if 'asks' in data and data['asks']: # 确保asks存在且不为空
+            asks = data['asks']
+            trade_value_remaining = human_out_base # 还需要买入的价值(USDT)
             trade_value = decimal.Decimal('0') # 累计的总价值 (Decimal)
             accumulated_volume = decimal.Decimal('0') # 累计吃单量
 
-            for orderbook in bids:
+            for orderbook in asks:
                 price = decimal.Decimal(orderbook[0])
                 volume = decimal.Decimal(orderbook[1])
 
-                if trade_volume_remaining <= decimal.Decimal('0'):
-                    break # 已经满足卖出
+                if trade_value_remaining <= decimal.Decimal('0'):
+                    break # 已经满足买入
 
-                can_fill = min(volume, trade_volume_remaining)
+                can_fill = min(volume, trade_value_remaining / price)
                 trade_value += price * can_fill
                 accumulated_volume += can_fill
-                trade_volume_remaining -= can_fill
+                trade_value_remaining -= price * can_fill
+
+            #     print(trade_value, accumulated_volume, trade_value_remaining)
             
-            if accumulated_volume == decimal.Decimal('0'): # 如果一点都没卖出去
-                 # logger.warning(f"MEXC无法以EXCHANGE_OUT_AMOUNT={EXCHANGE_OUT_AMOUNT}获取任何 efectiva 卖出价格,累积量为0")
-                return {"error": f"MEXC订单簿深度不足以卖出{EXCHANGE_OUT_AMOUNT} {TARGET_ASSET_SYMBOL}"}, decimal.Decimal('0')
+            # print('\n\n')
+            
+            if accumulated_volume == decimal.Decimal('0'): # 如果一点都没买入去
+                return {"error": f"MEXC订单簿深度不足以买入{human_out_base} {BASE_CURRENCY_SYMBOL}"}, decimal.Decimal('0')
 
-            # 计算平均卖出价格
-            # sell_price 代表 1 TARGET_ASSET = X USDT
-            sell_price = trade_value / accumulated_volume 
-            sell_price = sell_price.quantize(decimal.Decimal('1e-10'), rounding=decimal.ROUND_DOWN)
+            # 计算平均买入价格
+            # buy_price 代表 1 TARGET_ASSET = X USDT
+            buy_price = trade_value / accumulated_volume 
+            buy_price = buy_price.quantize(decimal.Decimal('1e-10'), rounding=decimal.ROUND_DOWN)
 
-            # trade_value 代表卖出 accumulated_volume 个 TARGET_ASSET 能得到的 USDT 总量
+            # trade_value 代表买入 accumulated_volume 个 TARGET_ASSET 能得到的 TARGET 总量
             return {
-                "price_target_per_usdt_bid1": sell_price # 这个名字其实是 RATO/USDT,所以可以叫 price_target_per_base
-            }, trade_value # 返回的是实际能卖出 EXCHANGE_OUT_AMOUNT (或更少,如果深度不足) 所得的 USDT 总额
+                "price_target_per_base": buy_price
+            }, trade_value # 返回的是实际能买入 EXCHANGE_OUT_AMOUNT (或更少,如果深度不足) 所得的 USDT 总额
         else:
-            # logger.warning(f"MEXC现货({pair_symbol}) bids 数据不存在或为空: {data}")
-            return {"error": f"MEXC现货({pair_symbol}) bids 数据不存在或为空"}, decimal.Decimal('0')
+            # logger.warning(f"MEXC现货({MEXC_TARGET_PAIR_USDT}) asks 数据不存在或为空: {data}")
+            return {"error": f"MEXC现货({MEXC_TARGET_PAIR_USDT}) asks 数据不存在或为空"}, decimal.Decimal('0')
     except requests.exceptions.RequestException as e:
-        # logger.error(f"MEXC现货({pair_symbol})请求错误: {e}")
-        return {"error": f"MEXC现货({pair_symbol})请求错误: {e}"}, decimal.Decimal('0')
+        # logger.error(f"MEXC现货({MEXC_TARGET_PAIR_USDT})请求错误: {e}")
+        return {"error": f"MEXC现货({MEXC_TARGET_PAIR_USDT})请求错误: {e}"}, decimal.Decimal('0')
     except Exception as e:
-        # logger.error(f"MEXC现货({pair_symbol})处理错误: {e}", exc_info=True)
-        return {"error": f"MEXC现货({pair_symbol})处理错误: {e}"}, decimal.Decimal('0')
+        # logger.error(f"MEXC现货({MEXC_TARGET_PAIR_USDT})处理错误: {e}", exc_info=True)
+        return {"error": f"MEXC现货({MEXC_TARGET_PAIR_USDT})处理错误: {e}"}, decimal.Decimal('0')
 
 latest_values_for_table = {
     f"oo_price_usdt_per_target": "N/A",
-    f"mexc_price_usdt_per_target_bid1": "N/A", # MEXC Bid1 (converted to USDT/TARGET)
-    f"diff_oo_vs_mexc_bid1_percentage": "N/A",
+    f"mexc_price_target_per_base": "N/A", # MEXC price (converted to USDT/TARGET)
+    f"diff_oo_vs_mexc_percentage": "N/A",
     "profit_value_for_table": "N/A", # 新增:用于表格的利润值
     "oo_error": None, "mexc_error": None,
     "last_updated": "N/A",
@@ -151,20 +158,18 @@ latest_values_for_table = {
 }
 data_lock = threading.Lock()
 
-def calculate_percentage_diff(price_a_base_per_target, price_b_base_per_target):
-    # price_a: MEXC卖价 (USDT/TARGET) - 链上买的目标币,拿到CEX卖掉
-    # price_b: 链上买价 (USDT/TARGET) - 链上用USDT买目标币
-    # 期望 price_a > price_b
-    if price_a_base_per_target is not None and price_b_base_per_target is not None and \
-       isinstance(price_a_base_per_target, decimal.Decimal) and \
-       isinstance(price_b_base_per_target, decimal.Decimal) and price_b_base_per_target != 0:
+def calculate_percentage_diff(sell_price, buy_price):
+    # 期望 sell_price > buy_price
+    if sell_price is not None and buy_price is not None and \
+       isinstance(sell_price, decimal.Decimal) and \
+       isinstance(buy_price, decimal.Decimal) and buy_price != 0:
         # (卖价 - 买价) / 买价
-        rst = (price_a_base_per_target - price_b_base_per_target) / price_b_base_per_target
+        rst = (sell_price - buy_price) / buy_price
         rst = rst.quantize(decimal.Decimal('1e-6'), rounding=decimal.ROUND_DOWN) # 提高精度
         return rst
     return None
 
-def send_arb_msg(profit_amount, chain_swap_data, mexc_price_usdt_per_target, in_amount_to_query_human):
+def send_arb_msg(profit_amount, chain_swap_data, mexc_price_usdt_per_target, human_out_base):
     # chain_swap_data 是从 get_chain_price_vs_target_currency 返回的第二个值
     if not (chain_swap_data and chain_swap_data.get('data') and chain_swap_data['data']):
         logger.error(f"套利消息发送失败:链上交易数据不完整 {chain_swap_data}")
@@ -177,10 +182,10 @@ def send_arb_msg(profit_amount, chain_swap_data, mexc_price_usdt_per_target, in_
     to_token_info = router_result['toToken']
 
     in_dec, out_dec = int(from_token_info['decimal']), int(to_token_info['decimal'])
-    # human_in_base 根据实际传入的 IN_AMOUNT_TO_QUERY (trade_value) 确定
-    # human_out_target 是链上swap的实际输出
+    # human_in_target 根据实际传入的 IN_AMOUNT_TO_QUERY (trade_value) 确定
+    # human_out_base 是链上swap的实际输出
     atomic_out_target = decimal.Decimal(router_result['toTokenAmount'])
-    human_out_target = atomic_out_target / (10 ** out_dec)
+    human_out_base = atomic_out_target / (10 ** out_dec)
 
     arbitrage_data = {
         "tx": tx, # 预签名交易
@@ -189,10 +194,10 @@ def send_arb_msg(profit_amount, chain_swap_data, mexc_price_usdt_per_target, in_
         # "mexcPriceUsdtPerTarget": str(mexc_price_usdt_per_target.quantize(decimal.Decimal('1e-8'))),
         "symbol": MEXC_TARGET_PAIR_USDT,
         "fromToken": IN_TOKEN_ADDRESS,
-        "fromTokenAmountHuman": str(in_amount_to_query_human.quantize(decimal.Decimal(f'1e-{in_dec}'))),
+        "fromTokenAmountHuman": str(human_out_base.quantize(decimal.Decimal(f'1e-{in_dec}'))),
         "fromTokenDecimal": str(in_dec),
         "toToken": OUT_TOKEN_ADDRESS,
-        "toTokenAmountHuman": str(human_out_target.quantize(decimal.Decimal(f'1e-{out_dec}'))),
+        "toTokenAmountHuman": str(human_out_base.quantize(decimal.Decimal(f'1e-{out_dec}'))),
         "toTokenDecimal": str(out_dec),
         "exchangeOutAmount": str(EXCHANGE_OUT_AMOUNT.quantize(decimal.Decimal(f'1e-{out_dec}'))), # CEX上期望卖出的目标币数量
         "strategy": STRATEGY,
@@ -223,45 +228,13 @@ def update_data_for_plotly_and_table():
         fetch_time_full = time.strftime("%Y-%m-%d %H:%M:%S")
         fetch_time_chart = time.strftime("%H:%M:%S")
 
-        # 1. MEXC: 获取 price_target_per_usdt_bid1 (例如 RATO/USDT) 和相应的 trade_value_usdt
-        # trade_value_usdt 是指如果以 EXCHANGE_OUT_AMOUNT 的目标代币在MEXC上砸盘卖出,能获得的USDT估值
-        mexc_data, trade_value_usdt = get_mexc_spot_price_target_usdt_bid(MEXC_TARGET_PAIR_USDT)
-        mexc_price_target_per_usdt_bid1 = mexc_data.get("price_target_per_usdt_bid1") # TARGET/USDT
-        mexc_err = mexc_data.get("error")
-
-        # price_target_per_usdt_bid1: 这是1个目标币能卖多少USDT, 即 USDT/TARGET
-        # 所以可以直接用,不需要转换,变量名应为 mexc_price_usdt_per_target_bid1
-        mexc_price_usdt_per_target_bid1_for_calc = None
-        if mexc_price_target_per_usdt_bid1 is not None and mexc_price_target_per_usdt_bid1 > 0:
-            mexc_price_usdt_per_target_bid1_for_calc = mexc_price_target_per_usdt_bid1 # RATO/USDT => USDT/TARGET (命名约定)
-        elif not mexc_err and mexc_price_target_per_usdt_bid1 is not None:
-             mexc_err = mexc_err or "MEXC价格为0或无效"
-        
-        if mexc_err or trade_value_usdt == decimal.Decimal('0'): # 如果MEXC有问题或无法确定砸盘价值,则跳过本次循环
-            logger.warning(f"MEXC数据获取问题: {mexc_err}, trade_value_usdt: {trade_value_usdt}. 跳过本次循环。")
-            with data_lock: # 依然更新错误信息
-                latest_values_for_table["mexc_error"] = mexc_err
-                latest_values_for_table["oo_error"] = latest_values_for_table.get("oo_error") # 保持上次的oo_error
-                latest_values_for_table["last_updated"] = fetch_time_full
-            time.sleep(REFRESH_INTERVAL_SECONDS)
-            continue
-
-        # 2. 确定链上查询的输入金额 (USDT)
-        # 使用 MEXC 卖出 EXCHANGE_OUT_AMOUNT 个目标币能得到的USDT数量 (trade_value_usdt)
-        # 作为链上购买目标币时花费的USDT数量 (in_amount_to_query_human)
-        in_amount_to_query_human = trade_value_usdt.quantize(decimal.Decimal('1.00'), rounding=decimal.ROUND_DOWN) # 保留两位小数,向下取整
-        if in_amount_to_query_human <= decimal.Decimal('0'):
-            logger.warning(f"计算出的链上查询金额为0或负数 ({in_amount_to_query_human} USDT),跳过。trade_value_usdt: {trade_value_usdt}")
-            time.sleep(REFRESH_INTERVAL_SECONDS)
-            continue
-
-        # 3. 获取链上价格:用 in_amount_to_query_human 这么多的USDT去买目标币,能买到多少,以及价格 (USDT/TARGET)
-        oo_data, chain_swap_full_response = get_chain_price_vs_target_currency(
+        # 1. 获取链上价格:卖出 IN_AMOUNT_TO_QUERY 这么多的TARGET去卖成USDT,能卖到多少,以及价格 (USDT/TARGET)
+        oo_data, chain_swap_full_response, human_out_base = get_chain_price_vs_target_currency(
             CHAIN_ID,
-            IN_TOKEN_ADDRESS,      # USDT
-            OUT_TOKEN_ADDRESS,     # TARGET
-            in_amount_to_query_human, # 花费的USDT数
-            IN_TOKEN_DECIMALS,     # USDT的精度
+            IN_TOKEN_ADDRESS,      # TARGET
+            OUT_TOKEN_ADDRESS,     # USDT
+            IN_AMOUNT_TO_QUERY,     # 链上卖出代币量
+            IN_TOKEN_DECIMALS,     # 链上卖出代币的精度
             SLIPPAGE,
             USER_WALLET,
             USER_EXCHANGE_WALLET
@@ -269,64 +242,54 @@ def update_data_for_plotly_and_table():
         oo_price_usdt_per_target = oo_data.get("price_base_per_target") # USDT/TARGET
         oo_err = oo_data.get("error")
 
-        # 4. 计算百分比差异
-        # diff = (MEXC卖价 - 链上买价) / 链上买价
-        diff_oo_vs_mexc_bid1_pct = calculate_percentage_diff(
-            mexc_price_usdt_per_target_bid1_for_calc, # MEXC卖价 (USDT/TARGET)
-            oo_price_usdt_per_target                  # 链上买价 (USDT/TARGET)
+        # 2. MEXC: 获取 price_target_per_base (例如 RATO/USDT)
+        # trade_value_usdt 是指如果以 EXCHANGE_OUT_AMOUNT 的目标代币在MEXC上砸盘卖出,能获得的USDT估值
+        mexc_data, trade_value_usdt = get_mexc_spot_price_target_usdt_ask(human_out_base)
+        mexc_price_target_per_base = mexc_data.get("price_target_per_base") # TARGET/USDT
+        mexc_err = mexc_data.get("error")
+
+        # 3. 计算百分比差异
+        # diff = (链上卖价 - MEXC买价) / MEXC买价
+        diff_oo_vs_mexc_pct = calculate_percentage_diff(
+            oo_price_usdt_per_target,                   # 链上卖价 (USDT/TARGET)
+            mexc_price_target_per_base,                 # MEXC买价 (USDT/TARGET)
         )
 
-        # 5. 计算实际利润额 (以USDT计价)
-        # 利润 = (MEXC每目标币卖价 - 链上每目标币买价) * 链上买入的目标币数量
-        # 链上买入的目标币数量 = in_amount_to_query_human / oo_price_usdt_per_target
+        # 4. 计算实际利润额 (以USDT计价)
         # 简化:利润百分比 * 投入的USDT金额
         actual_profit_usdt = None
-        if diff_oo_vs_mexc_bid1_pct is not None and oo_price_usdt_per_target is not None and oo_price_usdt_per_target > 0:
-            # 方案A: 基于百分比和投入金额
-            actual_profit_usdt = diff_oo_vs_mexc_bid1_pct * in_amount_to_query_human
-            # # 方案B: 基于单价差和数量 (更精确,如果chain_swap_full_response可用)
-            # if chain_swap_full_response and chain_swap_full_response.get('data'):
-            #     router_result = chain_swap_full_response['data'][0]['routerResult']
-            #     atomic_out_target = decimal.Decimal(router_result['toTokenAmount'])
-            #     out_dec = int(router_result['toToken']['decimal'])
-            #     human_out_target_onchain = atomic_out_target / (10 ** out_dec) # 链上实际换到的目标币
-
-            #     # 能在CEX卖出的目标币数量,取链上换到的数量和CEX预设卖出量的较小值,因为CEX订单深度是按EXCHANGE_OUT_AMOUNT查的
-            #     effective_target_to_sell_on_mexc = min(human_out_target_onchain, EXCHANGE_OUT_AMOUNT)
-
-            #     revenue_on_mexc = effective_target_to_sell_on_mexc * mexc_price_usdt_per_target_bid1_for_calc
-            #     cost_on_chain = in_amount_to_query_human # 这是我们实际在链上花的USDT
-            #     actual_profit_usdt_v2 = revenue_on_mexc - cost_on_chain
-            #     actual_profit_usdt = actual_profit_usdt_v2 # 使用更精确的V2版本
-
-        # 6. 满足利润条件,发送套利消息, PROFIT_LIMIT + 3的3是提前計算的成本,否則一直提交
+        if diff_oo_vs_mexc_pct is not None and oo_price_usdt_per_target is not None and oo_price_usdt_per_target > 0:
+            # 基于百分比和投入金额
+            actual_profit_usdt = diff_oo_vs_mexc_pct * human_out_base
+
+        # 5. 满足利润条件,发送套利消息, PROFIT_LIMIT + 3的3是提前計算的成本,否則一直提交
         global mode
         if actual_profit_usdt is not None and actual_profit_usdt > PROFIT_LIMIT + 3 and mode == 'trade':
             if chain_swap_full_response: # 确保有完整的链上数据
-                send_arb_msg(actual_profit_usdt, chain_swap_full_response, mexc_price_usdt_per_target_bid1_for_calc, in_amount_to_query_human)
+                send_arb_msg(actual_profit_usdt, chain_swap_full_response, mexc_price_target_per_base, human_out_base)
             else:
                 logger.warning("利润满足但链上数据不完整,无法发送套利消息。")
 
         current_point = {
             "time": fetch_time_chart,
             "oo_price_usdt_per_target": float(oo_price_usdt_per_target) if oo_price_usdt_per_target else None,
-            "mexc_price_usdt_per_target_bid1": float(mexc_price_usdt_per_target_bid1_for_calc) if mexc_price_usdt_per_target_bid1_for_calc else None,
-            "diff_oo_vs_mexc_bid1": float(diff_oo_vs_mexc_bid1_pct) if diff_oo_vs_mexc_bid1_pct is not None else None,
+            "mexc_price_target_per_base": float(mexc_price_target_per_base) if mexc_price_target_per_base else None,
+            "diff_oo_vs_mexc": float(diff_oo_vs_mexc_pct) if diff_oo_vs_mexc_pct is not None else None,
             "profit_value": float(actual_profit_usdt) if actual_profit_usdt is not None else None, # 新增:用于图表的实际利润额
         }
 
         with data_lock:
             historical_data_points.append(current_point)
             latest_values_for_table["oo_price_usdt_per_target"] = f"{oo_price_usdt_per_target:.8f}" if oo_price_usdt_per_target else "N/A"
-            latest_values_for_table["mexc_price_usdt_per_target_bid1"] = f"{mexc_price_usdt_per_target_bid1_for_calc:.8f}" if mexc_price_usdt_per_target_bid1_for_calc else "N/A"
-            latest_values_for_table["diff_oo_vs_mexc_bid1_percentage"] = f"{diff_oo_vs_mexc_bid1_pct:+.4%}" if diff_oo_vs_mexc_bid1_pct is not None else "N/A" # 显示为百分比
+            latest_values_for_table["mexc_price_target_per_base"] = f"{mexc_price_target_per_base:.8f}" if mexc_price_target_per_base else "N/A"
+            latest_values_for_table["diff_oo_vs_mexc_percentage"] = f"{diff_oo_vs_mexc_pct:+.4%}" if diff_oo_vs_mexc_pct is not None else "N/A" # 显示为百分比
             latest_values_for_table["profit_value_for_table"] = f"{actual_profit_usdt:.2f} {BASE_CURRENCY_SYMBOL}" if actual_profit_usdt is not None else "N/A" # 新增
             latest_values_for_table["oo_error"] = oo_err
             latest_values_for_table["mexc_error"] = mexc_err
             latest_values_for_table["last_updated"] = fetch_time_full
-            latest_values_for_table["in_amount_for_query_display"] = f"{in_amount_to_query_human:.2f} {BASE_CURRENCY_SYMBOL}" if in_amount_to_query_human > 0 else "N/A"
+            latest_values_for_table["in_amount_for_query_display"] = f"{human_out_base:.2f} {BASE_CURRENCY_SYMBOL}" if human_out_base > 0 else "N/A"
 
-        # logger.info(f"{fetch_time_chart} Price Query: Chain Input {in_amount_to_query_human:.2f} {BASE_CURRENCY_SYMBOL} | OKX Price: {oo_price_usdt_per_target_display} | MEXC Price: {mexc_price_usdt_per_target_bid1_display} | Diff: {diff_display} | Profit: {profit_display}")
+        # logger.info(f"{fetch_time_chart} Price Query: Chain Input {human_out_base:.2f} {BASE_CURRENCY_SYMBOL} | OKX Price: {oo_price_usdt_per_target_display} | MEXC Price: {mexc_price_target_per_base_display} | Diff: {diff_display} | Profit: {profit_display}")
         if oo_err or mexc_err :
              logger.warning(f"{fetch_time_chart} Errors: OO:{oo_err}, MEXC:{mexc_err}")
 
@@ -375,14 +338,14 @@ def get_plotly_chart_data():
         # Price Chart
         fig_prices = go.Figure()
         fig_prices.add_trace(go.Scatter(x=times, y=[p['oo_price_usdt_per_target'] for p in points], mode='lines',
-                                        name=f'Okx ({display_base_asset}/{display_target_asset})',
+                                        name=f'链上({display_base_asset}/{display_target_asset})',
                                         line=dict(color='rgb(75, 192, 192)'),
-                                        hovertemplate=f'<b>Okx链上价</b><br>价格: %{{y:.8f}} {display_base_asset}<extra></extra>',
+                                        hovertemplate=f'<b>链上价</b><br>价格: %{{y:.8f}} {display_base_asset}<extra></extra>',
                                         connectgaps=True)) # 处理None值不画线
-        fig_prices.add_trace(go.Scatter(x=times, y=[p['mexc_price_usdt_per_target_bid1'] for p in points], mode='lines',
-                                        name=f'MEXC卖1价 ({display_base_asset}/{display_target_asset})',
+        fig_prices.add_trace(go.Scatter(x=times, y=[p['mexc_price_target_per_base'] for p in points], mode='lines',
+                                        name=f'MEXC价 ({display_base_asset}/{display_target_asset})',
                                         line=dict(color='rgb(255, 99, 132)', dash='dash'),
-                                        hovertemplate=f'<b>MEXC卖出价</b><br>价格: %{{y:.8f}} {display_base_asset}<extra></extra>',
+                                        hovertemplate=f'<b>MEXC价</b><br>价格: %{{y:.8f}} {display_base_asset}<extra></extra>',
                                         connectgaps=True))
         fig_prices.update_layout(title_text=f'{display_base_asset}/{display_target_asset} 价格历史',
                                  xaxis=common_xaxis_config.copy(),
@@ -394,9 +357,9 @@ def get_plotly_chart_data():
         # Percentage Difference Chart
         fig_diffs = go.Figure()
         fig_diffs.add_trace(
-            go.Scatter(x=times, y=[p['diff_oo_vs_mexc_bid1'] for p in points], mode='lines', name=f'价差百分比 (MEXC卖价 vs Okx买价)',
+            go.Scatter(x=times, y=[p['diff_oo_vs_mexc'] for p in points], mode='lines', name=f'价差百分比 (MEXC卖价 vs 链上买价)',
                        line=dict(color='rgb(255, 159, 64)'),
-                       hovertemplate=f'<b>(MEXC卖价-Okx买价)/Okx买价</b><br>百分比: %{{y:+.4%}}<extra></extra>', # 显示为百分比
+                       hovertemplate=f'<b>(MEXC卖价-链上买价)/链上买价</b><br>百分比: %{{y:+.4%}}<extra></extra>', # 显示为百分比
                        connectgaps=True))
         fig_diffs.update_layout(title_text=f'价差百分比历史曲线',
                                 xaxis=common_xaxis_config.copy(),
@@ -434,7 +397,7 @@ if __name__ == "__main__":
                         choices=['trade', 'view'], # 限制可选值
                         help='运行模式: "trade" (执行交易) 或 "view" (仅观察)')
 
-    except_strategy = 'erc20_to_mexc'
+    except_strategy = 'mexc_to_erc20'
     if STRATEGY != except_strategy:
         raise Exception(f"策略不匹配! 期待{except_strategy}, 实际{STRATEGY}")
 

+ 7 - 7
checker/templates/index_plotly_dynamic_ok.html

@@ -48,17 +48,17 @@
                 </thead>
                 <tbody>
                     <tr>
-                        <td class="platform-name">Okx链上购买价 ({{ target_asset }}/{{ base_asset }})</td>
+                        <td class="platform-name">链上 价 ({{ target_asset }}/{{ base_asset }})</td>
                         <td id="oo-price-usdt-per-target" class="value-cell">加载中...</td>
                         <td id="oo-status" class="status-cell"></td>
                     </tr>
                     <tr>
-                        <td class="platform-name">MEXC交易所卖出价 ({{ mexc_pair_usdt }} &rarr; {{ target_asset }}/{{ base_asset }})</td>
+                        <td class="platform-name">MEXC 价 ({{ mexc_pair_usdt }} &rarr; {{ target_asset }}/{{ base_asset }})</td>
                         <td id="mexc-price-usdt-per-target-bid1" class="value-cell">加载中...</td>
                         <td id="mexc-status" class="status-cell"></td>
                     </tr>
                      <tr>
-                        <td class="platform-name" title="价差百分比 = (MEXC卖价 - Okx买价) / Okx买价">价差百分比 (MEXC vs Okx)</td>
+                        <td class="platform-name" title="价差百分比 = (MEXC价 - 链上价) / 链上价">价差百分比 (MEXC vs 链上)</td>
                         <td id="diff-oo-vs-mexc-bid1-percentage" class="value-cell">计算中...</td>
                         <td class="status-cell"></td> <!-- 此处一般无独立状态 -->
                     </tr>
@@ -79,7 +79,7 @@
         </div>
 
         <div class="container">
-            <h2 id="diff-chart-title">价差百分比历史曲线 (MEXC卖价 vs Okx买价)</h2>
+            <h2 id="diff-chart-title">价差百分比历史曲线 (MEXC价 vs 链上价)</h2>
             <div id='diffPercentageChartPlotly' class="chart-container diff-profit-chart-container"></div>
             <div id="diff-chart-status" class="status-line">加载价差百分比图表...</div>
         </div>
@@ -193,7 +193,7 @@
                 const data = await response.json();
 
                 document.getElementById('oo-price-usdt-per-target').textContent = formatPriceForTable(data.oo_price_usdt_per_target);
-                document.getElementById('mexc-price-usdt-per-target-bid1').textContent = formatPriceForTable(data.mexc_price_usdt_per_target_bid1);
+                document.getElementById('mexc-price-usdt-per-target-bid1').textContent = formatPriceForTable(data.mexc_price_target_per_base);
 
                 document.getElementById('oo-status').textContent = data.oo_error || '正常';
                 document.getElementById('mexc-status').textContent = data.mexc_error || '正常';
@@ -201,9 +201,9 @@
                 document.getElementById('mexc-status').className = data.mexc_error ? 'status-cell error-message' : 'status-cell';
 
                 const diffOOMexcBidEl = document.getElementById('diff-oo-vs-mexc-bid1-percentage');
-                // 后端传来的 diff_oo_vs_mexc_bid1_percentage 已经是 مطلوب格式,例如 "+0.0123" 或包含 %
+                // 后端传来的 diff_oo_vs_mexc_percentage 已经是 مطلوب格式,例如 "+0.0123" 或包含 %
                 // 但我们这里统一处理,假设后端传的是小数形式 0.00123
-                let rawDiffPercentage = data.diff_oo_vs_mexc_bid1_percentage;
+                let rawDiffPercentage = data.diff_oo_vs_mexc_percentage;
                 if (typeof rawDiffPercentage === 'string' && rawDiffPercentage.includes('%')) {
                     rawDiffPercentage = parseFloat(rawDiffPercentage.replace('%','')) / 100;
                 } else {