使用 Python 在本地实现 Bitfinex 借贷自动化:从小额到稳定运行的工程指南
Bitfinex lending automation 指的是在 Bitfinex 的 P2P 借贷市场中用程序自动完成资金借出、利率调整、到期续借等一系列操作。这类工具最常见的部署方式是放到云服务器上保持长期运行但如果你管理的资金规模不大、策略也不复杂完全可以把它运行在自己的 PC 上而不是租一台服务器。这篇文章会从 Bitfinex 借贷市场的基本机制讲起逐步完成环境准备、最小脚本实现、参数调整、运行验证和问题排查最后给出本地稳定运行的一整套工程建议。这套方案的核心特点是脚本和 API Key 都留在本地设备上不依赖远程机器通过计划任务或开机自启保持进程可用用完整的日志和状态文件保证重启后不误操作。适合已经掌握 Python 基础、想用自己电脑管理小额闲置资金的开发者也适合想理解“本地化交易自动化”设计思路的读者。1. 先想清楚自动化在 Bitfinex 借贷市场里到底能做什么做技术实现之前需要先理解业务场景。Bitfinex 的借贷市场不只适用于机构普通用户也可以把持有的资产放出去获得资金使用费。自动化要做的不是“预测行情”而是把重复操作从人工步骤变成程序步骤。1.1 借贷市场的最小概念在 Bitfinex 上借贷市场是一个资金供求双方直接撮合的市场。需求方通常是想加杠杆的交易者供给方则是你这样的资金提供者。你可以挂出一笔资金订单指定金额、期限和期望利率如果有交易者愿意接受这笔订单就会成交资金进入借贷关系到期后按约定利率返还。一个借贷挂单通常需要四个核心字段字段作用示例币种借出哪种资产USD、BTC、ETH 等金额借出多少数量1000期限借出天数30 天利率期望获得的资金费率0.0001这看起来和限价挂单很像。因此自动化程序的首要任务就是替用户完成“看余额、定价、挂单”这一步。之后还要处理更麻烦的问题订单没成交怎么办、到期后要不要重新挂单、程序重启后会不会重复下单。1.2 自动化的核心能力与边界一个完整的本地出借自动化程序至少应该包含以下几个模块读取钱包余额找出可借出的资产数量。获取当前市场行情或者根据配置的固定利率生成挂单价格。提交借贷挂单。记录本地订单状态避免重复提交。定期检查未成交订单和已成交订单。出错时写日志并在下一轮自动重试。自动化能提升的是执行效率和纪律性。它可以做到每 60 秒检查一次余额也可以做到在挂单成交后自动把到期资金重新投入市场。但它不能保证收益。市场利率可能下降订单可能长时间不成交交易者可能提前偿还这些都属于正常市场波动。任何宣传“自动出借稳赚不赔”的方案都不可信。注意所有自动化策略本质上都是把人的决策规则翻译成代码所以策略本身的漏洞也会被代码放大。首次运行务必使用小额资金。1.3 PC 与服务器成本、隔离、维护三个维度标题强调运行在 PC 而不是服务器这背后是三类工程取舍。第一是成本。云服务器按年付费即便是低配实例也需要维护公网 IP、系统补丁和监控告警。如果账户资金规模很小服务器租金会吃掉不少收益。PC 的最大优势是边际成本为零毕竟电脑平时也在使用。第二是安全隔离。API Key 放在服务器上意味着 Key 的物理位置在第三方机房。服务器一旦被入侵脚本配置和密钥可能一起泄露。运行在个人电脑上时Key 只存在于自己的设备上泄露面更小。代价是电脑本身也需要做好系统安全、锁屏和防病毒。第三是稳定性。服务器最大的优势是稳定机房电力、网络和散热都比家庭环境可靠。PC 可能被关机、睡眠、断网、断电甚至因为系统更新而重启。这不是“本地方案比服务器方案更好”而是“本地方案是否可用取决于你有没有能力补上稳定性短板”。维度PC 本地运行云服务器运行成本低复用日常电脑持续产生租用费用密钥位置本地设备远端机器暴露面更大持续运行受关机、睡眠、断电影响通常可长期运行网络稳定性家庭宽带、Wi-Fi 波动数据中心网络更稳定维护方式手动开机、手动更新需要远程登录配置适合场景小规模、低频策略大规模、高可用要求2. 本地运行环境用什么组合最省事实现本地自动化不需要复杂的分布式架构。一个 Python 脚本、一个配置文件、一个日志目录足以跑起最小闭环。但环境准备阶段有三个地方容易踩坑版本选择、API Key 权限、配置文件组织。2.1 运行环境与版本选择推荐使用 Python 3.9 以上版本主要原因是requests、python-dotenv等依赖在较新版本中维护良好类型注解和dataclass也能让脚本更清晰。操作系统可以是 Windows 10/11、macOS 或 Linux 桌面版只要支持 Python 即可。先创建项目目录和虚拟环境mkdir bitfinex-lending-local cd bitfinex-lending-local python -m venv .venvWindows 下激活虚拟环境.venv\Scripts\activatemacOS 或 Linux 下激活虚拟环境source .venv/bin/activate然后安装依赖pip install requests python-dotenvrequests负责 HTTP 请求python-dotenv负责读取本地环境变量。由于脚本会长期运行建议把依赖清单固定到requirements.txt方便以后在一台新电脑上还原环境。2.2 在 Bitfinex 后台申请 API Key在 Bitfinex 官方网站登录账户后进入 API Keys 页面创建新 Key。创建过程中需要选择权限这里有一个非常重要的原则只勾选自动化真正需要的权限不要勾选提现权限。推荐权限组合权限项是否需要原因读取钱包余额需要获取可用资金读取订单需要检查已有挂单提交资金订单需要实现自动出借取消资金订单建议处理撤单重挂场景提现不需要防止脚本或恶意代码动资产创建完成后页面只会完整显示一次 API Secret。务必把它保存到本地密码管理器或.env文件中。如果 Secret 丢失只能重新生成 Key。2.3 项目目录与配置管理为了让脚本容易维护建议按下面的结构组织项目bitfinex-lending-local/ ├─ .env ├─ .gitignore ├─ requirements.txt ├─ config.json ├─ bitfinex_client.py ├─ lending_bot.py ├─ main.py └─ logs/.env保存敏感信息内容如下BITFINEX_API_KEY你的API_KEY BITFINEX_API_SECRET你的API_SECRETconfig.json保存策略参数{ currency: USD, rate: 0.0001, period: 30, amount_ratio: 0.9, check_interval_seconds: 60, dry_run: true }.gitignore至少要忽略.env、logs/和__pycache__/避免密钥被提交到公开仓库.env logs/ __pycache__/把密钥和策略分离是最基础的安全习惯。.env只负责密钥config.json只负责策略参数代码不硬编码任何账户信息。3. 用 Python 把最小出借流程跑起来环境准备好之后就可以写核心代码。这一节给出的代码是结构示意重点在于展示“签名请求、读取余额、提交挂单、主循环”四个环节如何串联。具体 API 端点和字段的准确写法落地前一定要对照 Bitfinex 官方 API 文档确认。3.1 封装 Bitfinex 私有 API 签名请求Bitfinex 的私有接口需要签名认证。客户端类的主要职责是构造请求头并把业务接口的调用封装成简单方法。import hashlib import hmac import json import time from urllib.parse import urljoin import requests API_BASE https://api.bitfinex.com class BitfinexAuthClient: def __init__(self, api_key: str, api_secret: str): self.api_key api_key self.api_secret api_secret def _signed_headers(self, path: str, body: dict): nonce str(int(time.time() * 1000)) body_json json.dumps(body, separators(,, :)) # 注意Bitfinex 的签名拼接方式在 v1/v2 中并不完全相同。 # 示例使用常见的 v2 私有接口签名结构实际接入以官方文档为准。 signing_string /api path nonce body_json signature hmac.new( self.api_secret.encode(utf-8), signing_string.encode(utf-8), hashlib.sha384, ).hexdigest() return { Content-Type: application/json, bfx-nonce: nonce, bfx-apikey: self.api_key, bfx-signature: signature, } def post(self, path: str, body: dict): headers self._signed_headers(path, body) url urljoin(API_BASE, path) resp requests.post(url, headersheaders, jsonbody, timeout30) resp.raise_for_status() return resp.json()这里的签名核心是nonce。nonce必须保证单调递增通常用毫秒时间戳生成。如果同一毫秒内有多个请求或者本地系统时间回拨接口可能拒绝请求。实际项目里可以考虑用自增序列号或加锁保证nonce不重复。3.2 读取可用余额读取余额的私有接口可能返回钱包数组每个钱包包含账户类型、币种、总余额、可用余额等字段。下面代码按“数组字段顺序”方式读取是因为部分版本接口返回结构更接近数组而非对象。def get_funding_available(client: BitfinexAuthClient, currency: str) - float: path /v2/auth/r/wallets wallets client.post(path, {}) for wallet in wallets: # 常见字段顺序type, currency, balance, available wallet_type wallet[0] wallet_currency wallet[1] if wallet_type funding and wallet_currency currency.upper(): return float(wallet[3]) return 0.0这段代码只关心funding类型钱包因为出借资金通常来自资金账户。如果你的币种在exchange钱包里需要先手动转入funding钱包或者在程序里增加转账逻辑。第一次实现时建议手动转账减少程序复杂度。3.3 提交借贷挂单提交挂单时需要指定币种、数量、利率和期限。下面代码演示了一个最小提交函数。def submit_funding_offer( client: BitfinexAuthClient, currency: str, amount: float, rate: float, period: int, ): path /v2/auth/w/funding/offer/submit body { type: FRAME, symbol: ff{currency.upper()}, amount: str(amount), rate: str(rate), period: period, } return client.post(path, body)这里的rate单位需要以官方文档为准。有的接口要求十进制小数有的要求基于百分比扩展后的整数amount也可能要求字符串类型。如果接口返回参数错误优先检查这两个字段的格式。3.4 用主循环把流程串起来主循环负责周期性执行“查余额、算利率、提交挂单”。为了防止程序在运行多个轮次后重复挂单必须在提交前检查当前是否已经有未成交的借贷挂单。import logging import time logging.basicConfig( levellogging.INFO, format%(asctime)s %(levelname)s %(message)s, ) logger logging.getLogger(lending) class LendingBot: def __init__(self, client: BitfinexAuthClient, config: dict): self.client client self.config config def has_open_offer(self, currency: str) - bool: # 读取当前未成交资金订单这里省略具体接口调用和字段解析 # 建议实现调用查询资金订单接口返回列表中如果存在相同币种订单则返回 True return False def run_once(self): currency self.config[currency] available get_funding_available(self.client, currency) if available 0: logger.info(no available balance for %s, currency) return if self.has_open_offer(currency): logger.info(open offer already exists, skip submit) return amount round(available * self.config[amount_ratio], 4) rate self.config[rate] if self.config.get(dry_run, False): logger.info(dry-run: would submit offer amount%s rate%s, amount, rate) return result submit_funding_offer( self.client, currencycurrency, amountamount, raterate, periodself.config[period], ) logger.info(offer submitted: %s, result) def loop(self): while True: try: self.run_once() except Exception: logger.exception(run_once failed) time.sleep(self.config[check_interval_seconds])run_once设计成单轮执行好处是方便测试。loop只负责循环调用和异常兜底。最关键的防御逻辑是has_open_offer这一项不实现脚本重启后很可能重复挂单。4. 参数和状态管理自动化脚本最容易错的地方代码能跑通只是第一步。参数设计不合理、请求频率过高、状态记录缺失才是在真实运行中最常见的失败原因。4.1 利率参数要防止“挂不出去”和“收益被吃”利率是自动化策略里风险最大的参数。如果利率设置过高订单可能长期不成交如果设置过低虽然容易成交但资金使用收益会被压缩。更危险的是如果对rate的理解有误可能提交一个远高于市场价格的订单造成不必要的资金占用。参数含义初学建议设置过大的表现设置过小的表现rate期望利率先使用市场参考利率订单很难成交成交快但收益偏低period借贷期限30 天资金长期锁定到期频繁需要重新挂单amount_ratio可用资金使用比例0.9可能没有预留手续费资金利用率下降不要直接写死一个利率就上线。更稳妥的做法是第一周用 dry-run 模式记录“如果当时挂单会不会成交”再把历史市场利率拉出来对照。4.2 轮询间隔和接口频控本地脚本和服务器脚本一样也会触发交易所的频率限制。如果check_interval_seconds设置成 1 秒脚本每秒钟都去查询钱包和未成交订单很容易被接口限流。推荐策略查询类请求间隔不低于 30 秒。提交、撤单等写操作间隔不低于 10 秒。如果接口返回429或Too Many Requests立即增加重试等待时间。不要让多个实例同时运行。频控问题一旦触发日志里往往不会直接提示“你没有权限”而是出现请求超时或状态码 429。排查时要优先看日志里的时间间隔而不是只盯着业务结果。4.3 状态记录重启之后不重复挂单脚本重启是常态。手动更新代码、电脑重启、进程崩溃后重新拉起这些情况都会发生。如果状态只保存在内存里重启后脚本就“失忆”了。推荐用本地 JSON 文件记录已提交订单{ currency: USD, last_offer_id: 123456789, open_offers: [ { offer_id: 123456789, amount: 900.0, rate: 0.0001, period: 30, submit_time: 2025-01-01T10:00:00Z } ] }每次提交前读取这个文件。如果发现存在相同币种的open_offers就不重复提交而是先查询交易所那边的真实状态。只有确认未成交订单已经不存在或已经取消时才提交新订单。注意本地记录只是辅助手段永远以交易所接口返回的订单状态为准。两者不一致时要优先信任交易所的数据并修正本地记录。5. 从小额到稳定运行怎么验证和排查很多人写好脚本后直接设置成大额资金运行这是最危险的做法。正确顺序是先只读验证再用小额走通订单最后观察几天再放大金额。5.1 先做只读检查在config.json中把dry_run设置为true脚本不会真正提交订单只输出逻辑结果。运行命令python main.py --dry-run预期输出类似这样具体字段会随脚本实现不同2025-06-01 10:00:00 INFO no available balance for USD或者2025-06-01 10:00:00 INFO dry-run: would submit offer amount900.0 rate0.0001如果在这里已经看到异常比如 API Key 权限不足、余额读取为 0就不要继续往下走先把问题解决掉。5.2 用最小金额走通真实订单把dry_run改为false并把钱包里的资金换成最小测试数量。假设你准备借出 USD可以先转 10 USD 到funding钱包然后运行脚本。验证点包括日志中是否出现offer submitted并且返回了订单 ID。Bitfinex 页面或 API 查询中是否能看到这笔挂单。挂单的状态是ACTIVE还是立即成交。脚本下一次轮询时has_open_offer是否能正确识别已有订单避免重复提交。这一阶段不要使用全部余额也不要使用高利率。目标是验证 API 调用链路、字段格式和状态判断逻辑全部正确。5.3 观察日志和恢复能力小额真实订单跑通后建议连续运行 3 到 7 天重点观察以下场景电脑睡眠或锁屏后脚本是否继续运行。网络断线恢复后脚本能否自动重试。接口返回错误时日志是否足够定位问题。订单到期后脚本是否会重新挂单。如果三天内没有出现重复挂单、异常退出和频控问题再考虑逐步增加金额。每次增加金额后也要再观察至少一天。6. 本地运行的排错地图本地运行最大的问题不是代码逻辑复杂而是环境变化太多系统时间不对、家庭网络波动、电脑睡眠、API Key 权限配置错误。以下是一张可以直接对照的排错表。问题现象常见原因检查方式解决方案请求返回认证失败本地系统时间不同步、nonce 重复查看系统时间检查签名串生成逻辑开启自动时间同步同一毫秒内加锁或用递增序列号返回 permission deniedAPI Key 缺少资金订单权限登录 Bitfinex 后台查看 Key 权限重新生成 Key只勾选必要权限返回 invalid symbol 或 invalid amount币种符号或字段格式错误打印请求 body对照官方文档字段示例修正 symbol 前缀和 amount 的字符串格式请求超时或 429家庭网络不稳定或轮询太频繁观察日志中的请求间隔增加check_interval_seconds加入指数退避重试脚本运行几小时后退出电脑睡眠或电源管理关闭了进程查看系统事件日志和进程日志关闭休眠使用任务计划程序配置开机自启重复挂单状态记录没有持久化或逻辑失效查看本地 JSON 和交易所订单列表实现has_open_offer检查以交易所状态为准余额一直为 0资金在 exchange 钱包而不是 funding 钱包登录页面检查钱包类型手动把资金转入 funding 钱包更复杂的“崩溃后无法恢复”问题建议遵循下面这个排查顺序确认输入是否正确。检查config.json里的币种、利率、金额比例。确认文件路径和命名是否正确。确保.env位于脚本启动目录下。确认依赖版本是否匹配。requests版本过低或 Python 版本过高都可能出现兼容问题。确认配置是否生效。dry_run修改后是否重启了脚本。确认权限、端口、网络、环境变量。家庭网络是否允许访问交易所 API。查看日志中的明确异常。不要只看最后几行要查看异常发生前的上下文。确认是脚本问题还是交易所接口限制。对比官方 API 文档中的字段和状态码。7. 本地部署的工程化建议自启、日志、安全和回滚当脚本从“临时测试”变成“长期运行”后就需要像对待生产环境一样对待这台 PC。下面几条建议可以明显提高稳定性。7.1 开机自启和禁止睡眠笔记本如果合上盖子脚本就会暂停。建议在运行期间把电源计划设置为“从不睡眠”至少在使用自动化脚本期间要这样做。Windows 下可以用“任务计划程序”创建开机自启动任务触发条件选择“计算机启动时”操作指向虚拟环境中的 Python 和main.py。macOS 可以使用launchdLinux 桌面可以用桌面环境自带的“启动应用程序”功能。自启不是重点重点是自启之后要能在日志里看到启动时间。没有日志的自启崩溃后很难定位。7.2 日志轮转和异常通知长期运行的脚本日志文件会不断增长。建议使用logging.handlers.RotatingFileHandler把日志写入logs/lending.log并限制单文件大小。import logging from logging.handlers import RotatingFileHandler file_handler RotatingFileHandler( logs/lending.log, maxBytes5 * 1024 * 1024, backupCount3, encodingutf-8, ) logging.basicConfig( levellogging.INFO, format%(asctime)s %(levelname)s %(message)s, handlers[file_handler], )异常通知可以在except分支中发送邮件或即时消息提醒。这里不需要引入复杂组件只要在logger.exception后面加一个notify()函数把异常标题和日志摘要发出去即可。通知实现可以后续再补重点是先保证日志完整。7.3 API Key 和配置安全本地运行不等于内部安全。如果操作系统被植入恶意软件.env文件里的密钥依然可能被读取。因此API Key 只保留读取与资金订单相关权限绝不开启提现权限。定期轮换 API Key比如每 90 天重新生成一次。不要把.env文件放在桌面或公共目录建议放在项目根目录并设置访问权限。不要在日志里打印完整签名、API Secret 或请求头。不要在远程桌面、聊天工具中发送 Key 内容。密钥泄露的第一道防线是权限设计第二道防线是轮换机制。权限越小泄露后的损失就越可控。7.4 风控和回滚自动化脚本必须有“急停开关”。最简单的实现是在config.json中增加enabled字段{ enabled: false, currency: USD, rate: 0.0001, period: 30, amount_ratio: 0.9, check_interval_seconds: 60 }main.py启动时检查enabled为false时直接退出if not config.get(enabled, False): logger.info(bot is disabled, exit) return这样遇到市场异常、API 异常或策略需要调整时不需要删除 Key也不需要改代码只需把enabled改为false并重启脚本。代码版本管理也很重要。不要直接在运行目录里改代码而是把代码放到 Git 仓库每次改动打一个 tag。如果新策略有问题可以快速回退到上一个 tag 对应的版本。本地运行自动化最大的优势不是性能而是门槛低、密钥不离开设备。但这也意味着稳定性责任全部落在这台 PC 上开机状态、网络质量、系统时间、日志保留和异常恢复每一样都需要自己负责。一个比较稳妥的上线路径是先用 dry-run 跑一周观察日志再用最小金额跑通真实订单确认状态记录和重复挂单防护有效后再逐步增加资金。下一步可以继续扩展多币种支持、基于市场最高利率的动态定价、到期前撤单重挂以及把状态存储从 JSON 升级为 SQLite。每一个方向都会让这个本地小工具越来越像一个严肃的个人交易系统。