|
|
@@ -17,44 +17,88 @@ proxies = {
|
|
|
}
|
|
|
|
|
|
|
|
|
-# --- 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):
|
|
|
+# --- OpenOcean 的价格获取函数 ---
|
|
|
+def get_openocean_price_bsc(in_token_addr, out_token_addr, human_amount_in_decimal_for_request, gas_price=3):
|
|
|
+ """
|
|
|
+ 从 OpenOcean API 获取报价,并根据 API 返回的原子单位数量和代币小数位数计算价格。
|
|
|
+
|
|
|
+ 价格计算方式为: (通过 inAmount 和 inToken.decimals 计算得到的人类可读输入数量) / (通过 outAmount 和 outToken.decimals 计算得到的人类可读输出数量)
|
|
|
+ 这个价格表示 “每单位输出代币需要多少单位输入代币”。
|
|
|
+
|
|
|
+ Args:
|
|
|
+ in_token_addr (str): 输入代币的合约地址。
|
|
|
+ out_token_addr (str): 输出代币的合约地址。
|
|
|
+ human_amount_in_decimal_for_request (decimal.Decimal): 用于 API 请求的人类可读的输入金额。
|
|
|
+ API会根据此金额和实际代币信息处理请求。
|
|
|
+ gas_price (int, optional): Gas 价格。 默认为 3。
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ dict: 包含价格计算结果或错误信息的字典。
|
|
|
+ 成功时: {"price_in_per_out": calculated_price}
|
|
|
+ 失败时: {"error": "错误信息"}
|
|
|
+ """
|
|
|
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),
|
|
|
+ 'amount': str(human_amount_in_decimal_for_request), # API 请求中的 amount 参数
|
|
|
'gasPrice': gas_price,
|
|
|
}
|
|
|
try:
|
|
|
response = requests.get(url, params=params, proxies=proxies, timeout=10)
|
|
|
- response.raise_for_status()
|
|
|
+ response.raise_for_status() # 如果HTTP状态码表示错误,则抛出异常
|
|
|
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"}
|
|
|
+
|
|
|
+ if data.get('code') == 200 and data.get('data'):
|
|
|
+ api_data = data['data']
|
|
|
+
|
|
|
+ # 确保所有需要的数据都存在
|
|
|
+ if not (api_data.get('inToken') and api_data['inToken'].get('decimals') is not None and
|
|
|
+ api_data.get('outToken') and api_data['outToken'].get('decimals') is not None and
|
|
|
+ api_data.get('inAmount') is not None and api_data.get('outAmount') is not None):
|
|
|
+ return {"error": "API响应中缺少必要的代币信息、数量或小数位数"}
|
|
|
+
|
|
|
+ # 从 API 响应中获取原子单位的数量和小数位数
|
|
|
+ in_token_decimals = int(api_data['inToken']['decimals'])
|
|
|
+ out_token_decimals = int(api_data['outToken']['decimals'])
|
|
|
+
|
|
|
+ atomic_in_amount = decimal.Decimal(api_data['inAmount'])
|
|
|
+ atomic_out_amount = decimal.Decimal(api_data['outAmount'])
|
|
|
+
|
|
|
+ # 计算人类可读的输入数量
|
|
|
+ # derived_human_in_amount = atomic_in_amount / (decimal.Decimal('10') ** in_token_decimals)
|
|
|
+ # 根据您的反馈,OpenOcean的 inToken.volume 已经是人类可读的输入量,而 inAmount 是API内部处理后的原子单位量
|
|
|
+ # 如果您的意图是严格使用API返回的 inAmount 来反推人类可读输入量,那么上面的计算是正确的。
|
|
|
+ # 我们将基于 inAmount 和 outAmount 来计算,正如您所要求的。
|
|
|
+ derived_human_in_amount = atomic_in_amount / (decimal.Decimal('10') ** in_token_decimals)
|
|
|
+
|
|
|
+ # 计算人类可读的输出数量
|
|
|
+ derived_human_out_amount = atomic_out_amount / (decimal.Decimal('10') ** out_token_decimals)
|
|
|
+
|
|
|
+ if derived_human_out_amount == 0: # 避免除以零的错误
|
|
|
+ return {"error": "计算得出的人类可读输出数量为零,无法计算价格"}
|
|
|
+
|
|
|
+ if derived_human_in_amount == 0 and human_amount_in_decimal_for_request > 0:
|
|
|
+ # 如果API返回的inAmount为0,但请求量大于0,可能是一个问题或特殊情况
|
|
|
+ # 但我们仍按您的要求基于derived_human_in_amount计算
|
|
|
+ pass
|
|
|
+
|
|
|
+ # 根据您的要求:价格 = 输入数量 / 输出数量
|
|
|
+ # 这个价格表示:1 单位的输出代币等价于多少单位的输入代币
|
|
|
+ calculated_price = derived_human_in_amount / derived_human_out_amount
|
|
|
+
|
|
|
+ return {"price_in_per_out": calculated_price} # 使用更能描述价格含义的键名
|
|
|
+
|
|
|
else:
|
|
|
- error_message = data.get('message', 'N/A') if data else '无响应数据'
|
|
|
+ # API 返回的 code 不是 200 或没有 data 字段
|
|
|
+ error_message = data.get('message', 'N/A') if data else '无响应数据 (data is None)'
|
|
|
error_code = data.get('code', 'N/A') if data else 'N/A'
|
|
|
- return {"error": f"OO API Err Code: {error_code}, Msg: {error_message}"}
|
|
|
+ return {"error": f"OO API 错误 - Code: {error_code}, Msg: {error_message}"}
|
|
|
+
|
|
|
except requests.exceptions.RequestException as e:
|
|
|
return {"error": f"OO请求失败: {e}"}
|
|
|
- except Exception as e:
|
|
|
+ except Exception as e: # 捕获包括 JSONDecodeError 在内的其他潜在错误
|
|
|
return {"error": f"OO意外错误: {e}"}
|
|
|
|
|
|
|
|
|
@@ -113,7 +157,7 @@ def main():
|
|
|
)
|
|
|
|
|
|
if "error" not in oo_price_data:
|
|
|
- oo_rate_usdc_per_usdt = oo_price_data['rate_out_per_in']
|
|
|
+ oo_rate_usdc_per_usdt = oo_price_data['price_in_per_out']
|
|
|
oo_display_str = f"{oo_rate_usdc_per_usdt:.6f}" # 格式化价格
|
|
|
else:
|
|
|
oo_error_this_iteration = oo_price_data['error'] # 记录错误信息
|