Saber2pr's Blog

搭建freqtrade加密货币量化交易机器人

2024年会是币圈超级大牛市,有 BTC 减半和美元降息加持,如果你是程序员,量化交易机器人赶紧学起来!

前提,你熟悉云服务器ssh连接终端的用法,和基础的python语法(有js基础也可)

本文采用python量化机器人框架 freqtrade 开始操作!

freqtrade官方文档

官方文档内容过多,请先跟随本文入门阅读,后续深入学习可参考官方文档~

1. 准备云服务器 docker 环境

这里以云服务器选择 ubuntu 系统开始,先配置 docker 环境,依次执行以下命令:

sudo apt update

sudo apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

sudo apt update

sudo apt install docker-ce

2. 安装 freqtrade 和初始化项目

ssh登录你的服务器后,在终端内按以下命令开始:

建议使用 vscode 的 ssh remote container 开发更方便!

mkdir ft_userdata

cd ft_userdata/

curl https://raw.githubusercontent.com/freqtrade/freqtrade/stable/docker-compose.yml -o docker-compose.yml

docker compose pull

docker compose run --rm freqtrade create-userdir --userdir user_data

执行完后,会创建出来 docker-compose.yml文件、user_data目录

然后初始化配置文件 config.json

docker compose run --rm freqtrade new-config --config user_data/config.json

会连续问你几个问题,选择 Y/N 回车即可,按下面的日志示例操作:

telegram 机器人创建token以及telegram安装请自行google搜索查阅

? Do you want to enable Dry-run (simulated trades)? Yes ## 是否使用模拟账户操作,就是模拟交易。设置为false则会用你账户里的真实余额交易
? Please insert your stake currency: USDT ## 货币单位 这里用默认的USDT
? Please insert your stake amount (Number or 'unlimited'): unlimited ## 最大开仓占比,unlimited就是所有金额都会被机器人拿去用。也可以指定如 100,就是只给机器人 100 USDT 去交易
? Please insert max_open_trades (Integer or -1 for unlimited open trades): 3 ## 最大开仓数量,就是可以买几种币。3就是机器人最多只买3种货币。
? Time Have the strategy define timeframe. ## 是否用策略中的时间线,这里用默认的。一般策略中的时间线都是最好的。
? Please insert your display Currency (for reporting): USD ## 机器人展示收益用的单位,可以设置为 CNY,会用人民币表示
? Select exchange okx ## 选择交易所,这里示例选择okx欧易
? Do you want to trade Perpetual Swaps (perpetual futures)? Yes ## 是否启用合约模式。设置false为现货交易。
? Do you want to enable Telegram? Yes ## 启用 telegram 机器人
? Insert Telegram token # telegram 机器人 token
? Insert Telegram chat id # telegram 机器人 id
? Do you want to enable the Rest API (includes FreqUI)? Yes # web管理控制台页面
? Insert Api server Listen Address (0.0.0.0 for docker, otherwise best left untouched) 0.0.0.0 # web管理控制台ip端口
? Insert api-server username freqtrader # web管理控制台账号
? Insert api-server password ****** # web管理控制台密码

2.1 配置文件说明:

交易机器人的配置文件最重要,说明一下:

{
    // 交易模式,这里有三个选项,spot (默认,现货交易),future(期货交易,可以进行做空交易), margin(当前不可用)
    "trading_mode": "spot",
    // 当交易模式不是spot时,需要设置这里,当交易模式为future时,要设置为isolated, 当交易模式为margin时设置为cross(暂时不可用)
    // "margin_mode": "isolated",
    // 最大的单数
    "max_open_trades": 5,
    // 用于交易的加密货币
    "stake_currency": "USDT",
    // 机器人可用的金额,可以开启多个机器人用于交易,平横策略间的收益差异
    "stake_amount": 200,
    // 允许机器人交易的账户总余额的比率
    "tradable_balance_ratio": 1,
    // 从虚拟币转换为哪种法定货币,这里时美金,如果转换报错,可以不设置
    // "fiat_display_currency": "USD",
    // 运行模式,虚拟运行,还是真实运行,true是虚拟运行
    "dry_run": true,
    // 要使用的时间帧 1m,5m, 15m, 30m, 1h  ...,这个歌配置可以在这里缺省,并在策略文件进行再次配置,也就是策略文件的配置强于这里
    "timeframe": "3m",
    // 虚拟运行的总金额
    "dry_run_wallet": 1000,
    // 在退出时取消未结订单
    "cancel_open_orders_on_exit": true,
    // 必须的配置
    "unfilledtimeout": {
        // 只要有信号,机器人将等待未完成的挂单完成多长时间(以分钟或秒为单位),之后订单将被取消并以当前(新)价格重复。
        "entry": 10,
        // 只要有信号,机器人将等待未履行的退出订单完成多长时间(以分钟或秒为单位),之后订单将被取消并以当前(新)价格重复
        "exit": 30
    },
    // 交易所设置
    "exchange": {
        "name": "okx",
        // API key
        "key": "",
        // API secret
        "secret": "",
        // ccxt的一些配置,在国外一般用不到,在国内可以在这里配置代理
        "ccxt_config": {},
        "ccxt_async_config": {},
        // 交易对白名单
        // 1INCH/USDT是现货交易
        // 1INCH/USDT:USDT是期货交易
        // 在这里可以使用通配符进行批量的配置,不必列出所有交易对
        // 查看交易所支持的交易对,命令:freqtrade list-markets -c config.json 或者是 freqtrade list-pairs -c config.json 
        "pair_whitelist": [
            "1INCH/USDT:USDT",
            "ALGO/USDT:USDT"
        ],
        // 黑名单
        "pair_blacklist": [
            // 黑名单通配符设置,白名单同理
            "*/BNB"
        ]
    },
    "entry_pricing": {
        // 必须配置,选择机器人应查看价差的一侧以获得进入率。默认same 还有 ask, bid, other
        "price_side": "same",
        // 允许使用订单簿输入中的费率进行输入。缺省值为true
        "use_order_book": true,
        // 机器人将使用订单簿“price_side”中的前 N ​​个汇率来进入交易。即,值为 2 将允许机器人选择Order Book Entry中的第二个条目。
        "order_book_top": 1,
        // 必须配置,插值投标价格。
        "price_last_balance": 0.0,

        "check_depth_of_market": {
            // 如果Order Book中的买单和卖单有差异,则不要进入。
            "enabled": false,
            // 订单簿中的买单与卖单的差额比例。值低于 1 表示卖单规模较大,而值大于 1 表示买单规模较大
            "bids_to_ask_delta": 1
        }
    },
    "exit_pricing": {
        // 选择机器人应查看的价差一侧以获得退出率。默认same
        "price_side": "other",
        "use_order_book": true,
        "order_book_top": 1
    },
    // 定义要使用的一个或多个配对列表。
    "pairlists": [
        // 这里的方法有很多, [点击查看详情](https://www.freqtrade.io/en/stable/plugins/#pairlists-and-pairlist-handlers)
        // 大部分默认情况下都是使用StaticPairList
        {
            "method": "StaticPairList"
        }
    ],
    // 后面还可以添加frequi的配置,以及telegrambot的配置
    "bot_name": "",
    "force_entry_enable": true,
    "initial_state": "running",
    "internals": {
        "process_throttle_secs": 5
    }
}

config配置里,最重要的是dryrun、tradingmode。exchange里要配一下交易所的api key和secret,okx可以在app上的API里创建apiKey。

这里放一下我自己的config文件,是合约交易类型:

{
    "tradable_balance_ratio": 0.99,
    "fiat_display_currency": "CNY",
    "stake_amount": "unlimited",
    "stake_currency": "USDT",
    "amend_last_stake_amount": true,
    "dry_run_wallet": 1600,
    "max_open_trades": 10,
    "dry_run": false,
    "cancel_open_orders_on_exit": false,
    "trading_mode": "futures",
    "margin_mode": "isolated",
    "unfilledtimeout": {
        "entry": 10,
        "exit": 10,
        "exit_timeout_count": 0,
        "unit": "minutes"
    },
    "entry_pricing": {
        "price_side": "other",
        "use_order_book": true,
        "order_book_top": 1,
        "price_last_balance": 0.0,
        "check_depth_of_market": {
            "enabled": false,
            "bids_to_ask_delta": 1
        }
    },
    "exit_pricing": {
        "price_side": "other",
        "use_order_book": true,
        "order_book_top": 1
    },
    "exchange": {
        "name": "okx",
        "key": "",
        "secret": "",
        "password": "",
        "ccxt_config": {},
        "ccxt_async_config": {},
        "pair_blacklist": [
            "(BTCUSDT_.*|ETHUSDT_.*)",
            "(GT|HT)/.*",
            "(WBTC|BSV|BTCDOM|DEFI)/.*",
            ".*(_PREMIUM|BEAR|BULL|DOWN|HALF|HEDGE|UP|[1235][SL])/.*",
            "(AUD|BRZ|CAD|CHF|EUR|GBP|HKD|IDRT|JPY|NGN|RUB|SGD|TRY|UAH|USD|ZAR|UST)/.*",
            "(BUSD|CUSDT|DAI|PAX|PAXG|SUSD|TUSD|USDC|USDT|VAI|USDN)/.*",
            "(ACM|AFA|ALA|ALL|APL|ASR|ATM|BAR|CAI|CITY|FOR|GOZ|IBFK|LEG|LOCK-1|NAVI|NOV|OG|PFL|PSG|ROUSH|STV|TH|TRA|UCH|UFC|YBO)/.*"
        ]
    },
    "pairlists": [
        {
            "method": "VolumePairList",
            "number_assets": 20,
            "sort_key": "quoteVolume",
            "min_value": 0,
            "refresh_period": 86400,
            "lookback_days": 1
        },
        {
            "method": "AgeFilter",
            "min_days_listed": 10
        },
        {
            "method": "PrecisionFilter"
        },
        {
            "method": "PriceFilter",
            "low_price_ratio": 0.01
        },
        {
            "method": "SpreadFilter",
            "max_spread_ratio": 0.002
        },
        {
            "method": "RangeStabilityFilter",
            "lookback_days": 3,
            "min_rate_of_change": 0.02,
            "refresh_period": 1440
        },
        {
            "method": "ShuffleFilter",
            "seed": 42
        }
    ],
    "telegram": {
        "enabled": true,
        "token": "",
        "chat_id": "",
        "keyboard": [
            [
                "/daily",
                "/status table",
                "/count"
            ],
            [
                "/profit",
                "/performance",
                "/balance"
            ],
            [
                "/status",
                "/show_config",
                "/whitelist"
            ],
            [
                "/logs",
                "/reload_config",
                "/help"
            ]
        ]
    },
    "edge": {
        "enabled": false,
        "process_throttle_secs": 3600,
        "calculate_since_number_of_days": 14,
        "allowed_risk": 0.01,
        "stoploss_range_min": -0.01,
        "stoploss_range_max": -0.1,
        "stoploss_range_step": -0.01,
        "minimum_winrate": 0.60,
        "minimum_expectancy": 0.20,
        "min_trade_number": 10,
        "max_trade_duration_minute": 1440,
        "remove_pumps": false
    },
    "api_server": {
        "enabled": false,
        "listen_ip_address": "127.0.0.1",
        "listen_port": 8080,
        "verbosity": "error",
        "enable_openapi": false,
        "jwt_secret_key": "",
        "ws_token": "",
        "CORS_origins": [],
        "username": "",
        "password": ""
    },
    "bot_name": "freqtrade",
    "initial_state": "running",
    "force_entry_enable": true,
    "internals": {
        "process_throttle_secs": 5
    }
}

3. 编写配置交易策略

在 user_data/strategies 目录下,会给你一个默认的策略文件,内容简单说明一下:

简化代码

class SampleStrategy(IStrategy):
    INTERFACE_VERSION = 3 # 版本为3的才可以做空

    can_short: bool = False # 是否可以做空
    # 止盈
    minimal_roi = {
        "60": 0.01,
        "30": 0.02,
        "0": 0.04
    }
    # 止损
    stoploss = -0.10
    # 移动止损
    trailing_stop = False
    # 用哪个时间线的k线
    timeframe = '5m'
    # 设置指标
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        return dataframe
    # 入场
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                (qtpylib.crossed_above(dataframe['rsi'], self.buy_rsi.value))
            ),
            'enter_long'] = 1 # 开仓信号,做多

        dataframe.loc[
            (
                (qtpylib.crossed_above(dataframe['rsi'], self.short_rsi.value))
            ),
            'enter_short'] = 1 # 开仓信号,做空

        return dataframe
    # 出场
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                (qtpylib.crossed_above(dataframe['rsi'], self.sell_rsi.value)) # rsi穿过sell_rsi
            ),

            'exit_long'] = 1 # 平仓信号,平多

        dataframe.loc[
            (
                (qtpylib.crossed_above(dataframe['rsi'], self.exit_short_rsi.value)) # rsi穿过exit_short_rsi
            ),
            'exit_short'] = 1 # 平仓信号,平空

        return dataframe

这是示例的策略,不一定好用,社区有很多网站可以找到策略,直接复制过来可以用了。

启动项目

打开 docker.compose.yml文件:

---
version: '3'
services:
  freqtrade:
    image: freqtradeorg/freqtrade:stable # 官方镜像
    restart: unless-stopped
    container_name: freqtrade
    volumes:
      - "./user_data:/freqtrade/user_data"
    ports:
      - "127.0.0.1:8080:8080"
    command: >
      trade
      --logfile /freqtrade/user_data/logs/freqtrade.log # 输出日志的文件
      --db-url sqlite:////freqtrade/user_data/tradesv3.sqlite # 数据库
      --config /freqtrade/user_data/config.json # 机器人配置文件
      --strategy SmartTA # 策略文件class名

这里重点关注--strategy参数,后面的是策略代码里的class名,不是文件名

终端执行:

docker compose up -d

启动,然后在 user_data/logs/freqtrade.log 文件里可以看到启动日志了。telegram机器人也会打印日志出来。

回测

最后说一下回测,freqtrade提供了回测功能,这个功能,我只能说它只能验证一下策略文件代码逻辑是否有bug,回测的收益结果和正常去实测跑差距还是比较明显的。建议先实际环境dry-run一段时间稳定了再开真实账户。

推荐阅读

如果你想要深入研究量化领域,读下这几篇文章吧:

  1. freqtrade策略E0V1E支撑位解读
  2. Freqtrade如何正确DCA