Example #1
0
class POSITION:

    def __init__(self, platform, instrument_id, time_frame):
        self.__platform = platform
        self.__instrument_id = instrument_id
        self.__time_frame = time_frame
        self.__market = MARKET(self.__platform, self.__instrument_id, self.__time_frame)

    def direction(self):
        """获取当前持仓方向"""
        result = self.__platform.get_position()['direction']
        return result

    def amount(self):
        """获取当前持仓数量"""
        result = self.__platform.get_position()['amount']
        return result

    def price(self):
        """获取当前的持仓价格"""
        result = self.__platform.get_position()['price']
        return result

    def coverlong_profit(self):
        """计算平多的单笔交易利润"""
        self.__value = self.__market.contract_value() # 合约面值
        result = (self.__market.last() - self.price()) * (self.amount() * self.__value)
        return result

    def covershort_profit(self):
        """计算平空的单笔交易利润"""
        self.__value = self.__market.contract_value() # 合约面值
        result = (self.price() - self.__market.last()) * (self.amount() * self.__value)
        return result
Example #2
0
 def __init__(self, instrument_id, time_frame, start_asset):     # 策略初始化时需传入合约id、k线周期、初始资金参数
     print("{} {} 海龟交易策略已启动!".format(get_localtime(), instrument_id))    # 程序启动时打印提示信息
     config.loads("config.json")     # 载入配置文件
     self.instrument_id = instrument_id  # 合约id
     self.time_frame = time_frame    # k线周期
     self.exchange = OKEXFUTURES(config.access_key, config.secret_key, config.passphrase, self.instrument_id, leverage=20)   # 初始化交易所
     self.market = MARKET(self.exchange, self.instrument_id, self.time_frame)    # 初始化market
     self.position = POSITION(self.exchange, self.instrument_id, self.time_frame)    # 初始化position
     self.indicators = INDICATORS(self.exchange, self.instrument_id, self.time_frame)    # 初始化indicators
     self.database = "回测"  # 如从purequant服务器的数据库上获取历史k线数据进行回测,必须为"回测"
     self.datasheet = self.instrument_id.split("-")[0].lower() + "_" + time_frame    # 数据表
     if config.first_run == "true":  # 程序第一次启动时保存数据,实盘时如策略中止再重启时,可以将配置文件中的first_run改成"false",程序再次启动会直接读取数据库中保存的数据
         storage.mysql_save_strategy_run_info(self.database, self.datasheet, get_localtime(),
                                              "none", 0, 0, 0, 0, "none", 0, 0, 0, start_asset)
     # 读取数据库中保存的总资金、总盈亏数据
     self.total_asset = storage.read_mysql_datas(0, self.database, self.datasheet, "总资金", ">")[-1][-1]
     self.total_profit = storage.read_mysql_datas(0, self.database, self.datasheet, "总资金", ">")[-1][-2]  # 策略总盈亏
     # 一些策略参数
     self.contract_value = self.market.contract_value()  # 合约面值
     self.ATRLength = 20    # 平均波动周期
     self.boLength = 20  # 短周期 BreakOut Length
     self.fsLength = 55  # 长周期 FailSafe Length
     self.teLength = 10   # 离市周期 Trailing Exit Length
     self.LastProfitableTradeFilter = 1   # 使用入市过滤条件
     self.PreBreakoutFailure = False  # 前一次是否突破失败
     self.CurrentEntries = 0  # 当前持仓的开仓次数
     self.counter = 0    # 计数器,用以控制单根bar最大交易次数
Example #3
0
 def __init__(self, instrument_id, time_frame, fast_length, slow_length, long_stop, short_stop, start_asset):
     config.loads('config.json')  # 载入配置文件
     self.instrument_id = instrument_id  # 合约ID
     self.time_frame = time_frame  # k线周期
     self.exchange = OKEXFUTURES(config.access_key, config.secret_key, config.passphrase, self.instrument_id)  # 初始化交易所
     self.position = POSITION(self.exchange, self.instrument_id, self.time_frame)  # 初始化potion
     self.market = MARKET(self.exchange, self.instrument_id, self.time_frame)  # 初始化market
     self.indicators = INDICATORS(self.exchange, self.instrument_id, self.time_frame)    # 初始化indicators
     # 在第一次运行程序时,将初始资金数据保存至数据库中
     self.database = "回测"    # 无论实盘或回测,此处database名称可以任意命名
     self.datasheet = self.instrument_id.split("-")[0].lower() + "_" + time_frame
     if config.first_run:
         storage.mysql_save_strategy_run_info(self.database, self.datasheet, "策略参数为" + str(fast_length) + "&" + str(slow_length),
                                         "none", 0, 0, 0, 0, "none", 0, 0, 0, start_asset)
     # 读取数据库中保存的总资金数据
     self.total_asset = storage.read_mysql_datas(0, self.database, self.datasheet, "总资金", ">")[-1][-1]
     self.counter = 0  # 计数器
     self.fast_length = fast_length  # 短周期均线长度
     self.slow_length = slow_length  # 长周期均线长度
     self.long_stop = long_stop   # 多单止损幅度
     self.short_stop = short_stop    # 空单止损幅度
     self.total_profit = 0
     self.contract_value = self.market.contract_value()  # 合约面值,每次获取需发起网络请求,故于此处声明变量,优化性能
     # 声明持仓方向、数量与价格变量,每次开平仓后手动重新赋值
     self.hold_direction = "none"
     self.hold_amount = 0
     self.hold_price = 0
     print("{} {} 双均线多空策略已启动!".format(get_localtime(), instrument_id))  # 程序启动时打印提示信息
Example #4
0
 def __init__(self, instrument_id, time_frame, fast_length, slow_length, long_stop, short_stop, start_asset):
     try:
         print("{} {} 双均线多空策略已启动!".format(get_localtime(), instrument_id))   # 程序启动时打印提示信息
         config.loads('config.json')  # 载入配置文件
         self.instrument_id = instrument_id  # 合约ID
         self.time_frame = time_frame  # k线周期
         self.exchange = OKEXFUTURES(config.access_key, config.secret_key, config.passphrase, self.instrument_id)  # 初始化交易所
         self.position = POSITION(self.exchange, self.instrument_id, self.time_frame)  # 初始化potion
         self.market = MARKET(self.exchange, self.instrument_id, self.time_frame)  # 初始化market
         self.indicators = INDICATORS(self.exchange, self.instrument_id, self.time_frame)    # 初始化indicators
         # 在第一次运行程序时,将初始资金数据保存至数据库中
         self.database = "回测"    # 回测时必须为"回测"
         self.datasheet = self.instrument_id.split("-")[0].lower() + "_" + time_frame
         if config.first_run == "true":
             storage.mysql_save_strategy_run_info(self.database, self.datasheet, get_localtime(),
                                             "none", 0, 0, 0, 0, "none", 0, 0, 0, start_asset)
         # 读取数据库中保存的总资金数据
         self.total_asset = storage.read_mysql_datas(0, self.database, self.datasheet, "总资金", ">")[-1][-1]
         self.total_profit = storage.read_mysql_datas(0, self.database, self.datasheet, "总资金", ">")[-1][-2]  # 策略总盈亏
         self.counter = 0  # 计数器
         self.fast_length = fast_length  # 短周期均线长度
         self.slow_length = slow_length  # 长周期均线长度
         self.long_stop = long_stop   # 多单止损幅度
         self.short_stop = short_stop    # 空单止损幅度
         self.contract_value = self.market.contract_value()  # 合约面值,每次获取需发起网络请求,故于此处声明变量,优化性能
     except:
         logger.warning()
Example #5
0
    def __init__(self, platform, symbol, time_frame):

        self.__platform = platform
        self.__symbol = symbol
        self.__time_frame = time_frame
        self.__market = MARKET(self.__platform, self.__symbol,
                               self.__time_frame)

        # pull some data
        self.__indicators = INDICATORS(self.__platform, self.__symbol,
                                       self.__time_frame)
        self.__kline = platform.get_kline(self.__time_frame)
        self.__kline.reverse()

        # format it in pandas
        try:  # dataframe有7列的情况
            self.__df = pd.DataFrame(self.__kline,
                                     columns=[
                                         'time', 'open', 'high', 'low',
                                         'close', 'volume', 'currency_volume'
                                     ])
            self.__df = self.__df.astype({
                'time': 'datetime64[ns]',
                'open': 'float64',
                'close': 'float64',
                'high': 'float64',
                'low': 'float64',
                'volume': 'float64',
                'currency_volume': 'float64'
            })
        except:  # dataframe只有6列的情况,如okex的现货k线数据
            self.__df = pd.DataFrame(
                self.__kline,
                columns=['time', 'open', 'high', 'low', 'close', 'volume'])
            self.__df = self.__df.astype({
                'time': 'datetime64[ns]',
                'open': 'float64',
                'close': 'float64',
                'high': 'float64',
                'low': 'float64',
                'volume': 'float64'
            })

        # create three plot 创建三层图纸,第一层画k线,第二层画成交量,第三层画一些适宜于副图显示的指标
        fplt.foreground = '#FFFFFF'  # 前景色
        fplt.background = '#333333'  # 背景色
        fplt.odd_plot_background = '#333333'  # 第二层图纸的背景色
        fplt.cross_hair_color = "#FFFFFF"  # 准星的颜色
        self.__ax, self.__ax2, self.__ax3 = fplt.create_plot(symbol, rows=3)

        # plot candle sticks
        candles = self.__df[['time', 'open', 'close', 'high', 'low']]
        fplt.candlestick_ochl(candles, ax=self.__ax)

        # overlay volume on the plot
        volumes = self.__df[['time', 'open', 'close', 'volume']]
        fplt.volume_ocv(volumes, ax=self.__ax2)
        fplt.add_legend("VOLUME", self.__ax2)  # 增加"VOLUME"图例
Example #6
0
 def __init__(self, databank, database, data_sheet, exchange, instrument_id, time_frame):
     print("{} {} 持仓同步功能已启动!".format(get_localtime(), instrument_id))
     self.__databank = databank
     self.__database = database
     self.__datasheet = data_sheet
     self.__exchange = exchange
     self.__instrument_id = instrument_id
     self.__time_frame = time_frame
     self.__position = POSITION(self.__exchange, self.__instrument_id, self.__time_frame)
     self.__market = MARKET(self.__exchange, self.__instrument_id, self.__time_frame)
     self.__overprice_range = config.overprice_range
 def __init__(self, instrument_id, time_frame, bollinger_lengths,
              filter_length, start_asset):
     try:
         # 策略启动时控制台输出提示信息
         print("{} {} 布林强盗突破策略已启动!".format(get_localtime(),
                                           instrument_id))  # 程序启动时打印提示信息
         config.loads("config.json")  # 载入配置文件
         # 初始化
         self.instrument_id = instrument_id  # 合约ID
         self.time_frame = time_frame  # k线周期
         self.exchange = OKEXFUTURES(config.access_key, config.secret_key,
                                     config.passphrase,
                                     self.instrument_id)  # 交易所
         self.market = MARKET(self.exchange, self.instrument_id,
                              self.time_frame)  # 行情
         self.position = POSITION(self.exchange, self.instrument_id,
                                  self.time_frame)  # 持仓
         self.indicators = INDICATORS(self.exchange, self.instrument_id,
                                      self.time_frame)  # 指标
         # 在第一次运行程序时,将初始资金、总盈亏等数据保存至数据库中
         self.database = "回测"  # 数据库,回测时必须为"回测"
         self.datasheet = self.instrument_id.split(
             "-")[0].lower() + "_" + time_frame  # 数据表
         if config.first_run == "true":
             storage.mysql_save_strategy_run_info(self.database,
                                                  self.datasheet,
                                                  get_localtime(), "none",
                                                  0, 0, 0, 0, "none", 0, 0,
                                                  0, start_asset)
         # 读取数据库中保存的总资金数据
         self.total_asset = storage.read_mysql_datas(
             0, self.database, self.datasheet, "总资金", ">")[-1][-1]
         self.total_profit = storage.read_mysql_datas(
             0, self.database, self.datasheet, "总资金", ">")[-1][-2]  # 策略总盈亏
         # 策略参数
         self.contract_value = self.market.contract_value()  # 合约面值
         self.counter = 0  # 计数器
         self.bollinger_lengths = bollinger_lengths  # 布林通道参数
         self.filter_length = filter_length  # 过滤器参数
         self.out_day = 50  # 自适应出场ma的初始值为50,开仓后赋值为布林通道参数的值
     except:
         logger.warning()
Example #8
0
 def __init__(self, instrument_id, time_frame, fast_length, slow_length,
              long_stop, short_stop, start_asset, precision):
     try:
         print("{} {} 双均线多空策略已启动!".format(get_localtime(),
                                          instrument_id))  # 程序启动时打印提示信息
         config.loads('config.json')  # 载入配置文件
         self.instrument_id = instrument_id  # 合约ID
         self.time_frame = time_frame  # k线周期
         self.precision = precision  # 精度,即币对的最小交易数量
         self.exchange = OKEXSPOT(config.access_key, config.secret_key,
                                  config.passphrase,
                                  self.instrument_id)  # 初始化交易所
         self.position = POSITION(self.exchange, self.instrument_id,
                                  self.time_frame)  # 初始化potion
         self.market = MARKET(self.exchange, self.instrument_id,
                              self.time_frame)  # 初始化market
         self.indicators = INDICATORS(self.exchange, self.instrument_id,
                                      self.time_frame)  # 初始化indicators
         # 在第一次运行程序时,将初始资金数据保存至数据库中
         self.database = "回测"  # 回测时必须为"回测"
         self.datasheet = self.instrument_id.split(
             "-")[0].lower() + "_" + time_frame
         if config.first_run == "true":
             storage.mysql_save_strategy_run_info(self.database,
                                                  self.datasheet,
                                                  get_localtime(), "none",
                                                  0, 0, 0, 0, "none", 0, 0,
                                                  0, start_asset)
         # 读取数据库中保存的总资金数据
         self.total_asset = storage.read_mysql_datas(
             0, self.database, self.datasheet, "总资金", ">")[-1][-1]
         self.total_profit = storage.read_mysql_datas(
             0, self.database, self.datasheet, "总资金", ">")[-1][-2]  # 策略总盈亏
         self.counter = 0  # 计数器
         self.fast_length = fast_length  # 短周期均线长度
         self.slow_length = slow_length  # 长周期均线长度
         self.long_stop = long_stop  # 多单止损幅度
         self.short_stop = short_stop  # 空单止损幅度
         self.hold_price = 0  # 注意:okex的现货没有获取持仓均价的接口,故需实盘时需要手动记录入场价格。此种写法对于不同的交易所是通用的。
         # 此种写法,若策略重启,持仓价格会回归0
     except:
         logger.warning()
Example #9
0
class Strategy:

    def __init__(self, instrument_id, time_frame, fast_length, slow_length, long_stop, short_stop, start_asset):
        try:
            print("{} {} 双均线多空策略已启动!".format(get_localtime(), instrument_id))   # 程序启动时打印提示信息
            config.loads('config.json')  # 载入配置文件
            self.instrument_id = instrument_id  # 合约ID
            self.time_frame = time_frame  # k线周期
            self.exchange = OKEXFUTURES(config.access_key, config.secret_key, config.passphrase, self.instrument_id)  # 初始化交易所
            self.position = POSITION(self.exchange, self.instrument_id, self.time_frame)  # 初始化potion
            self.market = MARKET(self.exchange, self.instrument_id, self.time_frame)  # 初始化market
            self.indicators = INDICATORS(self.exchange, self.instrument_id, self.time_frame)    # 初始化indicators
            # 在第一次运行程序时,将初始资金数据保存至数据库中
            self.database = "回测"    # 回测时必须为"回测"
            self.datasheet = self.instrument_id.split("-")[0].lower() + "_" + time_frame
            if config.first_run == "true":
                storage.mysql_save_strategy_run_info(self.database, self.datasheet, get_localtime(),
                                                "none", 0, 0, 0, 0, "none", 0, 0, 0, start_asset)
            # 读取数据库中保存的总资金数据
            self.total_asset = storage.read_mysql_datas(0, self.database, self.datasheet, "总资金", ">")[-1][-1]
            self.total_profit = storage.read_mysql_datas(0, self.database, self.datasheet, "总资金", ">")[-1][-2]  # 策略总盈亏
            self.counter = 0  # 计数器
            self.fast_length = fast_length  # 短周期均线长度
            self.slow_length = slow_length  # 长周期均线长度
            self.long_stop = long_stop   # 多单止损幅度
            self.short_stop = short_stop    # 空单止损幅度
            self.contract_value = self.market.contract_value()  # 合约面值,每次获取需发起网络请求,故于此处声明变量,优化性能
        except:
            logger.warning()

    def begin_trade(self, kline=None):
        try:
            if self.indicators.CurrentBar(kline=kline) < self.slow_length:  # 如果k线数据不够长就返回
                return
            timestamp = ts_to_datetime_str(utctime_str_to_ts(kline[-1][0])) if kline else get_localtime()    # 非回测模式下时间戳就是当前本地时间
            # 计算策略信号
            ma = self.indicators.MA(self.fast_length, self.slow_length, kline=kline)
            fast_ma = ma[0]
            slow_ma = ma[1]
            cross_over = fast_ma[-2] >= slow_ma[-2] and fast_ma[-3] < slow_ma[-3]   # 不用当根k线上的ma来计算信号,防止信号闪烁
            cross_below = slow_ma[-2] >= fast_ma[-2] and slow_ma[-3] < fast_ma[-3]
            if self.indicators.BarUpdate(kline=kline):     # 如果k线更新,计数器归零
                self.counter = 0
            if self.counter < 1:
                # 按照策略信号开平仓
                if cross_over:     # 金叉时
                    if self.position.amount() == 0:     # 若当前无持仓,则买入开多并推送下单结果
                        price = self.market.open(-1, kline=kline)  # 下单价格=此根k线收盘价
                        amount = round(self.total_asset / self.contract_value)   # 数量=总资金/价格/合约面值
                        info = self.exchange.buy(price, amount)
                        push(info)
                        storage.mysql_save_strategy_run_info(self.database, self.datasheet, timestamp, "买入开多",
                                                        price, amount, amount * self.contract_value, price,
                                                        "long", amount, 0, self.total_profit, self.total_asset)     # 将信息保存至数据库
                    if self.position.direction() == 'short':    # 若当前持空头,先平空再开多
                        profit = self.position.covershort_profit(market_type="usd_contract", last=self.market.open(-1, kline=kline))  # 在平空前先计算逻辑盈亏,当前最新成交价为开盘价
                        self.total_profit += profit
                        self.total_asset += profit  # 计算此次盈亏后的总资金
                        cover_short_price = self.market.open(-1, kline=kline)
                        cover_short_amount = self.position.amount()
                        open_long_price = self.market.open(-1, kline=kline)
                        open_long_amount = round(self.total_asset / self.contract_value)
                        info = self.exchange.BUY(cover_short_price, cover_short_amount, open_long_price, open_long_amount)
                        push("此次盈亏:{} 当前总资金:{}".format(profit, self.total_asset) + str(info))   # 需将返回的下单结果info转换为字符串后进行拼接
                        storage.mysql_save_strategy_run_info(self.database, self.datasheet, timestamp, "平空开多",
                                                        open_long_price, open_long_amount, open_long_amount * self.contract_value,
                                                        open_long_price, "long", open_long_amount, profit, self.total_profit, self.total_asset)
                if cross_below:     # 死叉时
                    if self.position.amount() == 0:
                        price = self.market.open(-1, kline=kline)
                        amount = round(self.total_asset / self.contract_value)
                        info = self.exchange.sellshort(price, amount)
                        push(info)
                        storage.mysql_save_strategy_run_info(self.database, self.datasheet, timestamp, "卖出开空",
                                                    price, amount, amount * self.contract_value, price,
                                                    "short", amount, 0, self.total_profit, self.total_asset)
                    if self.position.direction() == 'long':
                        profit = self.position.coverlong_profit(market_type="usd_contract", last=self.market.open(-1, kline=kline))     # 在平多前先计算逻辑盈亏,当前最新成交价为开盘价
                        self.total_profit += profit
                        self.total_asset += profit
                        cover_long_price = self.market.open(-1, kline=kline)
                        cover_long_amount = self.position.amount()
                        open_short_price = self.market.open(-1, kline=kline)
                        open_short_amount = round(self.total_asset / self.contract_value)
                        info = self.exchange.SELL(cover_long_price,
                                                  cover_long_amount,
                                                  open_short_price,
                                                  open_short_amount)
                        push("此次盈亏:{} 当前总资金:{}".format(profit, self.total_asset) + str(info))
                        storage.mysql_save_strategy_run_info(self.database, self.datasheet, timestamp, "平多开空",
                                                        open_short_price, open_short_amount,
                                                        open_short_amount * self.contract_value,
                                                        open_short_price, "short", open_short_amount, profit, self.total_profit,
                                                        self.total_asset)
                # 止损
                if self.position.amount() > 0:
                    if self.position.direction() == 'long' and self.market.low(-1, kline=kline) <= self.position.price() * self.long_stop:    # 多单止损
                        profit = self.position.coverlong_profit(market_type="usd_contract", last=self.position.price() * self.long_stop)    # 在平多前先计算逻辑盈亏,当前最新成交价为止损价
                        self.total_profit += profit
                        self.total_asset += profit
                        price = self.position.price() * self.long_stop
                        amount = self.position.amount()
                        info = self.exchange.sell(price, amount)
                        push("此次盈亏:{} 当前总资金:{}".format(profit, self.total_asset) + str(info))
                        storage.mysql_save_strategy_run_info(self.database, self.datasheet, timestamp,
                                                        "卖出止损", price, amount,
                                                        amount * self.contract_value,
                                                        0, "none", 0, profit, self.total_profit,
                                                        self.total_asset)
                        self.counter += 1   # 计数器加1,控制此根k线上不再下单

                    if self.position.direction() == 'short' and self.market.high(-1, kline=kline) >= self.position.price() * self.short_stop:  # 空头止损
                        profit = self.position.covershort_profit(market_type="usd_contract", last=self.position.price() * self.short_stop)
                        self.total_profit += profit
                        self.total_asset += profit
                        price = self.position.price() * self.short_stop
                        amount = self.position.amount()
                        info = self.exchange.buytocover(price, amount)
                        push("此次盈亏:{} 当前总资金:{}".format(profit, self.total_asset) + str(info))
                        storage.mysql_save_strategy_run_info(self.database, self.datasheet, timestamp,
                                                        "买入止损", price, amount,
                                                        amount * self.contract_value,
                                                        0, "none", 0, profit, self.total_profit,
                                                        self.total_asset)
                        self.counter += 1
        except:
            logger.info()
Example #10
0
 def __init__(self, platform, instrument_id, time_frame):
     self.__platform = platform
     self.__instrument_id = instrument_id
     self.__time_frame = time_frame
     self.__market = MARKET(self.__platform, self.__instrument_id, self.__time_frame)
Example #11
0
class POSITION:

    def __init__(self, platform, instrument_id, time_frame):
        self.__platform = platform
        self.__instrument_id = instrument_id
        self.__time_frame = time_frame
        self.__market = MARKET(self.__platform, self.__instrument_id, self.__time_frame)

    def direction(self):
        """获取当前持仓方向"""
        if config.backtest is False:    # 实盘模式下实时获取账户实际持仓方向,仅支持单向持仓模式下的查询
            result = self.__platform.get_position()['direction']
            return result
        else:   # 回测模式下从数据库中读取持仓方向
            result = storage.read_mysql_datas(0, "回测", self.__instrument_id.split("-")[0].lower() + "_" + self.__time_frame, "总资金", ">")[-1][6]
            return result

    def amount(self, mode=None, side=None):
        """获取当前持仓数量"""
        if config.backtest is False:    # 实盘模式下实时获取账户实际持仓数量
            if mode == "both":  # 如果传入参数"both",查询双向持仓模式的持仓数量
                result = self.__platform.get_position(mode=mode)
                if side == "long":
                    long_amount = result["long"]["amount"]
                    return long_amount
                elif side == "short":
                    short_amount = result["short"]["amount"]
                    return short_amount
            else:
                result = self.__platform.get_position()['amount']
                return result
        else:   # 回测模式下从数据库中读取持仓数量
            result = storage.read_mysql_datas(0, "回测", self.__instrument_id.split("-")[0].lower() + "_" + self.__time_frame, "总资金", ">")[-1][7]
            return result

    def price(self, mode=None, side=None):
        """获取当前的持仓价格"""
        if config.backtest is False:    # 实盘模式下实时获取账户实际持仓价格
            if mode == "both":  # 如果传入参数"both",查询双向持仓模式的持仓价格
                result = self.__platform.get_position(mode=mode)
                if side == "long":
                    long_price = result["long"]["price"]
                    return long_price
                elif side == "short":
                    short_price = result["short"]["price"]
                    return short_price
            else:
                result = self.__platform.get_position()['price']
                return result
        else:   # 回测模式下从数据库中读取持仓价格
            result = storage.read_mysql_datas(0, "回测", self.__instrument_id.split("-")[0].lower() + "_" + self.__time_frame, "总资金", ">")[-1][5]
            return result


    def coverlong_profit(self, market_type=None, last=None):
        """
        计算平多的单笔交易利润
        :param market_type: 默认是USDT合约,可填"usd_contract"(币本位合约)或者"spot"(现货)
        :param last: 回测模式可以传入最新成交价
        :return: 返回计算出的利润结果
        """
        if market_type == "usd_contract":    # 如果是币本位合约
            self.__value = self.__market.contract_value()  # 合约面值
            if config.backtest and last is not None:    # 如果是回测模式且传入了last最新成交价
                result = (last - self.price()) * ((self.amount() * self.__value) / self.price())    # 利润=价差*(合约张数*面值)/持仓价格
            else:   # 如果是实盘模式
                result = (self.__market.last() - self.price()) * ((self.amount() * self.__value) / self.price())  # 利润=价差*(合约张数*面值)/持仓价格

        elif market_type == "spot":    # 如果是现货
            if config.backtest and last is not None:    # 如果是回测模式且传入了last最新成交价
                result = (last - self.price()) * self.amount()    # 利润=价差*持仓数量
            else:   # 如果是实盘模式
                result = (self.__market.last() - self.price()) * self.amount()

        else:   # 默认是usdt合约
            self.__value = self.__market.contract_value()  # 合约面值
            if config.backtest and last is not None:    # 如果是回测模式且传入了last最新成交价
                result = (last - self.price()) * (self.amount() * self.__value) # 利润=价差*(持仓数量*面值)
            else:   # 如果是实盘模式
                result = (self.__market.last() - self.price()) * (self.amount() * self.__value)
        return result

    def covershort_profit(self, market_type=None, last=None):
        """
        计算平空的单笔交易利润
        :param market_type: 默认是USDT合约,可填"usd_contract"(币本位合约)或者"spot"(现货)
        :param last: 回测模式可以传入最新成交价
        :return: 返回计算出的利润结果
        """
        if market_type == "usd_contract":  # 如果是币本位合约
            self.__value = self.__market.contract_value()  # 合约面值
            if config.backtest and last is not None:    # 如果是回测模式且传入了last最新成交价
                result = (self.price() - last) * ((self.amount() * self.__value) / self.price())  # 利润=价差*(合约张数*面值)/持仓价格
            else:  # 如果是实盘模式
                result = (self.price() - self.__market.last()) * (
                            (self.amount() * self.__value) / self.price())  # 利润=价差*(合约张数*面值)/持仓价格

        elif market_type == "spot":  # 如果是现货
            if config.backtest and last is not None:    # 如果是回测模式且传入了last最新成交价
                result = (self.price() - last) * self.amount()  # 利润=价差*持仓数量
            else: # 如果是实盘模式
                result = (self.price() - self.__market.last()) * self.amount()

        else:  # 默认是usdt合约
            self.__value = self.__market.contract_value()  # 合约面值
            if config.backtest and last is not None:    # 如果是回测模式且传入了last最新成交价
                result = (self.price() - last) * (self.amount() * self.__value)  # 利润=价差*(持仓数量*面值)
            else: # 如果是实盘模式
                result = (self.price() - self.__market.last()) * (self.amount() * self.__value)
        return result
class Strategy:
    """布林强盗策略"""
    def __init__(self, instrument_id, time_frame, bollinger_lengths,
                 filter_length, start_asset):
        try:
            # 策略启动时控制台输出提示信息
            print("{} {} 布林强盗突破策略已启动!".format(get_localtime(),
                                              instrument_id))  # 程序启动时打印提示信息
            config.loads("config.json")  # 载入配置文件
            # 初始化
            self.instrument_id = instrument_id  # 合约ID
            self.time_frame = time_frame  # k线周期
            self.exchange = OKEXFUTURES(config.access_key, config.secret_key,
                                        config.passphrase,
                                        self.instrument_id)  # 交易所
            self.market = MARKET(self.exchange, self.instrument_id,
                                 self.time_frame)  # 行情
            self.position = POSITION(self.exchange, self.instrument_id,
                                     self.time_frame)  # 持仓
            self.indicators = INDICATORS(self.exchange, self.instrument_id,
                                         self.time_frame)  # 指标
            # 在第一次运行程序时,将初始资金、总盈亏等数据保存至数据库中
            self.database = "回测"  # 数据库,回测时必须为"回测"
            self.datasheet = self.instrument_id.split(
                "-")[0].lower() + "_" + time_frame  # 数据表
            if config.first_run == "true":
                storage.mysql_save_strategy_run_info(self.database,
                                                     self.datasheet,
                                                     get_localtime(), "none",
                                                     0, 0, 0, 0, "none", 0, 0,
                                                     0, start_asset)
            # 读取数据库中保存的总资金数据
            self.total_asset = storage.read_mysql_datas(
                0, self.database, self.datasheet, "总资金", ">")[-1][-1]
            self.total_profit = storage.read_mysql_datas(
                0, self.database, self.datasheet, "总资金", ">")[-1][-2]  # 策略总盈亏
            # 策略参数
            self.contract_value = self.market.contract_value()  # 合约面值
            self.counter = 0  # 计数器
            self.bollinger_lengths = bollinger_lengths  # 布林通道参数
            self.filter_length = filter_length  # 过滤器参数
            self.out_day = 50  # 自适应出场ma的初始值为50,开仓后赋值为布林通道参数的值
        except:
            logger.warning()

    def begin_trade(self, kline=None):
        try:  # 异常处理
            if self.indicators.CurrentBar(
                    kline=kline) < self.bollinger_lengths:  # 如果k线数据不够长就返回
                return

            timestamp = ts_to_datetime_str(utctime_str_to_ts(
                kline[-1]
                [0])) if kline else get_localtime()  # 非回测模式下时间戳就是当前本地时间

            if self.indicators.BarUpdate(kline=kline):
                self.counter = 0  # k线更新时还原计数器
                if self.out_day > 10:  # 计算MA的天数最小递减到10。如果达到10,则不再递减。
                    self.out_day -= 1  # 自适应出场ma的长度参数根据持仓周期递减,持有头寸的时间每多一天,计算MA的天数减1

            deviation = float(
                self.indicators.STDDEV(self.bollinger_lengths,
                                       nbdev=2,
                                       kline=kline)[-1])  # 标准差
            middleband = float(
                self.indicators.BOLL(self.bollinger_lengths,
                                     kline=kline)['middleband'][-1])  # 布林通道中轨
            upperband = float(middleband + deviation)  # 布林通道上轨
            lowerband = float(middleband - deviation)  # 布林通道下轨
            filter = float(
                self.market.close(-1, kline=kline) - self.market.close(
                    (self.filter_length * -1) - 1,
                    kline=kline))  # 过滤器:当日收盘价减去30日前的收盘价
            ma = float(self.indicators.MA(self.out_day,
                                          kline=kline)[-1])  # 自适应移动出场平均线

            # 策略主体
            # 若k线数据足够长,且满足过滤条件,且当根k线最高价大于等于布林通道上轨,买入开多。
            # 开仓处也设置计数器过滤,是为了防止没有启用交易助手的情况下挂单未成交,仓位为零时当根k线一直满足开仓条件,会重复挂单。
            if self.indicators.CurrentBar(
                    kline=kline
            ) >= self.bollinger_lengths and filter > 0 and self.market.high(
                    -1, kline=kline) > upperband and self.counter < 1:
                if self.position.amount() == 0:  # 若当前无持仓
                    price = upperband  # 开多价格为布林通道上轨的值
                    amount = round(self.total_asset / upperband /
                                   self.contract_value)  # 合约张数取整
                    info = self.exchange.buy(price,
                                             amount)  # 买入开多,并将返回的信息赋值给变量info
                    push(info)  # 推送信息
                    storage.mysql_save_strategy_run_info(
                        self.database, self.datasheet, timestamp, "买入开多",
                        price, amount, amount * price * self.contract_value,
                        price, "long", amount, 0, self.total_profit,
                        self.total_asset)  # 将信息保存至数据库
                    self.counter += 1  # 此策略是在盘中开仓,而在回测时,每根bar只会运行一次,每根bar上的价格不分时间先后,故此处开仓后计数器加1,也就是当根k线不平仓
                    # 因为实盘时每个ticker进来策略就会运行一次。注意回测和实盘策略运行机制的不同。
                    self.out_day = self.bollinger_lengths  # 开仓后赋值
            # 开空
            if self.indicators.CurrentBar(
                    kline=kline
            ) >= self.bollinger_lengths and filter < 0 and self.market.low(
                    -1, kline=kline) < lowerband and self.counter < 1:
                if self.position.amount() == 0:
                    price = lowerband
                    amount = round(self.total_asset / upperband /
                                   self.contract_value)
                    info = self.exchange.sellshort(price, amount)
                    push(info)
                    storage.mysql_save_strategy_run_info(
                        self.database, self.datasheet, timestamp, "卖出开空",
                        price, amount, amount * price * self.contract_value,
                        price, "short", amount, 0, self.total_profit,
                        self.total_asset)
                    self.counter += 1
                    self.out_day = self.bollinger_lengths  # 开仓后赋值
            # 如果当前持多,且当根k线最低价小于等于中轨值,触发保护性止损,就平多止损
            # 因为回测是一根k线上运行整个策略一次,所以要实现当根k线开仓后当根k线不平仓,需要将self.counter < 1的条件加在平仓的地方
            if self.position.direction() == "long" and self.market.low(
                    -1, kline=kline) < middleband and self.counter < 1:
                profit = self.position.coverlong_profit(
                    last=middleband)  # 此处计算平多利润时,传入最新价last为中轨值,也就是触发止损价格的那个值。
                self.total_profit += profit  # 计算经过本次盈亏后的总利润
                self.total_asset += profit  # 计算经过本次盈亏后的总资金
                price = middleband  # 平多价格为中轨值
                amount = self.position.amount()  # 平仓数量为当前持仓数量
                info = self.exchange.sell(price, amount)
                push(info)
                self.counter += 1
                storage.mysql_save_strategy_run_info(
                    self.database, self.datasheet, timestamp, "卖出止损", price,
                    amount, price * amount * self.contract_value, 0, "none", 0,
                    profit, self.total_profit, self.total_asset)
            if self.position.direction() == "short" and self.market.high(
                    -1, kline=kline) > middleband and self.counter < 1:
                profit = self.position.covershort_profit(last=middleband)
                self.total_profit += profit
                self.total_asset += profit
                price = middleband
                amount = self.position.amount()
                info = self.exchange.buytocover(price, amount)
                push(info)
                self.counter += 1
                storage.mysql_save_strategy_run_info(
                    self.database, self.datasheet, timestamp, "买入止损", price,
                    amount, amount * price * self.contract_value, 0, "none", 0,
                    profit, self.total_profit, self.total_asset)
            # 平多
            if self.position.direction(
            ) == "long" and upperband > ma > self.market.low(
                    -1, kline=kline) and self.counter < 1:
                profit = self.position.coverlong_profit(last=ma)
                self.total_profit += profit
                self.total_asset += profit
                price = ma  # 平仓价格为自适应出场均线的值
                amount = self.position.amount()
                info = self.exchange.sell(price, amount)
                push(info)
                self.counter += 1
                storage.mysql_save_strategy_run_info(
                    self.database, self.datasheet, timestamp, "卖出平多", price,
                    amount, price * amount * self.contract_value, 0, "none", 0,
                    profit, self.total_profit, self.total_asset)
            # 平空
            if self.position.direction(
            ) == "short" and lowerband < ma < self.market.high(
                    -1, kline=kline) and self.counter < 1:
                profit = self.position.covershort_profit(last=ma)
                self.total_profit += profit
                self.total_asset += profit
                price = ma
                amount = self.position.amount()
                info = self.exchange.buytocover(price, amount)
                push(info)
                self.counter += 1
                storage.mysql_save_strategy_run_info(
                    self.database, self.datasheet, timestamp, "买入平空", price,
                    amount, amount * price * self.contract_value, 0, "none", 0,
                    profit, self.total_profit, self.total_asset)
        except:
            logger.error()
Example #13
0
class SYNCHRONIZE:
    """持仓同步"""

    def __init__(self, databank, database, data_sheet, exchange, instrument_id, time_frame):
        print("{} {} 持仓同步功能已启动!".format(get_localtime(), instrument_id))
        self.__databank = databank
        self.__database = database
        self.__datasheet = data_sheet
        self.__exchange = exchange
        self.__instrument_id = instrument_id
        self.__time_frame = time_frame
        self.__position = POSITION(self.__exchange, self.__instrument_id, self.__time_frame)
        self.__market = MARKET(self.__exchange, self.__instrument_id, self.__time_frame)
        self.__overprice_range = config.overprice_range

    def save_strategy_position(self, strategy_direction, strategy_amount):
        """下单后将仓位信息保存至数据库."""
        if self.__databank == "mysql":
            storage.mysql_save_strategy_position(self.__database, self.__datasheet, strategy_direction,
                                           strategy_amount)
        elif self.__databank == "mongodb":
            storage.mongodb_save(data={"时间": get_localtime(), "strategy_direction": strategy_direction, "strategy_amount": strategy_amount}, database=self.__database, collection=self.__datasheet)
        else:
            raise DataBankError

    def match(self):
        # 获取当前账户持仓信息
        account_direction = self.__position.direction()
        account_amount = self.__position.amount()

        # 获取当前策略应持仓位信息
        if self.__databank == "mysql":
            strategy_direction = storage.read_mysql_datas(0, self.__database, self.__datasheet, "amount", ">=")[-1][-2]
            strategy_amount = storage.read_mysql_datas(0, self.__database, self.__datasheet, "amount", ">=")[-1][-1]
        elif self.__databank == "mongodb":
            strategy_direction = storage.mongodb_read_data(self.__database, self.__datasheet)[-1][0]["strategy_direction"]
            strategy_amount = int(storage.mongodb_read_data(self.__database, self.__datasheet)[-1][0]["strategy_amount"])
        else:
            strategy_direction = None
            strategy_amount = None
            raise DataBankError

        # 比较账户持仓与策略持仓,如不匹配则同步之
        if strategy_direction == "long" and account_direction == "long":
            if account_amount < strategy_amount:
                receipt = self.__exchange.buy(self.__market.last() * (1 + self.__overprice_range), strategy_amount - account_amount, 0)
                return "当前持多,当前实际持仓小于策略应持仓位数量,自动同步结果:{}".format(receipt)
            elif account_amount > strategy_amount:
                receipt = self.__exchange.sell(self.__market.last() * (1 - self.__overprice_range), account_amount - strategy_amount, 0)
                return "当前持多,当前实际持仓大于策略应持仓位数量,自动同步结果:{}".format(receipt)
        if strategy_direction == "short" and account_direction == "short": # 策略与账户均持空时
            if account_amount < strategy_amount:
                receipt = self.__exchange.sellshort(self.__market.last() * (1 - self.__overprice_range), strategy_amount - account_amount, 0)
                return "当前持空,当前实际持仓小于策略应持仓位数量,自动同步结果:{}".format(receipt)
            elif account_amount > strategy_amount:
                receipt = self.__exchange.buytocover(self.__market.last() * (1 + self.__overprice_range), account_amount - strategy_amount, 0)
                return "当前持空,当前实际持仓大于策略应持仓位数量,自动同步结果:{}".format(receipt)
        if strategy_direction == "long" and account_direction == "short": # 策略持多,账户却持空时
            receipt1 = self.__exchange.buytocover(self.__market.last() * (1 + self.__overprice_range), account_amount, 0)
            if "完全成交" not in receipt1:
                return "策略应持多,当前实际持空,自动同步结果:{}".format(receipt1)
            else:
                receipt2 = self.__exchange.buy(self.__market.last() * (1 + self.__overprice_range), strategy_amount, 0)
                return "策略应持多,当前实际持空,自动同步结果:{}".format(receipt1 + receipt2)
        if strategy_direction == "short" and account_direction == "long": # 策略持空,账户却持多时
            receipt1 = self.__exchange.sell(self.__market.last() * (1 - self.__overprice_range), account_amount, 0)
            if "完全成交" not in receipt1:
                return "策略应持空,当前实际持多,自动同步结果:{}".format(receipt1)
            else:
                receipt2 = self.__exchange.sellshort(self.__market.last() * (1 - self.__overprice_range), strategy_amount, 0)
                return "策略应持空,当前实际持多,自动同步结果:{}".format(receipt1 + receipt2)
        if strategy_direction == "none" and account_direction == "long":    # 策略无持仓,账户却持多时
            receipt = self.__exchange.sell(self.__market.last() * (1 - self.__overprice_range), account_amount, 0)
            return "策略应无持仓,当前实际持多,自动同步结果:{}".format(receipt)
        if strategy_direction == "none" and account_direction == "short":     # 策略无持仓,账户却持空时
            receipt = self.__exchange.buytocover(self.__market.last() * (1 + self.__overprice_range), account_amount, 0)
            return "策略应无持仓,当前实际持空,自动同步结果:{}".format(receipt)
        if account_direction == "none" and strategy_direction == "long":     # 账户无持仓,策略却应持多时
            receipt = self.__exchange.buy(self.__market.last() * (1 + self.__overprice_range), strategy_amount, 0)
            return "策略应持多仓,当前实际无持仓,自动同步结果:{}".format(receipt)
        if account_direction == "none" and strategy_direction == "short":     # 账户无持仓,策略却应持空
            receipt = self.__exchange.sellshort(self.__market.last() * (1 - self.__overprice_range), strategy_amount, 0)
            return "策略应持空仓,当前实际无持仓,自动同步结果:{}".format(receipt)
        if account_amount == strategy_amount and account_direction == strategy_direction:
            dict = {"策略持仓方向": strategy_direction, "策略持仓数量": strategy_amount, "账户实际持仓方向": account_direction,
                    "账户实际持仓数量": account_amount}
            return "策略持仓与账户持仓匹配! {}".format(dict)
        else:
            raise MatchError
Example #14
0
class Strategy:

    def __init__(self, instrument_id, time_frame, start_asset):     # 策略初始化时需传入合约id、k线周期、初始资金参数
        print("{} {} 海龟交易策略已启动!".format(get_localtime(), instrument_id))    # 程序启动时打印提示信息
        config.loads("config.json")     # 载入配置文件
        self.instrument_id = instrument_id  # 合约id
        self.time_frame = time_frame    # k线周期
        self.exchange = OKEXFUTURES(config.access_key, config.secret_key, config.passphrase, self.instrument_id, leverage=20)   # 初始化交易所
        self.market = MARKET(self.exchange, self.instrument_id, self.time_frame)    # 初始化market
        self.position = POSITION(self.exchange, self.instrument_id, self.time_frame)    # 初始化position
        self.indicators = INDICATORS(self.exchange, self.instrument_id, self.time_frame)    # 初始化indicators
        self.database = "回测"  # 如从purequant服务器的数据库上获取历史k线数据进行回测,必须为"回测"
        self.datasheet = self.instrument_id.split("-")[0].lower() + "_" + time_frame    # 数据表
        if config.first_run == "true":  # 程序第一次启动时保存数据,实盘时如策略中止再重启时,可以将配置文件中的first_run改成"false",程序再次启动会直接读取数据库中保存的数据
            storage.mysql_save_strategy_run_info(self.database, self.datasheet, get_localtime(),
                                                 "none", 0, 0, 0, 0, "none", 0, 0, 0, start_asset)
        # 读取数据库中保存的总资金、总盈亏数据
        self.total_asset = storage.read_mysql_datas(0, self.database, self.datasheet, "总资金", ">")[-1][-1]
        self.total_profit = storage.read_mysql_datas(0, self.database, self.datasheet, "总资金", ">")[-1][-2]  # 策略总盈亏
        # 一些策略参数
        self.contract_value = self.market.contract_value()  # 合约面值
        self.ATRLength = 20    # 平均波动周期
        self.boLength = 20  # 短周期 BreakOut Length
        self.fsLength = 55  # 长周期 FailSafe Length
        self.teLength = 10   # 离市周期 Trailing Exit Length
        self.LastProfitableTradeFilter = 1   # 使用入市过滤条件
        self.PreBreakoutFailure = False  # 前一次是否突破失败
        self.CurrentEntries = 0  # 当前持仓的开仓次数
        self.counter = 0    # 计数器,用以控制单根bar最大交易次数

    def begin_trade(self, kline=None):  # 实盘时从交易所实时获取k线数据,回测时传入自定义的kline
        try:
            # 如果k线数据不够长就返回
            if self.indicators.CurrentBar(kline=kline) < self.fsLength:
                return
            # 非回测模式下时间戳就是当前本地时间
            timestamp = ts_to_datetime_str(utctime_str_to_ts(kline[-1][0])) if kline else get_localtime()
            # k线更新时计数器归零
            if self.indicators.BarUpdate(kline=kline):
                self.counter = 0
            AvgTR = self.indicators.ATR(self.ATRLength, kline=kline)     # 计算真实波幅
            N = float(AvgTR[-2])   # N值为前一根bar上的ATR值,需将numpy.float64数据类型转换为float类型,下面的转换同理
            Units = int(self.total_asset / self.contract_value / 5)    # 每一份头寸大小为总资金的20%
            """计算短周期唐奇安通道"""
            # 唐奇安通道上轨,延后1个Bar
            DonchianHi = float(self.indicators.HIGHEST(self.boLength, kline=kline)[-2])
            # 唐奇安通道下轨,延后1个Bar
            DonchianLo = float(self.indicators.LOWEST(self.boLength, kline=kline)[-2])
            """计算长周期唐奇安通道"""
            # 唐奇安通道上轨,延后1个Bar,长周期
            fsDonchianHi = float(self.indicators.HIGHEST(self.fsLength, kline=kline)[-2])
            # 唐奇安通道下轨,延后1个Bar,长周期
            fsDonchianLo = float(self.indicators.LOWEST(self.fsLength, kline=kline)[-2])
            """计算止盈唐奇安通道"""
            # 离市时判断需要的N周期最低价
            ExitLowestPrice = float(self.indicators.LOWEST(self.teLength, kline=kline)[-2])
            # 离市时判断需要的N周期最高价
            ExitHighestPrice = float(self.indicators.HIGHEST(self.teLength, kline=kline)[-2])
            # 当不使用过滤条件,或者使用过滤条件且条件PreBreakoutFailure为True时,短周期开仓
            if self.indicators.CurrentBar(kline=kline) >= self.boLength and self.position.amount() == 0 and (self.LastProfitableTradeFilter != 1 or self.PreBreakoutFailure == False) and self.counter < 1:
                if self.market.high(-1, kline=kline) >= DonchianHi:  # 突破了短周期唐奇安通道上轨
                    price = DonchianHi  # 开多价格为短周期唐奇安通道上轨
                    amount = Units  # 开多数量为Units
                    receipt = self.exchange.buy(price, amount)  # 开多
                    push(receipt)   # 推送下单结果
                    self.CurrentEntries += 1    # 记录一次开仓次数
                    self.PreBreakoutFailure = False  # 将标识重置为默认值,根据离场时的盈亏情况再修改
                    storage.mysql_save_strategy_run_info(self.database, self.datasheet, timestamp, "买入开多",
                                                         price, amount, amount * self.contract_value, price,
                                                         "long", amount, 0, self.total_profit,
                                                         self.total_asset)  # 将信息保存至数据库
                    self.counter += 1   # 计数器加1
                if self.market.low(-1, kline=kline) <= DonchianLo: # 突破了短周期唐奇安通道下轨
                    price = DonchianLo  # 开空价格为DonchianLo
                    amount = Units  # 开空数量为Units
                    receipt = self.exchange.sellshort(price, amount)    # 开空
                    push(receipt)   # 推送下单结果
                    self.CurrentEntries += 1    # 记录一次开仓次数
                    self.PreBreakoutFailure = False     # 将标识重置为默认值,根据离场时的盈亏情况再修改
                    storage.mysql_save_strategy_run_info(self.database, self.datasheet, timestamp, "卖出开空",
                                                         price, amount, amount * self.contract_value, price,
                                                         "short", amount, 0, self.total_profit, self.total_asset)   # 保存信息至数据库
                    self.counter += 1   # 计数器加1
            # 长周期突破开仓,其他逻辑和短周期突破开仓一样。
            if self.indicators.CurrentBar(kline=kline) >= self.fsLength and self.position.amount() == 0 and self.counter < 1:
                if self.market.high(-1, kline=kline) >= fsDonchianHi:   # 突破了长周期唐奇安通道上轨
                    price = fsDonchianHi    # 开多价格为长周期唐奇安通道上轨值
                    amount = Units  # 数量为Units
                    receipt = self.exchange.buy(price, amount)  # 下单并返回下单结果
                    push(receipt)   # 推送下单结果
                    self.CurrentEntries += 1    # 记录一次开仓次数
                    self.PreBreakoutFailure = False     # 将标识重置为默认值
                    storage.mysql_save_strategy_run_info(self.database, self.datasheet, timestamp, "买入开多",
                                                         price, amount, amount * self.contract_value, price,
                                                         "long", amount, 0, self.total_profit,
                                                         self.total_asset)  # 将信息保存至数据库
                    self.counter += 1   # 计数器加1
                if self.market.low(-1, kline=kline) <= fsDonchianLo:    # 突破长周期唐奇安通道下轨
                    price = fsDonchianLo    # 开空价格为长周期唐奇安通道下轨值
                    amount = Units  # 开空数量为Units
                    receipt = self.exchange.sellshort(price, amount)    # 下单并返回下单结果
                    push(receipt)  # 推送下单结果
                    self.CurrentEntries += 1  # 记录一次开仓次数
                    self.PreBreakoutFailure = False   # 将标识重置为默认值
                    storage.mysql_save_strategy_run_info(self.database, self.datasheet, timestamp, "卖出开空",
                                                         price, amount, amount * self.contract_value, price,
                                                         "short", amount, 0, self.total_profit, self.total_asset)
                    self.counter += 1   # 计数器加1
            # 止盈、加仓和止损
            if self.position.direction() == "long" and self.counter < 1:     # 持多仓的情况。回测时是一根k线上整个策略从上至下运行一次,所以在此处设置计数器过滤
                if self.market.low(-1, kline=kline) <= ExitLowestPrice:    # 跌破止盈价
                    profit = self.position.coverlong_profit(last=ExitLowestPrice, market_type="usd_contract")   # 平仓前计算利润,传入最新价以及计算盈利的合约类型
                    self.total_profit += profit  # 计算经过本次盈亏后的总利润
                    self.total_asset += profit  # 计算经过本次盈亏后的总资金
                    price = ExitLowestPrice     # 平多价格为ExitLowestPrice
                    amount = self.position.amount()     # 数量为当前持仓数量
                    receipt = self.exchange.sell(price, amount)    # 平所有多单仓位
                    push(receipt)   # 推送下单结果
                    storage.mysql_save_strategy_run_info(self.database, self.datasheet, timestamp, "卖出平多",
                                                         price, amount, amount * self.contract_value,
                                                         0, "none", 0, profit, self.total_profit, self.total_asset)
                    self.counter += 1   # 计数器加1
                    self.CurrentEntries = 0   # 平仓后将开仓次数还原为0
                else:
                    # 加仓指令
                    '''以最高价为标准,判断是否能加仓,并限制最大加仓次数
                       如果价格过前次开仓价格1/2N,则直接加仓
                    '''
                    while self.market.high(-1, kline=kline) >= (self.position.price() + 0.5 * N) and (self.CurrentEntries <= 4):
                        price = self.position.price() + 0.5 * N     # 加仓的开仓价格为持仓价格+0.5 * N
                        amount = Units  # 数量为Units
                        storage.mysql_save_strategy_run_info(self.database, self.datasheet, timestamp, "多头加仓",
                                                             price, amount, amount * self.contract_value,
                                                             (self.position.price() + price) / 2,
                                                             "long", self.position.amount() + amount,
                                                             0, self.total_profit, self.total_asset)
                        receipt = self.exchange.buy(price, amount)
                        push(receipt)
                        self.CurrentEntries += 1
                    # 止损指令
                    if self.market.low(-1, kline=kline) <= (self.position.price() - 2 * N):   # 如果回落大于最后下单价格-2n,就止损
                        profit = self.position.coverlong_profit(last=self.position.price() - 2 * N, market_type="usd_contract")
                        self.total_profit += profit  # 计算经过本次盈亏后的总利润
                        self.total_asset += profit  # 计算经过本次盈亏后的总资金
                        price = self.position.price() - 2 * N
                        amount = self.position.amount()
                        receipt = self.exchange.sell(price, amount)  # 全部止损平仓
                        push(receipt)
                        self.PreBreakoutFailure = True  # 记录为突破失败,下次交易将使用长周期开仓
                        storage.mysql_save_strategy_run_info(self.database, self.datasheet, timestamp, "卖出止损",
                                                             price, amount, amount * self.contract_value,
                                                             0, "none", 0, profit, self.total_profit, self.total_asset)
                        self.counter += 1
                        self.CurrentEntries = 0  # 平仓后将开仓次数还原为0
            elif self.position.direction() == "short" and self.counter < 1: # 持空头的情况,除方向以外,其他逻辑和上面持多仓的一致
                if self.market.high(-1, kline=kline) >= ExitHighestPrice:
                    profit = self.position.covershort_profit(last=ExitHighestPrice, market_type="usd_contract")
                    self.total_profit += profit
                    self.total_asset += profit
                    price = ExitHighestPrice
                    amount = self.position.amount()
                    receipt = self.exchange.buytocover(price, amount)
                    push(receipt)
                    storage.mysql_save_strategy_run_info(self.database, self.datasheet, timestamp,
                                                         "买入平空", price, amount, amount * self.contract_value,
                                                         0, "none", 0, profit, self.total_profit, self.total_asset)
                    self.counter += 1
                    self.CurrentEntries = 0  # 平仓后将开仓次数还原为0
                else:
                    while self.market.low(-1, kline=kline) <= (self.position.price() - 0.5 * N) and (self.CurrentEntries <= 4):
                        price = self.position.price() - 0.5 * N
                        amount = Units
                        storage.mysql_save_strategy_run_info(self.database, self.datasheet, timestamp, "空头加仓",
                                                             price, amount, amount * self.contract_value,
                                                             (self.position.price() + price) / 2,
                                                             "short", self.position.amount() + amount,
                                                             0, self.total_profit, self.total_asset)
                        receipt = self.exchange.sellshort(self.position.price() - 0.5 * N, Units)
                        push(receipt)
                        self.CurrentEntries += 1
                    if self.market.high(-1, kline=kline) >= (self.position.price() + 2 * N):
                        profit = self.position.covershort_profit(last=self.position.price() + 2 * N, market_type="usd_contract")
                        self.total_profit += profit
                        self.total_asset += profit
                        price = self.position.price() + 2 * N
                        amount = self.position.amount()
                        receipt = self.exchange.buytocover(price, amount)
                        push(receipt)
                        self.PreBreakoutFailure = True
                        storage.mysql_save_strategy_run_info(self.database, self.datasheet, timestamp,
                                                             "买入止损", price, amount, amount * self.contract_value,
                                                             0, "none", 0, profit, self.total_profit, self.total_asset)
                        self.counter += 1
                        self.CurrentEntries = 0  # 平仓后将开仓次数还原为0
        except:
            logger.error()
Example #15
0
class SIGNALIZE:
    """实盘时根据从交易所获取的k线数据绘制k线图、成交量图及指标"""
    def __init__(self, platform, symbol, time_frame):

        self.__platform = platform
        self.__symbol = symbol
        self.__time_frame = time_frame
        self.__market = MARKET(self.__platform, self.__symbol,
                               self.__time_frame)

        # pull some data
        self.__indicators = INDICATORS(self.__platform, self.__symbol,
                                       self.__time_frame)
        self.__kline = platform.get_kline(self.__time_frame)
        self.__kline.reverse()

        # format it in pandas
        try:  # dataframe有7列的情况
            self.__df = pd.DataFrame(self.__kline,
                                     columns=[
                                         'time', 'open', 'high', 'low',
                                         'close', 'volume', 'currency_volume'
                                     ])
            self.__df = self.__df.astype({
                'time': 'datetime64[ns]',
                'open': 'float64',
                'close': 'float64',
                'high': 'float64',
                'low': 'float64',
                'volume': 'float64',
                'currency_volume': 'float64'
            })
        except:  # dataframe只有6列的情况,如okex的现货k线数据
            self.__df = pd.DataFrame(
                self.__kline,
                columns=['time', 'open', 'high', 'low', 'close', 'volume'])
            self.__df = self.__df.astype({
                'time': 'datetime64[ns]',
                'open': 'float64',
                'close': 'float64',
                'high': 'float64',
                'low': 'float64',
                'volume': 'float64'
            })

        # create three plot 创建三层图纸,第一层画k线,第二层画成交量,第三层画一些适宜于副图显示的指标
        fplt.foreground = '#FFFFFF'  # 前景色
        fplt.background = '#333333'  # 背景色
        fplt.odd_plot_background = '#333333'  # 第二层图纸的背景色
        fplt.cross_hair_color = "#FFFFFF"  # 准星的颜色
        self.__ax, self.__ax2, self.__ax3 = fplt.create_plot(symbol, rows=3)

        # plot candle sticks
        candles = self.__df[['time', 'open', 'close', 'high', 'low']]
        fplt.candlestick_ochl(candles, ax=self.__ax)

        # overlay volume on the plot
        volumes = self.__df[['time', 'open', 'close', 'volume']]
        fplt.volume_ocv(volumes, ax=self.__ax2)
        fplt.add_legend("VOLUME", self.__ax2)  # 增加"VOLUME"图例

    """
    plot indicators
    """

    def show(self):
        """最后必须调用此函数以显示图像"""
        fplt.show()

    def plot_last(self, color=None):
        """在图上画出最新成交价这根横线,便于观察"""
        last = self.__market.last()
        array = np.empty(len(self.__kline))
        array.fill(last)
        color = color if color is not None else "#CD7F32"  # 默认设置为红色
        fplt.plot(self.__df['time'],
                  array,
                  color=color,
                  ax=self.__ax,
                  legend="LAST {}".format(last))

    def plot_array(self, array, ax, legend, color=None):
        """
        绘制任意的数组成线性
        :param array: 传入一个数组
        :param ax: 加载在第几行的图上
        :param legend: 图例名称
        :param color: 颜色
        :return:
        """
        if ax == 1:
            ax = self.__ax
        elif ax == 2:
            ax = self.__ax2
        elif ax == 3:
            ax = self.__ax3
        color = color if color is not None else "#FF0000"  # 默认设置为红色
        fplt.plot(self.__df['time'], array, color=color, ax=ax, legend=legend)

    def plot_atr(self, length, color=None):
        """
        在图上画出ATR
        :param length: ATR指标参数
        :param color: 线的颜色
        :return:
        """
        color = color if color is not None else "#FF0000"  # 默认设置为红色
        fplt.plot(self.__df['time'],
                  self.__indicators.ATR(length),
                  color=color,
                  ax=self.__ax3,
                  legend='ATR({})'.format(length))

    def plot_boll(self, length, color1=None, color2=None, color3=None):
        """
        在图上画出布林通道的上轨、中轨、下轨
        :param length: BOLL指标参数
        :param upperband_color: 上轨颜色
        :param middleband_color: 中轨颜色
        :param lowerband_color: 下轨颜色
        :return:
        """
        color1 = color1 if color1 is not None else "#FF0000"  # 默认设置为红色
        color2 = color2 if color2 is not None else "#00FF00"  # 默认设置为绿色
        color3 = color3 if color3 is not None else "#0000FF"  # 默认设置为蓝色
        upperband_array = self.__indicators.BOLL(length)['upperband']
        middleband_array = self.__indicators.BOLL(length)["middleband"]
        lowerband_array = self.__indicators.BOLL(length)["lowerband"]
        fplt.plot(self.__df['time'],
                  upperband_array,
                  color=color1,
                  ax=self.__ax,
                  legend='BOLL({})-UPPERBAND'.format(length))
        fplt.plot(self.__df['time'],
                  middleband_array,
                  color=color2,
                  ax=self.__ax,
                  legend='BOLL({})-MIDDLEBAND'.format(length))
        fplt.plot(self.__df['time'],
                  lowerband_array,
                  color=color3,
                  ax=self.__ax,
                  legend='BOLL({})-LOWERBAND'.format(length))
        # 副图上也加载
        fplt.plot(self.__df['time'],
                  upperband_array,
                  color=color1,
                  ax=self.__ax3,
                  legend='BOLL({})-UPPERBAND'.format(length))
        fplt.plot(self.__df['time'],
                  middleband_array,
                  color=color2,
                  ax=self.__ax3,
                  legend='BOLL({})-MIDDLEBAND'.format(length))
        fplt.plot(self.__df['time'],
                  lowerband_array,
                  color=color3,
                  ax=self.__ax3,
                  legend='BOLL({})-LOWERBAND'.format(length))

    def plot_highest(self, length, color=None):
        """
        在图上画出最高价
        :param length: HIGHEST指标参数
        :param color: 线的颜色
        :return:
        """
        color = color if color is not None else "#FF0000"  # 默认设置红黑色
        fplt.plot(self.__df['time'],
                  self.__indicators.HIGHEST(length),
                  color=color,
                  ax=self.__ax,
                  legend='HIGHEST({})'.format(length))
        # 副图也加载
        fplt.plot(self.__df['time'],
                  self.__indicators.HIGHEST(length),
                  color=color,
                  ax=self.__ax3,
                  legend='HIGHEST({})'.format(length))

    def plot_ma(self, length, color=None):
        """
        在图上画出移动平均线
        :param length: 简单移动平均线参数
        :param color: 线的颜色
        :return:
        """
        color = color if color is not None else "#FF0000"  # 默认设置为红色
        # 主图与副图加载指标
        fplt.plot(self.__df['time'],
                  self.__indicators.MA(length),
                  color=color,
                  ax=self.__ax,
                  legend='MA({})'.format(length))
        fplt.plot(self.__df['time'],
                  self.__indicators.MA(length),
                  color=color,
                  ax=self.__ax3,
                  legend='MA({})'.format(length))

    def plot_macd(self,
                  fastperiod,
                  slowperiod,
                  signalperiod,
                  color1=None,
                  color2=None,
                  color3=None):
        """
        在图上画出MACD指标
        :param fastperiod:
        :param slowperiod:
        :param signalperiod:
        :param color1:
        :param color2:
        :param color3:
        :return:
        """
        color1 = color1 if color1 is not None else "#FF0000"  # 默认设置为红色
        color2 = color2 if color2 is not None else "#00FF00"  # 默认设置为绿色
        color3 = color3 if color3 is not None else "#0000FF"  # 默认设置为蓝色
        dif = self.__indicators.MACD(fastperiod, slowperiod,
                                     signalperiod)['DIF']
        dea = self.__indicators.MACD(fastperiod, slowperiod,
                                     signalperiod)["DEA"]
        macd = self.__indicators.MACD(fastperiod, slowperiod,
                                      signalperiod)["MACD"]
        fplt.plot(self.__df['time'],
                  dif,
                  color=color1,
                  ax=self.__ax3,
                  legend='MACD({}, {}, {})-DIF'.format(fastperiod, slowperiod,
                                                       signalperiod))
        fplt.plot(self.__df['time'],
                  dea,
                  color=color2,
                  ax=self.__ax3,
                  legend='MACD({}, {}, {})-DEA'.format(fastperiod, slowperiod,
                                                       signalperiod))
        fplt.plot(self.__df['time'],
                  macd,
                  color=color3,
                  ax=self.__ax3,
                  legend='MACD({}, {}, {})-MACD'.format(
                      fastperiod, slowperiod, signalperiod))

    def plot_ema(self, length, color=None):
        """
        在图上画出EMA指标
        :param length:
        :param color:
        :return:
        """
        color = color if color is not None else "#FF0000"  # 默认设置为红色
        fplt.plot(self.__df['time'],
                  self.__indicators.EMA(length),
                  color=color,
                  ax=self.__ax,
                  legend='EMA({})'.format(length))
        # 副图也加载
        fplt.plot(self.__df['time'],
                  self.__indicators.EMA(length),
                  color=color,
                  ax=self.__ax3,
                  legend='EMA({})'.format(length))

    def plot_kama(self, length, color=None):
        """在图上画出KAMA指标"""
        color = color if color is not None else "#FF0000"  # 默认设置为红色
        fplt.plot(self.__df['time'],
                  self.__indicators.KAMA(length),
                  color=color,
                  ax=self.__ax,
                  legend='KAMA({})'.format(length))
        # 副图也加载
        fplt.plot(self.__df['time'],
                  self.__indicators.KAMA(length),
                  color=color,
                  ax=self.__ax3,
                  legend='KAMA({})'.format(length))

    def plot_kdj(self,
                 fastk_period,
                 slowk_period,
                 slowd_period,
                 color1=None,
                 color2=None):
        """
        在图上画出KDJ指标
        :param fastk_period:
        :param slowk_period:
        :param slowd_period:
        :param color1:
        :param color2:
        :param color3:
        :return:
        """
        color1 = color1 if color1 is not None else "#FF0000"  # 默认设置为红色
        color2 = color2 if color2 is not None else "#00FF00"  # 默认设置为绿色
        k = self.__indicators.KDJ(fastk_period, slowk_period,
                                  slowd_period)['k']
        d = self.__indicators.KDJ(fastk_period, slowk_period,
                                  slowd_period)["d"]
        # 仅副图加载
        fplt.plot(self.__df['time'],
                  k,
                  color=color1,
                  ax=self.__ax3,
                  legend='KDJ({}, {}, {})-K'.format(fastk_period, slowk_period,
                                                    slowd_period))
        fplt.plot(self.__df['time'],
                  d,
                  color=color2,
                  ax=self.__ax3,
                  legend='KDJ({}, {}, {})-D'.format(fastk_period, slowk_period,
                                                    slowd_period))

    def plot_lowest(self, length, color=None):
        """LOWEST"""
        color = color if color is not None else "#FF0000"  # 默认设置红黑色
        fplt.plot(self.__df['time'],
                  self.__indicators.LOWEST(length),
                  color=color,
                  ax=self.__ax,
                  legend='LOWEST({})'.format(length))
        # 副图也加载
        fplt.plot(self.__df['time'],
                  self.__indicators.LOWEST(length),
                  color=color,
                  ax=self.__ax3,
                  legend='LOWEST({})'.format(length))

    def plot_obv(self, color=None):
        """OBV"""
        color = color if color is not None else "#FF0000"  # 默认设置红黑色
        # 仅副图加载
        fplt.plot(self.__df['time'],
                  self.__indicators.OBV(),
                  color=color,
                  ax=self.__ax3,
                  legend='OBV')

    def plot_rsi(self, length, color=None):
        """RSI"""
        color = color if color is not None else "#FF0000"  # 默认设置为红色
        # 仅副图加载
        fplt.plot(self.__df['time'],
                  self.__indicators.RSI(length),
                  color=color,
                  ax=self.__ax3,
                  legend='RSI({})'.format(length))

    def plot_roc(self, length, color=None):
        """ROC"""
        color = color if color is not None else "#FF0000"  # 默认设置为红色
        # 仅副图加载
        fplt.plot(self.__df['time'],
                  self.__indicators.ROC(length),
                  color=color,
                  ax=self.__ax3,
                  legend='ROC({})'.format(length))

    def plot_stochrsi(self,
                      timeperiod,
                      fastk_period,
                      fastd_period,
                      color1=None,
                      color2=None):
        """STOCHRSI"""
        color1 = color1 if color1 is not None else "#FF0000"  # 默认设置为红色
        color2 = color2 if color2 is not None else "#00FF00"  # 默认设置为绿色
        stochrsi = self.__indicators.STOCHRSI(timeperiod, fastk_period,
                                              fastd_period)['stochrsi']
        fastk = self.__indicators.STOCHRSI(timeperiod, fastk_period,
                                           fastd_period)["fastk"]
        # 仅副图加载
        fplt.plot(self.__df['time'],
                  stochrsi,
                  color=color1,
                  ax=self.__ax3,
                  legend='STOCHRSI({}, {}, {})-STOCHRSI'.format(
                      timeperiod, fastk_period, fastd_period))
        fplt.plot(self.__df['time'],
                  fastk,
                  color=color2,
                  ax=self.__ax3,
                  legend='STOCHRSI({}, {}, {})-FASTK'.format(
                      timeperiod, fastk_period, fastd_period))

    def plot_sar(self, color=None):
        """
        在图上画出SAR
        :param length: SAR指标参数
        :param color: 线的颜色
        :return:
        """
        color = color if color is not None else "#FF0000"  # 默认设置为红色
        # 主副图均加载
        fplt.plot(self.__df['time'],
                  self.__indicators.SAR(),
                  color=color,
                  ax=self.__ax,
                  legend='SAR')
        fplt.plot(self.__df['time'],
                  self.__indicators.SAR(),
                  color=color,
                  ax=self.__ax3,
                  legend='SAR')

    def plot_stddev(self, length, color=None):
        """STDDEV"""
        color = color if color is not None else "#FF0000"  # 默认设置为红色
        # 仅副图加载
        fplt.plot(self.__df['time'],
                  self.__indicators.STDDEV(length),
                  color=color,
                  ax=self.__ax3,
                  legend='STDDEV({})'.format(length))

    def plot_trix(self, length, color=None):
        """STDDEV"""
        color = color if color is not None else "#FF0000"  # 默认设置为红色
        # 仅副图加载
        fplt.plot(self.__df['time'],
                  self.__indicators.TRIX(length),
                  color=color,
                  ax=self.__ax3,
                  legend='TRIX({})'.format(length))

    def plot_volume(self, color=None):
        """VOLUME"""
        color = color if color is not None else "#FF0000"  # 默认设置为红色
        # 仅副图均加载
        fplt.plot(self.__df['time'],
                  self.__indicators.VOLUME(),
                  color=color,
                  ax=self.__ax3,
                  legend='VOLUME')
from purequant.trade import HUOBISPOT
from purequant.market import MARKET
from purequant.indicators import INDICATORS
from purequant.position import POSITION

# 账户和策略参数等信息
accessy_key = 'your access_key'
secret_key = 'your secret_key'
instrument_id = 'ETC-USDT'
time_frame = '1d'

# 初始化交易所、行情模块与指标等模块
exchange = HUOBISPOT(accessy_key, secret_key, instrument_id)
market = MARKET(exchange, instrument_id, time_frame)
indicators = INDICATORS(exchange, instrument_id, time_frame)
position = POSITION(exchange, instrument_id, time_frame)

# 下单交易,买入和卖出
# info = exchange.buy(7.35, 0.02)           # 以7.35的价格买入0.02个ETC
# info = exchange.sell(7.35, 0.01)            # 卖出0.02个ETC

# 获取行情信息
# info = exchange.get_kline(time_frame)      # 获取k线数据
# info = market.last()                      # 获取ETC-USDT的最新成交价
# info = market.open(-1)                      # 获取ETC-USDT的当日开盘价
# info = market.high(-1)                      # 获取ETC-USDT的当日最高价
# info = market.low(-1)                      # 获取ETC-USDT的当日最低价
# info = market.close(-1)                      # 获取ETC-USDT的当日收盘价

# 持仓信息
# info = position.amount()                  # 获取ETC-USDT交易对的ETC可用余额
Example #17
0
class Strategy:
    def __init__(self, instrument_id, time_frame, fast_length, slow_length,
                 long_stop, short_stop, start_asset, precision):
        config.loads('config.json')  # 载入配置文件
        self.instrument_id = instrument_id  # 合约ID
        self.time_frame = time_frame  # k线周期
        self.precision = precision  # 精度,即币对的最小交易数量
        self.exchange = OKEXSPOT(config.access_key, config.secret_key,
                                 config.passphrase,
                                 self.instrument_id)  # 初始化交易所
        self.position = POSITION(self.exchange, self.instrument_id,
                                 self.time_frame)  # 初始化potion
        self.market = MARKET(self.exchange, self.instrument_id,
                             self.time_frame)  # 初始化market
        self.indicators = INDICATORS(self.exchange, self.instrument_id,
                                     self.time_frame)  # 初始化indicators
        # 在第一次运行程序时,将初始资金数据保存至数据库中
        self.database = "回测"  # 回测时必须为"回测"
        self.datasheet = self.instrument_id.split(
            "-")[0].lower() + "_" + time_frame
        if config.first_run:
            storage.mysql_save_strategy_run_info(self.database, self.datasheet,
                                                 get_localtime(), "none", 0, 0,
                                                 0, 0, "none", 0, 0, 0,
                                                 start_asset)
        # 读取数据库中保存的总资金数据
        self.total_asset = storage.read_mysql_datas(0, self.database,
                                                    self.datasheet, "总资金",
                                                    ">")[-1][-1]
        self.total_profit = storage.read_mysql_datas(0, self.database,
                                                     self.datasheet, "总资金",
                                                     ">")[-1][-2]  # 策略总盈亏
        self.counter = 0  # 计数器
        self.fast_length = fast_length  # 短周期均线长度
        self.slow_length = slow_length  # 长周期均线长度
        self.long_stop = long_stop  # 多单止损幅度
        self.short_stop = short_stop  # 空单止损幅度
        self.hold_price = 0  # 注意:okex的现货没有获取持仓均价的接口,故需实盘时需要手动记录入场价格。此种写法对于不同的交易所是通用的。
        # 此种写法,若策略重启,持仓价格会回归0
        print("{} {} 双均线多空策略已启动!".format(get_localtime(),
                                         instrument_id))  # 程序启动时打印提示信息

    def begin_trade(self, kline=None):
        try:
            if self.indicators.CurrentBar(
                    kline=kline) < self.slow_length:  # 如果k线数据不够长就返回
                return
            timestamp = ts_to_datetime_str(utctime_str_to_ts(
                kline[-1]
                [0])) if kline else get_localtime()  # 非回测模式下时间戳就是当前本地时间
            # 计算策略信号
            ma = self.indicators.MA(self.fast_length,
                                    self.slow_length,
                                    kline=kline)
            fast_ma = ma[0]
            slow_ma = ma[1]
            cross_over = fast_ma[-2] >= slow_ma[-2] and fast_ma[-3] < slow_ma[
                -3]  # 不用当根k线上的ma来计算信号,防止信号闪烁
            cross_below = slow_ma[-2] >= fast_ma[-2] and slow_ma[-3] < fast_ma[
                -3]
            if self.indicators.BarUpdate(kline=kline):  # 如果k线更新,计数器归零
                self.counter = 0
            if self.counter < 1:
                # 按照策略信号开平仓
                if cross_over and round(
                        self.position.amount()
                ) < self.precision:  # 金叉时,若当前无持仓,则买入开多并推送下单结果。0.1这个数值根据每个币对的最小交易数量决定
                    price = float(self.market.open(
                        -1, kline=kline))  # 下单价格=此根k线开盘价
                    self.hold_price = price  # 记录开仓价格
                    amount = float(self.total_asset / price)  # 数量=总资金/价格
                    info = self.exchange.buy(price, amount)
                    push(info)
                    storage.mysql_save_strategy_run_info(
                        self.database, self.datasheet, timestamp, "买入开多",
                        price, amount, amount * price, price, "long", amount,
                        0, self.total_profit, self.total_asset)  # 将信息保存至数据库
                if cross_below and round(
                        self.position.amount(), 1
                ) >= self.precision:  # 死叉时,如果当前持多就卖出平多。当前持仓数量根据币对的最小交易数量取小数
                    price = float(self.market.open(-1, kline=kline))
                    amount = float(self.position.amount())
                    profit = (price - self.hold_price) * amount  # 计算逻辑盈亏
                    self.total_profit += profit
                    self.total_asset += profit
                    info = self.exchange.sell(price, amount)
                    push(info)
                    self.hold_price = 0  # 平多后记录持仓价格为0
                    storage.mysql_save_strategy_run_info(
                        self.database, self.datasheet, timestamp, "卖出平多",
                        price, amount, amount * price, 0, "none", 0, profit,
                        self.total_profit, self.total_asset)
                # 如果当前持多且最低价小于等于持仓均价*止损幅度,触发止损,卖出平多止损
                if round(self.position.amount(),
                         1) >= self.precision and self.market.low(
                             -1,
                             kline=kline) <= self.hold_price * self.long_stop:
                    price = float(self.hold_price * self.long_stop)
                    amount = float(self.position.amount())
                    profit = (price - self.hold_price) * amount  # 计算逻辑盈亏
                    self.total_profit += profit
                    self.total_asset += profit
                    info = self.exchange.sell(price, amount)
                    push("此次盈亏:{} 当前总资金:{}".format(profit, self.total_asset) +
                         str(info))
                    self.hold_price = 0  # 平多后记录持仓价格为0
                    storage.mysql_save_strategy_run_info(
                        self.database, self.datasheet, timestamp, "卖出止损",
                        price, amount, amount * price, 0, "none", 0, profit,
                        self.total_profit, self.total_asset)
                    self.counter += 1  # 计数器加1,控制此根k线上不再下单
        except Exception as e:
            logger.info()