Loading ...

高年化CTA策略搬运

Author: 科莫多的巨蜥, Created: 2021-02-06 17:23:48, Updated:

1. 缘起 看到backtrader健身房里有两个年轻人,发了个帖子。比特币交易策略还附源码的,啪的一下,很快啊。我就点进来了。链接在这里(https://community.backtrader.com/topic/2271/bitcoin-trading-strategy/7)点进来以后自然是,不讲武德。左手一个ctrl+c,右手一个ctrl+v就借鉴(抄袭)了一下。还不戳。下面是自己的backtrader上的代码。回测结果和报告上差不多,就不贴图了(https://ethz.ch/content/dam/ethz/special-interest/mtec/chair-of-entrepreneurial-risks-dam/documents/dissertation/master%20thesis/Master_Thesis_Gl%C3%BCcksmann_13June2019.pdf

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)
import backtrader as bt
import pandas as pd
from backtrader.indicators import MovAv
import backtrader.indicators as btind
from datetime import datetime, timedelta
class BollingerBandsW(bt.Indicator):
    alias = ('BBW',)
    lines = ('bbw',)
    params = (('period', 20), ('devfactor', 2.0), ('movav', MovAv.Simple),)

    plotinfo = dict(subplot=True,
                    bbw=dict(_name='bbw', color='green', ls='--', _skipnan=True), )

    def __init__(self):
        self.boll = bt.indicators.BollingerBands(self.datas[0], period=self.params.period,
                                                 devfactor=self.params.devfactor)
        self.lines.bbw = (self.boll.top - self.boll.bot) / self.boll.mid


class VolatilityLevel(bt.Indicator):
    alias = ('VolatilityLevelIndex',)

    lines = ('bbw', 'VLI_fast', 'VLI_top', 'VLI_slow',)

    params = (
        ('period', 20),
        ('devfactor', 2.0),
        ('movav', MovAv.Simple),
        ('fast_period', 20),
        ('slow_period', 100),
    )

    plotinfo = dict(subplot=True,
                    bbw=dict(_name='bbw', color='green', ls='--', _skipnan=True),
                    VLI_fast=dict(_name='VLI_fast', color='red', ls='--', _skipnan=True),
                    VLI_top=dict(_name='VLI_top', color='lightred', ls='--', _skipnan=True),
                    VLI_slow=dict(_name='VLI_slow', color='blue', ls='--', _skipnan=True),
                    )

    def __init__(self):
        self.lines.bbw = BollingerBandsW(self.data, period=self.params.period, devfactor=self.params.devfactor)
        self.lines.VLI_fast = self.p.movav(self.lines.bbw, period=self.p.fast_period)
        self.lines.VLI_slow = self.p.movav(self.lines.bbw, period=self.p.slow_period)
        std = bt.ind.StdDev(self.lines.bbw, period=self.p.slow_period)

        self.lines.VLI_top = self.lines.VLI_slow + 2 * std


class HFT(bt.Strategy):
    params = (
        ('highest_high', 20),  # 需要优化的参数1,短期均线窗口
        ('StdDev', 100),  # 需要优化的参数2,长期均线窗口
    )

    def log(self, txt, dt=None):
        ''' Logging function fot this strategy'''
        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))

    def __init__(self):
        self.dataclose = self.datas[0].close
        self.datavolume = self.datas[0].volume
        self.datalow = self.datas[0].low
        self.sma_veryfast = btind.MovingAverageSimple(self.dataclose, period=10)
        self.sma_fast = btind.MovingAverageSimple(self.dataclose, period=20)
        self.sma_mid = btind.MovingAverageSimple(self.dataclose, period=50)
        self.sma_slow = btind.MovingAverageSimple(self.dataclose, period=100)
        self.sma_veryslow = btind.MovingAverageSimple(self.dataclose, period=200)

        # BollingerBandsWidth = upperband - lowerband/middleband.
        self.bbw = BollingerBandsW()
        self.boll = btind.BollingerBands()
        self.std = btind.StdDev(self.bbw.l.bbw, period=self.params.StdDev)
        self.lines_bbw = (self.boll.l.top - self.boll.l.bot) / self.boll.l.mid

        self.volatility_level = VolatilityLevel()
        self.low_volatility_level = self.volatility_level.l.VLI_fast < self.volatility_level.l.VLI_slow
        self.high_volatility_level = self.volatility_level.l.VLI_fast > self.volatility_level.l.VLI_slow
        self.extreme_volatility_level = self.bbw.l.bbw > self.volatility_level.l.VLI_top
        self.vol_condition = (btind.MovingAverageSimple(self.datavolume, period=10) >
                              btind.MovingAverageSimple(self.datavolume, period=50))
        self.crossdown_boll_top = bt.ind.CrossDown(self.dataclose, self.boll.top)
        self.crossup_boll_bot = bt.ind.CrossUp(self.dataclose, self.boll.bot)

        self.highest_high = btind.Highest(self.dataclose, period=self.params.highest_high)
        self.low_of_last_candle = self.datalow[0]
        self.close_of_price = self.dataclose[0]
        self.stop_win = None
        self.stop_loss = None
        self.order = None

    @staticmethod
    def get_params():
        return dict()

    def notify_order(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            return

        if order.status == order.Completed:
            if order.isbuy():
                self.stop_loss = self.stop_loss if self.stop_loss else 0.05 * self.dataclose[-1]

                take_profit = order.executed.price * (1.0 + self.stop_loss)
                sl_ord = self.sell(exectype=bt.Order.Stop,
                                   price=order.executed.price * (1.0 - self.stop_loss))
                sl_ord.addinfo(name="Stop")
                tkp_ord = self.buy(exectype=bt.Order.Limit,
                                   price=take_profit)
                tkp_ord.addinfo(name="Prof")

                if self.stop_win:
                    self.sell(price=(order.executed.price * (1 + self.stop_win)),
                              exectype=bt.Order.Limit)

        self.order = None

    def start(self):
        self.counter = 0
        print('START')

    def prenext(self):
        self.counter += 1
        print('prenext len %d - counter %d' % (len(self), self.counter))

    def next(self):
        if self.order:
            return
        self.log('当前价格, %.2f' % self.dataclose[0])
        if self.crossdown_boll_top and self.vol_condition:
            if self.close_of_price > self.sma_fast:
                if self.bbw.bbw < self.volatility_level.l.VLI_top:
                    if self.low_volatility_level:
                        if self.sma_mid > self.sma_veryslow:
                            self.buy()
                            #self.log('买入, %.2f' % self.dataclose[0])
                    else:
                        self.buy()
                        #self.log('买入, %.2f' % self.dataclose[0])
            elif self.sma_slow > self.sma_veryslow:
                self.buy()
                #self.log('买入, %.2f' % self.dataclose[0])
                self.stop_loss = self.low_of_last_candle

        if self.crossup_boll_bot and self.vol_condition:
            self.close() # 这个被我注释了
            self.stop_loss = None
            self.stop_win = None
            portfolio_value = self.broker.get_value()
            if portfolio_value !=0:
                trade_profit = self.broker.get_value([self.data]) / portfolio_value
            else:
                trade_profit = 0
            if trade_profit > 0.03:
                self.stop_win = 0.01
            elif trade_profit > 0.20:
                self.stop_win = 0.15
            elif trade_profit > 0.25:
                self.stop_win = 0.20
            elif trade_profit > 0.30:
                self.stop_win = 0.25
            elif trade_profit > 0.35:
                self.stop_win = 0.30
            elif trade_profit > 0.40:
                self.stop_win = 0.35

        if self.crossup_boll_bot and self.vol_condition:
            self.sell()
            #self.log('卖出, %.2f' % self.dataclose[0])
            self.stop_loss = self.highest_high


def ak_get(df):
    df.index = pd.to_datetime(df["date"], format='%Y-%m-%d %H:%M:%S')
    df.index.name = "datetime"
    df = df.drop('hold', axis=1, inplace=False)
    df = df.drop('date', axis=1, inplace=False)
    df[['open', 'high', 'low', 'close', 'volume']] = df[['open', 'high', 'low', 'close', 'volume']].astype("float")
    return df


if __name__ == '__main__':
    cerebro = bt.Cerebro()
    # 下面使用AK的期货数据
    cerebro = bt.Cerebro()

    #!!bm密钥
    broker_config = {
        # 'verbose': True,
        'apiKey': 'tOpE94M1cu62ONaVTPeK33lu',
        'secret': 'xx',   # 我的bitmex密钥
    }
    broker = bt.brokers.CCXTBroker(exchange='bitmex', currency='BTC/USD', config=broker_config)
    cerebro.setbroker(broker)

    hist_start_date = datetime.utcnow() - timedelta(minutes=1000)
    data = bt.feeds.CCXT(
        exchange='binance',
        symbol='BTC/USDT',
        timeframe=bt.TimeFrame.Minutes,
        fromdate=hist_start_date,
        historical='True',
        compression = 5,
        )

    # Add the Data Feed to Cerebro
    cerebro.adddata(data)
    # cerebro.resampledata(data, timeframe=bt.TimeFrame.Minutes)
    cerebro.addstrategy(HFT)
    cerebro.run()

2. 实操 我练回测练的正起劲呢。突然有两个年轻人跑过来说,你在backtrader上练回测练死劲了不管用。我说我这个有用,backtrader是阔以搞实盘的,阔以四两拨千斤。Github上是有它和ccxt的brunch的。很快啊,我就实盘了。但我大意了啊,没有闪,这个东西啊,它BUG奇多。难搞无比 。于是我又花时间把代码改到了FMZ上。(回测结果也差不多,还阔以,就是有亿点慢) 代码:

# !/usr/local/bin/python
# -*- coding: UTF-8 -*-
'''backtest
start: 2016-11-01 00:00:00
end: 2020-11-04 05:20:00
period: 1h
basePeriod: 15m
exchanges: [{"eid":"Binance","currency":"BTC_USDT","stocks":0}]
'''
import numpy as np
import talib
import pandas as pd

exchange.SetContractType('BTC_USDT')
# ------------------------------ 策略部分开始 --------------------------
print(exchange.GetAccount())  # 调用一些接口,打印其返回值。
initAccount = exchange.GetAccount()
print(exchange.GetTicker())
exchange.SetMaxBarLen(2000)  # 设置K线数量,最大一次两千根
stop_loss = None
stop_win = None  # 这两个应该是全局变量
wait_for = False  # 空头的开仓等待期
amount = 5000    # 1000个合约
# exchange.SetMarginLevel(3)    # 加x3杠杆
def BollingerBandsW(record):
    bbw_list = []
    boll = TA.BOLL(record)
    upLine = boll[0][21:]
    midLine = boll[1][21:]
    downLine = boll[2][21:]
    # try:
    for i in range(0, len(upLine) - 1):
        bbw = (upLine[-i] - downLine[-i]) / midLine[-i]
        print(bbw)
        bbw_list.append(bbw)
    # except TypeError:
    #   pass
    return bbw_list

def VolatilityLevel(record):
    alias = ('VolatilityLevelIndex',)
    lines = ('bbw', 'VLI_fast', 'VLI_top', 'VLI_slow',)
    params = (
        ('period', 20),
        ('devfactor', 2.0),
        ('fast_period', 20),
        ('slow_period', 100),
    )

    bbw = BollingerBandsW(record)
    VLI_fast = TA.MA(bbw, 200)[201:]
    VLI_slow = TA.MA(bbw, 1000)[1001:]
    VLI_top = []
    std = talib.STDDEV(np.array(bbw), 1000)[1001:]
    for i in range(0, len(VLI_slow) - 1):
        VLI_top.append(VLI_slow[-i] + 2 * std[-i])

    return bbw, VLI_fast, VLI_top, VLI_slow


# def log(str_):
  #  print(str_)
def onTick(e):
    global stop_loss
    global stop_win
    global wait_for
    ticker = exchange.GetTicker()
    account = exchange.GetAccount()
    position = exchange.GetPosition()
    kliens = exchange.GetRecords(PERIOD_H1)  # 五分钟的K线
    if len(kliens) < 1500:
        #Log('total bars: ', len(kliens))
        return
    bbw = BollingerBandsW(kliens)
    # B = VolatilityLevel(kliens)
    boll = TA.BOLL(kliens, 20, 2)
    upLine = boll[0]
    midLine = boll[1]
    downLine = boll[2]
    dataclose = kliens.Close
    datavolume = kliens.Volume
    datalow = kliens.Low
    sma_veryfast = TA.MA(dataclose, 10)
    sma_mid = TA.MA(dataclose, 50)
    sma_slow = TA.MA(dataclose, 100)
    sma_veryslow = TA.MA(dataclose, 200)
    sma_fast = TA.MA(dataclose, 20)
    volatility_level = VolatilityLevel(kliens)  # bbw,VLI_fast,VLI_top,VLI_slow
    VLI_fast, VLI_top, VLI_slow = volatility_level[1], volatility_level[2], volatility_level[3]
    ma_vol_10 = TA.MA(datavolume, 10)
    ma_vol_50 = TA.MA(datavolume, 50)
    low_volatility_level = VLI_fast[-1] < VLI_slow[-1]  # 这个要做成计数的两个都是列表
    high_volatility_level = VLI_fast[-1] > VLI_slow[-1]
    extreme_volatility_level = bbw > VLI_top  # 这个还暂时不清楚
    low_of_last_candle = kliens[-1]["Low"]  # 最新的K线?的最低价
    vol_condition = (ma_vol_10[-1] > ma_vol_50[-1])
    crossdown_boll_top = dataclose[-1] < upLine[-1] and dataclose[-2] > upLine[-2]  # 代替了crossdown
    # crossup_boll_bot = CrossUp(self.dataclose, self.boll.bot)
    crossup_boll_bot = dataclose[-1] > upLine[-1] and dataclose[-2] < upLine[-2]  # 代替了crossup
    highest_high = TA.Highest(kliens, 20, "Close")

    if exchange.GetOrders() != []:  # 如果有委托
        return

    if position == []:
        ismid_ = sma_mid[-1] < sma_slow[-1] and sma_slow[-1] < sma_veryslow[-1]
        exchange.SetDirection("buy")
        if crossdown_boll_top and vol_condition and position == []:
            if dataclose[-1] > sma_fast[-1]:
                if bbw[-1] < VLI_top[-1]:
                    if low_volatility_level:
                        if sma_mid[-1] > sma_veryslow[-1]:
                            exchange.Buy(ticker.Sell, amount)  # 市价单买入一手
                            stop_loss = low_of_last_candle * 0.95
                            return
                    elif not ismid_:  # 可以再加一个high_vol的条件
                        exchange.Buy(ticker.Sell, amount)  # 这些条件可以注释掉
                        stop_loss = low_of_last_candle * 0.95
                        return
                elif sma_slow[-1] > sma_veryslow[-1]:
                    exchange.Buy(ticker.Sell, amount)
                    stop_loss = low_of_last_candle * 0.95
                    return
                #stop_loss = low_of_last_candle * 0.95

        # if crossup_boll_bot and vol_condition:
        # 上面尝试开仓多头
        exchange.SetDirection("sell")
        if crossup_boll_bot and bbw[-1] < VLI_slow[-1]:
            # wait for 应该怎么判断
            wait_for = True
            if sma_slow[-1] < sma_mid[-1] and sma_slow[-2] > sma_mid[-2]:
                exchange.Sell(ticker.Buy, amount)  # 市价单卖出一手
                wait_for = False
                return

        if wait_for and sma_slow[-1] < sma_mid[-1] and sma_slow[-2] > sma_mid[-2] and bbw[-1] < VLI_top[-1]:
            exchange.Sell(ticker.Buy, amount)  # 市价单卖出一手
            wait_for = False
            return 
        # 上面尝试开仓空头
        # 上面尝试开仓空头

    portfolio_value = initAccount.Balance #account["Stocks"]
    # average_price = initAccount
    if position != None:    # position != []
        average_price = position.price
        trade_profit = average_price/portfolio_value
    else:
        trade_profit = 0
    # if trade_profit > 0.03:
    # stop_win = 0.01
    if trade_profit > 0.20:
        stop_win = 0.15
    elif trade_profit > 0.25:
        stop_win = 0.20
    elif trade_profit > 0.30:
        stop_win = 0.25
    elif trade_profit > 0.35:
        stop_win = 0.30
    elif trade_profit > 0.40:
        stop_win = 0.35

    if position !=None and len(position) > 0 and position[0].Type == 0:  # 表示在多仓的时候
        close_long = False  # 一开始假设不平仓
        if trade_profit < 0:
            if stop_loss != None:
                if ticker.Buy < stop_loss:
                    close_long = True
        elif stop_win != None:
            if trade_profit <= stop_win:
                close_long = True

        if (crossup_boll_bot and vol_condition) or close_long:  # close 的第一个条件
            exchange.SetDirection("closebuy")
            exchange.Sell(ticker.Buy, position[0].Amount)  # 全平
            stop_loss = None 
            stop_win = None  # 置零
            return

    if position !=None and len(position) > 0 and position[0].Type == 1:  # 表示在空头仓时
        """
        0-10 : 20HH
        10-15 : 10HH
        15-25 : 20HH
        """
        close_sell = False
        highest_high_20 = TA.Highest(kliens[-2:], 20, "Low")
        highest_high_5 = TA.Highest(kliens[-2:], 5, "Low")
        highest_high_10 = TA.Highest(kliens[-2:], 10, "Low")
        if trade_profit < 0:
            # 止损
            if ticker.Sell > highest_high_20:
                close_sell = True

        elif stop_win!= None:
            if (trade_profit < stop_win):
                # 止盈条件
                close_sell = True
            elif trade_profit < 0.1 and ticker.Sell < highest_high_20:
                close_sell = True
            elif trade_profit > 0.1 and trade_profit < 0.15 and ticker.Sell < highest_high_10:
                close_sell = True
            elif trade_profit > 0.15 and trade_profit < 0.25 and ticker.Sell < highest_high_5:
                close_sell = True

        if close_sell:
            exchange.SetDirection("closesell")
            exchange.Buy(ticker.Sell, position[0].Amount)  # 全平
            stop_loss = None 
            stop_win = None  # 置零
            return
    if position !=None and position != []:
        if stop_win!=None:
            if trade_profit < stop_win:
                exchange.SetDirection("closebuy")
                #log("止损")
                exchange.Sell(ticker.Buy, position[0].Amount)  # 多头止损
                stop_loss = None
                stop_win = None
                return
        if stop_loss!=None:
            if dataclose[-1] > stop_loss:  # 空头止损
                exchange.SetDirection("closesell")
                #log("止损")
                exchange.Buy(ticker.Sell, position[0].Amount)
                stop_loss = None
                stop_win = None
                return
"""
    if crossup_boll_bot and vol_condition:
        exchange.SetDirection("sell")
        exchange.Sell(ticker.Buy, 1)
        stop_loss = highest_high
"""

def main():
    while True:
        onTick(exchange)
        Sleep(1000)
呃呃,谨慎实盘。但是,因为实盘机器人刚开始的时候要填充指标。所以阔能这个机器人要亿段时间才能开始交易。具体策略组合看文档(在开头有link)吧。

3. 亿点思考 劝各位年轻人,耗子尾汁,好好反思。(强行点题)早日做出无纸印钞机。 And 各位 新年快落 /(ㄒoㄒ)/~~


More

冰糖葫芦娃 crossdown_boll_top = dataclose[-1] < upLine[-1] and dataclose[-2] > upLine[-2] # 代替了crossdown # crossup_boll_bot = CrossUp(self.dataclose, self.boll.bot) crossup_boll_bot = dataclose[-1] > upLine[-1] and dataclose[-2] < upLine[-2] # 代替了crossup 这里不对吧,应该是上穿upline,下穿downline才对

syue 运行部了啊

科莫多的巨蜥 托管者要python环境,并且有第三方库