Exemplo n.º 1
0
class TurtleSignalStrategy(CtaTemplate):
    """"""
    author = "中科云集"

    entry_window = 20
    exit_window = 10
    atr_window = 20
    fixed_size = 1

    entry_up = 0
    entry_down = 0
    exit_up = 0
    exit_down = 0
    atr_value = 0

    long_entry = 0
    short_entry = 0
    long_stop = 0
    short_stop = 0

    parameters = ["entry_window", "exit_window", "atr_window", "fixed_size"]
    variables = ["entry_up", "entry_down", "exit_up", "exit_down", "atr_value"]

    def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
        """"""
        super(TurtleSignalStrategy, self).__init__(cta_engine, strategy_name,
                                                   vt_symbol, setting)

        self.bg = BarGenerator(self.on_bar)
        self.am = ArrayManager()

    def on_init(self):
        """
        Callback when strategy is inited.
        """
        self.write_log("策略初始化")
        self.load_bar(20)

    def on_start(self):
        """
        Callback when strategy is started.
        """
        self.write_log("策略启动")

    def on_stop(self):
        """
        Callback when strategy is stopped.
        """
        self.write_log("策略停止")

    def on_tick(self, tick: TickData):
        """
        Callback of new tick data update.
        """
        self.bg.update_tick(tick)

    def on_bar(self, bar: BarData):
        """
        Callback of new bar data update.
        """
        self.cancel_all()

        self.am.update_bar(bar)
        if not self.am.inited:
            return

        self.entry_up, self.entry_down = self.am.donchian(self.entry_window)
        self.exit_up, self.exit_down = self.am.donchian(self.exit_window)

        if not self.pos:
            self.atr_value = self.am.atr(self.atr_window)

            self.long_entry = 0
            self.short_entry = 0
            self.long_stop = 0
            self.short_stop = 0

            self.send_buy_orders(self.entry_up)
            self.send_short_orders(self.entry_down)
        elif self.pos > 0:
            self.send_buy_orders(self.long_entry)

            sell_price = max(self.long_stop, self.exit_down)
            self.sell(sell_price, abs(self.pos), True)

        elif self.pos < 0:
            self.send_short_orders(self.short_entry)

            cover_price = min(self.short_stop, self.exit_up)
            self.cover(cover_price, abs(self.pos), True)

        self.put_event()

    def on_trade(self, trade: TradeData):
        """
        Callback of new trade data update.
        """
        if trade.direction == Direction.LONG:
            self.long_entry = trade.price
            self.long_stop = self.long_entry - 2 * self.atr_value
        else:
            self.short_entry = trade.price
            self.short_stop = self.short_entry + 2 * self.atr_value

    def on_order(self, order: OrderData):
        """
        Callback of new order data update.
        """
        pass

    def on_stop_order(self, stop_order: StopOrder):
        """
        Callback of stop order update.
        """
        pass

    def send_buy_orders(self, price):
        """"""
        t = self.pos / self.fixed_size

        if t < 1:
            self.buy(price, self.fixed_size, True)

        if t < 2:
            self.buy(price + self.atr_value * 0.5, self.fixed_size, True)

        if t < 3:
            self.buy(price + self.atr_value, self.fixed_size, True)

        if t < 4:
            self.buy(price + self.atr_value * 1.5, self.fixed_size, True)

    def send_short_orders(self, price):
        """"""
        t = self.pos / self.fixed_size

        if t > -1:
            self.short(price, self.fixed_size, True)

        if t > -2:
            self.short(price - self.atr_value * 0.5, self.fixed_size, True)

        if t > -3:
            self.short(price - self.atr_value, self.fixed_size, True)

        if t > -4:
            self.short(price - self.atr_value * 1.5, self.fixed_size, True)
Exemplo n.º 2
0
class TurtleSignal:
    """ 用于产生海龟策略交易信号,包括入场,止损,止盈委托价格与目标仓位 """

    def __init__(self, portfolio, vt_symbol,
                 entry_window, exit_window, atr_window,
                 profit_check=False):
        """Constructor, 初始化海龟信号的策略参数(默认不检查上一笔盈亏,默认缓存60根K线)"""
        self.portfolio = portfolio  # 投资组合

        self.vt_symbol = vt_symbol  # 合约代码
        self.entry_window = entry_window  # 入场通道周期数
        self.exit_window = exit_window  # 出场通道周期数
        self.atr_window = atr_window  # 计算ATR周期数
        self.profit_check = profit_check  # 是否检查上一笔盈利

        self.am = ArrayManager(60)  # K线容器

        self.atr_volatility = 0  # ATR波动率
        self.entry_up = 0  # 入场通道
        self.entry_down = 0
        self.exit_up = 0  # 出场通道
        self.exit_down = 0

        self.long_entry1 = 0  # 多头入场位
        self.long_entry2 = 0
        self.long_entry3 = 0
        self.long_entry4 = 0
        self.long_stop = 0  # 多头止损位

        self.short_entry1 = 0  # 空头入场位
        self.short_entry2 = 0
        self.short_entry3 = 0
        self.short_entry4 = 0
        self.short_stop = 0  # 空头止损位

        self.unit = 0  # 信号持仓
        self.result = None  # 当前的交易
        self.result_list = []  # 交易列表
        self.bar = None  # 最新K线

    def on_bar(self, bar):
        """ 缓存足够K线后,开始计算相关技术指标,判断交易信号 """
        self.bar = bar
        self.am.update_bar(bar)
        if not self.am.inited:
            return
        self.generate_signal(bar)
        self.calulate_indicator()

    def generate_signal(self, bar):
        """
        判断交易信号
        要注意在任何一个数据点:buy/sell/short/cover只允许执行一类动作

        负责交易信号的判断,平仓信号与开仓信号是分开的:优先检查平仓,没有仓位或者持有多头仓位的时候,在设置好入场位做多或加仓;
        没有仓位或者持有空头仓位的时候,在设置好入场位做空或者加仓
        """
        # 如果指标尚未初始化,则忽略
        # 在 calculate_indicator里面初始化,初始化的时候,所有long+short 1,2,3,4都计算完成且不会变了。
        if not self.long_entry1:
            return

        # 优先检查平仓
        if self.unit > 0:
            long_exit = max(self.long_stop, self.exit_down)

            if bar.low <= long_exit:
                self.sell(long_exit)
                return
        elif self.unit < 0:
            short_exit = min(self.short_stop, self.exit_up)
            if bar.high >= short_exit:
                self.cover(short_exit)
                return

        # 没有仓位或者持有多头仓位的时候,可以做多(加仓)
        if self.unit >= 0:
            trade = False

            if bar.high >= self.long_entry1 and self.unit < 1:
                self.buy(self.long_entry1, 1)
                trade = True

            if bar.high >= self.long_entry2 and self.unit < 2:
                self.buy(self.long_entry2, 1)
                trade = True

            if bar.high >= self.long_entry3 and self.unit < 3:
                self.buy(self.long_entry3, 1)
                trade = True

            if bar.high >= self.long_entry4 and self.unit < 4:
                self.buy(self.long_entry4, 1)
                trade = True

            if trade:
                return

        # 没有仓位或者持有空头仓位的时候,可以做空(加仓)
        if self.unit <= 0:
            if bar.low <= self.short_entry1 and self.unit > -1:
                self.short(self.short_entry1, 1)

            if bar.low <= self.short_entry2 and self.unit > -2:
                self.short(self.short_entry2, 1)

            if bar.low <= self.short_entry3 and self.unit > -3:
                self.short(self.short_entry3, 1)

            if bar.low <= self.short_entry4 and self.unit > -4:
                self.short(self.short_entry4, 1)

    def calculate_indicator(self):
        """ 计算技术指标
            负责计算指标的产生,包括计算入场和止盈离场的唐奇安通道上下轨,判断到有单位持仓后,
            计算ATR指标并且设定随后8个入场位置(做多4个和做空4个),同时初始化离场价格。
        """
        self.entry_up, self.entry_down = self.am.donchian(self.entry_window)
        self.exit_up, self.exit_down = self.am.donchian(self.exit_window)

        # 有持仓后,ATR波动率和入场位等都不再变化
        if not self.unit:
            self.atr_volatility = self.am.atr(self.atr_window)

            self.long_entry1 = self.entry_up
            self.long_entry2 = self.entry_up + self.atr_volatility * 0.5
            self.long_entry3 = self.entry_up + self.atr_volatility * 1
            self.long_entry4 = self.entry_up + self.atr_volatility * 1.5
            self.long_stop = 0  # 只有等第一笔买入时才能计算

            self.short_entry1 = self.entry_down
            self.short_entry2 = self.entry_down - self.atr_volatility * 0.5
            self.short_entry3 = self.entry_down - self.atr_volatility * 1
            self.short_entry4 = self.entry_down - self.atr_volatility * 1.5
            self.short_stop = 0 # 只有等第一笔卖出时才能计算

    def new_signal(self, direction, offset, price, volume):
        """ 定义海龟投资组合的发单委托,分别是多空方向、开仓平仓、停止单价格、合约手数。 """
        # ??? 最终的交易是在portfolio class 里完成的吗???
        self.portfolio.new_signal(self, direction, offset, price, volume)

    def buy(self, price, volume):
        """ 买入开仓;
            先传入计算好的停止单价格,缓存开仓委托的价格和手数,发出投资组合的多开委托,基于最后一次加仓价格计算止损离场位置。
            关键字:开仓 """
        price = self.calculate_trade_price(Direction.LONG, price)

        self.open(price, volume)
        self.new_signal(Direction.LONG, Offset.OPEN, price, volume)

        # 以最后一次加仓价格,加上两倍N计算止损
        self.long_stop = price - self.atr_volatility * 2

    def sell(self, price, volume):
        """ 卖出平仓;
            先传入计算好的停止单价格,缓存平仓委托的价格,发出投资组合空平的委托
            关键字:平仓 """
        price = self.calculate_trade_price(Direction.SHORT, price)
        volume = abs(self.unit)

        self.close(price)
        self.new_signal(Direction.SHORT, Offset.CLOSE, price, volume)

    def short(self, price, volume):
        """卖出开仓
            先传入计算好的停止单价格,缓存开仓委托的价格和手数,发出投资组合的空开委托,基于最后一次加仓价格计算止损离场位置。
            关键字:开仓 """
        price = self.calculate_trade_price(Direction.SHORT, price)

        self.open(price, -volume)
        self.new_signal(Direction.SHORT, Offset.OPEN, price, volume)

        # 以最后一次加仓价格,加上两倍N计算止损
        self.short_stop = price + self.atr_volatility * 2

    def cover(self, price, volume):
        """买入平仓;
            先传入计算好的停止单价格,缓存平仓委托的价格,发出投资组合多平的委托。
            关键字:平仓 """
        price = self.calculate_trade_price(Direction.LONG, price)
        volume = abs(self.unit)

        self.close(price)
        self.new_signal(Direction.LONG, Offset.CLOSE, price, volume)

    def open(self, price, change):
        """ 开仓
            计算累计开仓手数 / 单位头寸,调用TurtleResult类定义的open函数计算开仓平均成本。"""
        self.unit += change

        if not self.result:
            self.result = TurtleResult()
        self.result.open(price, change)

    def close(self, price):
        """平仓
            调用TurtleResult类定义的close函数计算单笔开平仓交易盈亏。创建列表专门缓存开平仓交易盈亏。"""
        self.unit = 0

        self.result.close(price)
        self.result_list.append(self.result)
        self.result = None

    def get_last_pnl(self):
        """ 获取上一笔交易的盈亏;
            在开平仓交易盈亏列表中获取上一笔交易的盈亏"""
        if not self.result_list:
            return 0

        result = self.result_list[-1]
        return result.pnl

    def calculate_trade_price(self, direction, price):
        """计算成交价格; 设置停止单价格,要求买入时,停止单成交的最优价格不能低于当前K线开盘价;
        卖出时,停止单成交的最优价格不能高于当前K线开盘价"""
        # 买入时,停止单成交的最优价格不能低于当前K线开盘价
        if direction == Direction.LONG:
            # ZL: 如果开盘的时候突破了通道,则只能以开盘的价格来成交
            # 这里关键字是:停止单
            trade_price = max(self.bar.open, price)
        # 卖出时,停止单成交的最优价格不能高于当前K线开盘价
        else:
            trade_price = min(self.bar.open, price)

        return trade_price
Exemplo n.º 3
0
class TurtleSignal(object):
    """海龟信号"""

    #----------------------------------------------------------------------
    def __init__(self,
                 portfolio,
                 vtSymbol,
                 entryWindow,
                 exitWindow,
                 atrWindow,
                 profitCheck=False):
        """Constructor"""
        self.portfolio = portfolio  # 投资组合

        self.vtSymbol = vtSymbol  # 合约代码
        self.entryWindow = entryWindow  # 入场通道周期数
        self.exitWindow = exitWindow  # 出场通道周期数
        self.atrWindow = atrWindow  # 计算ATR周期数
        self.profitCheck = profitCheck  # 是否检查上一笔盈利

        self.am = ArrayManager(60)  # K线容器

        self.atrVolatility = 0  # ATR波动率
        self.entryUp = 0  # 入场通道
        self.entryDown = 0
        self.exitUp = 0  # 出场通道
        self.exitDown = 0

        self.longEntry1 = 0  # 多头入场位
        self.longEntry2 = 0
        self.longEntry3 = 0
        self.longEntry4 = 0
        self.longStop = 0  # 多头止损位

        self.shortEntry1 = 0  # 空头入场位
        self.shortEntry2 = 0
        self.shortEntry3 = 0
        self.shortEntry4 = 0
        self.shortStop = 0  # 空头止损位

        self.unit = 0  # 信号持仓
        self.result = None  # 当前的交易
        self.resultList = []  # 交易列表
        self.bar = None  # 最新K线

    #----------------------------------------------------------------------
    def onBar(self, bar):
        """"""
        self.bar = bar
        self.am.updateBar(bar)
        if not self.am.inited:
            return

        self.generateSignal(bar)
        self.calculateIndicator()

    #----------------------------------------------------------------------
    def generateSignal(self, bar):
        """
        判断交易信号
        要注意在任何一个数据点:buy/sell/short/cover只允许执行一类动作
        """
        # 如果指标尚未初始化,则忽略
        if not self.longEntry1:
            return

        # 优先检查平仓
        if self.unit > 0:
            longExit = max(self.longStop, self.exitDown)

            if bar.low <= longExit:
                self.sell(longExit)
                return
        elif self.unit < 0:
            shortExit = min(self.shortStop, self.exitUp)
            if bar.high >= shortExit:
                self.cover(shortExit)
                return

        # 没有仓位或者持有多头仓位的时候,可以做多(加仓)
        if self.unit >= 0:
            trade = False

            if bar.high >= self.longEntry1 and self.unit < 1:
                self.buy(self.longEntry1, 1)
                trade = True

            if bar.high >= self.longEntry2 and self.unit < 2:
                self.buy(self.longEntry2, 1)
                trade = True

            if bar.high >= self.longEntry3 and self.unit < 3:
                self.buy(self.longEntry3, 1)
                trade = True

            if bar.high >= self.longEntry4 and self.unit < 4:
                self.buy(self.longEntry4, 1)
                trade = True

            if trade:
                return

        # 没有仓位或者持有空头仓位的时候,可以做空(加仓)
        if self.unit <= 0:
            if bar.low <= self.shortEntry1 and self.unit > -1:
                self.short(self.shortEntry1, 1)

            if bar.low <= self.shortEntry2 and self.unit > -2:
                self.short(self.shortEntry2, 1)

            if bar.low <= self.shortEntry3 and self.unit > -3:
                self.short(self.shortEntry3, 1)

            if bar.low <= self.shortEntry4 and self.unit > -4:
                self.short(self.shortEntry4, 1)

    #----------------------------------------------------------------------
    def calculateIndicator(self):
        """计算技术指标"""
        self.entryUp, self.entryDown = self.am.donchian(self.entryWindow)
        self.exitUp, self.exitDown = self.am.donchian(self.exitWindow)

        # 有持仓后,ATR波动率和入场位等都不再变化
        if not self.unit:
            self.atrVolatility = self.am.atr(self.atrWindow)

            self.longEntry1 = self.entryUp
            self.longEntry2 = self.entryUp + self.atrVolatility * 0.5
            self.longEntry3 = self.entryUp + self.atrVolatility * 1
            self.longEntry4 = self.entryUp + self.atrVolatility * 1.5
            self.longStop = 0

            self.shortEntry1 = self.entryDown
            self.shortEntry2 = self.entryDown - self.atrVolatility * 0.5
            self.shortEntry3 = self.entryDown - self.atrVolatility * 1
            self.shortEntry4 = self.entryDown - self.atrVolatility * 1.5
            self.shortStop = 0

    #----------------------------------------------------------------------
    def newSignal(self, direction, offset, price, volume):
        """"""
        self.portfolio.newSignal(self, direction, offset, price, volume)

    #----------------------------------------------------------------------
    def buy(self, price, volume):
        """买入开仓"""
        price = self.calculateTradePrice(Direction.LONG, price)

        self.open(price, volume)
        self.newSignal(Direction.LONG, Offset.OPEN, price, volume)

        # 以最后一次加仓价格,加上两倍N计算止损
        self.longStop = price - self.atrVolatility * 2

    #----------------------------------------------------------------------
    def sell(self, price):
        """卖出平仓"""
        price = self.calculateTradePrice(Direction.SHORT, price)
        volume = abs(self.unit)

        self.close(price)
        self.newSignal(Direction.SHORT, Offset.CLOSE, price, volume)

    #----------------------------------------------------------------------
    def short(self, price, volume):
        """卖出开仓"""
        price = self.calculateTradePrice(Direction.SHORT, price)

        self.open(price, -volume)
        self.newSignal(Direction.SHORT, Offset.OPEN, price, volume)

        # 以最后一次加仓价格,加上两倍N计算止损
        self.shortStop = price + self.atrVolatility * 2

    #----------------------------------------------------------------------
    def cover(self, price):
        """买入平仓"""
        price = self.calculateTradePrice(Direction.LONG, price)
        volume = abs(self.unit)

        self.close(price)
        self.newSignal(Direction.LONG, Offset.CLOSE, price, volume)

    #----------------------------------------------------------------------
    def open(self, price, change):
        """开仓"""
        self.unit += change

        if not self.result:
            self.result = TurtleResult()
        self.result.open(price, change)

    #----------------------------------------------------------------------
    def close(self, price):
        """平仓"""
        self.unit = 0

        self.result.close(price)
        self.resultList.append(self.result)
        self.result = None

    #----------------------------------------------------------------------
    def getLastPnl(self):
        """获取上一笔交易的盈亏"""
        if not self.resultList:
            return 0

        result = self.resultList[-1]
        return result.pnl

    #----------------------------------------------------------------------
    def calculateTradePrice(self, direction, price):
        """计算成交价格"""
        # 买入时,停止单成交的最优价格不能低于当前K线开盘价
        if direction == Direction.LONG:
            tradePrice = max(self.bar.open, price)
        # 卖出时,停止单成交的最优价格不能高于当前K线开盘价
        else:
            tradePrice = min(self.bar.open, price)

        return tradePrice
Exemplo n.º 4
0
class SuperTurtleStrategyHNTest(CtaTemplate):
    """"""
    author = "Huang Ning"

    entry_window = 28
    exit_window = 7
    atr_window = 4
    risk_level = 0.2

    trading_size = 0
    entry_up = 0
    entry_down = 0
    exit_up = 0
    exit_down = 0
    atr_value = 0

    long_entry = 0
    short_entry = 0
    long_stop = 0
    short_stop = 0

    parameters = ["entry_window", "exit_window", "atr_window", "risk_level"]
    variables = [
        "entry_up", "entry_down", "exit_up", "exit_down", "trading_size",
        "atr_value"
    ]

    def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
        """"""
        super().__init__(cta_engine, strategy_name, vt_symbol, setting)

        self.bg = XminBarGenerator(self.on_bar,
                                   1,
                                   self.on_hour_bar,
                                   interval=Interval.HOUR)
        self.am = ArrayManager()

        self.liq_price = 0
        self.trading_size = 0

        self.on_bar_time = time1(0, 0)
        self.clearance_time = time1(14, 57)
        self.liq_time = time1(14, 59)

        self.day_clearance = False

        self.buy_svt_orderids = []
        self.sell_svt_orderids = []
        self.short_svt_orderids = []
        self.cover_svt_orderids = []

        self.sell_lvt_orderids = []
        self.cover_lvt_orderids = []

    def on_init(self):
        """"""
        self.write_log("策略初始化")
        self.load_bar(20)

    def on_start(self):
        """"""
        self.write_log("策略启动")

    def on_stop(self):
        """"""
        self.write_log("策略停止")

    def on_tick(self, tick: TickData):
        """"""
        self.bg.update_tick(tick)

    def on_bar(self, bar: BarData):
        """"""
        self.liq_price = bar.close_price
        self.on_bar_time = bar.datetime.time()
        self.on_bar_date = bar.datetime.date()

        # 2021年5月10日有极端行情,收益异常高,不具有普遍参考价值,因此在回测中跳过这一天
        extreme_date = "2021-05-10"
        extreme_date = time.strptime(extreme_date, "%Y-%m-%d")
        year, month, day = extreme_date[:3]
        extreme_date = datetime.date(year, month, day)

        self.day_clearance = (self.clearance_time <= self.on_bar_time <=
                              self.liq_time)

        if self.on_bar_date != extreme_date:

            self.bg.update_bar(bar)

            if self.day_clearance:

                if not self.buy_svt_orderids and not self.short_svt_orderids\
                    and not self.sell_svt_orderids and not self.cover_svt_orderids\
                        and not self.sell_lvt_orderids and not self.cover_lvt_orderids:

                    if self.pos > 0:
                        self.sell_lvt_orderids = self.sell(
                            self.liq_price - 5, abs(self.pos))

                    elif self.pos < 0:
                        self.cover_lvt_orderids = self.cover(
                            self.liq_price + 5, abs(self.pos))

                else:
                    for buf_orderids in [
                            self.buy_svt_orderids, self.sell_svt_orderids,
                            self.short_svt_orderids, self.cover_svt_orderids,
                            self.sell_lvt_orderids, self.cover_lvt_orderids
                    ]:

                        if buf_orderids:
                            for vt_orderid in buf_orderids:
                                self.cancel_order(vt_orderid)

    def on_hour_bar(self, bar: BarData):
        """"""

        am = self.am
        am.update_bar(bar)
        if not am.inited:
            return

        self.entry_up, self.entry_down = self.am.donchian(self.entry_window)
        self.exit_up, self.exit_down = self.am.donchian(self.exit_window)

        if not self.day_clearance:
            if not self.pos:
                self.atr_value = self.am.atr(self.atr_window)

                if self.atr_value == 0:
                    return

                atr_risk = talib.ATR(1 / self.am.high, 1 / self.am.low,
                                     1 / self.am.close, self.atr_window)[-1]
                self.trading_size = max(int(self.risk_level / atr_risk), 1)

                self.long_entry = 0
                self.short_entry = 0
                self.long_stop = 0
                self.short_stop = 0

                self.buy(self.entry_up, self.trading_size, True)
                self.short(self.entry_down, self.trading_size, True)

            elif self.pos > 0:
                sell_price = max(self.long_stop, self.exit_down)
                self.sell(sell_price, abs(self.pos), True)

            elif self.pos < 0:
                cover_price = min(self.short_stop, self.exit_up)
                self.cover(cover_price, abs(self.pos), True)

        self.put_event()

    def on_trade(self, trade: TradeData):
        """
        Callback of new trade data update.
        """
        if trade.direction == Direction.LONG:
            self.long_entry = trade.price
            self.long_stop = self.long_entry - 2 * self.atr_value
        else:
            self.short_entry = trade.price
            self.short_stop = self.short_entry + 2 * self.atr_value

        self.sync_data()

    def on_order(self, order: OrderData):
        """
        Callback of new order data update.
        """
        pass

    def on_stop_order(self, stop_order: StopOrder):
        """
        Callback of stop order update.
        """
        pass