بلنگ نے یکساں طور پر _vnpy_botvs کی توسیع کی

مصنف:ipqhjjybj، تاریخ: 2017-08-27 20:38:30
ٹیگز:بولنگرپیش رفت

یہ ایک ایسا طریقہ ہے جس میں botvs کے انٹرفیس کو Vnpy کے ساتھ آسانی سے پیک کیا جاتا ہے تاکہ اسے بعد میں بلایا جا سکے! یہ فیوچر کی حکمت عملی تھی ، جو بٹ کوائن پر براہ راست پیرامیٹرز کو تبدیل کرتی تھی۔ فیوچر پر منٹ کی سطح پر تبادلہ کیا جاتا ہے جبکہ بٹ کوائن فیوچر پر گھنٹے کی سطح پر تبادلہ کیا جاتا ہے۔ اس کا مطلب یہ ہے کہ آپ کو اس کی ضرورت نہیں ہے. اگر آپ کی پالیسی میں بہتری آتی ہے تو براہ کرم اپنے آپ سے رابطہ کریں 250657661

       bar.minute.hour  代表是小时级别 
       bar.minute.minute  代表是分钟级别

'''
策略名称: BollingBreaker趋势策略
策略作者: ipqhjjybj
策略描述:  
           这是将 botvs的接口用 Vnpy 的写法 方式简单封装掉,便于后期的调用!
           这本来是期货的 策略, 直接改参数套在 比特币上。
           期货上要切换到分钟级别, 比特币期货则用小时级别的
           实盘时需要调整参数。
           如有策略改进,请多多与本人交流   250657661

           bar.minute.hour  代表是小时级别 
           bar.minute.minute  代表是分钟级别

           
------------------------------------------------------------------

          当前只支持 比特币OKCOIN 期货, 如果要弄到 CTP期货,需要微调

趋势跟踪策略
'''
import time
from datetime import datetime
import numpy as np
import talib

EMPTY_STRING = ""
EMPTY_INT = 0
EMPTY_FLOAT = 0.0
EMPTY_UNICODE = u''

DIRECTION_LONG = u'long'
DIRECTION_SHORT = u'short'

OFFSET_OPEN = u'kaicang'
OFFSET_CLOSE = u'pingcang'

# CTA引擎中涉及到的交易方向类型
CTAORDER_BUY = "buy"
CTAORDER_SELL = "closebuy"
CTAORDER_SHORT = "sell"
CTAORDER_COVER = "closesell"


# 本地停止单状态
STOPORDER_WAITING = u'waiting'
STOPORDER_CANCELLED = u'canceled'
STOPORDER_TRIGGERED = u'touched'

# 本地停止单前缀
STOPORDERPREFIX = 'CtaStopOrder'



########################################################################
class VtBarData:
    """K线数据"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        
        self.vtSymbol = EMPTY_STRING        # vt系统代码
        self.symbol = EMPTY_STRING          # 代码
        self.exchange = EMPTY_STRING        # 交易所
    
        self.open = EMPTY_FLOAT             # OHLC
        self.high = EMPTY_FLOAT
        self.low = EMPTY_FLOAT
        self.close = EMPTY_FLOAT
        
        self.date = EMPTY_STRING            # bar开始的时间,日期
        self.time = EMPTY_STRING            # 时间
        self.datetime = None                # python的datetime时间对象
        
        self.volume = EMPTY_INT             # 成交量
        self.openInterest = EMPTY_INT       # 持仓量    

########################################################################
class VtTickData:
    """Tick行情数据类"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        
        # 代码相关
        self.exchange = EMPTY_STRING            # 交易所代码
        self.vtSymbol = EMPTY_STRING            # 合约在vt系统中的唯一代码,通常是 合约代码.交易所代码
        
        # 成交数据
        self.lastPrice = EMPTY_FLOAT            # 最新成交价
        self.lastVolume = EMPTY_INT             # 最新成交量
        self.volume = EMPTY_INT                 # 今天总成交量
        self.openInterest = EMPTY_INT           # 持仓量
        self.time = EMPTY_STRING                # 时间 11:20:56.5
        self.date = EMPTY_STRING                # 日期 20151009
        self.datetime = None                    # python的datetime时间对象
        
        # 常规行情
        self.openPrice = EMPTY_FLOAT            # 今日开盘价
        self.highPrice = EMPTY_FLOAT            # 今日最高价
        self.lowPrice = EMPTY_FLOAT             # 今日最低价
        self.preClosePrice = EMPTY_FLOAT
        
        self.upperLimit = EMPTY_FLOAT           # 涨停价
        self.lowerLimit = EMPTY_FLOAT           # 跌停价
        
        # 五档行情
        self.bidPrice1 = EMPTY_FLOAT
        self.bidPrice2 = EMPTY_FLOAT
        self.bidPrice3 = EMPTY_FLOAT
        self.bidPrice4 = EMPTY_FLOAT
        self.bidPrice5 = EMPTY_FLOAT
        
        self.askPrice1 = EMPTY_FLOAT
        self.askPrice2 = EMPTY_FLOAT
        self.askPrice3 = EMPTY_FLOAT
        self.askPrice4 = EMPTY_FLOAT
        self.askPrice5 = EMPTY_FLOAT        
        
        self.bidVolume1 = EMPTY_INT
        self.bidVolume2 = EMPTY_INT
        self.bidVolume3 = EMPTY_INT
        self.bidVolume4 = EMPTY_INT
        self.bidVolume5 = EMPTY_INT
        
        self.askVolume1 = EMPTY_INT
        self.askVolume2 = EMPTY_INT
        self.askVolume3 = EMPTY_INT
        self.askVolume4 = EMPTY_INT
        self.askVolume5 = EMPTY_INT         


########################################################################
class StopOrder(object):
    """本地停止单"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        self.vtSymbol = EMPTY_STRING
        self.orderType = EMPTY_UNICODE
        self.direction = EMPTY_UNICODE
        self.offset = EMPTY_UNICODE
        self.price = EMPTY_FLOAT
        self.volume = EMPTY_INT
        
        self.strategy = None             # 下停止单的策略对象
        self.stopOrderID = EMPTY_STRING  # 停止单的本地编号 
        self.status = EMPTY_STRING       # 停止单状态


class BollingerBreakerStrategy:
    #品种属性
    vtSymbol = EMPTY_STRING # 是什么品种

    # 策略参数
    minute_use = 6              # 多少分钟级别的K线

    bar = None                  # 1分钟K线对象
    fiveBar = None              # 1分钟K线对象

    # 策略参数
    bollLength = 20         # 通道窗口数
    topDev = 1.3            # 开仓偏差
    trailingPrcnt = 2       # 移动止损百分比
    use_range = 10          # use_range天内有突破最高价
    N = 10                  # 多少天突破

    bufferSize = 40                     # 需要缓存的数据的大小
    bufferCount = 0                     # 目前已经缓存了的数据的计数


    realBuyCond = 0                     # 买卖的状态
    realSellCond = 0                    # 买卖的状态
    
    bollMid = 0                         # 布林带中轨
    bollStd = 0                         # 布林带宽度
    entryUp = 0                         # 开仓上轨

    barMinute = EMPTY_STRING            # K线当前的分钟

    fixedSize = 1

    stopOrderCount = 0                  # 记录停止单的数量

    pos = 0                             # 仓位

    LastBarTime = None                  # python 上一根Tick

    currency = EMPTY_STRING

    def __init__(self, _exchange , setting ):
        self.exchange = _exchange
        for key in setting.keys():
            if key == "vtSymbol":
                self.vtSymbol = setting[key]
            if key == "currency":
                self.currency = setting[key]
            if key == 'minute_use':
                self.minute_use = setting[key]
            if key == "bollLength":
                self.bollLength = setting[key]
            if key == "topDev":
                self.topDev = setting[key]
            if key == "trailingPrcnt":
                self.trailingPrcnt = setting[key]
            if key == "use_range":
                self.use_range = setting[key]
            if key == "N":
                self.N = setting[key]
        Log(setting)

        self.pos = 0
        self.order_PreUse = {}            # vtPreID , pushDealAmount  已经推送过的成交数据
        self.workingStopOrderDict = {}
        self.stopOrderDict = {}
        self.orderList = []               # 保存委托代码的列表
        self.fixedSize = 1
        ##################
        self.bufferSize = 40
        #################
        self.highArray = np.zeros(self.bufferSize) 
        self.lowArray = np.zeros(self.bufferSize)
        self.closeArray = np.zeros(self.bufferSize)
        
        self.buyValue = np.zeros(self.bufferSize)


    def onCall(self):
        try:
            #self.exchange.IO("currency" , self.currency)
            need_remove = []
            for orderId in self.orderList:
                # 订单状态, 参考常量里的订单状态,以下是此键值的常量。
                # ORDER_STATE_PENDING  :未完成
                # ORDER_STATE_CLOSED   :已关闭   已完成
                # ORDER_STATE_CANCELED :已取消
                # STOPORDERPREFIX 是否是 系统内部的 停止单
                if orderId != None and type(orderId) != type(1) and STOPORDERPREFIX in orderId:
                    continue
                botvsOrder = self.exchange.GetOrder(orderId)
                preAmount = 0.0
                if botvsOrder != None:
                    if botvsOrder["Status"] in [ORDER_STATE_CLOSED,ORDER_STATE_CANCELED]:
                        try:
                            preAmount = self.order_PreUse[orderId]
                        except Exception,ex:
                            Log("Error in preAmount",ex)
                            preAmount = 0.0
                        Log("preAmount:" , preAmount)
                        incAmount = botvsOrder["DealAmount"] - preAmount
                        if incAmount > 0:
                            self.order_PreUse[orderId] = botvsOrder["DealAmount"]
                            botvsOrder["preAmount"] = preAmount
                            botvsOrder["incAmount"] = incAmount
                            self.onTrade( botvsOrder )


                    if botvsOrder["Status"] == ORDER_STATE_CLOSED:
                        need_remove.append(orderId)
                else:
                    Log("None order!")

            for orderId in need_remove:
                Log("remove order:" , orderId)
                self.orderList.remove(orderId)

            
            # Log("currency",self.currency)
            botvsTick = self.exchange.GetTicker()


            if self.LastBarTime != botvsTick["Time"]:
                newTick = VtTickData()
                newTick.datetime = datetime.fromtimestamp(botvsTick["Time"] / 1000.0)
                newTick.vtSymbol = self.vtSymbol
                newTick.lastPrice = float(botvsTick["Last"])
                newTick.lastVolume = float(botvsTick["Volume"])
                newTick.volume = float(botvsTick["Volume"])
                newTick.highPrice = float(botvsTick["High"])
                newTick.lowPrice = float(botvsTick["Low"])

                newTick.upperLimit = newTick.highPrice * 1.03
                newTick.lowerLimit = newTick.lowPrice * 0.97

                newTick.exchange = self.exchange.GetName()

                newTick.date = newTick.datetime.strftime("%Y%m%d")
                newTick.time = newTick.datetime.strftime("%Y:%m:%d")

                self.onTick(newTick)

                self.processStopOrder(newTick)
        except Exception,ex:
            Log(ex , "error in onCall , maybe getTicker wrong!")

    #----------------------------------------------------------------------
    def onTrade(self, trade):
        # 发出状态更新事件
        #'Type': 0           # 订单类型, 参考常量里的订单类型,以下是此键值的常量。
                             # ORDER_TYPE_BUY   :买单
                             # ORDER_TYPE_SELL  :卖单
        try:
            Log("trade:",trade)
            newPos = 0.0
            if trade["Type"] == ORDER_TYPE_BUY:
                newPos += trade["incAmount"]
            elif trade["Type"] == ORDER_TYPE_SELL:
                newPos -= trade["incAmount"]
            else:
                Log("What ? trade Type error!")
            self.pos += newPos
        except Exception,ex:
            print ex
    #----------------------------------------------------------------------
    def processStopOrder(self, tick):
        """收到行情后处理本地停止单(检查是否要立即发出)"""
        vtSymbol = tick.vtSymbol
        
        # 遍历等待中的停止单,检查是否会被触发
        for so in self.workingStopOrderDict.values():
            if so.vtSymbol == vtSymbol:
                longTriggered = so.direction==DIRECTION_LONG and tick.lastPrice>=so.price        # 多头停止单被触发
                shortTriggered = so.direction==DIRECTION_SHORT and tick.lastPrice<=so.price     # 空头停止单被触发
                
                if longTriggered or shortTriggered:
                    # 买入和卖出分别以涨停跌停价发单(模拟市价单)
                    if so.direction==DIRECTION_LONG:
                        price = tick.upperLimit
                    else:
                        price = tick.lowerLimit
                    
                    so.status = STOPORDER_TRIGGERED
                    orderIDList = self.sendOrder(so.vtSymbol, so.orderType, price, so.volume, False ,so.strategy)
                    for orderID in orderIDList:
                        self.orderList.append(orderID)
                    del self.workingStopOrderDict[so.stopOrderID]
                    so.strategy.onStopOrder(so)

    def onStopOrder(self, vtStopOrder):
        Log("stopOrder Deal ID:", vtStopOrder.stopOrderID , vtStopOrder.status )

    def sendStopOrder(self, vtSymbol, orderType, price, volume, strategy ):
        """发停止单(本地实现)"""
        self.stopOrderCount += 1
        stopOrderID = STOPORDERPREFIX + str(self.vtSymbol) + str(self.stopOrderCount)

        so = StopOrder()
        so.vtSymbol = vtSymbol
        so.orderType = orderType
        so.price = price
        so.volume = volume
        so.strategy = strategy
        so.stopOrderID = stopOrderID
        so.status = STOPORDER_WAITING

        if orderType == CTAORDER_BUY:
            so.direction = DIRECTION_LONG
            so.offset = OFFSET_OPEN
        elif orderType == CTAORDER_SELL:
            so.direction = DIRECTION_SHORT
            so.offset = OFFSET_CLOSE
        elif orderType == CTAORDER_SHORT:
            so.direction = DIRECTION_SHORT
            so.offset = OFFSET_OPEN
        elif orderType == CTAORDER_COVER:
            so.direction = DIRECTION_LONG
            so.offset = OFFSET_CLOSE      

        # 保存stopOrder对象到字典中
        self.stopOrderDict[stopOrderID] = so
        self.workingStopOrderDict[stopOrderID] = so
        
        # 推送停止单状态
        strategy.onStopOrder(so)
        return stopOrderID

    def sendOrder(self , vtSymbol , orderType , price, volume , stop , strategy ):
        #   id1 = exchange.Buy(4300,1)     # 日期                  平台    类型  价格     数量   信息
        #                                  # 2016-10-21 00:00:00  OKCoin  买入  4300     1
        #   id2 = exchange.Buy(-1, 8000)   # 市价单 的第二个参数的意义是  购买8000金额的 币数。
        #   id1 = exchange.Sell(4300,1)    #     日期                     平台        类型      价格      数量     信息
        #                                  #     2016-10-21 00:00:00     OKCoin      卖出      市价单     1    
        # id2 = exchange.Sell(-1, 1)       #     日期                     平台        类型      价格      数量     信息
                                           #     2016-10-21 00:00:00     OKCoin      卖出      4300      1
                                           # 一般错误提示: 小于允许的最小交易单位,大部分是这个原因(参数1是1块钱而不是1个币)。
        if stop == True:
            vtOrderID = self.sendStopOrder(self.vtSymbol, orderType, price, volume, self)
            return vtOrderID
        else:
            ret_order_list = []
            self.exchange.SetDirection( orderType )
            if orderType in [ CTAORDER_BUY , CTAORDER_COVER]:
                ret_order_list.append( self.exchange.Buy( price , volume ))
            elif orderType in [CTAORDER_SELL , CTAORDER_SHORT]:
                ret_order_list.append( self.exchange.Sell( price , volume ))
            return ret_order_list

    def buy(self , price , volume , stop = False):
        Log(CTAORDER_BUY,price,volume)
        return self.sendOrder( self.vtSymbol , CTAORDER_BUY , price , volume , stop , self)
    def sell(self , price , volume , stop = False):
        Log(CTAORDER_SELL,price,volume)
        return self.sendOrder( self.vtSymbol , CTAORDER_SELL , price , volume , stop , self)
    def short(self , price , volume , stop = False):
        Log(CTAORDER_SELL,price,volume)
        return self.sendOrder( self.vtSymbol , CTAORDER_SHORT , price , volume , stop , self)
    def cover(self , price , volume , stop = False):
        Log("cover",price,volume)
        return self.sendOrder( self.vtSymbol , CTAORDER_COVER , price , volume , stop , self)

    #----------------------------------------------------------------------
    def cancelStopOrder(self, stopOrderID):
        """撤销停止单"""
        # 检查停止单是否存在
        if stopOrderID in self.workingStopOrderDict:
            so = self.workingStopOrderDict[stopOrderID]
            so.status = STOPORDER_CANCELLED
            del self.workingStopOrderDict[stopOrderID]
            so.strategy.onStopOrder(so)

        if stopOrderID in self.orderList:
            self.orderList.remove(stopOrderID)

    def cancelOrder(self , vtOrderId):
        Log("cancelOrder:",vtOrderId)
        if STOPORDERPREFIX in vtOrderId:
            self.cancelStopOrder(vtOrderId)
        else:
            self.exchange.CancelOrder(vtOrderId)

    def onTick(self, tick):

        # self.orderList = []
        # orderIDList = self.buy(tick.lastPrice , abs(self.fixedSize))
        # #Log( str(self.vtSymbol) + " cover 0 1 " + str(self.fixedSize) +" " +str(','.join(orderIDList))  + "\n")
        # #print str(self.vtSymbol) , "cover 0 1" , self.fixedSize , orderID
        # for orderID in orderIDList:
        #     self.orderList.append(orderID)

        # 聚合为1分钟K线
        tickMinute = tick.datetime.hour

        if tickMinute != self.barMinute:  
            if self.bar:
                self.onBar(self.bar)

            bar = VtBarData()              
            bar.vtSymbol = tick.vtSymbol
            bar.exchange = tick.exchange

            bar.open = tick.lastPrice
            bar.high = tick.lastPrice
            bar.low = tick.lastPrice
            bar.close = tick.lastPrice

            bar.date = tick.date
            bar.time = tick.time
            bar.datetime = tick.datetime    # K线的时间设为第一个Tick的时间

            self.bar = bar                  # 这种写法为了减少一层访问,加快速度
            self.barMinute = tickMinute     # 更新当前的分钟
        else:                               # 否则继续累加新的K线
            bar = self.bar                  # 写法同样为了加快速度

            bar.high = max(bar.high, tick.lastPrice)
            bar.low = min(bar.low, tick.lastPrice)
            bar.close = tick.lastPrice

    def onBar(self , bar):
        
        if bar.datetime.hour  % self.minute_use == 0:         # bar.datetime.minute 则切换成分钟级别
            # 如果已经有聚合5分钟K线
            if self.fiveBar:
                # 将最新分钟的数据更新到目前5分钟线中
                fiveBar = self.fiveBar
                fiveBar.high = max(fiveBar.high, bar.high)
                fiveBar.low = min(fiveBar.low, bar.low)
                fiveBar.close = bar.close
                
                # 推送5分钟线数据
                self.onFiveBar(fiveBar)
                
                # 清空5分钟线数据缓存
                self.fiveBar = None
        else:
            # 如果没有缓存则新建
            if not self.fiveBar:
                fiveBar = VtBarData()
                
                fiveBar.vtSymbol = bar.vtSymbol
                fiveBar.symbol = bar.symbol
                fiveBar.exchange = bar.exchange
            
                fiveBar.open = bar.open
                fiveBar.high = bar.high
                fiveBar.low = bar.low
                fiveBar.close = bar.close
            
                fiveBar.date = bar.date
                fiveBar.time = bar.time
                fiveBar.datetime = bar.datetime 
                
                self.fiveBar = fiveBar
            else:
                fiveBar = self.fiveBar
                fiveBar.high = max(fiveBar.high, bar.high)
                fiveBar.low = min(fiveBar.low, bar.low)
                fiveBar.close = bar.close


    def onFiveBar(self , bar):
        #Log( self.currency , bar.close , self.pos , self.orderList)

        for orderID in self.orderList:
            self.cancelOrder(orderID)
        self.orderList = []
    
        # 保存K线数据
        self.closeArray[0:self.bufferSize-1] = self.closeArray[1:self.bufferSize]
        self.highArray[0:self.bufferSize-1] = self.highArray[1:self.bufferSize]
        self.lowArray[0:self.bufferSize-1] = self.lowArray[1:self.bufferSize]
        self.buyValue[0:self.bufferSize-1] = self.buyValue[1:self.bufferSize]

        self.closeArray[-1] = bar.close
        self.highArray[-1] = bar.high
        self.lowArray[-1] = bar.low
    
        # 计算指标数值
        self.bollMid = talib.MA(self.closeArray, self.bollLength)[-1]
        self.bollStd = talib.STDDEV(self.closeArray, self.bollLength)[-1]
        self.entryUp = self.bollMid + self.bollStd * self.topDev

        self.buyValue[-1] = self.entryUp

        self.bufferCount += 1
        if self.bufferCount < self.bufferSize:
            return

        # 判断是否要进行交易
        cond1 = 0
        for i in range(1 , self.use_range + 1):
            if self.highArray[-i] > self.buyValue[-i]:
                cond1 = 1
        cond2 = 0

        # newHigh = [float(x) for x in self.highArray]
        # if bar.high >= max(newHigh[-self.N : ]) and self.highArray[-2] >= max(newHigh[-self.N-1 : -1]):
        #     cond2 = 1
        if self.pos == 0 and cond1 > 0:
            self.intraTradeHigh = bar.high
            newHigh = [float(x) for x in self.highArray]
            entryBuyPrice = max(newHigh[-self.N:])
            orderID = self.buy( entryBuyPrice, self.fixedSize , stop=True)
            self.orderList.append(orderID)

        elif self.pos > 0:
            self.intraTradeHigh = max(bar.high , self.intraTradeHigh)
            exitPrice = self.intraTradeHigh * (1 - self.trailingPrcnt / 100.0) 
            orderID = self.sell( exitPrice , self.fixedSize , stop=True)
            self.orderList.append(orderID)

'''
bollLength = 20         # 通道窗口数
topDev = 1.3            # 开仓偏差
trailingPrcnt = 2       # 移动止损百分比
use_range = 10          # use_range天内有突破最高价
N = 10                  # 多少天突破
'''
running_key = {
    "BTC":{ "bollLength":20 , "topDev":1.3 , "trailingPrcnt": 2 , "use_range": 10 , "N":10 , "minute_use": 6},
    "LTC":{ "bollLength":20 , "topDev":1.3 , "trailingPrcnt": 2 , "use_range": 10 , "N":10 , "minute_use": 6}
}

def main():
    global LoopInterval 

    objs = []
    for e in exchanges:
        if e.GetName() != 'Futures_OKCoin':
            raise Error_noSupport
        e.SetRate(1)
        use_symbol = ["this_week","next_week","quarter"][ContractTypeIdx]
        e.SetContractType(use_symbol) 
        e.SetMarginLevel([10,20][MarginLevelIdx])

        e_currency = e.GetCurrency().upper()
        Log(e_currency)
        st = BollingerBreakerStrategy(e , {
        "vtSymbol":e.GetName() + "_" + use_symbol + "_" + e.GetCurrency(), 
        "currency":e_currency,
        "minute_use":running_key[e_currency]["minute_use"],
        "bollLength":running_key[e_currency]["bollLength"],
        "topDev": running_key[e_currency]["topDev"],
        "trailingPrcnt": running_key[e_currency]["trailingPrcnt"],
        "use_range": running_key[e_currency]["use_range"],
        "N": running_key[e_currency]["N"]
        })

        objs.append(st)

    while True:
        for st in objs:
            st.onCall()
        Sleep(LoopInterval * 1000)

متعلقہ

مزید

z1101728116global name 'Error_noSupport' is not defined ہیلو، جب دوبارہ جانچ پڑتال ہو رہی ہے تو کیا مسئلہ ہے؟

الیکسلیبِلن لائن کو سادہ، وسیع اور گہرا سمجھا جاتا ہے، اور فیوچر کے ماہرین بِلن لائن کو کھیلتے ہیں۔