class KkStrategy(CtaTemplate):
    """基于King Keltner通道的交易策略"""
    className = 'KkStrategy'
    author = u'用Python的交易员'

    # 策略参数
    kkLength = 11  # 计算通道中值的窗口数
    kkDev = 1.6  # 计算通道宽度的偏差
    trailingPrcnt = 0.8  # 移动止损
    initDays = 10  # 初始化数据所用的天数
    fixedSize = 1  # 每次交易的数量

    # 策略变量
    kkUp = 0  # KK通道上轨
    kkDown = 0  # KK通道下轨
    intraTradeHigh = 0  # 持仓期内的最高点
    intraTradeLow = 0  # 持仓期内的最低点

    buyOrderIDList = []  # OCO委托买入开仓的委托号
    shortOrderIDList = []  # OCO委托卖出开仓的委托号
    orderList = []  # 保存委托代码的列表

    # 参数列表,保存了参数的名称
    paramList = [
        'name', 'className', 'author', 'vtSymbol', 'kkLength', 'kkDev'
    ]

    # 变量列表,保存了变量的名称
    varList = ['inited', 'trading', 'pos', 'kkUp', 'kkDown']

    #----------------------------------------------------------------------
    def __init__(self, ctaEngine, setting):
        """Constructor"""
        super(KkStrategy, self).__init__(ctaEngine, setting)

        self.bm = BarManager(self.onBar, 5, self.onFiveBar)  # 创建K线合成器对象
        self.am = ArrayManager()

    #----------------------------------------------------------------------
    def onInit(self):
        """初始化策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略初始化' % self.name)

        # 载入历史数据,并采用回放计算的方式初始化策略数值
        initData = self.loadBar(self.initDays)
        for bar in initData:
            self.onBar(bar)

        self.putEvent()

    #----------------------------------------------------------------------
    def onStart(self):
        """启动策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略启动' % self.name)
        self.putEvent()

    #----------------------------------------------------------------------
    def onStop(self):
        """停止策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略停止' % self.name)
        self.putEvent()

    #----------------------------------------------------------------------
    def onTick(self, tick):
        """收到行情TICK推送(必须由用户继承实现)"""
        self.bm.updateTick(tick)

    #----------------------------------------------------------------------
    def onBar(self, bar):
        """收到Bar推送(必须由用户继承实现)"""
        self.bm.updateBar(bar)

    #----------------------------------------------------------------------
    def onFiveBar(self, bar):
        """收到5分钟K线"""
        # 撤销之前发出的尚未成交的委托(包括限价单和停止单)
        for orderID in self.orderList:
            self.cancelOrder(orderID)
        self.orderList = []

        # 保存K线数据
        am = self.am
        am.updateBar(bar)
        if not am.inited:
            return

        # 计算指标数值
        self.kkUp, self.kkDown = am.keltner(self.kkLength, self.kkDev)

        # 判断是否要进行交易

        # 当前无仓位,发送OCO开仓委托
        if self.pos == 0:
            self.intraTradeHigh = bar.high
            self.intraTradeLow = bar.low
            self.sendOcoOrder(self.kkUp, self.kkDown, self.fixedSize)

        # 持有多头仓位
        elif self.pos > 0:
            self.intraTradeHigh = max(self.intraTradeHigh, bar.high)
            self.intraTradeLow = bar.low

            l = self.sell(self.intraTradeHigh * (1 - self.trailingPrcnt / 100),
                          abs(self.pos), True)
            self.orderList.extend(l)

        # 持有空头仓位
        elif self.pos < 0:
            self.intraTradeHigh = bar.high
            self.intraTradeLow = min(self.intraTradeLow, bar.low)

            l = self.cover(self.intraTradeLow * (1 + self.trailingPrcnt / 100),
                           abs(self.pos), True)
            self.orderList.extend(l)

        # 发出状态更新事件
        self.putEvent()

    #----------------------------------------------------------------------
    def onOrder(self, order):
        """收到委托变化推送(必须由用户继承实现)"""
        pass

    #----------------------------------------------------------------------
    def onTrade(self, trade):
        if self.pos != 0:
            # 多头开仓成交后,撤消空头委托
            if self.pos > 0:
                for shortOrderID in self.shortOrderIDList:
                    self.cancelOrder(shortOrderID)
            # 反之同样
            elif self.pos < 0:
                for buyOrderID in self.buyOrderIDList:
                    self.cancelOrder(buyOrderID)

            # 移除委托号
            for orderID in (self.buyOrderIDList + self.shortOrderIDList):
                if orderID in self.orderList:
                    self.orderList.remove(orderID)

        # 发出状态更新事件
        self.putEvent()

    #----------------------------------------------------------------------
    def sendOcoOrder(self, buyPrice, shortPrice, volume):
        """
        发送OCO委托
        
        OCO(One Cancel Other)委托:
        1. 主要用于实现区间突破入场
        2. 包含两个方向相反的停止单
        3. 一个方向的停止单成交后会立即撤消另一个方向的
        """
        # 发送双边的停止单委托,并记录委托号
        self.buyOrderIDList = self.buy(buyPrice, volume, True)
        self.shortOrderIDList = self.short(shortPrice, volume, True)

        # 将委托号记录到列表中
        self.orderList.extend(self.buyOrderIDList)
        self.orderList.extend(self.shortOrderIDList)

    #----------------------------------------------------------------------
    def onStopOrder(self, so):
        """停止单推送"""
        pass
Exemple #2
0
class MultiTimeframeStrategy(CtaTemplate):
    """跨时间周期交易策略"""
    className = 'MultiTimeframeStrategy'
    author = u'用Python的交易员'

    # 策略参数
    rsiSignal = 20  # RSI信号阈值
    rsiWindow = 14  # RSI窗口
    fastWindow = 5  # 快速均线窗口
    slowWindow = 20  # 慢速均线窗口

    initDays = 10  # 初始化数据所用的天数
    fixedSize = 1  # 每次交易的数量

    # 策略变量
    rsiValue = 0  # RSI指标的数值
    rsiLong = 0  # RSI买开阈值
    rsiShort = 0  # RSI卖开阈值
    fastMa = 0  # 5分钟快速均线
    slowMa = 0  # 5分钟慢速均线
    maTrend = 0  # 均线趋势,多头1,空头-1

    # 参数列表,保存了参数的名称
    paramList = [
        'name', 'className', 'author', 'vtSymbol', 'rsiSignal', 'rsiWindow',
        'fastWindow', 'slowWindow'
    ]

    # 变量列表,保存了变量的名称
    varList = [
        'inited', 'trading', 'pos', 'rsiValue', 'rsiLong', 'rsiShort',
        'fastMa', 'slowMa', 'maTrend'
    ]

    # 同步列表,保存了需要保存到数据库的变量名称
    syncList = ['pos']

    #----------------------------------------------------------------------
    def __init__(self, ctaEngine, setting):
        """Constructor"""
        super(MultiTimeframeStrategy, self).__init__(ctaEngine, setting)

        self.rsiLong = 50 + self.rsiSignal
        self.rsiShort = 50 - self.rsiSignal

        # 创建K线合成器对象
        self.bm5 = BarManager(self.onBar, 5, self.on5MinBar)
        self.am5 = ArrayManager()

        self.bm15 = BarManager(self.onBar, 15, self.on15MinBar)
        self.am15 = ArrayManager()

    #----------------------------------------------------------------------
    def onInit(self):
        """初始化策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略初始化' % self.name)

        # 载入历史数据,并采用回放计算的方式初始化策略数值
        initData = self.loadBar(self.initDays)
        for bar in initData:
            self.onBar(bar)

        self.putEvent()

    #----------------------------------------------------------------------
    def onStart(self):
        """启动策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略启动' % self.name)
        self.putEvent()

    #----------------------------------------------------------------------
    def onStop(self):
        """停止策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略停止' % self.name)
        self.putEvent()

    #----------------------------------------------------------------------
    def onTick(self, tick):
        """收到行情TICK推送(必须由用户继承实现)"""
        # 只需要要在一个BM中合成1分钟K线
        self.bm5.updateTick(tick)

    #----------------------------------------------------------------------
    def onBar(self, bar):
        """收到Bar推送(必须由用户继承实现)"""
        # 基于15分钟判断趋势过滤,因此先更新
        self.bm15.updateBar(bar)

        # 基于5分钟判断
        self.bm5.updateBar(bar)

    #----------------------------------------------------------------------
    def on5MinBar(self, bar):
        """5分钟K线"""
        self.cancelAll()

        # 保存K线数据
        self.am5.updateBar(bar)
        if not self.am5.inited:
            return

        # 如果15分钟数据尚未初始化完毕,则直接返回
        if not self.maTrend:
            return

        # 计算指标数值
        self.rsiValue = self.am5.rsi(self.rsiWindow)

        # 判断是否要进行交易

        # 当前无仓位
        if self.pos == 0:
            if self.maTrend > 0 and self.rsiValue >= self.rsiLong:
                self.buy(bar.close + 5, self.fixedSize)

            elif self.maTrend < 0 and self.rsiValue <= self.rsiShort:
                self.short(bar.close - 5, self.fixedSize)

        # 持有多头仓位
        elif self.pos > 0:
            if self.maTrend < 0 or self.rsiValue < 50:
                self.sell(bar.close - 5, abs(self.pos))

        # 持有空头仓位
        elif self.pos < 0:
            if self.maTrend > 0 or self.rsiValue > 50:
                self.cover(bar.close + 5, abs(self.pos))

        # 发出状态更新事件
        self.putEvent()

    #----------------------------------------------------------------------
    def on15MinBar(self, bar):
        """15分钟K线推送"""
        self.am15.updateBar(bar)

        if not self.am15.inited:
            return

        # 计算均线并判断趋势
        self.fastMa = self.am15.sma(self.fastWindow)
        self.slowMa = self.am15.sma(self.slowWindow)

        if self.fastMa > self.slowMa:
            self.maTrend = 1
        else:
            self.maTrend = -1

    #----------------------------------------------------------------------
    def onOrder(self, order):
        """收到委托变化推送(必须由用户继承实现)"""
        pass

    #----------------------------------------------------------------------
    def onTrade(self, trade):
        # 发出状态更新事件
        self.putEvent()

    #----------------------------------------------------------------------
    def onStopOrder(self, so):
        """停止单推送"""
        pass
Exemple #3
0
class RBreakStrategy5(CtaTemplate):
    """改进型RBreak策略"""
    className = "RBreakStrategy5"
    autor = "张英杰"

    # 策略参数
    initDays = 10  # 数据初始化天数
    N2 = 8.5
    N3 = 5.0
    N4 = 6.0
    N5 = 7.5
    N6 = 4.5
    fixedSize = 1  # 每次交易数量

    # 策略变量
    intraTradeHigh = 0.0  # 移动止损用的持仓期内最高价
    intraTradeLow = 0.0  # 移动止损用的持仓期内最低价
    open_price = None  # 最新开仓价格
    order_list = []  # 下单列表

    # 参数列表,保存了参数的名称
    paramList = [
        'name',
        'className',
        'autor',
        'vtSymbol',
    ]

    # 变量列表,保存了变量的名称
    varList = [
        'inited',
        'trading',
        'pos',
    ]

    def __init__(self, ctaEngine, setting):
        """Constructor"""
        super(RBreakStrategy5, self).__init__(ctaEngine, setting)

        # 创建bar管理器(K线合成对象),用于合成bar和处理自定义周期的bar回调函数
        self.bm = BarManager(onBar=self.onBar,
                             xmin=5,
                             onXminBar=self.onFifteenBar)
        # 创建k线序列管理工具
        self.am = ArrayManager2()
        self.dam = DailyArrayManager()

        # 策略变量
        self.date = None  # 日期
        self.pre_date = None  # 上个交易日日期
        # 基础数据获取
        self.HH1 = 0.  # 上个交易日收盘bar最高价
        self.LL1 = 0.  # 上个交易日收盘bar最低价
        self.CC1 = 0.  # 上个交易日收盘价
        self.HH2 = 0.  # 前个交易日收盘bar最高价
        self.LL2 = 0.  # 前个交易日收盘bar最低价

        self.C1 = 0.  # 当日开盘价
        self.MN1 = 0.  # MN1=当日开盘价/2 + (昨日最高+昨日最低+昨日收盘)/6
        self.RANGE1 = 0.  # RANGE1 = (昨日最高-昨日最低)*0.65 + (前日最高-前日最低)*0.35
        self.SIZECK1 = 0.  # SIZECK1 = (当前BAR收盘价 - 当前BAR开盘价)的绝对值/当前BAR收盘价   < 0.5%  振幅不超过0.5%
        self.SIZECK2 = 0.
        #
        self.U2 = 0.  # 追买中轴线
        self.U3 = 0.  # 追买止损线
        self.U4 = 0.  # 卖出止损线
        self.U5 = 0.  # 卖出中轴线
        self.U6 = 0.  # 突破做空

        self.D6 = 0.  # 突破做多
        self.D5 = 0.  # 做多中轴线
        self.D4 = 0.  # 做多止损线
        self.D3 = 0.  # 追卖止损线
        self.D2 = 0.  # 追卖中轴线

    def onInit(self):
        """初始化策略,必须由用户继承实现"""
        self.writeCtaLog("%s初始化策略" % self.name)

        # 载入历史数据,并采用回放计算的方式初始化策略数值
        initData = self.loadBar(days=self.initDays)
        # 预处理初始化的bar
        for bar in initData:
            self.onBar(bar)

        # 发出策略状态变化事件
        self.putEvent()

    def onStart(self):
        '''启动策略(必须由用户继承实现)'''
        self.writeCtaLog("%s策略启动" % self.name)

        # 发出策略状态变化事件
        self.putEvent()  # 目前的结论是回测时候该方法为pass,实盘通常用于通知界面更新

    def onStop(self):
        '''停止策略(必须由用户继承实现)'''
        self.writeCtaLog("%s策略停止" % self.name)

        # 发出策略状态变化事件
        self.putEvent()

    def onTick(self, tick):
        '''收到TICK推送,必须由用户继承实现'''
        # 更新tick,合成k线
        self.bm.updateTick(tick)

    def onBar(self, bar):
        """收到bar推送(必须由用户继承实现)"""
        self.bm.updateBar(
            bar)  # 一分钟一个bar,执行周期大于1分钟的策略在自定义的执行方法里,到时间了由bm.updataBar回调执行

    def onFifteenBar(self, bar):
        '''收到15分钟K线推送的回调函数'''

        # if bar.date == "20171222":
        #     print(111)

        # 保存K线数据
        am = self.am
        am.updateBar(bar)

        # 保存日K数据
        dam = self.dam
        dam.updateBar(bar)

        # 日期更换
        # 第一天
        if self.date is None:
            self.date = bar.date
        # 更换日期
        elif self.date != bar.date:
            self.pre_date = self.date
            self.date = bar.date

            self.CC1 = dam.close[-2]  # 上个交易日收盘价
            self.HH2 = self.HH1  # 前个交易日收盘bar最高价
            self.LL2 = self.LL1  # 前个交易日收盘bar最低价

            self.HH1 = am.close[-2]  # 重置昨日每个bar收盘最高价
            self.LL1 = am.close[-2]  # 重置昨日每个bar收盘最低价

            for i in range(-3, -100, -1):
                if str(am.date[i]) != self.pre_date:
                    break
                # 基础数据计算

                self.HH1 = max(am.close[i], self.HH1)  # 上个交易日收盘bar最高价
                self.LL1 = min(am.close[i], self.LL1)  # 上个交易日收盘bar最低价

            self.C1 = dam.open[-1]  # 当日开盘价
            self.MN1 = self.C1 / 2 + (self.HH1 + self.LL1 + self.CC1
                                      ) / 6  # MN1=当日开票价/2 + (昨日最高+昨日最低+昨日收盘)/6
            self.RANGE1 = (self.HH1 - self.LL1) * 0.65 + (
                self.HH2 - self.LL2
            ) * 0.35  # RANGE1 = (昨日最高-昨日最低)*0.65 + (前日最高-前日最低)*0.35

            # 参数计算
            self.U2 = self.MN1 + self.RANGE1 * self.N2 / 10  # 追买中轴线
            self.U3 = self.MN1 + self.RANGE1 * self.N3 / 10  # 追买止损线
            self.U4 = self.MN1 + self.RANGE1 * self.N4 / 10  # 卖出止损线
            self.U5 = self.MN1 + self.RANGE1 * self.N5 / 10  # 卖出中轴线
            self.U6 = self.MN1 + self.RANGE1 * self.N6 / 10  # 突破做空

            self.D6 = self.MN1 - self.RANGE1 * self.N6 / 10  # 突破做多
            self.D5 = self.MN1 - self.RANGE1 * self.N5 / 10  # 做多中轴线
            self.D4 = self.MN1 - self.RANGE1 * self.N4 / 10  # 做多止损线
            self.D3 = self.MN1 - self.RANGE1 * self.N3 / 10  # 追卖止损线
            self.D2 = self.MN1 - self.RANGE1 * self.N2 / 10  # 追卖中轴线

        # 如果k线还没有初始化完成,就不执行
        if not am.inited:
            return
        # 如果日k线还没有初始化完成,就不执行
        if not dam.inited:
            return

        # 当前bar上下开仓的振幅是否符合条件
        self.SIZECK1 = abs(
            bar.close - bar.open
        ) / bar.close < 5 / 1000  # SIZECK1 = (当前BAR收盘价 - 当前BAR开盘价)的绝对值/当前BAR收盘价   < 0.5%  振幅不超过0.5%
        self.SIZECK2 = abs(bar.close - bar.open) / bar.close < 5 / 1000

        # 交易决策
        # 1.观察区突破开仓
        time_now = bar.datetime.time()
        time_0915 = time_now.replace(hour=9, minute=15, second=0)

        time_1429 = time_now.replace(hour=14, minute=29, second=0)
        time_1500 = time_now.replace(hour=15, minute=0, second=0)
        time_last = time_now.replace(hour=15, minute=9, second=0)

        # 空仓
        if self.pos == 0:
            # 持有期内最高最低价
            self.intraTradeHigh = bar.close
            self.intraTradeLow = bar.close
            # self.open_price = None

            # 9:15 -- 14:29 之间
            if time_now >= time_0915 and time_now <= time_1429:
                # 价格下穿U6,股价前30个BAR(不包括当前)的收盘最高值大于U5,当前BAR振幅小于0.5%,开空头
                if am.close[-1] < self.U6 and am.close[
                        -2] >= self.U6 and am.close[-3] > self.U6 and am.close[
                            -31:-1].max() > self.U5 and self.SIZECK2:
                    self.cancelAll()
                    self.short(price=bar.close - 0.01, volume=self.fixedSize)
                    # self.open_price = bar.close
                # 价格上穿D6,最近30个BAR收盘价(不包括当前)的最小值小于D5,当前BAR振幅小于0.5%,开多头
                if am.close[-1] > self.D6 and am.close[
                        -2] <= self.D6 and am.close[-3] < self.D6 and am.close[
                            -31:-1].min() < self.D5 and self.SIZECK1:
                    self.cancelAll()
                    self.buy(price=bar.close + 0.01, volume=self.fixedSize)
                    self.open_price = bar.close

            # 时间在9:50和14:29之间 TIME>0915 && TIME<1500
            if time_now >= time_0915 and time_now <= time_1500:
                # && CROSSUP(C,U2) && SIZECK1,BK 突破开仓
                if am.close[-1] > self.U2 and am.close[
                        -2] <= self.U2 and am.close[
                            -3] < self.U2 and self.SIZECK1:
                    self.cancelAll()
                    self.buy(price=bar.close + 0.01, volume=self.fixedSize)
                    # self.open_price = bar.close
                # CROSSDOWN(C,D2) && SIZECK2,SK;
                elif am.close[-1] < self.D2 and am.close[
                        -2] >= self.D2 and am.close[
                            -3] > self.D2 and self.SIZECK2:
                    self.cancelAll()
                    self.short(price=bar.close - 0.01, volume=self.fixedSize)
                    # self.open_price = bar.close

        # 持有多头:
        elif self.pos > 0:
            self.intraTradeHigh = max(self.intraTradeHigh, bar.close)
            self.intraTradeLow = min(self.intraTradeLow, bar.close)

            #  价格上穿U6,止盈
            if am.close[-1] > self.U6 and am.close[-2] <= self.U6:
                self.cancelAll()
                self.sell(price=bar.close - 0.01, volume=self.fixedSize)
            # 价格下穿D4,止损
            elif am.close[-1] < self.D4 and am.close[-2] >= self.U6:
                self.cancelAll()
                self.sell(price=bar.close - 0.01, volume=self.fixedSize)
            # 价格下穿U3,平仓
            elif am.close[-1] < self.U3 and am.close[-2] >= self.U3:
                self.cancelAll()
                self.sell(price=bar.close - 0.01, volume=self.fixedSize)
            # 上一次买入到现在(不含当前bar)的最高价 > 买入价格 * (1 + 0.002), 并且 (当前收盘价 - 成本)<= (到目前为止收盘最高价 - 成本)* 0.5

            elif self.intraTradeHigh > self.open_price * (1 + 0.002) and (
                    am.close[-1] - self.open_price) <= (self.intraTradeHigh -
                                                        self.open_price) * 0.5:
                self.cancelAll()
                self.sell(price=bar.close - 0.01, volume=self.fixedSize)
            # 价格下穿 开仓价格 * (1 - 0.9/1000) 止损
            elif am.close[-1] < self.open_price * (
                    1 - 0.9 / 1000) and am.close[-2] >= self.open_price * (
                        1 - 0.9 / 1000):
                self.cancelAll()
                self.sell(price=bar.close - 0.01, volume=self.fixedSize)

        # 持有空头;
        elif self.pos < 0:
            self.intraTradeHigh = max(self.intraTradeHigh, bar.close)
            self.intraTradeLow = min(self.intraTradeLow, bar.close)

            # 价格下穿D6,平掉空头,止盈
            if am.close[-1] < self.D6 and am.close[-2] >= self.D6:
                self.cancelAll()
                self.cover(price=bar.close + 0.01, volume=self.fixedSize)
            # 价格上穿U4,平掉空头(止损)
            elif am.close[-1] > self.U4 and am.close[-2] <= self.U4:
                self.cancelAll()
                self.cover(price=bar.close + 0.01, volume=self.fixedSize)
            # 价格上穿D3,平仓
            elif am.close[-1] > self.D3 and am.close[-2] <= self.D3:
                self.cancelAll()
                self.cover(price=bar.close + 0.01, volume=self.fixedSize)
            # 上一次买入到现在(不含当前bar)的最低价 < 买入价格 * (1 + 0.002), 并且 (成本 - 当前收盘价)<= (成本 - 到目前为止收盘最低价)* 0.5  止盈
            elif self.intraTradeLow < self.open_price * (
                    1 - 0.002) and self.open_price - bar.close <= (
                        self.open_price - self.intraTradeLow) * 0.5:
                self.cancelAll()
                self.cover(price=bar.close + 0.01, volume=self.fixedSize)
            # 价格上 开仓价格 * (1 + 0.9 / 1000) 止损
            elif am.close[-1] > self.open_price * (
                    1 + 0.9 / 1000) and am.close[-2] <= self.open_price * (
                        1 + 0.9 / 1000):
                self.cancelAll()
                self.cover(price=bar.close + 0.01, volume=self.fixedSize)

        # 当天收盘则平仓所有
        if time_now >= time_last and bar.datetime.weekday() == 5 - 1:
            self.closeAllPosition(bar)

        self.putEvent()

    def onOrder(self, order):
        """收到委托变化推送(必须由用户继承实现)"""
        pass

    def onTrade(self, trade):
        """收到交易发生事件推送"""
        if trade.offset == "平仓":
            self.open_price = None

        if trade.offset == "开仓":
            self.open_price = trade.price

        self.putEvent()

    def onStopOrder(self, so):
        """收到停止单事件推送"""
        self.putEvent()

    def closeAllPosition(self, bar):
        self.cancelAll()
        if self.pos > 0:
            self.sell(price=bar.close - 0.01, volume=self.fixedSize)
        elif self.pos < 0:
            self.cover(price=bar.close + 0.01, volume=self.fixedSize)
Exemple #4
0
class DualThrustStrategyZR15(CtaTemplate):
    """DualThrust策略"""
    className = "DualThrustStrategyZR15"
    autor = "张英杰"

    # 策略参数
    initDays = 30 # 数据初始化天数
    X1 = 1.3
    X2 = 1.5

    fixedSize = 1  # 每次交易数量

    open_time = datetime.time(hour=9, minute=15, second=0)   # 开仓开始时间
    close_time = datetime.time(hour=15, minute=0, second=0)  # 开仓结束时间

    # 参数列表,保存了参数的名称
    paramList = ['name',
                 'className',
                 'autor',
                 'vtSymbol',

                 ]

    # 变量列表,保存了变量的名称
    varList = ['inited',
               'trading',
               'pos',
               ]

    def __init__(self, ctaEngine, setting):
        """Constructor"""
        super(DualThrustStrategyZR15, self).__init__(ctaEngine, setting)

        # 创建bar管理器(K线合成对象),用于合成bar和处理自定义周期的bar回调函数
        self.bm = BarManager(onBar=self.onBar, xmin=15, onXminBar=self.onFifteenBar)
        # 创建k线序列管理工具
        self.am = ArrayManager2(size=300)
        self.dam = DailyArrayManager()

        # 创建变量
        # self.NN = None  # 昨天离今天的k线个数,即目前为今天的第几根bar,文华需要,nvpy不需要
        self.HH1 = None  # 昨天所有bar的收盘最高价
        self.LL1 = None  # 昨天所有bar的收盘价最低价
        self.CC1 = None  # 昨天收盘价

        self.HH2 = None  # 前天所有bar最高收盘价
        self.LL2 = None  # 前天所有bar最低收盘价
        self.CC2 = None  # 前天收盘价

        self.RANGE0 = None  # 这两天的波幅
        self.RANGE1 = None  #

        self.YJS = None  # 昨天的结算价
        self.DTC = None  # 根据昨天计算价判断今天是否会有跌停和涨停的风险

        self.OO1 = None  # 今天的开盘价
        self.MN1 = None  # 中轴价  偏向于趋势

        self.UBUY = None  # 追买中轴线
        self.DSELL = None  # 追卖中轴线

        self.USELL = None  # 卖出中轴线
        self.DBUY = None   # 做多中轴线

        self.USELL1 = None  # 卖出中轴线
        self.DBUY1 = None   # 做多中轴线

        self.DGKX = np.zeros(68*3)
        self.DGKX_count = 0
        self.SIZEK = None

        self.ZS = None
        self.KK = None

        self.now_date = None  # 当前日期
        self.pre_date = None  # 上个交易日日期
        self.now_time = None  # 当前时间

        self.last_trade_price = None    # 最后成交价格

        self.intraTradeHigh = None  # 持有期内最高价
        self.intraTradeLow = None  # 持有期内最低价

        # 为了计算DGKX 和 SIZEK


    def onInit(self):
        """初始化策略,必须由用户继承实现"""
        self.writeCtaLog("%s初始化策略" % self.name)

        # 载入历史数据,并采用回放计算的方式初始化策略数值
        initData = self.loadBar(days=self.initDays)
        # 预处理初始化的bar
        for bar in initData:
            self.onBar(bar)

        # 发出策略状态变化事件
        self.putEvent()


    def onStart(self):
        '''启动策略(必须由用户继承实现)'''
        self.writeCtaLog("%s策略启动" % self.name)

        # 发出策略状态变化事件
        self.putEvent() # 目前的结论是回测时候该方法为pass,实盘通常用于通知界面更新

    def onStop(self):
        '''停止策略(必须由用户继承实现)'''
        self.writeCtaLog("%s策略停止" % self.name)

        # 发出策略状态变化事件
        self.putEvent()

    def onTick(self, tick):
        '''收到TICK推送,必须由用户继承实现'''
        # 更新tick,合成k线
        self.bm.updateTick(tick)

    def onBar(self, bar):
        """收到bar推送(必须由用户继承实现)"""
        self.bm.updateBar(bar) # 一分钟一个bar,执行周期大于1分钟的策略在自定义的执行方法里,到时间了由bm.updataBar回调执行

    def onFifteenBar(self, bar):
        """收到5分钟bar推送"""
        # 保存K线数据
        am = self.am
        am.updateBar(bar)
        # 保存日K数据
        dam = self.dam
        dam.updateBar(bar)

        self.now_time = bar.datetime.time()
        # 初始化
        if self.now_date is None:
            self.now_date = bar.date
            return
        # 日期改变
        elif self.now_date != bar.date:
            self.pre_date = self.now_date
            self.now_date = bar.date

            # 计算变量
            # 计算昨天、前天所有bar最高、最低收盘价,昨天结算价
            self.HH2 = self.HH1
            self.LL2 = self.LL1
            self.CC2 = self.CC1

            self.HH1 = am.close[-2]
            self.LL1 = am.close[-2]
            self.CC1 = am.close[-2]

            i = -3
            while am.date[i] == self.pre_date:
                if am.close[i] > self.HH1:
                    self.HH1 = am.close[i]
                if am.close[i] < self.LL1:
                    self.LL1 = am.close[i]
                i -= 1

            # 计算这两天的波幅
            if self.HH1 is not None and self.HH2 is not None:
                self.RANGE0 = (((self.HH1 - self.LL1) * 0.65 + (self.HH2 - self.LL2) * 0.35) / am.sma(68 * 2, array=True)[-2]) * 100
                self.RANGE1 = 6 if self.RANGE0 > 6 else self.RANGE0

            # 昨天的结算价
            tmp_sum = 0.
            volume_sum = 0.
            for i in range(-2, -2 - 15, -1):
                tmp_sum += self.am.close[i] * self.am.volume[i]
                volume_sum += self.am.volume[i]

            self.YJS = tmp_sum / volume_sum
            # 获取今日开盘价
            self.OO1 = bar.open

        # 数据足以支持计算DGKX
        if am.count >= 68 * 2:
            self.DGKX[0: self.DGKX.size-1] = self.DGKX[1: self.DGKX.size]
            if abs(bar.high - bar.low) / bar.close * 10000 < 4:
                self.DGKX[-1] = talib.SMA(abs(am.close-am.open), 68*2)[-1]
            else:
                self.DGKX[-1] = abs(bar.close - bar.open)
            self.DGKX_count += 1

            # 数据足以支持计算SIZEK
            if self.DGKX_count >= 68 * 3:
                self.SIZEK = talib.SMA(self.DGKX, 68*3)[-1] / talib.SMA(self.am.close, 68*3)[-1] * 1000
                self.ZS = (self.SIZEK ** 0.5) * 2
                self.KK = abs(bar.close - bar.open) / bar.open < 0.006

        # 如果k线还没有初始化完成,就不执行
        if not am.inited:
            return
        # 如果日k线还没有初始化完成,就不执行
        if not dam.inited:
            return
        #
        # if bar.date == "20171215":
        #     print(1)

        # 根据昨日计算价判断是否有涨停和跌停风险
        self.DTC = bar.close > self.YJS * (1 - 0.085) and bar.close < self.YJS * (1 + 0.085)


        self.MN1 = (self.OO1 + (self.HH1 + self.LL1 + self.CC1) / 3) / 2    # 中轴价,偏向于趋势

        self.UBUY = self.MN1 * (1 + self.X1 * self.RANGE1 / 100)    # 追买中轴线
        self.DSELL = self.MN1 * (1 - self.X2 * self.RANGE1 / 100)   # 追买中轴线

        self.USELL = self.MN1 * (1 + 1.3 * self.RANGE1 / 100)   # 卖出中轴线
        self.DBUY = self.MN1 * (1 - 1.6 * self.RANGE1 / 100)    # 做多中轴线

        self.USELL1 = self.USELL * (1 + 0.2 * self.RANGE1 / 100)  # 卖出中轴线
        self.DBUY1 = self.DBUY * (1 - 0.2 * self.RANGE1 / 100)    # 做多中轴线



        # 开仓时间

        if self.pos == 0:
            self.intraTradeHigh = bar.close
            self.intraTradeLow = bar.close

            if self.now_time >= self.open_time and self.now_time <= self.close_time:
                # 金叉开仓
                if self.am.close[-1] > self.UBUY and self.am.close[-2] <= self.UBUY and self.am.close[-3] < self.UBUY and self.KK:
                    self.cancelAll()
                    self.buy(price=bar.close+0.01, volume=self.fixedSize)
                # 死叉做空
                elif self.am.close[-1] < self.DSELL and self.am.close[-2] >= self.DSELL and self.am.close[-3] > self.DSELL and self.KK:
                    self.cancelAll()
                    self.short(price=bar.close-0.01, volume=self.fixedSize)

        elif self.pos > 0:
            self.intraTradeHigh = max(self.intraTradeHigh, bar.close)
            self.intraTradeLow = min(self.intraTradeLow, bar.close)
            is_sell = False

            if self.intraTradeHigh > self.last_trade_price * (1 + self.ZS/1000*3) and bar.close - self.last_trade_price <= (self.intraTradeHigh - self.last_trade_price) * 0.4:
                is_sell = True
            if self.intraTradeHigh > self.last_trade_price * (1 + self.ZS/1000*6) and bar.close - self.last_trade_price <= (self.intraTradeHigh - self.last_trade_price) * 0.5:
                is_sell = True

            yuzhi = self.last_trade_price * (1 - self.ZS/1000) # 阈值缓存
            if self.am.close[-1] < yuzhi and self.am.close[-2] >= yuzhi:
                is_sell = True
            yuzhi = self.last_trade_price * (1 + 10 * self.ZS / 1000)
            if self.am.close[-1] > yuzhi and self.am.close[-2] <= yuzhi:
                is_sell = True
            if is_sell:
                self.cancelAll()
                self.sell(price=bar.close - 0.01, volume=self.fixedSize)

        elif self.pos < 0:
            self.intraTradeHigh = max(self.intraTradeHigh, bar.close)
            self.intraTradeLow = min(self.intraTradeLow, bar.close)
            is_cover = False

            if self.intraTradeLow < self.last_trade_price * (1 - self.ZS/1000*3) and self.last_trade_price - bar.close <= (self.last_trade_price - self.intraTradeLow) * 0.5:
                is_cover = True
            if self.intraTradeLow < self.last_trade_price * (1 - self.ZS/1000*6) and self.last_trade_price - bar.close <= (self.last_trade_price - self.intraTradeLow) * 0.7:
                is_cover = True

            yuzhi = self.last_trade_price * (1 + self.ZS/1000)
            if self.am.close[-1] > yuzhi and self.am.close[-2] <= yuzhi:
                is_cover = True
            yuzhi = self.last_trade_price * (1 - 10 * self.ZS / 1000)
            if self.am.close[-1] < yuzhi and self.am.close[-2] >= yuzhi:
                is_cover = True
            if is_cover:
                self.cancelAll()
                self.cover(price=bar.close + 0.01, volume=self.fixedSize)


    def onOrder(self, order):
        """收到委托变化推送(必须由用户继承实现)"""
        pass

    def onTrade(self, trade):
        """收到交易发生事件推送"""
        self.last_trade_price = trade.price
        print("发生交易")
        print(trade.__dict__)
        print("\n")
        self.putEvent()

    def onStopOrder(self, so):
        """收到停止单事件推送"""
        self.putEvent()

    def closeAllPosition(self, bar):
        self.cancelAll()
        if self.pos > 0:
            self.sell(price=bar.close - 0.01, volume=self.fixedSize)
        elif self.pos < 0:
            self.cover(price=bar.close  + 0.01, volume=self.fixedSize)
Exemple #5
0
class tick_strategy(CtaTemplate):
    """结合ATR和RSI指标的一个分钟线交易策略"""
    className = 'CN'
    author = u'张安翔'
    buyOrderIDList = []  # 委托买入开仓的委托号
    shortOrderIDList = []  # 委托卖出开仓的委托号
    orderList = []  # 保存委托代码的列表

    # 参数列表,保存了参数的名称
    paramList = [
        'name', 'className', 'author', 'vtSymbol', 'maxHold', 'maxWin',
        'minLose', 'mutiple', 'volume', 'init_asset', 'minEnterPrice',
        'MaxTolerate'
    ]

    # 变量列表,保存了变量的名称
    varList = [
        'LongSignalSpread', 'ShortSignalSpread', 'pos', 'atrValue', 'atrMa',
        'rsiValue', 'rsiBuy', 'rsiSell'
    ]

    def __init__(self, ctaEngine, setting):

        # 策略参数
        self.contract_size = 100
        self.MaxTolerate = 0
        self.maxHold = 0
        self.Ticks = 4  #追单数量
        self.stopping = False
        self.stoppingdt = None  # 止损后最小开仓时间
        self.stoppPeriods = 15  # 止损后15分钟不能开仓
        self.ShortSignalSpread = None
        self.LongSignalSpread = None
        self.short_enter_price = None
        self.short_exit_price = None
        self.long_enter_price = None
        self.long_exit_price = None
        self.maxHold = 0
        self.maxWin = 0
        self.minLose = 0
        self.mutiple = 0
        self.minEnterPrice = 0
        self.volume = 0
        self.init_asset = 0
        """Constructor"""
        super(tick_strategy, self).__init__(ctaEngine, setting)

        # 策略变量
        self.bm = BarManager(self.onBar, 1)  # 创建K线合成器对象
        self.am = ArrayManager()

        # # time.sleep(5)
        # ct = VtContractData()
        # ct.gatewayName = "ONETOKEN"
        # ct.symbol = 'huobip/btc.usdt'
        # ct.exchange = 'huobip'
        # ct.vtSymbol = 'huobip/btc.usdt'
        # self.ctaEngine.mainEngine.dataEngine.contractDict['huobip/btc.usdt'] = ct
        # self.ctaEngine.mainEngine.dataEngine.saveContracts()
        # ctaEngine.subscribe(req, 'ONETOKEN')

    def onInit(self):
        """初始化策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略初始化' % self.name)

        # 载入历史数据,并采用回放计算的方式初始化策略数值

        # initData = self.loadBar(self.initDays)
        # for bar in initData:
        #     self.onBar(bar)

    def onStart(self):
        """启动策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略启动' % self.name)
        self.putEvent()

    def onStop(self):
        """停止策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略停止' % self.name)
        self.putEvent()

    def onTick(self, tick):
        """收到行情TICK推送(必须由用户继承实现)"""
        self.bm.updateTick(tick)
        dt = tick.datetime
        # TODO: 计算spread
        preSpread = self.spread
        self.spread = new_spread  # 更近spread
        spread = self.spread

        price = tick.lastPrice
        if self.long_enter:  # 判断信号
            if self.LongSignalSpread + self.MaxTolerate > spread:
                self.buyOrderIDList.append(self.buy(price, 1))  # 开多
                self.count = 0
                self.maxdt = dt + datetime.timedelta(
                    minutes=self.maxHold)  # 只持仓到那个时间点
                self.minExitdt = dt + datetime.timedelta(
                    minutes=0.5)  # TODO:半分钟,or不要?
            else:
                if self.count == self.Ticks:
                    self.qidan(price, dt, spread, direction='long')
                    self.count = 0

                else:
                    self.count += 1

        elif self.short_enter:
            if self.ShortSignalSpread - self.MaxTolerate < spread:
                self.shortOrderIDList.append(self.short(price, 1))  # 卖空
                self.count = 0
                self.maxdt = dt + datetime.timedelta(minutes=self.maxHold)
                self.minExitdt = dt + datetime.timedelta(minutes=0.5)  # TODO

            else:  # 经过几个tick后还未达到想要的价格,就弃单
                if self.count == self.Ticks:
                    self.qidan(price, dt, spread, direction='short')
                    self.count = 0

                else:
                    self.count += 1

        elif self.short_exit:
            if self.count == self.Ticks:
                self.buyOrderIDList.append(self.cover(price, 1))  # 平空
                self.count = 0
                if self.stopwinning or self.stopLosing:
                    self.stopping = True
                    self.stoppingdt = dt + datetime.timedelta(
                        minutes=self.stoppPeriods)
                    self.maxdt = None
                    self.minExitdt = None

                else:
                    self.maxdt = None
                    self.minExitdt = None
                    self.minEnterdt = dt + datetime.timedelta(minutes=0.5)

            else:
                self.count += 1

        elif self.long_exit:
            if self.count == self.Ticks:
                self.shortOrderIDList.append(self.sell(price, 1))  # 平多
                self.count = 0
                if self.stopwinning or self.stopLosing:
                    self.stopping = True
                    self.stoppingdt = dt + datetime.timedelta(
                        minutes=self.stoppPeriods)
                    self.maxdt = None
                    self.minExitdt = None

                else:
                    self.maxdt = None
                    self.minExitdt = None
                    self.minEnterdt = dt + datetime.timedelta(minutes=0.5)
            else:
                self.count += 1

        self.ResetSignal()
        dtAbove = datetime.datetime.combine(dt.date(),
                                            datetime.time(dt.hour, dt.minute))
        try:
            # TODO: 需要计算Std,std是一小时1min_bar的收盘价的std
            std = self.cal_std()
        except:
            std = np.nan

        # 开空仓信号
        if not self.stopping:
            if self.pos == 0:
                if dt > self.minEnterdt:  # 如果满足最小进场时间
                    if preSpread:
                        if ((spread - preSpread) > self.minEnterPrice
                            ) or (spread - preSpread) > +self.mutiple * std:
                            self.short_enter = True
                            self.ShortSignalSpread = spread

                        elif ((preSpread - spread) > self.minEnterPrice
                              ) or (preSpread - spread) > self.mutiple * std:
                            self.long_enter = True
                            self.LongSignalSpread = spread
                        else:
                            pass

            if self.pos == -1:
                if dt > self.minExitdt:  # 如果满足最小进场时间
                    if dt >= self.maxdt:  # 如果超过最大持仓时间
                        self.short_exit = True
                        self.ShortSignalSpread = spread

                    else:
                        if (self.short_enter_price -
                                spread) > self.maxWin:  # 止盈
                            self.short_exit = True
                            self.stopwinning = True
                            self.ShortSignalSpread = spread

                        elif (spread -
                              self.short_enter_price) > self.minLose:  # 止损
                            self.short_exit = True
                            self.stopLosing = True
                            self.ShortSignalSpread = spread
                        else:
                            pass

            if self.pos == 1:  # 若有仓位
                if dt > self.minExitdt:  # 若符合最小进场时间
                    if dt >= self.maxdt:  # 若超过最大持仓周期
                        self.long_exit = True
                        self.LongSignalSpread = spread

                    else:
                        if (spread -
                                self.long_enter_price) > self.maxWin:  # 止盈
                            self.long_exit = True
                            self.stopwinning = True
                            self.LongSignalSpread = spread

                        elif (self.long_enter_price -
                              spread) > self.minLose:  # 止损
                            self.long_exit = True
                            self.stopLosing = True
                            self.LongSignalSpread = spread

                        else:
                            pass
        else:
            if dt >= self.stoppingdt:
                self.stopping = False
                self.stoppingdt = None

    def updateTick(self, event):
        print('-' * 30, 'strategy_updateTick', '-' * 30)

    def onBar(self, bar):
        self.bm.updateBar(bar)

    '''交易后的账户余额或保证金'''

    def onOrder(self, order):
        """收到委托变化推送(必须由用户继承实现)"""
        # print(order)
        # self.writeCtaLog(u'委托变化推送: %s' % order)
        # pass

    def onTrade(self, trade):
        if self.pos != 0:
            # 多头开仓成交后,撤消空头委托
            if self.pos > 0:
                for shortOrderID in self.shortOrderIDList:
                    self.cancelOrder(shortOrderID)
            # 反之同样
            elif self.pos < 0:
                for buyOrderID in self.buyOrderIDList:
                    self.cancelOrder(buyOrderID)

            # 移除委托号
            for orderID in (self.buyOrderIDList + self.shortOrderIDList):
                if orderID in self.orderList:
                    self.orderList.remove(orderID)

        # 发出状态更新事件
        self.putEvent()

    def ResetSignal(self):
        self.long_enter = False
        self.long_exit = False
        self.short_enter = False
        self.short_exit = False
        self.stopwinning = False
        self.stopLosing = False

    def cal_std(self):
        pass

    def getPos(self):
        """获取cn仓位"""
        # 0:空仓,1:做多,-1:做空 老版本的,已废除
        # fileName = 'CTA_setting.json'
        # try:
        #     f = file(fileName)
        # except IOError:
        #     print('读取param参数配置出错,请检查')
        # # 解析json文件-----------------------------------------------------
        # mysetting = json.load(f)
        # if self.pair == 'sc_wti':
        #     cnvtSymbol = str(mysetting[0]['vtSymbol'])
        #     usvtSymbol = str(mysetting[1]['vtSymbol']).split('.')[0]
        # elif self.pair == 'brent_sc':
        #     cnvtSymbol = str(mysetting[3]['vtSymbol'])
        #     usvtSymbol = str(mysetting[2]['vtSymbol']).split('.')[0]
        # reqID = self.api.getTrades({'instrument': usvtSymbol})
        # cnPosObj = self.banzhuan_query_position(cnvtSymbol)
        cnPosObj = self.banzhuan_query_position()  # 已改为不传参数,待测试是否有效
        # print '-------------------------昨仓今仓-------------------------\nlongYd:', cnPosObj.longYd, '\nlongTd:', cnPosObj.longTd, '\nshortYd:', cnPosObj.shortYd, '\nshortTd:', cnPosObj.shortTd
        # self.longYd = cnPosObj.longYd   # vtEngine.py 633行。昨仓今仓分别获取。
        # self.longTd = cnPosObj.longTd
        # self.shortYd = cnPosObj.shortYd
        # self.shortTd = cnPosObj.shortTd
        self.r.set('cnpositionlongYd', cnPosObj.longYd)
        self.r.set('cnpositionlongTd', cnPosObj.longTd)
        self.r.set('cnpositionshortYd', cnPosObj.shortYd)
        self.r.set('cnpositionshortTd', cnPosObj.shortTd)
class BollChannelStrategy(CtaTemplate):
    """基于布林通道的交易策略"""
    className = 'BollChannelStrategy'
    author = u'用Python的交易员'

    # 策略参数
    bollWindow = 18                     # 布林通道窗口数
    bollDev = 3.4                       # 布林通道的偏差
    cciWindow = 10                      # CCI窗口数
    atrWindow = 30                      # ATR窗口数
    slMultiplier = 5.2                  # 计算止损距离的乘数
    initDays = 10                       # 初始化数据所用的天数
    fixedSize = 1                       # 每次交易的数量

    # 策略变量
    bollUp = 0                          # 布林通道上轨
    bollDown = 0                        # 布林通道下轨
    cciValue = 0                        # CCI指标数值
    atrValue = 0                        # ATR指标数值
    
    intraTradeHigh = 0                  # 持仓期内的最高点
    intraTradeLow = 0                   # 持仓期内的最低点
    longStop = 0                        # 多头止损
    shortStop = 0                       # 空头止损

    # 参数列表,保存了参数的名称
    paramList = ['name',
                 'className',
                 'author',
                 'vtSymbol',
                 'bollWindow',
                 'bollDev',
                 'cciWindow',
                 'atrWindow',
                 'slMultiplier',
                 'initDays',
                 'fixedSize']    

    # 变量列表,保存了变量的名称
    varList = ['inited',
               'trading',
               'pos',
               'bollUp',
               'bollDown',
               'cciValue',
               'atrValue',
               'intraTradeHigh',
               'intraTradeLow',
               'longStop',
               'shortStop']  

    #----------------------------------------------------------------------
    def __init__(self, ctaEngine, setting):
        """Constructor"""
        super(BollChannelStrategy, self).__init__(ctaEngine, setting)
        
        self.bm = BarManager(self.onBar, 15, self.onXminBar)        # 创建K线合成器对象
        self.am = ArrayManager()
        
    #----------------------------------------------------------------------
    def onInit(self):
        """初始化策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略初始化' %self.name)
        
        # 载入历史数据,并采用回放计算的方式初始化策略数值
        initData = self.loadBar(self.initDays)
        for bar in initData:
            self.onBar(bar)

        self.putEvent()

    #----------------------------------------------------------------------
    def onStart(self):
        """启动策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略启动' %self.name)
        self.putEvent()

    #----------------------------------------------------------------------
    def onStop(self):
        """停止策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略停止' %self.name)
        self.putEvent()

    #----------------------------------------------------------------------
    def onTick(self, tick):
        """收到行情TICK推送(必须由用户继承实现)""" 
        self.bm.updateTick(tick)

    #----------------------------------------------------------------------
    def onBar(self, bar):
        """收到Bar推送(必须由用户继承实现)"""
        self.bm.updateBar(bar)
    
    #----------------------------------------------------------------------
    def onXminBar(self, bar):
        """收到X分钟K线"""
        # 全撤之前发出的委托
        self.cancelAll()
    
        # 保存K线数据
        am = self.am
        
        am.updateBar(bar)
        
        if not am.inited:
            return
        
        # 计算指标数值
        self.bollUp, self.bollDown = am.boll(self.bollWindow, self.bollDev)
        self.cciValue = am.cci(self.cciWindow)
        self.atrValue = am.atr(self.atrWindow)
        
        # 判断是否要进行交易
    
        # 当前无仓位,发送开仓委托
        if self.pos == 0:
            self.intraTradeHigh = bar.high
            self.intraTradeLow = bar.low            
            
            if self.cciValue > 0:
                self.buy(self.bollUp, self.fixedSize, True)
                
            elif self.cciValue < 0:
                self.short(self.bollDown, self.fixedSize, True)
    
        # 持有多头仓位
        elif self.pos > 0:
            self.intraTradeHigh = max(self.intraTradeHigh, bar.high)
            self.intraTradeLow = bar.low
            self.longStop = self.intraTradeHigh - self.atrValue * self.slMultiplier
            
            self.sell(self.longStop, abs(self.pos), True)
    
        # 持有空头仓位
        elif self.pos < 0:
            self.intraTradeHigh = bar.high
            self.intraTradeLow = min(self.intraTradeLow, bar.low)
            self.shortStop = self.intraTradeLow + self.atrValue * self.slMultiplier
            
            self.cover(self.shortStop, abs(self.pos), True)
    
        # 发出状态更新事件
        self.putEvent()        

    #----------------------------------------------------------------------
    def onOrder(self, order):
        """收到委托变化推送(必须由用户继承实现)"""
        pass

    #----------------------------------------------------------------------
    def onTrade(self, trade):
        # 发出状态更新事件
        self.putEvent()

    #----------------------------------------------------------------------
    def onStopOrder(self, so):
        """停止单推送"""
        pass
Exemple #7
0
class BollChannelStrategy(CtaTemplate):
    """基于布林通道的交易策略"""
    className = 'BollChannelStrategy'
    author = u'用Python的交易员'

    # 策略参数
    bollWindow = 18                     # 布林通道窗口数
    bollDev = 3.4                       # 布林通道的偏差
    cciWindow = 10                      # CCI窗口数
    atrWindow = 30                      # ATR窗口数
    slMultiplier = 5.2                  # 计算止损距离的乘数
    initDays = 10                       # 初始化数据所用的天数
    fixedSize = 1                       # 每次交易的数量

    # 策略变量
    bollUp = 0                          # 布林通道上轨
    bollDown = 0                        # 布林通道下轨
    cciValue = 0                        # CCI指标数值
    atrValue = 0                        # ATR指标数值
    
    intraTradeHigh = 0                  # 持仓期内的最高点
    intraTradeLow = 0                   # 持仓期内的最低点
    longStop = 0                        # 多头止损
    shortStop = 0                       # 空头止损

    # 参数列表,保存了参数的名称
    paramList = ['name',
                 'className',
                 'author',
                 'vtSymbol',
                 'bollWindow',
                 'bollDev',
                 'cciWindow',
                 'atrWindow',
                 'slMultiplier',
                 'initDays',
                 'fixedSize']    

    # 变量列表,保存了变量的名称
    varList = ['inited',
               'trading',
               'pos',
               'bollUp',
               'bollDown',
               'cciValue',
               'atrValue',
               'intraTradeHigh',
               'intraTradeLow',
               'longStop',
               'shortStop']  
    
    # 同步列表,保存了需要保存到数据库的变量名称
    syncList = ['pos',
                'intraTradeHigh',
                'intraTradeLow']    

    #----------------------------------------------------------------------
    def __init__(self, ctaEngine, setting):
        """Constructor"""
        super(BollChannelStrategy, self).__init__(ctaEngine, setting)
        
        self.bm = BarManager(self.onBar, 15, self.onXminBar)        # 创建K线合成器对象
        self.bm30 = BarManager(self.onBar, 30, self.on30minBar)
        self.am = ArrayManager()
        
    #----------------------------------------------------------------------
    def on30minBar(self, bar):
        """"""
        
        
    #----------------------------------------------------------------------
    def onInit(self):
        """初始化策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略初始化' %self.name)
        
        # 载入历史数据,并采用回放计算的方式初始化策略数值
        initData = self.loadBar(self.initDays)
        for bar in initData:
            self.onBar(bar)

        self.putEvent()

    #----------------------------------------------------------------------
    def onStart(self):
        """启动策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略启动' %self.name)
        self.putEvent()

    #----------------------------------------------------------------------
    def onStop(self):
        """停止策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略停止' %self.name)
        self.putEvent()

    #----------------------------------------------------------------------
    def onTick(self, tick):
        """收到行情TICK推送(必须由用户继承实现)""" 
        self.bm.updateTick(tick)

    #----------------------------------------------------------------------
    def onBar(self, bar):
        """收到Bar推送(必须由用户继承实现)"""
        self.bm.updateBar(bar)
    
    #----------------------------------------------------------------------
    def onXminBar(self, bar):
        """收到X分钟K线"""
        # 全撤之前发出的委托
        self.cancelAll()
    
        # 保存K线数据
        am = self.am
        
        am.updateBar(bar)
        
        if not am.inited:
            return
        
        # 计算指标数值
        self.bollUp, self.bollDown = am.boll(self.bollWindow, self.bollDev)
        self.cciValue = am.cci(self.cciWindow)
        self.atrValue = am.atr(self.atrWindow)
        
        # 判断是否要进行交易
    
        # 当前无仓位,发送开仓委托
        if self.pos == 0:
            self.intraTradeHigh = bar.high
            self.intraTradeLow = bar.low            
            
            if self.cciValue > 0:
                self.buy(self.bollUp, self.fixedSize, True)
                
            elif self.cciValue < 0:
                self.short(self.bollDown, self.fixedSize, True)
    
        # 持有多头仓位
        elif self.pos > 0:
            self.intraTradeHigh = max(self.intraTradeHigh, bar.high)
            self.intraTradeLow = bar.low
            self.longStop = self.intraTradeHigh - self.atrValue * self.slMultiplier
            
            self.sell(self.longStop, abs(self.pos), True)
    
        # 持有空头仓位
        elif self.pos < 0:
            self.intraTradeHigh = bar.high
            self.intraTradeLow = min(self.intraTradeLow, bar.low)
            self.shortStop = self.intraTradeLow + self.atrValue * self.slMultiplier
            
            self.cover(self.shortStop, abs(self.pos), True)
            
        # 同步数据到数据库
        self.saveSyncData()        
    
        # 发出状态更新事件
        self.putEvent()        

    #----------------------------------------------------------------------
    def onOrder(self, order):
        """收到委托变化推送(必须由用户继承实现)"""
        pass

    #----------------------------------------------------------------------
    def onTrade(self, trade):
        # 发出状态更新事件
        self.putEvent()

    #----------------------------------------------------------------------
    def onStopOrder(self, so):
        """停止单推送"""
        pass
Exemple #8
0
class BarMStrategy(CtaTemplate):
    """多周期策略"""
    className = 'BarMStrategy'
    author = u'toriphy'

    # 策略参数
    kkLength = 15  # 计算通道中值的窗口数
    kkDevUp = 2.2  # 计算通道宽度的偏差 取2.1或者2.2比较好
    kkDevDown = 1.9  # 初始值1.9
    trailingPrcnt = 1.1  # 移动止损, 初始值1.2
    thresholdRatio = 0.15  # 持仓量指标阈值
    initDays = 10  # 初始化数据所用的天数,注意这个值是天数而不是bar的个数
    fixedSize = 1  # 每次交易的数量
    barBin = 5  # 五分钟线 短周期
    barLongBin = 15  # 十五分钟线,长周期

    shortMAperiod = 6
    longMAperiod = 12

    bufferSize = 100  # 需要缓存的数据的大小 65
    longCycleBufferSize = 30  # 长周期需要缓存的数据大小 20
    bufferCount = 0  # 目前已经缓存了的数据的计数
    longCycleBufferCount = 0  # 长周期目前已经缓存了的数据的计数

    longCycleTradingFlag = 0  # 长周期交易信号

    atrValue = 0  # 最新的ATR指标数值
    kkMid = 0  # KK通道中轨
    kkUp = 0  # KK通道上轨
    kkDown = 0  # KK通道下轨
    openRatioModi = 0  # 开仓指标调整
    intraTradeHigh = 0  # 持仓期内的最高点
    intraTradeLow = 0  # 持仓期内的最低点
    longStop = 0  # 多头的移动止损点位
    shortStop = 0  # 空头的移动止损点位

    openRatioModiArray = np.zeros(bufferSize)
    longCycleLLTArrayShort = np.zeros(longCycleBufferSize)  # 短周期部分 ,alpha

    # 其他不相关的
    buyOrderID = None  # OCO委托买入开仓的委托号
    shortOrderID = None  # OCO委托卖出开仓的委托号
    orderList = []  # 保存委托代码的列表

    # 参数列表,保存了参数的名称
    paramList = [
        'name', 'className', 'author', 'vtSymbol', 'kkLength', 'kkDevUp',
        'kkDevDown', 'thresholdRatio', 'trailingPrcnt'
    ]

    # 变量列表,保存了变量的名称
    varList = [
        'inited', 'trading', 'pos', 'kkMid', 'kkUp', 'kkDown',
        'barOpenRatioModi', 'longStop', 'shortStop', 'longCycleTradingFlag'
    ]

    # 同步列表,保存了需要保存到数据库的变量名称
    syncList = ['pos', 'longStop', 'shortStop']  # 持仓数据和trailing stop 是一定要保存的

    # ----------------------------------------------------------------------
    def __init__(self, ctaEngine, setting):
        """Constructor"""
        super(BarMStrategy, self).__init__(ctaEngine, setting)

        # 创建K线合成器对象
        self.bm = BarManager(self.onBar)  # 1分钟线(因为要调取1分钟线的持仓量和成交量信息)
        self.am = ArrayManager()

        self.bm5 = BarManager(self.onBar, self.barBin, self.on5MinBar)
        self.am5 = ArrayManager()

        self.bm15 = BarManager(self.onBar, self.barLongBin, self.on15MinBar)
        self.am15 = ArrayManager(size=self.longCycleBufferSize)

    # ----------------------------------------------------------------------
    def onInit(self):
        """初始化策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略初始化' % self.name)

        # 载入历史数据,并采用回放计算的方式初始化策略数值
        initData = self.loadBar(self.initDays)
        for bar in initData:
            self.onBar(bar)

        self.putEvent()

    # ----------------------------------------------------------------------
    def onStart(self):
        """启动策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略启动' % self.name)
        self.putEvent()

    # ----------------------------------------------------------------------
    def onStop(self):
        """停止策略(必须由用户继承实现)"""
        self.writeCtaLog(u'%s策略停止' % self.name)
        self.putEvent()

    # ----------------------------------------------------------------------
    def onTick(self, tick):
        """收到行情TICK推送(必须由用户继承实现)"""
        self.bm.updateTick(tick)

    # ----------------------------------------------------------------------
    def onBar(self, bar):
        """收到Bar推送(必须由用户继承实现)"""
        # 基于15分钟判断趋势过滤,因此先更新
        self.am.updateBar(bar)
        self.vArray1min = self.am.volume[-5:]
        self.oArray1min = self.am.openInterest[-6:]

        self.bm15.updateBar(bar)

        # 基于5分钟判断
        self.bm5.updateBar(bar)

    # ----------------------------------------------------------------------
    def on5MinBar(self, bar):
        """收到5分钟K线"""
        self.cancelAll()

        # 保存K线数据

        self.am5.updateBar(bar)

        barOpenRatioModi = ((np.sqrt(self.vArray1min) / np.sqrt(self.vArray1min).sum()) * ((self.oArray1min[1:] - \
                            self.oArray1min[:-1]) / self.vArray1min)).mean()

        self.openRatioModiArray[0:self.bufferSize -
                                1] = self.openRatioModiArray[1:self.bufferSize]
        self.openRatioModiArray[-1] = barOpenRatioModi  # 开仓比例

        self.bufferCount += 1

        if self.bufferCount < self.bufferSize:
            return

        # 计算指标数值
        self.atrValue = talib.ATR(self.am5.high, self.am5.low, self.am5.close,
                                  self.kkLength)[-1]
        self.kkMid = talib.MA(self.am5.close, self.kkLength)[-1]
        self.kkUp = self.kkMid + self.atrValue * self.kkDevUp
        self.kkDown = self.kkMid - self.atrValue * self.kkDevDown

        self.openRatioModi = self.openRatioModiArray[-1]  # 开仓量指标

        conditionKKBuy = self.am5.close[-1] > self.kkUp
        conditionKKSell = self.am5.close[-1] < self.kkDown

        conditionOpenRatioModiBuy = self.openRatioModi > 0.022
        conditionOpenRatioModiSell = self.openRatioModi > 0.026  # 最好0.026

        # 当前无仓位,
        if self.pos == 0:
            self.intraTradeHigh = bar.high
            self.intraTradeLow = bar.low
            if conditionKKBuy and conditionOpenRatioModiBuy and self.longCycleTradingFlag == 1:
                self.buy(bar.close + 5, self.fixedSize)
            elif conditionKKSell and conditionOpenRatioModiSell and self.longCycleTradingFlag == -1:
                self.short(bar.close - 5, self.fixedSize)

        # 持有多头仓位
        elif self.pos > 0:
            self.intraTradeHigh = max(self.intraTradeHigh, bar.high)
            self.intraTradeLow = bar.low

            self.longStop = self.intraTradeHigh * (1 -
                                                   self.trailingPrcnt / 100)
            orderID = self.sell(self.longStop, abs(self.pos), stop=True)
            self.writeCtaLog(u'多头止损价格:' + str(self.longStop))
            self.orderList.append(orderID)

        # 持有空头仓位
        elif self.pos < 0:
            self.intraTradeHigh = bar.high
            self.intraTradeLow = min(self.intraTradeLow, bar.low)

            self.shortStop = self.intraTradeLow * (1 +
                                                   self.trailingPrcnt / 100)
            orderID = self.cover(self.shortStop, abs(self.pos), stop=True)

            self.writeCtaLog(u'空头头止损价格:' + str(self.shortStop))
            self.orderList.append(orderID)

        # 发出状态更新事件
        self.putEvent()

    # ----------------------------------------------------------------------
    def on15MinBar(self, bar):
        """15分钟K线推送"""
        #print 'cycletime:', bar.datetime, self.longCycleTradingFlag
        self.am15.updateBar(bar)

        alpha = 0.3  # 5分钟线取0.3最佳
        alphatilde = 2 / (12 + 1)

        LLTShort = (alpha - (alpha ** 2) / 4) * self.am15.close[-1] + (alpha ** 2) / 2 * \
                   self.am15.close[-2] - \
                   (alpha - 3 * alpha ** 2 / 4) * self.am15.close[-3] + 2 * (1 - alpha) * \
                   self.longCycleLLTArrayShort[-1] - (1 - alpha ** 2) * self.longCycleLLTArrayShort[-2]

        self.longCycleLLTArrayShort[0:self.longCycleBufferSize -
                                    1] = self.longCycleLLTArrayShort[
                                        1:self.longCycleBufferSize]
        self.longCycleLLTArrayShort[-1] = LLTShort

        #print self.am15.close[-10:],"\n","LLT:", LLTShort

        self.longCycleBufferCount += 1
        if self.longCycleBufferCount < self.longCycleBufferSize:
            return

        longCycleMAlong = talib.MA(self.am15.close, self.longMAperiod)
        longCycleMAshort = talib.MA(self.am15.close, self.shortMAperiod)

        maLongCondition = longCycleMAshort[-1] > longCycleMAlong[-1]
        lltLongCondition = self.longCycleLLTArrayShort[
            -1] > self.longCycleLLTArrayShort[-2]
        lltShortCondition = self.longCycleLLTArrayShort[
            -1] < self.longCycleLLTArrayShort[-2]
        if maLongCondition:
            self.longCycleTradingFlag = 1
        elif lltShortCondition:
            self.longCycleTradingFlag = -1
        else:
            self.longCycleTradingFlag = 0

    # ----------------------------------------------------------------------
    def onOrder(self, order):
        """收到委托变化推送(必须由用户继承实现)"""
        pass

    #----------------------------------------------------------------------
    def onStopOrder(self, so):
        """停止单推送"""
        pass

    # ----------------------------------------------------------------------
    def onTrade(self, trade):
        """发出状态更新事件"""
        self.putEvent()

    # ----------------------------------------------------------------------
    def sendOcoOrder(self, buyPrice, shortPrice, volume):
        """
        发送OCO委托

        OCO(One Cancel Other)委托:
        1. 主要用于实现区间突破入场
        2. 包含两个方向相反的停止单
        3. 一个方向的停止单成交后会立即撤消另一个方向的
        """
        # 发送双边的停止单委托,并记录委托号
        self.buyOrderID = self.buy(buyPrice, volume, True)
        self.shortOrderID = self.short(shortPrice, volume, True)

        # 将委托号记录到列表中
        self.orderList.append(self.buyOrderID)
        self.orderList.append(self.shortOrderID)