量化交易之回测篇 - 海龟交易策略(初版)

"""
写这个模型花了小半天, 还没来得及测试, 先开源出来;
等期货回测器的cta分支没问题后,再测试该模型;
PS: 为了方便模型的回测, 价格设置上确实有不合理的地方, 勿用于实盘, 请见谅;
"""


from tqz_strategy.template import CtaTemplate
from public_module.object import StopOrder, TickData, BarData, TradeData, OrderData
from public_module.utility import BarGenerator


class TQZTurtleTradingStrategy(CtaTemplate):
    author = "tqz"

    # --- param part ---
    donchian_channel_window = 20

    fast_window = 10
    slow_window = 60

    clear_position_days_window = 10
    n_window = 20

    parameters = [
        "fast_window",
        "slow_window",
        "donchian_channel_window",
        "clear_position_days_window",
        "n_window"
    ]

    # --- var part ---
    fast_ma0 = 0.0
    fast_ma1 = 0.0

    slow_ma0 = 0.0
    slow_ma1 = 0.0

    donchian_channel_up = 0.0
    donchian_channel_down = 0.0

    clear_position_level_price = 0  # profit
    stop_loss_level_price = 0  # loss

    variables = [
        "fast_ma0",
        "fast_ma1",
        "slow_ma0",
        "slow_ma1",
        "donchian_channel_up",
        "donchian_channel_down",
        "stop_loss_level_price",
        "clear_position_level_price",
    ]

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

        self.bg = BarGenerator(self.on_bar)
        self.history_bars = []

    def on_init(self):
        """
        Callback when strategy is inited.
        """
        # self.write_log(msg=f'strategy_name: {self.strategy_name} on_init.')
        pass

    def on_start(self):
        """
        Callback when strategy is started.
        """
        # self.write_log(msg=f'strategy_name: {self.strategy_name} on_start.')
        pass

    def on_stop(self):
        """
        Callback when strategy is stopped.
        """
        # self.write_log(msg=f'strategy_name: {self.strategy_name} on_stop.')
        pass

    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.
        """
        if self.__update_params_ok(new_bar=bar) is False:
            return

        # trend direction: turtle user single ma
        long_market = bar.close_price > self.slow_ma0
        short_market = bar.close_price < self.slow_ma0

        lots = 1
        if long_market:
            if self.pos is 0:
                if bar.high_price > self.donchian_channel_up:
                    self.set_position(position=lots, position_change_price=self.donchian_channel_up)
            elif self.pos > 0:
                if bar.low_price < self.clear_position_level_price:
                    self.set_position(position=0, position_change_price=self.clear_position_level_price)
                elif bar.low_price < self.stop_loss_level_price and self.stop_loss_level_price is not 0:
                    self.set_position(position=0, position_change_price=self.stop_loss_level_price)
            elif self.pos < 0:
                self.set_position(position=0, position_change_price=bar.close_price)

        elif short_market:
            if self.pos is 0:
                if bar.low_price < self.donchian_channel_down:
                    self.set_position(position=-lots, position_change_price=self.donchian_channel_down)
            elif self.pos < 0:
                if bar.high_price > self.clear_position_level_price:
                    self.set_position(position=0, position_change_price=self.clear_position_level_price)
                elif bar.high_price > self.stop_loss_level_price and self.stop_loss_level_price is not 0:
                    self.set_position(position=0, position_change_price=self.stop_loss_level_price)
            elif self.pos > 0:
                self.set_position(position=0, position_change_price=bar.close_price)


    def __update_params_ok(self, new_bar: BarData) -> bool:
        if len(self.history_bars) < self.slow_window:
            self.history_bars.append(new_bar)
            return False

        first_bar = self.history_bars[0]
        self.history_bars.remove(first_bar)
        self.history_bars.append(new_bar)

        # fast ma value
        tmp_fast_ma0 = 0
        for bar in self.history_bars[-self.fast_window:]:
            tmp_fast_ma0 += bar.close_price

        tmp_fast_ma1 = 0
        for bar in self.history_bars[-self.fast_window-1:-1]:
            tmp_fast_ma1 += bar.close_price

        self.fast_ma0 = tmp_fast_ma0 / self.fast_window
        self.fast_ma1 = tmp_fast_ma1 / self.fast_window

        # slow ma value
        tmp_slow_ma0 = 0
        for bar in self.history_bars:
            tmp_slow_ma0 += bar.close_price

        tmp_slow_ma1 = 0
        for bar in self.history_bars[:-1]:
            tmp_slow_ma1 += bar.close_price
        tmp_slow_ma1 += first_bar.close_price

        self.slow_ma0 = tmp_slow_ma0 / self.slow_window
        self.slow_ma1 = tmp_slow_ma1 / self.slow_window

        # donchian up & down value
        self.donchian_channel_up, self.donchian_channel_down = self.__get_new_donchian_value()

        # clear_position_level_price
        self.clear_position_level_price = self.__get_clear_position_level_price()

        # stop_loss_level_price
        if self.pos > 0:
            self.stop_loss_level_price = self.position_change_price - self.__get_n_value() * 2
        elif self.pos < 0:
            self.stop_loss_level_price = self.position_change_price + self.__get_n_value() * 2

        return True


    def __get_n_value(self):
        n_value = 0
        if self.pos is not 0:
            n_values = []
            for index, bar in enumerate(self.history_bars):
                if index > len(self.history_bars) - self.n_window - 1:
                    pre_bar = self.history_bars[index - 1]
                    tr = max(bar.high_price - bar.close_price, bar.high_price - pre_bar.close_price, pre_bar.close_price - bar.low_price)
                    if len(n_values) is 0:
                        n_values.append(tr)
                    else:
                        n_values.append((n_values[-1] * (self.n_window - 1) + tr) / self.n_window)

            n_value = n_values[-1]

        return n_value

    def __get_clear_position_level_price(self):
        tmp_clear_position_level_price = 0
        if self.pos is not 0:
            for bar in self.history_bars[-self.clear_position_days_window:]:
                if self.pos > 0:
                    if bar.low_price < tmp_clear_position_level_price or tmp_clear_position_level_price is 0:
                        tmp_clear_position_level_price = bar.low_price
                elif self.pos < 0:
                    if bar.high_price > tmp_clear_position_level_price or tmp_clear_position_level_price is 0:
                        tmp_clear_position_level_price = bar.high_price

        return tmp_clear_position_level_price

    def __get_new_donchian_value(self):
        tmp_donchian_channel_up = 0
        tmp_donchian_channel_down = 0

        for bar in self.history_bars[-self.donchian_channel_window:]:
            if bar.high_price > self.donchian_channel_up or tmp_donchian_channel_up is 0:
                tmp_donchian_channel_up = bar.high_price
            if bar.low_price < self.donchian_channel_down or tmp_donchian_channel_down is 0:
                tmp_donchian_channel_down = bar.low_price

        return tmp_donchian_channel_up, tmp_donchian_channel_down

    def set_position(self, position: int, position_change_price: float):
        super().set_position(position=position, position_change_price=position_change_price)

        if self.pos > 0:
            self.stop_loss_level_price = position_change_price - 2 * self.__get_n_value()
        elif self.pos < 0:
            self.stop_loss_level_price = position_change_price + 2 * self.__get_n_value()
        else:
            self.stop_loss_level_price = 0



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

    def on_trade(self, trade: TradeData):
        """
        Callback of new trade data update.
        """
        pass

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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值