You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

火币API签名验证频繁失败问题求助

火币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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.13 19:39:51