In this episode, we’re deploying FreqTrade, an open-source CryptoCurrency trading bot, wrapping it up in a nice multi-tabbed webUI called Muximux, and managing it all with Traefik web proxy with real Let’s Encrypt auto-renewing SSL certificates! I’ll also take you through the config basics, what a simple strategy file looks like, and how crypto exchange order books work. Check it out! After the video, I’ve got your example code below!
Let’s start with a basic docker-compose.yml for your FreqTrade instance:
version: '3.6'
networks:
  proxy:
    external: true
services:
  freqtrade:
    image: freqtradeorg/freqtrade:develop
    networks:
      - proxy
    volumes:
      - "./user_data:/freqtrade/user_data"
      - /etc/timezone:/etc/timezone:ro
    # Default command used when running `docker compose up`
    command: >
      trade
      --logfile /freqtrade/user_data/logs/freqtrade.log
      --config /freqtrade/user_data/config.json
      --strategy BinHV45
      --db-url sqlite:///user_data/tradesv3.sqlite
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints:
          - node.role == manager
      restart_policy:
        condition: on-failure
        delay: 5s
      labels:
         - 'traefik.enable=true'
         - 'traefik.http.routers.binhv45.tls=true'
         - 'traefik.http.routers.binhv45.rule=Host(`binhv45.dmz.yourdomain.com`)'
         - 'traefik.http.services.binhv45.loadbalancer.server.port=8080'
If you don’t already have a Traefik proxy set up, you’ll want to refer to my video and example code for that. It’ll make your life so much easier!
Next, let’s take a look as a sample config.json file for FreqTrade. This will vary based on your config, but this is a good starting point:
{
    "max_open_trades": 25,
    "stake_currency": "USDT",
    "stake_amount": 50,
    "tradable_balance_ratio": 0.99,
    "fiat_display_currency": "USD",
    "timeframe": "1m",
    "dry_run": true,
    "cancel_open_orders_on_exit": false,
    "unfilledtimeout": {
        "buy": 10,
        "sell": 30
    },
    "bid_strategy": {
        "price_side": "bid",
        "ask_last_balance": 0.0,
        "use_order_book": false,
        "order_book_top": 1,
        "check_depth_of_market": {
            "enabled": false,
            "bids_to_ask_delta": 1
        }
    },
    "ask_strategy": {
        "price_side": "ask",
        "use_order_book": false,
        "order_book_min": 1,
        "order_book_max": 1,
        "use_sell_signal": true,
        "ignore_roi_if_buy_signal": true
    },
    "download_trades": true,
    "exchange": {
        "name": "binanceus",
        "key": "",
        "secret": "",
        "ccxt_config": {"enableRateLimit": true},
        "ccxt_async_config": {
            "enableRateLimit": true,
            "rateLimit": 200
        },
        "pair_whitelist": [
          "BTC/USDT",
          "ETH/USDT",
          "ETC/USDT",
          "LTC/USDT",
          "XLM/USDT",
          "ADA/USDT"
        ],
        "pair_blacklist": [
          "XRP/USD",
          "USDT/USD",
          "USDC/USD",
          "EUR/USD"
        ]
    },
    "pairlists": [
      {
//          "method": "StaticPairList"}
          "method": "VolumePairList",
          "number_assets": 50,
          "sort_key": "quoteVolume",
          "refresh_period": 1800
      },
      {"method": "AgeFilter", "min_days_listed": 10},
      {
        "method": "RangeStabilityFilter",
        "lookback_days": 5,
        "min_rate_of_change": 0.01,
        "refresh_period": 1440
      }
     ],
    "edge": {
        "enabled": false,
        "process_throttle_secs": 3600,
        "calculate_since_number_of_days": 7,
        "allowed_risk": 0.01,
        "minimum_winrate": 0.60,
        "minimum_expectancy": 0.20,
        "min_trade_number": 10,
        "max_trade_duration_minute": 1440,
        "remove_pumps": false
    },
    "telegram": {
        "enabled": false,
        "token": "",
        "chat_id": ""
    },
    "api_server": {
        "enabled": true,
        "enable_openapi": true,
        "listen_ip_address": "0.0.0.0",
        "listen_port": 8080,
        "verbosity": "info",
        "jwt_secret_key": "somethingrandom",
        "CORS_origins": [],
        "username": "api",
        "password": "api"
    },
    "initial_state": "running",
    "forcebuy_enable": false,
    "internals": {
        "process_throttle_secs": 5
    }
}
Next, you’ll need a strategy file. This is the example we’re using in the video, but I also recommend you check out the examples on FreqTrade’s own GitHub page. The below is BinHV45.py stored in /user_data/strategies/ folder:
# --- Do not remove these libs ---
from freqtrade.strategy.interface import IStrategy
from typing import Dict, List
from functools import reduce
from pandas import DataFrame
import numpy as np
# --------------------------------
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
def bollinger_bands(stock_price, window_size, num_of_std):
    rolling_mean = stock_price.rolling(window=window_size).mean()
    rolling_std = stock_price.rolling(window=window_size).std()
    lower_band = rolling_mean - (rolling_std * num_of_std)
    return rolling_mean, lower_band
class BinHV45(IStrategy):
    minimal_roi = {
    #    "0": 0.0125
      "0": 0.99
    }
    stoploss = -0.05
    timeframe = '1m'
    trailing_stop = True
    trailing_only_offset_is_reached = True
    trailing_stop_positive_offset = 0.00375  # Trigger positive stoploss once crosses above this percentage
    trailing_stop_positive = 0.00175 # Sell asset if it dips down this much
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        mid, lower = bollinger_bands(dataframe['close'], window_size=40, num_of_std=2)
        dataframe['mid'] = np.nan_to_num(mid)
        dataframe['lower'] = np.nan_to_num(lower)
        dataframe['bbdelta'] = (dataframe['mid'] - dataframe['lower']).abs()
        dataframe['pricedelta'] = (dataframe['open'] - dataframe['close']).abs()
        dataframe['closedelta'] = (dataframe['close'] - dataframe['close'].shift()).abs()
        dataframe['tail'] = (dataframe['close'] - dataframe['low']).abs()
        return dataframe
    def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                dataframe['lower'].shift().gt(0) &
                dataframe['bbdelta'].gt(dataframe['close'] * 0.008) &
                dataframe['closedelta'].gt(dataframe['close'] * 0.0175) &
                dataframe['tail'].lt(dataframe['bbdelta'] * 0.25) &
                dataframe['close'].lt(dataframe['lower'].shift()) &
                dataframe['close'].le(dataframe['close'].shift())
            ),
            'buy'] = 1
        return dataframe
    def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        no sell signal
        """
        dataframe.loc[:, 'sell'] = 0
        return dataframe


 
																								 
																								 
																								
wonderful work !!!
only can you please show us how to implement and activate the strategy ?
many thanks
In config.json look for “dry_run”: true,
change to false
Correct! Just be aware of two things:
Most importantly, know that switching dry_run: false; means you are now trading with real money, no simulation! Be sure.. extra sure.. that you are comfortable with what your strategy will do. I’m not responsible if your strategy loses your money!!
Second, in order for it to do anything, you must have the api key and secret populated, on an exchange supported by FreqTrade, and money in that account.
Cheers, and have fun!! Please let us know how your strategy performs!
10 / 10