|
@@ -0,0 +1,168 @@
|
|
|
|
|
+import requests
|
|
|
|
|
+import decimal # 导入 decimal 模块,用于更精确的货币运算
|
|
|
|
|
+import time # 导入 time 模块,用于实现轮询间隔
|
|
|
|
|
+
|
|
|
|
|
+# --- 配置部分 (与之前代码相同,此处省略以保持简洁) ---
|
|
|
|
|
+# proxies = None # 如果不使用代理
|
|
|
|
|
+GATEIO_SPOT_PAIR = 'MUBARAK_USDT'
|
|
|
|
|
+# BSC (币安智能链) 代币地址
|
|
|
|
|
+IN_TOKEN_ADDRESS_BSC = '0x55d398326f99059ff775485246999027b3197955' # BSC 上的 USDT 代币合约地址
|
|
|
|
|
+OUT_TOKEN_ADDRESS_BSC = '0x5C85D6C6825aB4032337F11Ee92a72DF936b46F6'
|
|
|
|
|
+AMOUNT_TO_QUERY_HUMAN = decimal.Decimal('1000') # 查询数量设置为1个单位的输入代币
|
|
|
|
|
+PROXY_HOST = '127.0.0.1'
|
|
|
|
|
+PROXY_PORT = '7890'
|
|
|
|
|
+proxies = {
|
|
|
|
|
+ 'http': f'http://{PROXY_HOST}:{PROXY_PORT}',
|
|
|
|
|
+ 'https': f'http://{PROXY_HOST}:{PROXY_PORT}',
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+# --- OpenOcean 和 Gate.io 的价格获取函数 (get_openocean_price_bsc, get_gateio_spot_price) ---
|
|
|
|
|
+# (这些函数与您上一版本代码中的基本一致,确保它们返回包含 'rate_out_per_in' 或 'price_base_in_quote' 的字典,
|
|
|
|
|
+# 或者在出错时返回包含 'error' 键的字典)
|
|
|
|
|
+# 为了完整性,我将它们包含进来,但假设它们是上一版本中我们确认过的:
|
|
|
|
|
+
|
|
|
|
|
+def get_openocean_price_bsc(in_token_addr, out_token_addr, human_amount_in_decimal, gas_price=3):
|
|
|
|
|
+ chain = 'bsc'
|
|
|
|
|
+ url = f'https://open-api.openocean.finance/v4/{chain}/quote'
|
|
|
|
|
+ params = {
|
|
|
|
|
+ 'inTokenAddress': in_token_addr,
|
|
|
|
|
+ 'outTokenAddress': out_token_addr,
|
|
|
|
|
+ 'amount': str(human_amount_in_decimal),
|
|
|
|
|
+ 'gasPrice': gas_price,
|
|
|
|
|
+ }
|
|
|
|
|
+ try:
|
|
|
|
|
+ response = requests.get(url, params=params, proxies=proxies, timeout=10)
|
|
|
|
|
+ response.raise_for_status()
|
|
|
|
|
+ data = response.json()
|
|
|
|
|
+ print(data)
|
|
|
|
|
+ if data.get('code') == 200 and data.get('data') and data['data'].get('outToken'):
|
|
|
|
|
+ out_token_info = data['data']['outToken']
|
|
|
|
|
+ human_out_amount_str = out_token_info.get('volume')
|
|
|
|
|
+ if human_out_amount_str is not None:
|
|
|
|
|
+ human_out_amount_decimal = decimal.Decimal(str(human_out_amount_str))
|
|
|
|
|
+ if human_amount_in_decimal > 0:
|
|
|
|
|
+ rate = human_amount_in_decimal / human_out_amount_decimal
|
|
|
|
|
+ return {"rate_out_per_in": rate}
|
|
|
|
|
+ else:
|
|
|
|
|
+ return {"error": "输入金额为零"}
|
|
|
|
|
+ else:
|
|
|
|
|
+ return {"error": "未找到outToken.volume"}
|
|
|
|
|
+ else:
|
|
|
|
|
+ error_message = data.get('message', 'N/A') if data else '无响应数据'
|
|
|
|
|
+ error_code = data.get('code', 'N/A') if data else 'N/A'
|
|
|
|
|
+ return {"error": f"OO API Err Code: {error_code}, Msg: {error_message}"}
|
|
|
|
|
+ except requests.exceptions.RequestException as e:
|
|
|
|
|
+ return {"error": f"OO请求失败: {e}"}
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ return {"error": f"OO意外错误: {e}"}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def get_gateio_spot_price(pair_symbol):
|
|
|
|
|
+ url = f'https://api.gateio.ws/api/v4/spot/tickers'
|
|
|
|
|
+ params = {'currency_pair': pair_symbol}
|
|
|
|
|
+ try:
|
|
|
|
|
+ response = requests.get(url, params=params, proxies=proxies, timeout=10)
|
|
|
|
|
+ response.raise_for_status()
|
|
|
|
|
+ data = response.json()
|
|
|
|
|
+ if isinstance(data, list) and len(data) > 0:
|
|
|
|
|
+ ticker_data = data[0]
|
|
|
|
|
+ if ticker_data.get('currency_pair') == pair_symbol:
|
|
|
|
|
+ last_price_str = ticker_data.get('last')
|
|
|
|
|
+ if last_price_str:
|
|
|
|
|
+ return {"price_base_in_quote": decimal.Decimal(last_price_str)}
|
|
|
|
|
+ else:
|
|
|
|
|
+ return {"error": "Gate未找到last price"}
|
|
|
|
|
+ else:
|
|
|
|
|
+ return {"error": f"Gate交易对不匹配"}
|
|
|
|
|
+ else:
|
|
|
|
|
+ return {"error": "Gate API数据格式错误"}
|
|
|
|
|
+ except requests.exceptions.RequestException as e:
|
|
|
|
|
+ return {"error": f"Gate请求失败: {e}"}
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ return {"error": f"Gate意外错误: {e}"}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+# --- 主逻辑 (已修改) ---
|
|
|
|
|
+def main():
|
|
|
|
|
+ print(f"开始轮询价格 (每秒一次), BSC vs Gate.io {GATEIO_SPOT_PAIR}")
|
|
|
|
|
+ print("按 Ctrl+C 停止。")
|
|
|
|
|
+ # 打印表头,使用格式化字符串使其对齐
|
|
|
|
|
+ header = f"{'OpenOcean ':<25} | {'Gate.io ':<25} | {'价差百分比':<15}"
|
|
|
|
|
+ print(header)
|
|
|
|
|
+ print("-" * (25 + 3 + 25 + 3 + 15)) # 打印与表头长度匹配的分隔线
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ while True:
|
|
|
|
|
+ # --- 初始化本轮迭代的变量 ---
|
|
|
|
|
+ oo_rate_usdc_per_usdt = None # OpenOcean 的汇率 (1 USDT = X USDC)
|
|
|
|
|
+ gate_rate_usdc_per_usdt_inverted = None # Gate.io 转换后的汇率 (1 USDT = X USDC)
|
|
|
|
|
+
|
|
|
|
|
+ oo_display_str = "N/A" # OpenOcean 在最终输出行中显示的字符串
|
|
|
|
|
+ gate_display_str = "N/A" # Gate.io 在最终输出行中显示的字符串
|
|
|
|
|
+ diff_percentage_display_str = "N/A" # 价差百分比在最终输出行中显示的字符串
|
|
|
|
|
+
|
|
|
|
|
+ oo_error_this_iteration = None # 存储本轮OpenOcean的错误信息
|
|
|
|
|
+ gate_error_this_iteration = None # 存储本轮Gate.io的错误信息
|
|
|
|
|
+
|
|
|
|
|
+ # --- 1. OpenOcean BSC 查询 ---
|
|
|
|
|
+ oo_price_data = get_openocean_price_bsc(
|
|
|
|
|
+ IN_TOKEN_ADDRESS_BSC,
|
|
|
|
|
+ OUT_TOKEN_ADDRESS_BSC,
|
|
|
|
|
+ AMOUNT_TO_QUERY_HUMAN
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ if "error" not in oo_price_data:
|
|
|
|
|
+ oo_rate_usdc_per_usdt = oo_price_data['rate_out_per_in']
|
|
|
|
|
+ oo_display_str = f"{oo_rate_usdc_per_usdt:.6f}" # 格式化价格
|
|
|
|
|
+ else:
|
|
|
|
|
+ oo_error_this_iteration = oo_price_data['error'] # 记录错误信息
|
|
|
|
|
+
|
|
|
|
|
+ # --- 2. Gate.io 现货查询 ---
|
|
|
|
|
+ gate_price_data = get_gateio_spot_price(GATEIO_SPOT_PAIR)
|
|
|
|
|
+
|
|
|
|
|
+ if "error" not in gate_price_data:
|
|
|
|
|
+ gate_rate_usdt_per_usdc = gate_price_data['price_base_in_quote']
|
|
|
|
|
+ if gate_rate_usdt_per_usdc is not None and gate_rate_usdt_per_usdc > 0:
|
|
|
|
|
+ # 转换 Gate.io 的汇率方向为 1 USDT = Y USDC (Y = 1 / X)
|
|
|
|
|
+ gate_rate_usdc_per_usdt_inverted = gate_rate_usdt_per_usdc
|
|
|
|
|
+ gate_display_str = f"{gate_rate_usdc_per_usdt_inverted:.6f}" # 格式化价格
|
|
|
|
|
+ elif gate_rate_usdt_per_usdc is not None: # 价格为0或负数
|
|
|
|
|
+ gate_error_this_iteration = f"Gate.io 无效汇率 ({gate_rate_usdt_per_usdc})"
|
|
|
|
|
+ gate_display_str = "Invalid" # 在行内显示为无效
|
|
|
|
|
+ # else: price_base_in_quote is None, error already handled by "error" key check
|
|
|
|
|
+ else:
|
|
|
|
|
+ gate_error_this_iteration = gate_price_data['error'] # 记录错误信息
|
|
|
|
|
+
|
|
|
|
|
+ # --- 3. 计算价差百分比 (仅当两边价格都有效时) ---
|
|
|
|
|
+ if oo_rate_usdc_per_usdt is not None and gate_rate_usdc_per_usdt_inverted is not None:
|
|
|
|
|
+ if gate_rate_usdc_per_usdt_inverted != 0: # 避免除以零
|
|
|
|
|
+ # 价差 = OpenOcean汇率 - Gate.io转换后汇率
|
|
|
|
|
+ difference = oo_rate_usdc_per_usdt - gate_rate_usdc_per_usdt_inverted
|
|
|
|
|
+ # 价差百分比 = (价差 / Gate.io转换后汇率) * 100
|
|
|
|
|
+ # 您可以根据需要选择以哪个价格为基准计算百分比,这里以 Gate.io 为基准
|
|
|
|
|
+ percentage_diff = (difference / gate_rate_usdc_per_usdt_inverted) * 100
|
|
|
|
|
+ diff_percentage_display_str = f"{percentage_diff:+.4f}%" # 显示正负号和小数点后4位
|
|
|
|
|
+ else:
|
|
|
|
|
+ diff_percentage_display_str = "Gate.io汇率为0" # Gate.io 汇率为0,无法计算百分比
|
|
|
|
|
+
|
|
|
|
|
+ # --- 4. 打印错误信息 (如果本轮有错误发生) ---
|
|
|
|
|
+ # 这些错误会打印在数据行的上方,以便用户了解具体问题
|
|
|
|
|
+ if oo_error_this_iteration:
|
|
|
|
|
+ print(f"[错误] OpenOcean: {oo_error_this_iteration}")
|
|
|
|
|
+ if gate_error_this_iteration:
|
|
|
|
|
+ print(f"[错误] Gate.io: {gate_error_this_iteration}")
|
|
|
|
|
+
|
|
|
|
|
+ # --- 5. 组合打印在一行 ---
|
|
|
|
|
+ # 使用格式化字符串确保列对齐
|
|
|
|
|
+ print(f"{oo_display_str:<25} | {gate_display_str:<25} | {diff_percentage_display_str:<15}")
|
|
|
|
|
+
|
|
|
|
|
+ time.sleep(1) # 等待1秒
|
|
|
|
|
+
|
|
|
|
|
+ except KeyboardInterrupt: # 允许用户通过 Ctrl+C 来停止脚本
|
|
|
|
|
+ print("\n轮询停止。")
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+if __name__ == "__main__":
|
|
|
|
|
+ decimal.getcontext().prec = 36 # 设置 decimal 的计算精度
|
|
|
|
|
+ main()
|