
最近折腾了一个加密货币交易 Bot,踩了几个坑,这篇把问题说清楚。
说实话,我受够了凌晨两点盯着 K 线图,咖啡都凉透了,眼巴巴盼着有个小机器人能在我睡着的时候自动抄底。之前在 Binance 上手动交易过几次,有过赚钱的快感,也有过因为调试一个完全无关的脚本而错过突然拉升的懊恼。熟悉这种感觉吗?
那一刻——当我发现自己在刷新 Reddit 帖子的时候眼睁睁看着行情走了 5%——感觉就像 Neo 吞下了红色药丸。我想要逃离那个无休止手动点击的虚拟世界,进入一个代码能 24 小时盯盘、执行简单策略、让我腾出手来做点实际东西的新境界。
于是我踏上了征途:做一个能在便宜 VPS(虚拟专用服务器)上跑的加密交易 Bot,接入主流交易所 API(应用程序接口),实现一个基础的均值回归策略。如果这个能跑通,后面就有底气去试验更复杂的想法了。
最大的"原来如此"不是来自什么 exotic 算法;而是意识到难点根本不在数学——而是在管道工程。一旦你有了可靠的市场数据、干净的下单方式、扎实的错误处理,策略本身其实就是几行逻辑的事。
我最终定了三个核心模块:
ccxt 库从 Binance 拉取 OHLCV 蜡烛图数据。关键在于关注点分离:获取 → 决策 → 执行。当每个模块独立工作正常后,把它们串起来的感觉就像施了个真能生效的魔法。
我的第一版是个单体脚本:获取数据、计算指标、下单、sleep 一分钟——全塞在一个 while True 循环里,零错误处理。大概长这样:
import time, ccxt exchange = ccxt.binance({'enableRateLimit': True})
symbol = 'BTC/USDT'
amount_usd = 20 while True: ohlcv = exchange.fetch_ohlcv(symbol, timeframe='1m', limit=21) closes = [c[4] for c in ohlcv] sma = sum(closes[-20:]) / 20 price = closes[-1] if price < sma * 0.98: # 2% below SMA → buy
exchange.create_market_buy_order(symbol, amount_usd / price) elif price > sma * 1.02: # 2% above SMA → sell (close)
# Oops! No position tracking → we might sell nothing or short!
exchange.create_market_sell_order(symbol, amount_usd / price) time.sleep(60)
踩过的坑:
我把脚本重构为三个明确的函数,加了一个简单的仓位追踪器,把交易所调用包进重试逻辑。改进版如下:
import time, ccxt, logging
from decimal import Decimal, ROUND_DOWN logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s') exchange = ccxt.binance({ 'enableRateLimit': True, 'options': {'defaultType': 'future'} # adjust if you trade spot or futures
}) symbol = 'BTC/USDT'
timeframe = '1m'
lookback = 20 # SMA period
threshold = Decimal('0.02') # 2%
usd_per_trade = Decimal('20')
position = 0 # positive = long, negative = short, 0 = flat
def fetch_sma(): ohlcv = exchange.fetch_ohlcv(symbol, timeframe=timeframe, limit=lookback + 1) closes = [Decimal(str(c[4])) for c in ohlcv] sma = sum(closes[:-1]) / lookback # exclude the forming candle
return sma, closes[-1] def amount_to_trade(price): # Ensure we respect the exchange's step size and min notional
market = exchange.market(symbol) step = Decimal(str(market['precision']['amount'])) min_notional = Decimal(str(market['limits']['cost']['min'])) if market['limits']['cost']['min'] else Decimal('0') raw = (usd_per_trade / price).quantize(step, rounding=ROUND_DOWN) if raw * price < min_notional: raise ValueError(f'Order too small: {raw} {symbol.split("/")[0]} < min_notional') return raw def place_order(side, qty): try: if side == 'buy': order = exchange.create_market_buy_order(symbol, float(qty)) else: order = exchange.create_market_sell_order(symbol, float(qty)) logging.info(f'{side.upper()} order placed: {qty} {symbol.split("/")[0]} @ market') return order except Exception as e: logging.error(f'Order failed: {e}') return None def main(): global position while True: try: sma, price = fetch_sma() signal = Decimal('0') if price < sma * (Decimal('1') - threshold): signal = Decimal('1') # go long
elif price > sma * (Decimal('1') + threshold): signal = Decimal('-1') # go flat (exit long)
if signal == 1 and position <= 0: # enter long
qty = amount_to_trade(price) if place_order('buy', qty): position = float(qty) elif signal == -1 and position > 0: # exit long
qty = amount_to_trade(price) if place_order('sell', qty): position = 0 else: logging.debug(f'No action. SMA={sma:.2f}, price={price:.2f}, position={position}') except ccxt.NetworkError as e: logging.warning(f'Network issue: {e}') except ccxt.ExchangeError as e: logging.error(f'Exchange error: {e}') except Exception as e: logging.exception(f'Unexpected error: {e}') time.sleep(60) # respect rate limits; adjust as needed
if __name__ == '__main__': main()
改动点:
amount_to_trade 严格遵守交易所的步长和最小名义价值,避免那些烦人的"订单太小"拒绝。跑在一个月花 35 块钱的 VPS 上,现在有了一个对睡眠友好的半自动化交易员,每次操作都有日志。已经抓到好几波我刷剧时会错过的快速回归了。
现在骨架搭好了,真正的乐趣才开始。我可以接入均线交叉策略、加个简单的止损,甚至试验机器学习信号——核心循环完全不用动。Bot 把交易从一个紧张刺激肾上腺素飙升的爱好,变成了一场系统性实验:我控制变量,从数据中学习。
更重要的是,它提醒我:自动化不是为了取代直觉,而是为了释放心智带宽,让我能把精力放在策略的"为什么"上,而不是纠结于怎么点按钮。如果你也曾陷在手动盯盘的循环里,不妨试试这个。也许你也会觉得自己躲过了一颗子弹——换我的情况来说,就是做早饭的时候没再错过 3% 的拉升。
拿上面的骨架,把 SMA 换成 RSI 阈值试试,或者加个追踪止损。先在测试网跑,观察日志,看看行情震荡时 Bot 的表现如何。你第一个想改的地方是什么?留个评论或者发条动态——咱们一起把这趟探险继续下去!
祝编码愉快,愿你的交易永远顺风顺水。 🚀