火币API签名验证频繁失败问题求助
我现在遇到一个头疼的问题:下面这段调用火币API的代码有时候能正常工作,但绝大多数时候都会返回签名无效的错误:
{'status': 'error', 'err-code': 'api-signature-not-valid', 'err-msg': 'Signature not valid: Verification failure [校验失败]', 'data': None}
我实在搞不懂为什么会时好时坏,看起来完全没有规律...有没有朋友遇到过类似的问题?能不能帮我分析下原因?我试过好几种方法都没解决,目前这个版本是唯一偶尔能跑通的了,提前谢谢大家的建议!
我的代码如下:
from datetime import datetime import hmac import hashlib import base64 from urllib.parse import urlparse import requests import time def generate_timestamp(): # Gera o timestamp no formato ISO 8601 timestamp = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S') # Substituir os caracteres para codificação de URL timestamp = timestamp.replace(":", "%3A").replace(" ", "%20").upper() return timestamp def generate_signature(api_secret, method, url, params): # Ordenar os parâmetros em ordem alfabética sorted_keys = sorted(params.keys()) query_string = "&".join([f"{key}={params[key]}" for key in sorted_keys]) # Montar o texto pré-assinado url_parts = urlparse(url) host = url_parts.netloc path = url_parts.path pre_signed_text = f"{method.upper()}\n{host}\n{path}\n{query_string}" print(f"Pre-signed text:\n{pre_signed_text}") # Gerar a assinatura usando HMAC-SHA256 signature = hmac.new( api_secret.encode('utf-8'), pre_signed_text.encode('utf-8'), hashlib.sha256 ).digest() # Retorna a assinatura em Base64 return base64.b64encode(signature).decode('utf-8') def fetch_balances(api_key, api_secret): spot_account_id = "29996280" endpoint = f"/v1/account/accounts/{spot_account_id}/balance" base_url = "https://api.huobi.pro" url = f"{base_url}{endpoint}" method = "GET" timestamp = generate_timestamp() # Parâmetros essenciais params = { "AccessKeyId": api_key, "SignatureMethod": "HmacSHA256", "SignatureVersion": "2", "Timestamp": timestamp } # Gerar a assinatura signature = generate_signature(api_secret, method, url, params) if not signature: print("Erro ao gerar a assinatura") return [] # Adicionar a assinatura aos parâmetros params["Signature"] = signature sorted_keys = sorted(params.keys()) query_string = "&".join([f"{key}={params[key]}" for key in sorted_keys]) # Construir a URL final final_url = f"{url}?{query_string}" print(f"URL Final: {final_url}") # Executar a requisição headers = {"Content-Type": "application/x-www-form-urlencoded"} response = requests.get(final_url, headers=headers) if response.status_code == 200: print("Requisição bem-sucedida") return response.json() else: print(f"Falha na requisição: {response.status_code}") print(f"Resposta JSON: {response.text}") return [] def loop_fetch_balances(api_key, api_secret): while True: # Loop infinito try: # Chama a função fetch_balances balances = fetch_balances(api_key, api_secret) # Imprime os balances, se disponíveis if balances: print("Inicio da resposta:", str(balances)[:200]) # Exibe os primeiros 100 caracteres else: print("Não foi possível obter os balances.") # Aguarda 5 segundos antes de repetir time.sleep(5) except KeyboardInterrupt: # Permite sair do loop com Ctrl+C print("Encerrando o loop...") break # Configurações da API api_key = "MY_API_KEY" api_secret = "MY_API_SECRET" # Executa o loop loop_fetch_balances(api_key, api_secret)
可能的问题点及修复建议
1. 时间戳格式不规范
你的generate_timestamp函数手动处理时间戳的URL编码,且没有遵循火币API要求的ISO 8601标准格式(UTC时间需带Z后缀),这可能导致时间戳校验不稳定。
修复方案:
改用标准方法生成时间戳并编码:
from urllib.parse import quote def generate_timestamp(): # 生成带Z后缀的标准UTC时间(符合ISO 8601) timestamp = datetime.utcnow().isoformat(timespec='seconds') + 'Z' # 使用标准URL编码,避免手动替换的疏漏 return quote(timestamp)
2. 参数值未做URL编码
在生成签名的query string时,你直接拼接了参数值,但没有对值做URL编码。如果参数值(比如时间戳)包含特殊字符,会导致签名计算与火币服务器端不一致。
修复方案:
修改generate_signature中的query string生成逻辑:
from urllib.parse import quote def generate_signature(api_secret, method, url, params): sorted_keys = sorted(params.keys()) # 对每个参数值做URL编码 query_string = "&".join([f"{key}={quote(str(params[key]))}" for key in sorted_keys]) # ... 其余代码不变
3. 系统时间偏差
火币API对时间戳的有效性有严格的窗口限制(通常为±5分钟),如果你的本地系统时间与UTC时间偏差过大,会导致签名验证失败。
检查方法:
对比本地UTC时间与标准UTC时间,若偏差超过3分钟,需同步系统时间。
4. 签名生成时的换行符问题
确保预签名文本的换行符是\n(Unix格式),避免因操作系统差异引入\r\n,这会导致签名计算完全错误。你的当前代码用的是\n,这部分没问题,但要注意不要在字符串拼接时引入多余空格。
测试建议
- 打印每次请求的预签名文本、时间戳、最终URL,对比成功与失败请求的差异,找出规律
- 用火币官方的签名示例(若有)验证你的签名生成逻辑是否正确
备注:内容来源于stack exchange,提问作者Cesar Augusto Carneiro Silva

