imkeo.app,tp117.app,btp3.app,tp114.app,bit114.app,tp115.app,bit115.app,imkei.app,tp116.app,btp1.app,btp1.app,im777.app,im555.app,im222.app,im666.app,im444.app,tcoken.im,im333.app,im83.app,tp666.app,tp77.app,tp11.app,tp666.app,tp99.app
在加密货币的领域中,币安是一个广为人知的平台。它的详细的 API 文档具有极大的价值。对于那些想要深入地利用币安的功能,尤其是像自动划转这类操作的用户而言,理解并运用 API 文档是最为关键的。许多人在面对它的时候都感到无从下手,然而,本文就如同一本实用的使用指南,将引领你深入其中。
文档的整体概况
币安提供的 API 文档很详细。它涵盖了各种 API 端点以及参数。这些内容既丰富又有条理。我们能够依据自身需求挑选合适的端点,进而执行自动划转等操作。在众多加密货币相关操作当中,这是一种极为高效的途径。比如在日常的交易策略规划中,有许多机构交易者就是凭借这些 API 来提高交易效率的。不同的端点对应着不同的功能,就如同不同的钥匙能打开不同的锁一样。
深入探究文档,实际上就是在了解币安这个平台交易规则的映射情况。它将各种规则和功能转化为技术语言,以便用户能够进行程序对接。其中蕴含着币安的核心技术逻辑,需要我们用心去钻研。
阅读文档的顺序建议
建议从开头开始阅读文档。这并非虚言,因为许多重要的参数解释以及专业术语都在开头部分。若一开始就去寻找 API,看到参数时可能会感到困惑。例如在交易中一些特殊值,不了解其定义就无法正确进行设置。把前面的公共参数理解透彻之后,后面的参数类型大致相似。当初我认识的一位开发者,直接跳到后面去寻找参数,结果错误频繁出现。
花时间在前面部分是值得的。不要急于求成。阅读时可做简单笔记,如同读书时划重点,便于日后查找和回顾。在关键数值或逻辑解释旁标记自己的理解。
工具测试先行
先使用工具进行测试是一个不错的选择。其中,ApiPost 工具就较为适宜。在将参数配置妥当后发送请求,并查看调用结果。这就如同先进行摸底探路一般。许多业余开发者缺乏这样的意识,一上来就开始编写代码,导致错误频出,且难以找到原因。比如在测试一个查询交易记录的 API 时,先通过工具查看结果,若存在问题还能及时对参数进行调整。
先从简单功能开始进行测试,接着逐渐过渡到复杂功能。不要一开始就去尝试难度较高的操作。当简单功能测试通过后,再去挑战复杂功能。这样能够一步一个脚印,稳稳地前进。就如同学走路时先借助学步车,等熟练了之后再自己独立行走。在实践过程中逐步掌握各种 API 的调用方法。
创建API密钥
首先需登录币安账户,接着进入“API 管理”页面来创建新的 API 密钥,此为整个 API 使用过程中的关键步骤。在进行转账等操作时,要添加 IP 地址,这样做是为了保障资金安全,避免资金被窃取。而普通查询操作无需进行此操作。这种双轨制的安全措施充分考量了不同操作的风险程度。
// 测试服务连通性,返回服务时间
https://data-api.binance.vision/api/v3/time
// 测试服务连通性,返回{}
https://data-api.binance.vision/api/v3/ping
生成的 API 秘钥串在接口鉴权方面极为关键。它需在 HTTP 头中通过 X - MBX - APIKEY 字段进行传递。就如同每把钥匙对应着一把锁一样,此秘钥串乃是开启 API 操作的关键钥匙。看似简单的秘钥传递,实际上是安全体系里的重要一环。
代码中的签名处理
币安文档中对签名有专门的介绍,尤其是在基本信息的 SIGNE 部分。下载代码之后,要搜索 HmacSignatureGenerator.java 类,其中的 getSignature 方法很重要。签名是针对传入的所有参数、值以及用“&”拼接的字符串来进行操作的,并且顺序不能混乱。
我曾遇到这个问题,按自己的顺序传值一直失败。之后,我规规矩矩地依照文档的顺序,通过 List 按照文档顺序添加参数,就成功了。这充分表明了按照文档进行操作的必要性,任何细节都不能遗漏。
// 这里以查询接口为例,进行账户参数解释
USER_DATA:表示这个接口需要有效的 API-Key 和签名
API-key:需要放到header中,以 X-MBX-APIKEY 为参数名传递
type:账户类型
startTime,endTime为查询资产快照起始时间,可以不填,默认七天。
limit:天数
recvWindow:这个比较重要,是以你传入的timestamp为起点,计算是否超过有效期,最大设置为60000,就是一分钟内有效
timestamp:签名需要这个参数,且要和 recvWindow 一起判断,有没有超过现在时间点,如果 timestamp+recvWindow<now(),就表示过期了。
signature:注意参数顺序,调用币安的签名函数,对data进行签名。
String data = "type=LIMIT&recvWindow=60000×tamp=1499827319559";
// ApiPost调用信息
https://api.binance.com/sapi/v1/accountSnapshot?type=SPOT&recvWindow=60000×tamp=1499827319559&signature=e335b4e7a2d7cede3246ed0f1915766d3fb8175ae183b09cc3a886aeb5ac256b
编码实践要点
在编码实践里,首先从那些既不需要鉴权也不需要代理的 API 着手。像用于测试服务连通性以及获取服务时间这类简单的操作。接着再去接触需要鉴权和代理的查询类 API。使用查询类 API 时,要开启 API - Key 权限,并且添加 IP 地址。对于提币功能而言,如果不进行这样的操作,就无法使其调通。
值得注意的是,要调通币安的很多操作需要开代理。开了代理之后,还得去查询自己的公网 IP,最好是静态外网 IP。否则,下次再使用时,又需要在币安登记 IP 地址。不过,经过亲身测试,这个过程三分钟也能够完成。虽然这有些繁琐,但是为了提高安全和使用效率,是可以接受的。
package com.binance.connector.client.utils.signaturegenerator;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.codec.binary.Hex;
import com.binance.connector.client.utils.ParameterChecker;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.net.URI;
import java.util.*;
public final class HmacSignatureGenerator implements SignatureGenerator {
private static final String apiKey = "this is my apiKey";
private static final String HMAC_SHA256 = "HmacSHA256";
private String apiSecret = "this is my secretKey"; // 和 apiKey一起生成的
public HmacSignatureGenerator() {
}
public HmacSignatureGenerator(String apiSecret) {
ParameterChecker.checkParameterType(apiSecret, String.class, "apiSecret");
this.apiSecret = apiSecret;
}
public String getSignature(String data) {
byte[] hmacSha256;
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(apiSecret.getBytes(), HMAC_SHA256);
Mac mac = Mac.getInstance(HMAC_SHA256);
mac.init(secretKeySpec);
hmacSha256 = mac.doFinal(data.getBytes());
} catch (Exception e) {
throw new RuntimeException("Failed to calculate hmac-sha256", e);
}
return Hex.encodeHexString(hmacSha256);
}
public static void main(String[] args) {
transferSpotAndFund();
}
/**
* 正式环境:一键划转现货账户和资金账户的某个币种的所有资金
*/
public static void transferSpotAndFund() {
// 1、获取现货账户资金余额
String spotAccountTokenInfo = spotAccountInfo();
// 2、获取资金账户的余额
String fundAccountTokenInfo = fundAccountInfo();
// 3、解析现货账户需要划转的币种的余额
String tokenName = "USDT";
String spotTokenFree = jsonParseSpotAccount(spotAccountTokenInfo, tokenName).split("\\.")[0];
System.out.println(String.format("现货账户转出的币种为:%s, 转出的金额为:%s", tokenName, spotTokenFree));
// 4、解析资金账户需要划转的币种的余额
String fundTokenFree = jsonParseFundAccount(fundAccountTokenInfo, tokenName).split("\\.")[0];
System.out.println(String.format("资金账户转出的币种为:%s, 转出的金额为:%s", tokenName, fundTokenFree));
// 5、对此金额进行划转(0现货账户,1资金账户)
// withdraw(tokenName, spotTokenFree, "0");
// withdraw(tokenName, fundTokenFree, "1");
}
/**
* 解析现货账户的JSON,获取指定币种的余额,没查到则返回 0
*/
public static String jsonParseSpotAccount(String jsonStr, String tokenName) {
String tokenNameKey = "asset"; // 币安返回币种列表的名称key
String tokenFreeKey = "free"; // 币安返回币种列表的余额key
JSONObject jsonObject = JSON.parseObject(jsonStr);
JSONArray balances = jsonObject.getJSONArray("balances");
for (int i = 0; i < balances.size(); i++) {
JSONObject balance = balances.getJSONObject(i);
if (tokenName.equals(balance.getString(tokenNameKey))) {
return balance.getString(tokenFreeKey);
}
}
return "0";
}
/**
* 解析资金账户的JSON,获取指定币种的余额,没查到则返回 0
*/
public static String jsonParseFundAccount(String jsonStr, String tokenName) {
String tokenNameKey = "asset"; // 币安返回币种列表的名称key
String tokenFreeKey = "free"; // 币安返回币种列表的余额key
JSONArray balances = JSON.parseArray(jsonStr);
for (int i = 0; i < balances.size(); i++) {
JSONObject balance = balances.getJSONObject(i);
if (tokenName.equals(balance.getString(tokenNameKey))) {
return balance.getString(tokenFreeKey);
}
}
return "0";
}
/**
* 正式环境:查询每日资产快照 (USER_DATA)
*/
public static void accountSnapshot() {
// 1、构造参数和签名
String type = "SPOT";
String startTime = String.valueOf(System.currentTimeMillis());
String limit = "1";
String recvWindow = "60000";
String timestamp = String.valueOf(System.currentTimeMillis());
String signature;
//TODO 注意:如果修改了上面的参数,需要修改签名,增加此参数,否则签名验证会失效
String data = String.format("type=%s&limit=%s&recvWindow=%s×tamp=%s", type, limit, recvWindow, timestamp);
HmacSignatureGenerator generator = new HmacSignatureGenerator();
signature = generator.getSignature(data);
System.out.println("需要签名的数据:" + data);
System.out.println("时间戳:" + timestamp);
System.out.println("签名:" + signature);
// 2、发送请求
// 构造参数的顺序必须一定,否则会导致签名验证失败
List<NameValuePair> paramList = new ArrayList<>();
paramList.add(new BasicNameValuePair("type", type));
paramList.add(new BasicNameValuePair("limit", limit));
paramList.add(new BasicNameValuePair("recvWindow", recvWindow));
paramList.add(new BasicNameValuePair("timestamp", timestamp));

paramList.add(new BasicNameValuePair("signature", signature));
String url = "https://api.binance.com/sapi/v1/accountSnapshot";
doGet(url, paramList);
}
/**
* 正式环境:提币 (USER_DATA)
*/
public static void withdraw(String coin, String amount, String walletType) {
// 1、构造参数和签名
String network = "TRX";
String address = "this is someone receiveAddress";
String transactionFeeFlag = "true"; // 当站内转账时免手续费, true: 手续费归资金转入方; false: 手续费归资金转出方; . 默认 false.
String recvWindow = "60000";
String timestamp = String.valueOf(System.currentTimeMillis());
String signature;
String data = String.format("coin=%s&network=%s&address=%s&amount=%s&transactionFeeFlag=%s&walletType=%s" +
"&recvWindow=%s×tamp=%s",
coin, network, address, amount, transactionFeeFlag, walletType, recvWindow, timestamp);
HmacSignatureGenerator generator = new HmacSignatureGenerator();
signature = generator.getSignature(data);
// System.out.println("需要签名的数据:" + data);
// System.out.println("时间戳:" + timestamp);
// System.out.println("签名:" + signature);
// 2、发送请求
// 构造参数的顺序必须一定,否则会导致签名验证失败
List<NameValuePair> paramList = new ArrayList<>();
paramList.add(new BasicNameValuePair("coin", coin));
paramList.add(new BasicNameValuePair("network", network));
paramList.add(new BasicNameValuePair("address", address));
paramList.add(new BasicNameValuePair("amount", amount));
paramList.add(new BasicNameValuePair("transactionFeeFlag", transactionFeeFlag));
paramList.add(new BasicNameValuePair("walletType", walletType));
paramList.add(new BasicNameValuePair("recvWindow", recvWindow));
paramList.add(new BasicNameValuePair("timestamp", timestamp));
paramList.add(new BasicNameValuePair("signature", signature));
String url = "https://api.binance.com/sapi/v1/capital/withdraw/apply";
doPost(url, paramList);
}
/**
* 正式环境:现货账户信息 (USER_DATA)
*/
public static String spotAccountInfo() {
// 1、构造参数和签名
String recvWindow = "60000";
String timestamp = String.valueOf(System.currentTimeMillis());
String signature;
//TODO 注意:如果修改了上面的参数,需要修改签名,增加此参数,否则签名验证会失效
String data = String.format("recvWindow=%s×tamp=%s", recvWindow, timestamp);
HmacSignatureGenerator generator = new HmacSignatureGenerator();
signature = generator.getSignature(data);
// System.out.println("需要签名的数据:" + data);
// System.out.println("时间戳:" + timestamp);
// System.out.println("签名:" + signature);
// 2、发送请求
// 构造参数的顺序必须一定,否则会导致签名验证失败
List<NameValuePair> paramList = new ArrayList<>();
paramList.add(new BasicNameValuePair("recvWindow", recvWindow));
paramList.add(new BasicNameValuePair("timestamp", timestamp));
paramList.add(new BasicNameValuePair("signature", signature));
String url = "https://api.binance.com/api/v3/account";
return doGet(url, paramList);
}
/**
* 正式环境:资金账户信息 (USER_DATA)
*/
public static String fundAccountInfo() {
// 1、构造参数和签名
String recvWindow = "60000";
String timestamp = String.valueOf(System.currentTimeMillis());
String signature;
//TODO 注意:如果修改了上面的参数,需要修改签名,增加此参数,否则签名验证会失效
String data = String.format("recvWindow=%s×tamp=%s", recvWindow, timestamp);
HmacSignatureGenerator generator = new HmacSignatureGenerator();
signature = generator.getSignature(data);
// 2、发送请求
// 构造参数的顺序必须一定,否则会导致签名验证失败
List<NameValuePair> paramList = new ArrayList<>();
paramList.add(new BasicNameValuePair("recvWindow", recvWindow));
paramList.add(new BasicNameValuePair("timestamp", timestamp));
paramList.add(new BasicNameValuePair("signature", signature));
String url = "https://api.binance.com/sapi/v1/asset/get-funding-asset";
return doPost(url, paramList);
}
/**
* POST请求
* 执行流程:
*/
public static String doPost(String url, List<NameValuePair> paramList) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("X-MBX-APIKEY", apiKey);
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded");
// 模拟表单
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
httpPost.setEntity(entity);
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
System.out.println("执行结果:" + resultString);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
/**
* GET请求
* 流程:
* 1、URL
* 2、转换为 URIBuilder 用来加参数
* 3、转换为 URI
* 4、构造 HTTPGET 对象并放入 URI
* 5、设置 header 并发送
*/
public static String doGet(String url, List<NameValuePair> paramList) {
// 创建Httpclient对象
CloseableHttpClient httpclient = HttpClients.createDefault();
String resultString = "";
CloseableHttpResponse response = null;
try {
// 2、转换为 URIBuilder 用来加参数
URIBuilder builder = new URIBuilder(url);
builder.addParameters(paramList);
// 3、转换为 URI
URI uri = builder.build();
// 4、构造 HTTPGET 对象并放入 URI
HttpGet httpGet = new HttpGet(uri);
httpGet.setHeader("X-MBX-APIKEY", apiKey);
// 5、执行请求
response = httpclient.execute(httpGet);
// 解析返回状态
if (response != null) {
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
System.out.println("查询结果:" + resultString);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
}
你在使用币安 API 文档时,是否遇到过特殊的问题?是否掌握了一些小技巧?希望你能在评论区将这些分享出来,同时也不要忘记点赞和分享本文。
imkeo.app,tp117.app,btp3.app,tp114.app,bit114.app,tp115.app,bit115.app,imkei.app,tp116.app,btp1.app,btp1.app,im777.app,im555.app,im222.app,im666.app,im444.app,tcoken.im,im333.app,im83.app,tp666.app,tp77.app,tp11.app,tp666.app,tp99.app