最近看到AI自动做加密货币交易比较火,围观地址:https://nof1.ai/

前两天deepseek资金翻倍了,现在稍微有点回撤,但是也是遥遥领先于Gemini和GPT两个亏损将近70%的。大家开玩笑说不愧是专门做量化的公司(幻方)做出来的LLM,不知道和这个有没有关联,不过说不定幻方一开始投入做LLM就是为了用来交易,在这方面还是下了点工夫的。
说到AI自动做交易,当然是离不开交易所的API了,说到加密货币交易所,当然离不开目前最大的CEX币安Binance了。一年前我写了好几篇文章介绍怎么使用币安的API来获取数据和交易,现在趁着重构了博客,把这些文章整合到一起,希望能帮到大家。
有账号和API的可以直接跳过第1部分了,各个部分内容:
- 注册、创建子账户、获取API
- 使用ccxt实例化交易所并获取数据
- 实现现货和合约交易
- 仓位查询和平仓
- 本地使用代理的问题
1.获取API
1.1 注册
币安作为全球最大的加密货币交易所,安全性比起其它交易所肯定是更好的。如果没有币安交易所,可以用我的邀请链接注册:

(吐槽一下Ghost这也能解析居然不能解析我在关于页面的X主页)
(虽然我本来以为看这个文章的都有账号了,但是这一年多里我的博客里币安API的文章几千阅读也有几个用我链接注册的)
注册完成之后登录看到的界面就是这样的:

可以看到我的本金是很少了(本金来源本来就少还玩亏了😭),这个是我自己的原因,之前跑了几个月的自动交易还是不亏不赚,自己上手就直接没了。
1.2 创建子账户
注册完只要完成KYC并启用2FA就可以创建子账户了。借助子账户,用户可以通过多个账户进行交易。子账户可用于分权分责以及管理交易。这里我们就是把资金隔离一下,用子账户的资金专门来跑自动交易。
点击首页右上角的头像然后进入总览,接着就是子账户->账户管理->创建子账户

因为我们不需要登录,创建的时候方便起见选择虚拟邮箱创建,然后输入一个名字

之后可以通过右边的操作来设置一些权限(比如允许合约)。
1.3 API创建
继续来到子账户下面的API管理

然后下一步,选择刚刚创建的子账户邮箱和输入API名字,下一步进行安全验证

然后API就创建好了,权限可以勾上允许现货及杠杆交易,IP访问限制有需要最好也使用以提高安全性(美国IP暂时不能用),可以将两个密钥API Key 和 Secret Key 复制到记事本,一定不能泄露(即使有IP白名单)!

2.交易所实例化和数据获取
2.1 ccxt安装
项目地址:
ccxt是一个用于加密货币交易和电子商务的 JavaScript / Python / PHP / C# 库,支持许多比特币/以太坊等加密货币交易所的 API。
我一般使用python,使用使用pip或conda安装,习惯用uv也行
pip install ccxt
# or
conda install ccxt查看支持的交易所:
import ccxt
print(ccxt.exchanges) # print a list of all available exchange classes2.2 交易所实例初始化
exchange = ccxt.binance({
'apiKey': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'secret': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'timeout': 30000,
'enableRateLimit': True,
'options': {'defaultType': 'future'}
})
exchange.load_markets()这里实例化了一个ccxt.binance类,用到了我们前面创建的API的apiKey和secret,其它几个参数的意思分别是:
'timeout'是请求超时时间,单位是毫秒。如果在这段时间内请求未得到响应,那么将会抛出一个超时错误。'enableRateLimit'是一个布尔值,如果设置为True,那么ccxt库将会尊重交易所的速率限制,避免发送过多的请求。'options'是一个字典,用于设置一些额外的选项。在这里,'defaultType': 'future'表示默认的交易类型是期货或者说合约,如果需要现货交易类型就把值设置为spot。
exchange.load_markets()这个方法,用于从Binance交易所加载所有可用的市场数据。这个方法通常在开始交易之前调用,以确保有最新的市场数据。
2.3 获取K线数据
为了方便对数据进行处理,我们需要用到pandas库,使用命令pip install pandas安装,然后再import pandas as pd引入这个库,之后就可以用pd来调用里面的类和方法了。
BTC_df= pd.DataFrame(exchange.fetch_ohlcv(symbol='BTC/USDT', timeframe='5m', limit=1000))这里使用exchange的fetch_ohlcv()方法来获取我们需要的K线数据,其中用到的参数有:
symbol是str类型,是我们需要获取数据的标的符号,如BTC/USDT标识比特币对USDT的价格。timeframe也是str类型,表示我们需要获取的K线的时间尺度,例如5分钟——5m,一天——1d(默认值为一分钟——1m)。limit是int类型,表示获取数据的条数,上限似乎是1000,这个我没有测试,大家可以试试看。
除此之外还有:
since:int类型,表示开始获取数据的timestamp。params:字典,存放额外的参数,一般用不到。
上面的示例代码就是获取比特币对USDT的最近1000条(没有since参数默认就是最近的limit条数据)5分钟级别K线数据。
得到的每一条数据依次包含以下条目:Timestamp(时间戳), Open(开盘价), High(最高价), Low(最低价), Close(收盘价), Vol(交易量)。
为了方便对这些数据进行处理,我们使用pandas设置一下列名:
BTC_df = pd.DataFrame(exchange.fetch_ohlcv('BTC/USDT', timeframe='5m', limit=1000))
BTC_df.columns = ['Timestamp', 'Open', 'High', 'Low', 'Close', 'Vol']得到的数据就是这样的(去年测的了,这个不是BTC价格):

如果1000条不够用的话,可以使用循环以及since参数获取更前的数据,例如这里我们可以看到在5分钟级别数据上,相邻数据之间Timestamp相差300000,因此我们可以指定since为BTC_df['Timestamp'][0]-1000*300000来获取更早的1000条数据,并和后面的数据拼接起来,代码如下:
COUNT = 3 # 需要获取数据的次数
BTC_df = pd.DataFrame(exchange.fetch_ohlcv(symbol='BTC/USDT', timeframe='5m', limit=1000))
for i in range(COUNT - 1):
BTC_df = pd.concat([pd.DataFrame(exchange.fetch_ohlcv(symbol='BTC/USDT',
timeframe='5m',
since=COIN_df['Timestamp'][0]-1000*300000,
limit=1000)), BTC_df], ignore_index=True)
time.sleep(1)
BTC_df.columns = ['Timestamp', 'Open', 'High', 'Low', 'Close', 'Vol']通过修改变量COUNT的值,就可以获取相应次数的数据。
3.现货和合约交易
前面两部分讲完了API创建和数据获取,接下来就是交易部分了。
3.1 现货账户与合约账户
在上一节我们实例化交易所类时提到了options可选参数中指定了defaultType为future,即合约账户:
exchange = ccxt.binance({
'apiKey': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'secret': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'timeout': 30000,
'enableRateLimit': True,
'options': {'defaultType': 'future'}
})这里的apiKey和secret需要替换成自己创建的API里面的值,其它参数不明白是什么意思的话可以返回上一章看看。
如果想要实例化现货账户的话,需要指定defaultType为spot:
exchange = ccxt.binance({
'apiKey': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'secret': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'timeout': 30000,
'enableRateLimit': True,
'options': {'defaultType': 'spot'}
})3.2 限价委托
现货的限价委托与合约使用的是同一个函数,只不过合约还需要指定杠杆倍数,再讲完市价与限价委托之后再写如何设置杠杆。
exchange.create_limit_buy_order(symbol='JUP/USDT', amount=6, price=0.9)很容易从参数名字看出来它的含义:
symbol就是需要交易的标的符号amount即买入的数量(这个数量不是花费的USDT的数量,而是你买入的币的数量,比如我这里是买入6个JUP,卖出也是同理)price即委托价格
同理卖出的函数为create_limit_sell_order(),参数意义与上面一样。
使用这些函数时需要账户里有足够的金额,且在非只减仓的情况下需要交易金额大于5USDT,否则返回错误代码如下:
ccxt.base.errors.InsufficientFunds: binance Account has insufficient balance for requested action.
正常情况下运行完上面那个限价买入的代码后查看现货交易可以看到相应的委托,可以设置一个较小的委托价格进行测试:

3.3 市价委托
现货与合约市价委托同样使用同一个函数。
exchange.create_market_buy_order(symbol='BTC/USDT', amount=0.01)只需要指定两个参数:
symbol同样是交易的标的符号amount就是交易的数量
市价委托不需要指定价格,如果账户余额足够,执行完这个函数马上就会买入相应数量的仓位,因此这里就不作演示了。
同理卖出函数为create_market_sell_order(),参数同上。
3.4 创建订单
上面的两个函数都是针对特定的委托以及方向使用不同的函数,但是实际上ccxt里面一个函数就可以全部搞定(感觉设计太冗余了)。
def create_order(
symbol: str,
type: OrderType,
side: OrderSide,
amount: float,
price: float = None,
params: Any = {}
) -> Order多了两个参数type和side:
type即委托类型,可以为市价market或限价limitside即方向,可以为买入buy或卖出sellprice默认为None,因为如果是市价委托就可以不指定价格
因此最开始的那个限价买入JUP可以写成下面这样:
exchange.create_order('JUP/USDT', 'limit', 'buy', 6, 0.9)除此之外还有create_market_order()、create_limit_order()函数等,通过上面的解释应该可以从名字就看出来函数是干什么的。
3.5 设置合约杠杆
在使用合约账户进行交易时,如果不指定杠杆倍数,默认会使用你最后一次在图形界面设置的杠杆倍数进行交易,如果需要进行设置的话就需要使用set_leverage(),有两个参数symbol和leverage:即交易的标的符号和杠杆倍数(int类型)。
因此合约开单前最好固定加上set_leverage()设置杠杆倍数:
exchange.set_leverage(10, 'JUP/USDT')
exchange.create_order('JUP/USDT', 'limit', 'buy', 6, 0.9)4.仓位状态获取及平仓
上一部分介绍了如何使用币安API进行交易,在交易成功之后,我们会希望获取当前仓位的状态来判断是否平仓,现在将介绍获取当前仓位的状态以进一步进行平仓操作。
4.1 查询余额
balance = exchange.fetch_balance({'type': 'future'})['total']['USDT']这里使用的是函数fetch_balance,传递了一个dict作为参数,type为future表示查询的是期货(合约)账户的余额,如果要查询现货账户那就设置为spot,看过前面一章的同学应该就知道这个。然后再加上下标['total']以及['USDT']表示查询USDT余额,其它币种就把下标改成对应的。
4.2 查询仓位
开了个仓然后查询仓位看了一下返回数据:
position = exchange.fetch_positions([COIN])参数为需要查询的仓位币种的列表,比如[BTC/USDT, ETH/USDT],打印出返回结果可以看到返回的是一个字典列表:
[
{
'info':
{
'symbol': 'JUPUSDT',
'positionAmt': '6',
'entryPrice': '0.9947',
'breakEvenPrice': '0.99519735',
'markPrice': '0.99363270',
'unRealizedProfit': '-0.00640380',
'liquidationPrice': '0',
'leverage': '1',
'maxNotionalValue': '8000000.0',
'marginType': 'isolated',
'isolatedMargin': '5.96820000',
'isAutoAddMargin': 'false',
'positionSide': 'BOTH',
'notional': '5.96179620',
'isolatedWallet': '5.97460380',
'updateTime': '1713106952248',
'isolated': True, 'adlQuantile': '2'
},
'id': None,
'symbol': 'JUP/USDT:USDT',
'contracts': 6.0,
'contractSize': 1.0,
'unrealizedPnl': -0.0064038,
'leverage': 1.0,
'liquidationPrice': None,
'collateral': 5.9682,
'notional': 5.9617962,
'markPrice': 0.9936327,
'entryPrice': 0.9947,
'timestamp': 1713106952248,
'initialMargin': 5.9617962,
'initialMarginPercentage': 1.0,
'maintenanceMargin': 0.089426943,
'maintenanceMarginPercentage': 0.015,
'marginRatio': 0.015,
'datetime': '2024-04-14T15: 02: 32.248Z',
'marginMode': 'isolated',
'marginType': 'isolated',
'side': 'long',
'hedged': False,
'percentage': -0.1,
'stopLossPrice': None,
'takeProfitPrice': None
}
]里面的key比较多,需要用到的几个比较重要的有:
contracts:合约数量contractSize:每个合约大小(应该与杠杆倍数有关)unrealizedPnl:浮动盈亏leverage:杠杆大小collateral:保证金notional:仓位大小(保证金加上浮动盈亏)markPrice:市价entryPrice:进场价side:方向(多long、空short)
使用position[0]["key"]来获取这些值,key就是需要获取的数据键名,需要带双引号,如"side"。
4.3 平仓
在币安API的文档中并没有显式的给出平仓的接口,但是平仓实际上就相当于开一个与当前仓位相反的仓位,比如做多(买入)一定数量的COIN,平仓就是卖出相应数量的这个COIN,因此只需要反方向开一个数量与开仓相同的仓位就可以平仓了(也可以与开仓数量不同,比如平掉一半)。
比如我先市价开一个多仓:
exchange.set_leverage(5, "BTC/USDT") # 设置杠杆倍数
exchange.create_market_order("BTC/USDT", "buy", 6, params={'reduceOnly': False})之后想要平仓的话,需要先知道上次开仓买入的数量,这就需要用到上面的仓位查询接口了:
position = exchange.fetch_positions(["BTC/USDT"])
last_amount = position[0]['contracts'] * position[0]['contractSize']使用合约数量乘以每个合约的大小就可以得到上次开仓的数量,然后反方向开仓即可平仓
exchange.create_market_order("BTC/USDT", "sell", last_amount, params={'reduceOnly': True})也可以平掉一半设置amount为0.5*last_amount,平仓时可以设置只减仓为True,防止数量太少(<5)无法交易成功。
5.本地代理
在我的前几部分关于连接交易所获取数据时,由于我是直接在我购买的国外服务器上跑的,所以不用担心网络问题。但是考虑到大部分人还是在自己本地跑,需要使用代理,所以我最近有时间还是试了试在本地使用代理的注意事项。(以下是本站迁移前的评论截图)

首先如果仅仅是获取数据的话,可以不用apiKey,直接指定代理就行,初始化交易所实例加上proxies字段:
exchange = ccxt.binance({
'timeout': 60000,
'proxies': {
'http': '127.0.0.1:7890',
'https': '127.0.0.1:7890',
}
})
exchange.load_markets()我之前使用的代理软件是是clash,代理端口默认是7890,clash-verge默认是7897,v2ray这些的话我就不知道了需要自己去查一查。
如果是在本地跑的话,ip白名单就只能设置成机场的ip了,还是不建议这样,最好买个VPS。
如果有什么疑问想联系我,请看关于页面的说明。
