def __init__(self):
        key_dict = {}
        # 读取配置文件
        with open('config', 'r') as f:
            for line in f.readlines():
                splited = line.split('=')
                if len(splited) == 2:
                    key_dict[splited[0].strip()] = splited[1].strip()

        self.client = OkexFutureClient(key_dict['OKEX_ACCESS_KEY'],
                                       key_dict['OKEX_SECRET_KEY'])
        self.spot_client = HuobiSpot(key_dict['HUOBI_ACCESS_KEY'],
                                     key_dict['HUOBI_SECRET_KEY'])

        # config logging
        self.logger = logging.getLogger("Future")

        # 指定logger输出格式
        formatter = logging.Formatter(
            '%(asctime)s %(levelname)-8s: %(message)s')

        # 文件日志
        file_handler = logging.FileHandler("term2.log")
        file_handler.setFormatter(formatter)

        # 控制台日志
        console_handler = logging.StreamHandler(sys.stdout)
        console_handler.formatter = formatter

        # 为logger添加的日志处理器
        self.logger.addHandler(file_handler)
        self.logger.addHandler(console_handler)

        # 指定日志的最低输出级别,默认为WARN级别
        self.logger.setLevel(logging.INFO)

        # 空头合约数量
        self.short_bear_amount = 0
        self.long_bear_amount = 0

        # 多头合约数量
        self.short_bull_amount = 0
        self.long_bull_amount = 0

        # 保证金
        self.keep_deposit = 0
        self.risk_rate = 0
        self.future_rights = 0
        self.profit_real = 0
        self.profit_unreal = 0
        self.bond = 0
        # 指数
        self.future_index = 0

        # 等待交割
        self.wait_for_delivery = False
示例#2
0
    def __init__(self):
        key_dict = {}
        # 读取配置文件
        with open('config', 'r') as f:
            for line in f.readlines():
                splited = line.split('=')
                if len(splited) == 2:
                    key_dict[splited[0].strip()] = splited[1].strip()

        self.huobi_client = HuobiSpot(key_dict['HUOBI_ACCESS_KEY3'],
                                      key_dict['HUOBI_SECRET_KEY3'])
        self.future_client = OkexFutureClient(key_dict['OKEX_ACCESS_KEY'],
                                              key_dict['OKEX_SECRET_KEY'])

        # 空头合约数量
        self.bear_amount = 0

        # 多头合约数量
        self.bull_amount = 0

        # 保证金
        self.keep_deposit = 0
        self.risk_rate = 0
        self.future_rights = 0
        self.profit_real = 0
        self.profit_unreal = 0
        self.bond = 0
        # 指数
        self.future_index = 0

        self.spot_free_btc = 0
        self.spot_free_usdt = 0
        self.spot_freezed_btc = 0
        self.spot_freezed_usdt = 0

        self.total_btc = 0
        self.total_usdt = 0

        # 统计收益

        # config logging
        self.logger = logging.getLogger("Future")

        # 指定logger输出格式
        formatter = logging.Formatter(
            '%(asctime)s %(levelname)-8s: %(message)s')

        # 文件日志
        file_handler = logging.FileHandler("term.log")
        file_handler.setFormatter(formatter)

        # 控制台日志
        console_handler = logging.StreamHandler(sys.stdout)
        console_handler.formatter = formatter

        # 为logger添加的日志处理器
        self.logger.addHandler(file_handler)
        self.logger.addHandler(console_handler)

        # 指定日志的最低输出级别,默认为WARN级别
        self.logger.setLevel(logging.INFO)

        # 平均成交差价
        self.avg_price_diff = 0

        # 2018/4/18
        self.spot_kline = []
        self.future_kline = []
示例#3
0
class TermArbitrage:
    # 正表示期货价格高于现货
    profit_rate = 0.05
    slippage = 0.0002
    # open_positive_rate = 40
    close__thd = 0

    # open_negative_thd = -40
    # close_thd = 0

    # 风险控制
    margin_thd = 200
    risk_thd = 0.5

    margin_coefficient = 0.6

    symbol = 'btc_usdt'
    contract_type = 'quarter'

    # 每次最大合约交易量
    max_contract_exchange_amount = 5

    # 1 开多 2 开空 3 平多 4 平空
    debug_type = 0

    status_dict = ['']

    def __init__(self):
        key_dict = {}
        # 读取配置文件
        with open('config', 'r') as f:
            for line in f.readlines():
                splited = line.split('=')
                if len(splited) == 2:
                    key_dict[splited[0].strip()] = splited[1].strip()

        self.huobi_client = HuobiSpot(key_dict['HUOBI_ACCESS_KEY3'],
                                      key_dict['HUOBI_SECRET_KEY3'])
        self.future_client = OkexFutureClient(key_dict['OKEX_ACCESS_KEY'],
                                              key_dict['OKEX_SECRET_KEY'])

        # 空头合约数量
        self.bear_amount = 0

        # 多头合约数量
        self.bull_amount = 0

        # 保证金
        self.keep_deposit = 0
        self.risk_rate = 0
        self.future_rights = 0
        self.profit_real = 0
        self.profit_unreal = 0
        self.bond = 0
        # 指数
        self.future_index = 0

        self.spot_free_btc = 0
        self.spot_free_usdt = 0
        self.spot_freezed_btc = 0
        self.spot_freezed_usdt = 0

        self.total_btc = 0
        self.total_usdt = 0

        # 统计收益

        # config logging
        self.logger = logging.getLogger("Future")

        # 指定logger输出格式
        formatter = logging.Formatter(
            '%(asctime)s %(levelname)-8s: %(message)s')

        # 文件日志
        file_handler = logging.FileHandler("term.log")
        file_handler.setFormatter(formatter)

        # 控制台日志
        console_handler = logging.StreamHandler(sys.stdout)
        console_handler.formatter = formatter

        # 为logger添加的日志处理器
        self.logger.addHandler(file_handler)
        self.logger.addHandler(console_handler)

        # 指定日志的最低输出级别,默认为WARN级别
        self.logger.setLevel(logging.INFO)

        # 平均成交差价
        self.avg_price_diff = 0

        # 2018/4/18
        self.spot_kline = []
        self.future_kline = []

    def update_future_position(self):
        self.logger.info('全仓用户持仓查询')
        try:
            info = self.future_client.position(self.symbol, self.contract_type)
        except Exception as e:
            self.logger.error('全仓用户持仓查询异常: %s' % e)
        else:
            # print info
            if info['result']:
                holding = info['holding']
                if len(holding) > 0:
                    # hold是数组
                    self.bull_amount = holding[0]['buy_amount']
                    self.bear_amount = holding[0]['sell_amount']
                    if self.bull_amount == 0 and self.bear_amount == 0:
                        self.logger.info('用户未持仓')
                        self.total_btc = self.future_rights + self.spot_free_btc
                        self.total_usdt = self.spot_free_usdt
                        self.logger.info('total:')
                        self.logger.info('BTC: %s\tUSDT: %s' %
                                         (self.total_btc, self.total_usdt))
                    else:
                        self.logger.info('多仓: %s\t空仓: %s' %
                                         (self.bull_amount, self.bear_amount))
            else:
                self.logger.info('postion_4fix result error')

    def update_spot_account(self):
        self.logger.info('获取SPOT账户信息')
        r = self.huobi_client.margin_balance('btcusdt')
        # print r
        if r['status'] == 'ok':
            for item in r['data'][0]['list']:
                if item['currency'] == 'btc' and item['type'] == 'trade':
                    self.spot_free_btc = TermArbitrage.cut2_float(
                        item['balance'], 8)
                elif item['currency'] == 'btc' and item['type'] == 'frozen':
                    freezed_btc = float(item['balance'])
                elif item['currency'] == 'usdt' and item['type'] == 'trade':
                    self.spot_free_usdt = TermArbitrage.cut2_float(
                        item['balance'], 8)
                elif item['currency'] == 'usdt' and item['type'] == 'frozen':
                    freezed_usdt = float(item['balance'])
                elif item['currency'] == 'btc' and item['type'] == 'loan':
                    spot_loan_btc = float(item['balance'])
                elif item['currency'] == 'usdt' and item['type'] == 'loan':
                    spot_loan_usdt = float(item['balance'])
                elif item['currency'] == 'btc' and item['type'] == 'interest':
                    spot_interest_btc = float(item['balance'])
                elif item['currency'] == 'usdt' and item['type'] == 'interest':
                    spot_interest_usdt = float(item['balance'])
            self.logger.info('spot_btc:%s\tspot_usdt:%s' %
                             (self.spot_free_btc, self.spot_free_usdt))
        else:
            if 'fail' == r['status']:
                self.logger.error('Huobi get_balance error: %s' % r['msg'])
            else:
                self.logger.error('Huobi get_balance error: %s' % r['err-msg'])

    def update_future_account(self, vertify=False):
        times = 0
        while times < 10:
            old_bond = self.bond
            self.logger.info('第%s次获取全仓账户信息' % (times + 1))
            try:
                future_info = self.future_client.userinfo()
            except Exception as e:
                self.logger.error('获取Future全仓账户信息异常: %s' % e)
            else:
                # print future_info
                if future_info['result']:
                    btc_info = future_info['info']['btc']
                    print btc_info
                    self.keep_deposit = btc_info['keep_deposit']
                    self.risk_rate = btc_info['risk_rate']
                    self.future_rights = btc_info['account_rights']
                    self.profit_real = btc_info['profit_real']
                    self.profit_unreal = btc_info['profit_unreal']
                    self.bond = self.keep_deposit + self.profit_unreal / 10
                    self.logger.info('bond:%s\trights:%s' %
                                     (self.bond, self.future_rights))
            if not vertify or abs(self.bond - old_bond) > 1e-9:
                break
            times += 1
            if times == 9:
                time.sleep(3)
        if times == 10:
            self.logger.info('账户信息未更新')

    @staticmethod
    def cut2_float(s, n):
        if isinstance(s, float):
            s = '{:.8f}'.format(s)
        pattern = re.compile(r'(\d+\.\d{1,%d})\d*' % n)
        return float(pattern.match(s).group(1))

    def calc_available_contract(self, price):
        available_btc = self.future_rights * self.margin_coefficient - self.bond
        return int(available_btc * price / 10) if available_btc > 0 else 0

    def judge_trend2(self):
        nowtime = time.strftime('%H:%M:%S', time.localtime(time.time()))
        second = int(nowtime.split(':')[2])
        if 30 < second < 40:
            self.future_kline = []
            self.spot_kline = []
            # .logger.info('get spot kline')
            spot_kline = self.huobi_client.get_kline('btcusdt', '1min', 120)
            if spot_kline['status'] == 'ok':
                # print spot_kline
                for item in spot_kline['data']:
                    self.spot_kline.append({
                        'ts':
                        item['id'],
                        'open':
                        item['open'],
                        'close':
                        item['close'],
                        'high':
                        item['high'],
                        'low':
                        item['low'],
                        'avg':
                        float('%.2f' % (item['vol'] / item['amount']))
                        if item['amount'] > 0 else
                        (item['high'] + item['low']) / 2
                    })
            else:
                self.logger.info('get spot kline error')

            # self.logger.info('get future kline')
            try:
                future_kline = self.future_client.kline(
                    self.symbol, '1min', self.contract_type)
            except Exception as e:
                self.logger.error('get future kline error: %s' % e)
            else:
                for item in future_kline[::-1][:120]:
                    self.future_kline.append({
                        'ts':
                        item[0] / 1000,
                        'open':
                        item[1],
                        'close':
                        item[4],
                        'high':
                        item[2],
                        'low':
                        item[3],
                        'avg':
                        float('%.2f' %
                              (100 * item[5] / item[6])) if item[6] > 0 else
                        (item[2] + item[3]) / 2
                    })
            max_diff1 = -1 << 31
            max_diff2 = -1 << 31
            min_diff1 = (1 << 31) - 1
            min_diff2 = (1 << 31) - 1
            # print self.spot_kline
            # print self.future_kline
            if len(self.spot_kline) == 120 and len(self.future_kline) == 120 and self.future_kline[0]['ts'] == \
                    self.spot_kline[0]['ts']:
                cur_diff = self.future_kline[0]['avg'] - self.spot_kline[0][
                    'avg']
                for i in xrange(1, 60):
                    diff = self.future_kline[i]['avg'] - self.spot_kline[i][
                        'avg']
                    if diff > max_diff1:
                        max_diff1 = diff
                    elif diff < min_diff1:
                        min_diff1 = diff
                    max_diff2 = max_diff1
                    min_diff2 = min_diff1
                for i in xrange(60, 120):
                    diff = self.future_kline[i]['avg'] - self.spot_kline[i][
                        'avg']
                    if diff > max_diff2:
                        max_diff2 = diff
                    elif diff < min_diff2:
                        min_diff2 = diff
                print cur_diff, max_diff1, max_diff2
                print cur_diff, min_diff1, min_diff2

                if cur_diff < 0:
                    if cur_diff - min_diff1 > 10 or cur_diff - min_diff2 > 15:
                        # 下降
                        return 1
                    elif cur_diff - max_diff1 < -10 or cur_diff - max_diff2 < -15:
                        # 上升
                        return 2
                else:
                    if cur_diff - max_diff1 < -10 or cur_diff - max_diff2 < -15:
                        # 下降
                        return 3
                    elif cur_diff - min_diff1 > 10 or cur_diff - min_diff2 > 15:
                        # 上升
                        return 4
        return 0

    def calc_exchange_contract_amount(self, spot_depth, future_depth):
        spot_amount, future_amount = 0, 0
        for d in spot_depth:
            if abs(d[0] -
                   spot_depth[0][0]) / spot_depth[0][0] <= self.slippage / 2:
                spot_amount += d[1]
        for d in future_depth:
            if abs(d[0] -
                   future_depth[0][0]) / future_depth[0][0] <= self.slippage:
                future_amount += d[1]
        return min(int(spot_amount * spot_depth[0][0] / 200),
                   int(future_amount / 2))

    def go(self):
        while True:
            # print('获取深度')
            try:
                future_depth = self.future_client.depth(
                    self.symbol, self.contract_type, 10)
            except Exception as e:
                self.logger.error('获取期货市场深度错误: %s' % e)
                time.sleep(3)
                continue
            future_bids = future_depth['bids']
            future_asks = future_depth['asks'][::-1]

            spot_depth = self.huobi_client.get_depth('btcusdt', 'step5')
            # print spot_depth
            if spot_depth['status'] == 'ok':
                spot_bids = spot_depth['tick']['bids']
                spot_asks = spot_depth['tick']['asks']
            else:
                time.sleep(3)
                self.logger.error('获取现货市场深度错误')
                continue

            price_index = (future_bids[0][0] + spot_asks[0][0]) / 2
            diff_thd = self.profit_rate * price_index
            d1 = future_bids[0][0] - spot_asks[0][0]
            d1 = float('%.2f' % d1)

            trend = self.judge_trend2()
            print d1, trend
            if abs(d1) > self.margin_thd:
                self.logger.info('期现差价超过阈值, 程序终止')
                break
            # 期货价格低于现货,且差价开始降低, 期货开多
            if trend == 1 and spot_bids[0][0] - future_asks[0][0] > diff_thd:
                available_bull_amount = self.calc_available_contract(
                    future_asks[0][0])
                if available_bull_amount == 0:
                    # self.logger.info('可开合约数量不足')
                    time.sleep(3)
                    continue
                self.logger.info('可开合约数量: %s' % available_bull_amount)
                self.logger.info('期货开多,现货卖出')
                self.logger.info('期货价格: %s,现货价格: %s,差价为: %s' %
                                 (future_asks[0][0], spot_bids[0][0],
                                  future_asks[0][0] - spot_bids[0][0]))
                self.logger.info(
                    'future_bond:%s\tspot_btc:%s\tspot_usdt:%s' %
                    (self.bond, self.spot_free_btc, self.spot_free_usdt))

                future_contract_amount = min(
                    self.max_contract_exchange_amount, available_bull_amount,
                    self.calc_exchange_contract_amount(spot_bids, future_asks),
                    int(self.spot_free_btc * (spot_bids[0][0] - 20) / 100))
                if future_contract_amount == 0:
                    continue
                self.logger.info('期货开多:%s 张' % future_contract_amount)
                # 限价购买期货合约
                price = float('%.2f' % (future_asks[0][0] *
                                        (1 + self.slippage)))
                try:
                    future_order = self.future_client.place_order(
                        self.symbol, self.contract_type, price,
                        future_contract_amount, '1', '0')
                except Exception as e:
                    self.logger.error('Future订单异常: %s' % e)
                    continue
                print future_order

                orderid = future_order['order_id']
                try:
                    order_info = self.future_client.order_info(
                        self.symbol, self.contract_type, 0, orderid, 1, 5)
                except Exception as e:
                    self.logger.error('获取订单信息异常: %s, 程序终止' % e)
                    break
                print order_info

                order_info = order_info['orders'][0]
                self.logger.info('order status: %s' % order_info['status'])
                # 等待成交或未成交
                if order_info['status'] == 0 or order_info['status'] == 1:
                    self.logger.info('撤销未完成委托')
                    try:
                        self.future_client.cancel(self.symbol,
                                                  self.contract_type, orderid)
                    except Exception as e:
                        self.logger.error('撤销异常: %s' % e)
                    else:
                        self.logger.info('撤销成功')

                    self.logger.info('更新订单状态')
                    # status:-1:已撤销  0:未成交  1:部分成交  2:完全成交 4:撤单处理中
                    times = 0
                    while times < 20:
                        self.logger.info('第%s次查询订单状态' % (times + 1))
                        try:
                            order_info = self.future_client.order_info(
                                self.symbol, self.contract_type, 0, orderid, 1,
                                5)
                            print order_info
                            order_info = order_info['orders'][0]
                            self.logger.info('订单状态: %s' % order_info['status'])
                            # 订单状态还有待确认
                            if order_info['status'] == 2 or order_info[
                                    'status'] == -1:
                                break
                        except Exception as e:
                            self.logger.error('查询订单信息异常: %s' % e)
                        times += 1

                        if times == 9:
                            time.sleep(3)
                        if times == 10:
                            time.sleep(10)
                    if times == 20:
                        self.logger.error('未知错误,程序终止')
                        break
                print order_info

                future_deal_contract_amount = order_info['deal_amount']

                if future_deal_contract_amount == 0:
                    self.logger.info('未完成任何委托')
                    continue

                future_deal_price = order_info['price_avg']
                # btc精度是8
                future_deal_btc_amount = float(
                    '%.8f' %
                    (future_deal_contract_amount * 10.0 / future_deal_price))

                self.bull_amount += future_deal_contract_amount

                self.logger.info('future_deal_contract:%d\tbtc:%s\tprice:%s' %
                                 (future_deal_contract_amount,
                                  future_deal_btc_amount, future_deal_price))
                # 市价卖出现货
                self.logger.info('现货卖出')
                spot_limited_price = spot_bids[0][0] - 20
                spot_btc_amount = float(
                    '%.4f' %
                    (100 * future_deal_contract_amount / spot_limited_price))

                spot_order = self.huobi_client.send_order(
                    spot_btc_amount, 'margin-api', 'btcusdt', 'sell-limit',
                    spot_limited_price)
                print spot_order

                if spot_order['status'] != 'ok':
                    if spot_order['status'] == 'fail':
                        self.logger.error('spot sell failed : %s' %
                                          spot_order['msg'])
                    else:
                        self.logger.error('spot sell failed : %s' %
                                          spot_order['err-msg'])
                    self.logger.info('开始回滚')
                    self.logger.info('终止程序')
                    break

                orderid = spot_order['data']
                field_cash_amount = 0
                field_amount = 0

                times = 0
                while times < 20:
                    self.logger.info('第%s次确认订单信息' % (times + 1))
                    order_info = self.huobi_client.order_info(orderid)
                    print order_info
                    if order_info['status'] == 'ok' and order_info['data'][
                            'state'] == 'filled':
                        self.logger.info('spot sell filled, orderId: %s' %
                                         orderid)
                        field_amount = float(
                            '%.8f' % float(order_info['data']['field-amount']))
                        field_cash_amount = float(
                            '%.8f' %
                            float(order_info['data']['field-cash-amount']))
                        field_price = float('%.2f' %
                                            (field_cash_amount / field_amount))
                        price_diff = future_deal_price - field_price

                        self.avg_price_diff = (
                            (self.bull_amount - future_deal_contract_amount) *
                            self.avg_price_diff + future_deal_contract_amount *
                            price_diff) / self.bull_amount
                        self.spot_free_btc -= field_amount
                        self.spot_free_usdt += field_cash_amount
                        break
                    times += 1
                    if times == 19:
                        time.sleep(15)

                if times == 20:
                    self.logger.info('现货卖出错误, 终止程序')
                    break

                self.logger.info(
                    'spot_field_amount:%.8f\tspot_field_cash_amount:%.8f' %
                    (field_amount, field_cash_amount))

                self.logger.info('avg_price_diff: %.2f' % self.avg_price_diff)
                self.update_future_account(True)

                # self.update_account_info()
            # 期货高于现货,且差价开始降低, 期货开空
            elif trend == 3 and future_bids[0][0] - spot_asks[0][0] > diff_thd:
                available_bear_amount = self.calc_available_contract(
                    future_bids[0][0])
                if available_bear_amount == 0:
                    # self.logger.info('可开合约数量不足')
                    time.sleep(3)
                    continue
                self.logger.info('可开合约数量为: %s' % available_bear_amount)
                self.logger.info('期货开空,现货买入')
                self.logger.info('期货价格: %s,现货价格: %s, 差价: %s' %
                                 (future_bids[0][0], spot_asks[0][0],
                                  future_bids[0][0] - spot_asks[0][0]))
                self.logger.info(
                    'future_bond:%s\tspot_btc:%s\tspot_usdt:%s' %
                    (self.bond, self.spot_free_btc, self.spot_free_usdt))
                future_contract_amount = min(
                    self.calc_exchange_contract_amount(spot_asks, future_bids),
                    self.max_contract_exchange_amount, available_bear_amount,
                    int(self.spot_free_usdt / 100))

                if future_contract_amount == 0:
                    continue
                # 限价购买期货合约
                self.logger.info('期货开空:%s' % future_contract_amount)
                price = float('%.2f' % (future_bids[0][0] *
                                        (1 - self.slippage)))
                try:
                    future_order = self.future_client.place_order(
                        self.symbol, self.contract_type, price,
                        future_contract_amount, '2', '0')
                except Exception as e:
                    self.logger.error('Future订单异常: %s' % e)
                    continue
                print future_order

                orderid = future_order['order_id']
                try:
                    order_info = self.future_client.order_info(
                        self.symbol, self.contract_type, 0, orderid, 1, 5)
                except Exception as e:
                    self.logger.error('获取订单信息异常: %s,程序终止' % e)
                    break
                print order_info

                order_info = order_info['orders'][0]
                self.logger.info('order status: %s' % order_info['status'])
                # 等待成交或未成交
                if order_info['status'] == 0 or order_info['status'] == 1:
                    self.logger.info('撤销未完成委托')
                    try:
                        self.future_client.cancel(self.symbol,
                                                  self.contract_type, orderid)
                    except Exception as e:
                        self.logger.error('撤销异常: %s' % e)
                    else:
                        self.logger.info('撤销成功')

                    self.logger.info('更新订单状态')
                    # status:-1:已撤销  0:未成交  1:部分成交  2:完全成交 4:撤单处理中
                    times = 0
                    while times < 20:
                        self.logger.info('第%s次查询订单状态' % (times + 1))
                        try:
                            order_info = self.future_client.order_info(
                                self.symbol, self.contract_type, 0, orderid, 1,
                                5)
                            print order_info
                            order_info = order_info['orders'][0]
                            self.logger.info('订单状态: %s' % order_info['status'])
                            # 订单状态还有待确认
                            if order_info['status'] == 2 or order_info[
                                    'status'] == -1:
                                break
                        except Exception as e:
                            self.logger.error('查询订单信息异常: %s' % e)
                        times += 1

                        if times == 9:
                            time.sleep(3)
                        if times == 19:
                            time.sleep(10)
                    if times == 20:
                        self.logger.error('未知错误,程序终止')
                        break

                print order_info

                future_deal_contract_amount = order_info['deal_amount']

                if future_deal_contract_amount == 0:
                    self.logger.info('未完成任何委托')
                    continue

                future_deal_price = order_info['price_avg']
                # btc精度是8
                future_deal_btc_amount = float(
                    '%.8f' %
                    (future_deal_contract_amount * 10.0 / future_deal_price))

                self.bear_amount += future_deal_contract_amount

                self.logger.info('future_deal:contract:%d\tbtc:%s\tprice:%s' %
                                 (future_deal_contract_amount,
                                  future_deal_btc_amount, future_deal_price))
                # 市价买入现货
                spot_usdt_amount = 100 * future_deal_contract_amount
                self.logger.info('现货买入 %sUSDT' % spot_usdt_amount)
                spot_order = self.huobi_client.send_order(
                    spot_usdt_amount, 'margin-api', 'btcusdt', 'buy-market')
                if spot_order['status'] != 'ok':
                    if spot_order['status'] == 'fail':
                        self.logger.error('spot buy failed : %s' %
                                          spot_order['msg'])
                    else:
                        self.logger.error('spot buy failed : %s' %
                                          spot_order['err-msg'])
                    # TODO
                    self.logger.info('开始回滚')
                    self.logger.info('终止程序')
                    break
                orderid = spot_order['data']
                field_cash_amount = 0
                field_amount = 0
                times = 0
                while times < 20:
                    self.logger.info('第%s次确认订单信息' % (times + 1))
                    order_info = self.huobi_client.order_info(orderid)
                    print order_info
                    if order_info['status'] == 'ok' and order_info['data'][
                            'state'] == 'filled':
                        self.logger.info('spot buy filled, orderId: %s' %
                                         orderid)
                        field_amount = float(
                            '%.8f' % float(order_info['data']['field-amount']))
                        field_cash_amount = float(
                            '%.8f' %
                            float(order_info['data']['field-cash-amount']))
                        field_price = float('%.2f' %
                                            (field_cash_amount / field_amount))
                        price_diff = future_deal_price - field_price

                        self.avg_price_diff = (
                            (self.bear_amount - future_deal_contract_amount) *
                            self.avg_price_diff + future_deal_contract_amount *
                            price_diff) / self.bear_amount
                        self.spot_free_btc += field_amount
                        self.spot_free_usdt -= field_cash_amount
                        break
                    times += 1
                    if times == 19:
                        time.sleep(15)
                if times == 20:
                    self.logger.info('现货买入错误, 终止程序')
                    break

                self.logger.info(
                    'spot_field_amount:%.8f\tspot_field_cash_amount:%.8f' %
                    (float(field_amount), float(field_cash_amount)))
                self.logger.info('avg_price_diff: %.2f' % self.avg_price_diff)
                self.update_future_account(True)
            # 平空
            if self.bear_amount > 0 and \
                    (self.debug_type == 4
                     or self.risk_rate < self.risk_thd
                     or future_asks[0][0] - spot_bids[0][0] < 0
                     or (trend == 4 and self.avg_price_diff - future_asks[0][0] + spot_bids[0][0] > diff_thd)):

                self.logger.info('期货平空,现货卖出')
                self.logger.info('期货价格: %s,现货价格: %s,差价: %s' %
                                 (future_asks[0][0], spot_bids[0][0],
                                  future_asks[0][0] - spot_bids[0][0]))
                self.logger.info('当前持空仓: %s' % self.bear_amount)
                self.logger.info(
                    'future_bond:%s\tspot_btc:%s\tspot_usdt:%s' %
                    (self.bond, self.spot_free_btc, self.spot_free_usdt))
                future_contract_amount = min(
                    self.max_contract_exchange_amount, self.bear_amount,
                    self.calc_exchange_contract_amount(spot_bids, future_asks),
                    int(self.spot_free_btc * (float(spot_asks[0][0]) - 20) /
                        100))

                if future_contract_amount == 0:
                    continue
                self.logger.info('期货平空: %s' % future_contract_amount)

                price = float('%.2f' % (future_asks[0][0] *
                                        (1 + self.slippage)))
                try:
                    future_order = self.future_client.place_order(
                        self.symbol, self.contract_type, price,
                        future_contract_amount, '4', '0')
                except Exception as e:
                    self.logger.error('Future订单异常: %s' % e)
                    continue
                print future_order

                orderid = future_order['order_id']
                try:
                    order_info = self.future_client.order_info(
                        self.symbol, self.contract_type, 0, orderid, 1, 5)
                except Exception as e:
                    self.logger.error('查询订单信息异常:%s, 程序终止' % e)
                    break

                order_info = order_info['orders'][0]
                self.logger.info('order status: %s' % order_info['status'])
                # 等待成交或未成交
                if order_info['status'] == 0 or order_info['status'] == 1:
                    self.logger.info('撤销未完成委托')
                    try:
                        self.future_client.cancel(self.symbol,
                                                  self.contract_type, orderid)
                    except Exception as e:
                        self.logger.error('撤销异常: %s' % e)
                    else:
                        self.logger.info('撤销成功')

                    self.logger.info('更新订单状态')
                    # status:-1:已撤销  0:未成交  1:部分成交  2:完全成交 4:撤单处理中
                    times = 0
                    while times < 20:
                        self.logger.info('第%s次查询订单状态' % (times + 1))
                        try:
                            order_info = self.future_client.order_info(
                                self.symbol, self.contract_type, 0, orderid, 1,
                                5)
                            print order_info
                            order_info = order_info['orders'][0]
                            self.logger.info('订单状态: %s' % order_info['status'])
                            # 订单状态还有待确认
                            if order_info['status'] == 2 or order_info[
                                    'status'] == -1:
                                break
                        except Exception as e:
                            self.logger.error('查询订单信息异常: %s' % e)
                        times += 1

                        if times == 9:
                            time.sleep(3)
                        if times == 19:
                            time.sleep(10)

                    if times == 20:
                        self.logger.error('未知错误,程序终止')
                        break

                print order_info

                future_deal_contract_amount = order_info['deal_amount']
                # 未完成任何委托
                if future_deal_contract_amount == 0:
                    self.logger.info('未完成任何委托')
                    continue
                future_deal_price = order_info['price_avg']
                # btc精度是8
                future_deal_btc_amount = float(
                    '%.8f' %
                    (future_deal_contract_amount * 10.0 / future_deal_price))

                self.bear_amount -= future_deal_contract_amount

                self.logger.info('future_deal:contract:%d\tbtc:%s\tprice:%s' %
                                 (future_deal_contract_amount,
                                  future_deal_btc_amount, future_deal_price))

                spot_limited_price = spot_bids[0][0] - 20
                spot_btc_amount = float(
                    '%.4f' %
                    (100 * future_deal_contract_amount / spot_limited_price))
                self.logger.info('现货卖出: %sUSDT' %
                                 (100 * future_deal_contract_amount))

                spot_order = self.huobi_client.send_order(
                    spot_btc_amount, 'margin-api', 'btcusdt', 'sell-limit',
                    spot_limited_price)
                print spot_order

                if spot_order['status'] != 'ok':
                    if spot_order['status'] == 'fail':
                        self.logger.error('spot sell failed : %s' %
                                          spot_order['msg'])
                    else:
                        self.logger.error('spot sell failed : %s' %
                                          spot_order['err-msg'])
                    self.logger.info('开始回滚')
                    self.logger.info('终止程序')
                    break

                orderid = spot_order['data']
                field_amount = 0
                field_cash_amount = 0
                times = 0
                while times < 20:
                    self.logger.info('第%s次确认订单信息' % (times + 1))
                    order_info = self.huobi_client.order_info(orderid)
                    print order_info
                    if order_info['status'] == 'ok' and order_info['data'][
                            'state'] == 'filled':
                        self.logger.info('spot sell filled, orderId: %s' %
                                         orderid)
                        field_amount = float(
                            '%.8f' % float(order_info['data']['field-amount']))
                        field_cash_amount = float(
                            '%.8f' %
                            float(order_info['data']['field-cash-amount']))

                        self.spot_free_btc -= field_amount
                        self.spot_free_usdt += field_cash_amount
                        break
                    times += 1
                    if times == 19:
                        time.sleep(15)

                if times == 20:
                    self.logger.info('现货卖出错误, 终止程序')
                    break

                self.logger.info('field_amount:%.8f\tfield_cash_amount:%.8f' %
                                 (field_amount, field_cash_amount))

                # 计算当前收益
                if self.bear_amount == 0:
                    time.sleep(15)
                    self.update_spot_account()
                    self.update_future_account()
                    self.logger.info(
                        'total_btc: %s\tfuture_rights: %s\tspot_btc: %s' %
                        (self.future_rights + self.spot_free_btc,
                         self.future_rights, self.spot_free_btc))
            # 平多
            if self.bull_amount > 0 and \
                    (self.debug_type == 3
                     or self.risk_rate < self.risk_thd
                     or future_bids[0][0] - spot_asks[0][0] > 0
                     or (trend == 3 and self.avg_price_diff - future_bids[0][0] + spot_asks[0][0] < -1 * diff_thd)):
                self.logger.info('期货平多,现货买入')
                self.logger.info('期货价格: %s,现货价格 %s, 差价: %s' %
                                 (future_bids[0][0], spot_asks[0][0],
                                  future_bids[0][0] - spot_asks[0][0]))
                self.logger.info('当前持多仓: %s' % self.bull_amount)
                self.logger.info(
                    'future_bond:%s\tspot_btc:%s\tspot_usdt:%s' %
                    (self.bond, self.spot_free_btc, self.spot_free_usdt))
                future_contract_amount = min(
                    self.max_contract_exchange_amount, self.bull_amount,
                    self.calc_exchange_contract_amount(spot_asks, future_bids),
                    int(self.spot_free_usdt / 100))

                if future_contract_amount == 0:
                    continue
                self.logger.info('期货平多: %s' % future_contract_amount)
                # 限价购买期货合约
                price = float('%.2f' % (future_bids[0][0] *
                                        (1 - self.slippage)))
                try:
                    future_order = self.future_client.place_order(
                        self.symbol, self.contract_type, price,
                        future_contract_amount, '3', 0)
                except Exception as e:
                    self.logger.error('Future订单异常: %s' % e)
                    continue
                print future_order

                orderid = future_order['order_id']
                try:
                    order_info = self.future_client.order_info(
                        self.symbol, self.contract_type, 0, orderid, 1, 5)
                except Exception as e:
                    self.logger.error('查询订单信息异常: %s,程序终止' % e)
                    break

                order_info = order_info['orders'][0]
                self.logger.info('order status: %s' % order_info['status'])
                # 等待成交或未成交
                if order_info['status'] == 0 or order_info['status'] == 1:
                    self.logger.info('撤销未完成委托')
                    try:
                        self.future_client.cancel(self.symbol,
                                                  self.contract_type, orderid)
                    except Exception as e:
                        self.logger.error('撤销异常: %s' % e)
                    else:
                        self.logger.info('撤销成功')

                    self.logger.info('更新订单状态')
                    # status:-1:已撤销  0:未成交  1:部分成交  2:完全成交 4:撤单处理中
                    times = 0
                    while times < 20:
                        self.logger.info('第%s次查询订单状态' % (times + 1))
                        try:
                            order_info = self.future_client.order_info(
                                self.symbol, self.contract_type, 0, orderid, 1,
                                5)
                            print order_info
                            order_info = order_info['orders'][0]
                            self.logger.info('订单状态: %s' % order_info['status'])
                            # 订单状态还有待确认
                            if order_info['status'] == 2 or order_info[
                                    'status'] == -1:
                                break
                        except Exception as e:
                            self.logger.error('查询订单信息异常: %s' % e)
                        times += 1

                        if times == 9:
                            time.sleep(3)
                        if times == 19:
                            time.sleep(10)

                    if times == 20:
                        self.logger.error('未知错误,程序终止')
                        break

                print order_info

                future_deal_contract_amount = order_info['deal_amount']
                # 未完成任何委托
                if future_deal_contract_amount == 0:
                    self.logger.info('未完成任何委托')
                    continue
                future_deal_price = order_info['price_avg']
                # btc精度是8
                future_deal_btc_amount = float(
                    '%.8f' %
                    (future_deal_contract_amount * 10.0 / future_deal_price))

                self.bull_amount -= future_deal_contract_amount

                self.logger.info('future_deal:contract:%d\tbtc:%s\tprice:%s' %
                                 (future_deal_contract_amount,
                                  future_deal_btc_amount, future_deal_price))
                # 现货买入
                spot_usdt_amount = 100 * future_deal_contract_amount
                self.logger.info('现货买入: %sUSDT' % spot_usdt_amount)

                spot_order = self.huobi_client.send_order(
                    spot_usdt_amount, 'margin-api', 'btcusdt', 'buy-market')
                if spot_order['status'] != 'ok':
                    if spot_order['status'] == 'fail':
                        self.logger.error('spot buy failed : %s' %
                                          spot_order['msg'])
                    else:
                        self.logger.error('spot buy failed : %s' %
                                          spot_order['err-msg'])
                    self.logger.info('开始回滚')
                    self.logger.info('终止程序')
                    break
                orderid = spot_order['data']
                field_cash_amount = 0
                field_amount = 0
                times = 0
                while times < 20:
                    self.logger.info('第%s次确认订单信息' % (times + 1))
                    order_info = self.huobi_client.order_info(orderid)
                    print order_info
                    if order_info['status'] == 'ok' and order_info['data'][
                            'state'] == 'filled':
                        self.logger.info('huobi buy filled, orderId: %s' %
                                         orderid)
                        field_amount = float(
                            '%.8f' % float(order_info['data']['field-amount']))
                        field_cash_amount = float(
                            '%.8f' %
                            float(order_info['data']['field-cash-amount']))

                        self.spot_free_btc += field_amount
                        self.spot_free_usdt -= field_cash_amount
                        break
                    times += 1
                    if times == 19:
                        time.sleep(15)

                if times == 20:
                    self.logger.info('现货买入错误, 终止程序')
                    break

                self.logger.info(
                    'spot_field_amount:%.8f\tspot_field_cash_amount:%.8f' %
                    (float(field_amount), float(field_cash_amount)))

                # self.update_future_account()
                # 计算当前收益
                if self.bull_amount == 0:
                    time.sleep(15)
                    self.update_spot_account()
                    self.update_future_account()
                    self.logger.info(
                        'total_btc: %s\tfuture_rights: %s\tspot_btc: %s' %
                        (self.future_rights + self.spot_free_btc,
                         self.future_rights, self.spot_free_btc))

            time.sleep(1)
class TermArbitrage:
    # 正表示期货价格高于现货
    profit_rate = [0.0035, 0.004, 0.005]
    slippage = 0.0002
    close__thd = 0

    # 风险控制
    margin_thd = 100
    risk_thd = 0.5
    lever_rate = 20

    margin_coefficient = [0.3, 0.8, 1]

    symbol = 'btc_usdt'
    short_contract_type = 'this_week'
    long_contract_type = 'quarter'

    # 每次最大合约交易量
    max_contract_exchange_amount = 15

    # 1 开多 2 开空 3 平多 4 平空
    debug_type = 0

    def __init__(self):
        key_dict = {}
        # 读取配置文件
        with open('config', 'r') as f:
            for line in f.readlines():
                splited = line.split('=')
                if len(splited) == 2:
                    key_dict[splited[0].strip()] = splited[1].strip()

        self.client = OkexFutureClient(key_dict['OKEX_ACCESS_KEY'],
                                       key_dict['OKEX_SECRET_KEY'])
        self.spot_client = HuobiSpot(key_dict['HUOBI_ACCESS_KEY'],
                                     key_dict['HUOBI_SECRET_KEY'])

        # config logging
        self.logger = logging.getLogger("Future")

        # 指定logger输出格式
        formatter = logging.Formatter(
            '%(asctime)s %(levelname)-8s: %(message)s')

        # 文件日志
        file_handler = logging.FileHandler("term2.log")
        file_handler.setFormatter(formatter)

        # 控制台日志
        console_handler = logging.StreamHandler(sys.stdout)
        console_handler.formatter = formatter

        # 为logger添加的日志处理器
        self.logger.addHandler(file_handler)
        self.logger.addHandler(console_handler)

        # 指定日志的最低输出级别,默认为WARN级别
        self.logger.setLevel(logging.INFO)

        # 空头合约数量
        self.short_bear_amount = 0
        self.long_bear_amount = 0

        # 多头合约数量
        self.short_bull_amount = 0
        self.long_bull_amount = 0

        # 保证金
        self.keep_deposit = 0
        self.risk_rate = 0
        self.future_rights = 0
        self.profit_real = 0
        self.profit_unreal = 0
        self.bond = 0
        # 指数
        self.future_index = 0

        # 等待交割
        self.wait_for_delivery = False

    def update_future_position(self):
        self.logger.info('全仓用户持仓查询')
        try:
            info = self.client.position(self.symbol, self.short_contract_type)
        except Exception as e:
            self.logger.error('全仓用户持仓查询异常: %s' % e)
        else:
            # print info
            if info['result']:
                holding = info['holding']
                if len(holding) > 0:
                    # hold是数组
                    self.short_bull_amount = holding[0]['buy_amount']
                    self.short_bear_amount = holding[0]['sell_amount']
        try:
            info = self.client.position(self.symbol, self.long_contract_type)
        except Exception as e:
            self.logger.error('全仓用户持仓查询异常: %s' % e)
        else:
            # print info
            if info['result']:
                holding = info['holding']
                if len(holding) > 0:
                    # hold是数组
                    self.long_bull_amount = holding[0]['buy_amount']
                    self.long_bear_amount = holding[0]['sell_amount']
        self.logger.info('近期多仓: %s  近期空仓: %s' %
                         (self.short_bull_amount, self.short_bear_amount))
        self.logger.info('远期多仓: %s  远期空仓: %s' %
                         (self.long_bull_amount, self.long_bear_amount))

    def update_future_account(self, vertify=False):
        times = 0
        while times < 10:
            old_bond = self.bond
            self.logger.info('第%s次获取全仓账户信息' % (times + 1))
            try:
                future_info = self.client.userinfo()
            except Exception as e:
                self.logger.error('获取Future全仓账户信息异常: %s' % e)
            else:
                # print future_info
                if future_info['result']:
                    btc_info = future_info['info']['btc']
                    print btc_info
                    self.keep_deposit = btc_info['keep_deposit']
                    self.risk_rate = btc_info['risk_rate']
                    self.future_rights = btc_info['account_rights']
                    self.profit_real = btc_info['profit_real']
                    self.profit_unreal = btc_info['profit_unreal']
                    self.bond = self.keep_deposit
                    self.logger.info('bond: %s\trights: %s' %
                                     (self.bond, self.future_rights))
            if not vertify or abs(self.bond - old_bond) > 1e-9:
                break
            times += 1
            if times == 9:
                time.sleep(3)
        if times == 10:
            self.logger.info('账户信息未更新')

    def test_order(self):
        orderid = '684493875909632'
        try:
            order_info = self.client.order_info(self.symbol,
                                                self.long_contract_type, 0,
                                                orderid, 1, 5)
        except Exception as e:
            self.logger.error('获取订单信息异常: %s, 程序终止' % e)
        else:
            print order_info

    def test_available(self):
        out = open('diff', 'a+')
        i = 0
        out_format = "{}     {:<10.2f}{:<10.2f}{:<10.2f}{:<10.2f}\n"
        while True:
            try:
                short_ticker = self.client.ticker(self.symbol,
                                                  self.short_contract_type)
            except Exception as e:
                print('获取近期合约行情错误: %s' % e)
                time.sleep(3)
                continue
            try:
                long_ticker = self.client.ticker(self.symbol,
                                                 self.long_contract_type)
            except Exception as e:
                print('获取远期合约行情错误: %s' % e)
                time.sleep(3)
                continue
            spot_ticker = self.spot_client.get_ticker('btcusdt')
            if spot_ticker['status'] == 'ok':
                pass
            else:
                print 'spot ticker error'
                time.sleep(3)
                continue

            diff1 = short_ticker['ticker']['buy'] - long_ticker['ticker'][
                'sell']
            diff2 = short_ticker['ticker']['sell'] - long_ticker['ticker'][
                'buy']
            diff3 = short_ticker['ticker']['buy'] - spot_ticker['tick']['bid'][
                0]
            diff4 = short_ticker['ticker']['sell'] - spot_ticker['tick'][
                'ask'][0]
            nowtime = time.strftime("%Y-%m-%d %H:%M:%S",
                                    time.localtime(time.time()))
            print out_format.format(nowtime, diff1, diff2, diff3, diff4)
            out.write(out_format.format(nowtime, diff1, diff2, diff3, diff4))
            i += 1
            if i % 2 == 0:
                out.flush()
            time.sleep(5)

    def calc_available_contract(self, price):
        available_btc = self.future_rights - self.bond
        if self.risk_rate:
            pass
        return int(available_btc * price / 20) if available_btc > 0 else 0

    def calc_exchange_contract_amount(self, short_depth, long_depth):
        short_amount, long_amount = 0, 0
        for d in short_depth:
            if abs(d[0] -
                   short_depth[0][0]) / short_depth[0][0] <= self.slippage / 2:
                short_amount += d[1]
        for d in long_depth:
            if abs(d[0] -
                   long_depth[0][0]) / long_depth[0][0] <= self.slippage:
                long_amount += d[1]
        return min(int(short_amount), int(long_amount / 2))

    def rollback_order(self, type, price, amount):
        pass

    @staticmethod
    def sms_notify(msg):
        url = 'http://221.228.17.88:8080/sendmsg/send'
        params = {'phonenum': '18118999630', 'msg': msg}
        requests.get(url, params=params)

    def go(self):
        while True:
            # 放到前面
            localtime = time.localtime()
            nowtime = time.strftime('%H:%M:%S', localtime)
            weekday = time.strftime("%w", localtime)
            print nowtime, weekday
            if nowtime.startswith('08:30'):
                self.logger.info('短信通知')
                TermArbitrage.sms_notify(
                    'rights: %s' % (self.future_rights - self.profit_unreal))
                time.sleep(60)
            if weekday == 5 and re.match(r'^1[0-6].*', nowtime):
                self.logger.info('等待交割')
                self.wait_for_delivery = True

            print('获取深度')
            try:
                short_depth = self.client.depth(self.symbol,
                                                self.short_contract_type, 10)
            except Exception as e:
                print('获取近期市场深度错误: %s' % e)
                time.sleep(3)
                continue
            short_bids = short_depth['bids']
            short_asks = short_depth['asks'][::-1]

            try:
                long_depth = self.client.depth(self.symbol,
                                               self.long_contract_type, 10)
            except Exception as e:
                print('获取远期市场深度错误: %s' % e)
                time.sleep(3)
                continue
            long_bids = long_depth['bids']
            long_asks = long_depth['asks'][::-1]

            print short_asks[0][0] - long_bids[0][0]
            print short_bids[0][0] - long_asks[0][0]

            price_index = (long_bids[0][0] + short_asks[0][0]) / 2
            open_thd = self.profit_rate * price_index

            if not self.wait_for_delivery and (
                    self.debug_type == 1
                    or short_asks[0][0] - long_bids[0][0] < -1 * open_thd):
                # 近期开多,远期开空
                contract_amount = min(
                    self.max_contract_exchange_amount,
                    self.calc_available_contract(short_asks[0][0]),
                    self.calc_exchange_contract_amount(short_asks, long_bids))
                if contract_amount == 0:
                    time.sleep(3)
                    continue
                self.logger.info('近期开多: %s张' % contract_amount)
                order_price = float('%.2f' % (short_asks[0][0] *
                                              (1 + self.slippage)))
                try:
                    order = self.client.place_order(self.symbol,
                                                    self.short_contract_type,
                                                    order_price,
                                                    contract_amount,
                                                    '1',
                                                    '0',
                                                    lever_rate=self.lever_rate)
                except Exception as e:
                    self.logger.error('订单异常: %s' % e)
                    continue
                print order

                orderid = order['order_id']
                try:
                    order_info = self.client.order_info(
                        self.symbol, self.short_contract_type, 0, orderid, 1,
                        5)
                except Exception as e:
                    self.logger.error('获取订单信息异常: %s, 程序终止' % e)
                    break
                print order_info

                order_info = order_info['orders'][0]
                self.logger.info('order status: %s' % order_info['status'])
                # 等待成交或未成交
                if order_info['status'] == 0 or order_info['status'] == 1:
                    self.logger.info('撤销未完成委托')
                    try:
                        self.client.cancel(self.symbol,
                                           self.short_contract_type, orderid)
                    except Exception as e:
                        self.logger.error('撤销异常: %s' % e)
                    else:
                        self.logger.info('撤销成功')

                    self.logger.info('更新订单状态')
                    # status:-1:已撤销  0:未成交  1:部分成交  2:完全成交 4:撤单处理中 5:撤单中
                    times = 0
                    while times < 20:
                        self.logger.info('第%s次查询订单状态' % (times + 1))
                        try:
                            order_info = self.client.order_info(
                                self.symbol, self.short_contract_type, 0,
                                orderid, 1, 5)
                            print order_info
                            order_info = order_info['orders'][0]
                            self.logger.info('订单状态: %s' % order_info['status'])

                            if order_info['status'] == 2 or order_info[
                                    'status'] == -1:
                                break
                        except Exception as e:
                            self.logger.error('查询订单信息异常: %s' % e)
                        times += 1

                        if times == 19:
                            self.logger.info('撤单处理中...')
                            time.sleep(3)
                    if times == 20:
                        self.logger.error('未知错误,程序终止')
                        break
                deal_contract_amount = order_info['deal_amount']
                deal_price = order_info['price_avg']
                if deal_contract_amount == 0:
                    self.logger.info('未完成任何委托')
                    continue
                self.short_bull_amount += deal_contract_amount
                self.logger.info('short_deal_contract: %d\tprice: %s' %
                                 (deal_contract_amount, deal_price))

                self.logger.info('远期开空: %s张' % deal_contract_amount)
                order_price = float('%.2f' % (long_bids[0][0] * (1 - 0.002)))
                try:
                    order = self.client.place_order(self.symbol,
                                                    self.long_contract_type,
                                                    order_price,
                                                    deal_contract_amount,
                                                    '2',
                                                    '0',
                                                    lever_rate=self.lever_rate)
                except Exception as e:
                    self.logger.error('订单异常: %s' % e)
                    break
                print order

                orderid = order['order_id']
                # status:-1:已撤销  0:未成交  1:部分成交  2:完全成交 4:撤单处理中
                deal_contract_amount = 0
                deal_price = 0
                times = 0
                while times < 20:
                    self.logger.info('第%s次查询订单状态' % (times + 1))
                    try:
                        order_info = self.client.order_info(
                            self.symbol, self.long_contract_type, 0, orderid,
                            1, 5)
                        print order_info
                        order_info = order_info['orders'][0]
                        self.logger.info('订单状态: %s' % order_info['status'])
                        if order_info['status'] == 2:
                            deal_contract_amount = order_info['deal_amount']
                            deal_price = order_info['price_avg']
                            self.long_bear_amount += deal_contract_amount
                            break
                    except Exception as e:
                        self.logger.error('查询订单信息异常: %s' % e)
                    times += 1
                    if times == 9:
                        time.sleep(2)
                    if times == 19:
                        time.sleep(15)
                if times == 20:
                    self.logger.error('未知错误,程序终止')
                    break
                self.logger.info('long_deal_contract: %d\tprice: %s' %
                                 (deal_contract_amount, deal_price))
                self.update_future_account(True)

            if not self.wait_for_delivery and (
                    self.debug_type == 2
                    or short_bids[0][0] - long_asks[0][0] > open_thd):
                # 近期开空,远期开多
                contract_amount = min(
                    self.max_contract_exchange_amount,
                    self.calc_available_contract(short_bids[0][0]),
                    self.calc_exchange_contract_amount(short_bids, long_asks))
                if contract_amount == 0:
                    time.sleep(3)
                    continue
                self.logger.info('近期开空: %s张' % contract_amount)
                order_price = float('%.2f' % (short_bids[0][0] *
                                              (1 - self.slippage)))
                try:
                    order = self.client.place_order(self.symbol,
                                                    self.short_contract_type,
                                                    order_price,
                                                    contract_amount,
                                                    '2',
                                                    '0',
                                                    lever_rate=self.lever_rate)
                except Exception as e:
                    self.logger.error('订单异常: %s' % e)
                    continue
                print order

                orderid = order['order_id']
                try:
                    order_info = self.client.order_info(
                        self.symbol, self.short_contract_type, 0, orderid, 1,
                        5)
                except Exception as e:
                    self.logger.error('获取订单信息异常: %s, 程序终止' % e)
                    break
                print order_info

                order_info = order_info['orders'][0]
                self.logger.info('order status: %s' % order_info['status'])
                # 等待成交或未成交
                if order_info['status'] == 0 or order_info['status'] == 1:
                    self.logger.info('撤销未完成委托')
                    try:
                        self.client.cancel(self.symbol,
                                           self.short_contract_type, orderid)
                    except Exception as e:
                        self.logger.error('撤销异常: %s' % e)
                    else:
                        self.logger.info('撤销成功')

                    self.logger.info('更新订单状态')
                    # status:-1:已撤销  0:未成交  1:部分成交  2:完全成交 4:撤单处理中
                    times = 0
                    while times < 20:
                        self.logger.info('第%s次查询订单状态' % (times + 1))
                        try:
                            order_info = self.client.order_info(
                                self.symbol, self.short_contract_type, 0,
                                orderid, 1, 5)
                            print order_info
                            order_info = order_info['orders'][0]
                            self.logger.info('订单状态: %s' % order_info['status'])
                            if order_info['status'] == 2 or order_info[
                                    'status'] == -1:
                                break
                        except Exception as e:
                            self.logger.error('查询订单信息异常: %s' % e)
                        times += 1

                        if times == 19:
                            self.logger.info('撤单处理中...')
                            time.sleep(3)
                    if times == 20:
                        self.logger.error('未知错误,程序终止')
                        break
                print order_info
                deal_contract_amount = order_info['deal_amount']
                deal_price = order_info['price_avg']

                if deal_contract_amount == 0:
                    self.logger.info('未完成任何委托')
                    continue
                self.short_bear_amount += deal_contract_amount
                self.logger.info('short_deal_contract: %d\tprice: %s' %
                                 (deal_contract_amount, deal_price))

                self.logger.info('远期开多: %s张' % deal_contract_amount)
                order_price = float('%.2f' % (long_asks[0][0] * (1 + 0.002)))
                try:
                    order = self.client.place_order(self.symbol,
                                                    self.long_contract_type,
                                                    order_price,
                                                    deal_contract_amount,
                                                    '1',
                                                    '0',
                                                    lever_rate=self.lever_rate)
                except Exception as e:
                    self.logger.error('订单异常: %s' % e)
                    break
                print order

                orderid = order['order_id']
                # status:-1:已撤销  0:未成交  1:部分成交  2:完全成交 4:撤单处理中
                deal_contract_amount = 0
                deal_price = 0
                times = 0
                while times < 20:
                    self.logger.info('第%s次查询订单状态' % (times + 1))
                    try:
                        order_info = self.client.order_info(
                            self.symbol, self.long_contract_type, 0, orderid,
                            1, 5)
                        print order_info
                        order_info = order_info['orders'][0]
                        self.logger.info('订单状态: %s' % order_info['status'])
                        if order_info['status'] == 2:
                            deal_contract_amount = order_info['deal_amount']
                            deal_price = order_info['price_avg']
                            self.long_bull_amount += deal_contract_amount
                            break
                    except Exception as e:
                        self.logger.error('查询订单信息异常: %s' % e)
                    times += 1

                    if times == 9:
                        time.sleep(1)
                    if times == 10:
                        time.sleep(15)
                if times == 20:
                    self.logger.error('未知错误,程序终止')
                    break
                self.logger.info('long_deal_contract: %d\tprice: %s' %
                                 (deal_contract_amount, deal_price))
                self.update_future_account(True)
            if self.short_bull_amount > 0 and (
                    self.debug_type == 3
                    or short_bids[0][0] - long_asks[0][0] > 0):
                # 近期平多,远期平空
                contract_amount = min(
                    self.max_contract_exchange_amount, self.short_bull_amount,
                    self.calc_exchange_contract_amount(short_bids, long_asks))
                if contract_amount == 0:
                    time.sleep(3)
                    continue
                self.logger.info('近期平多: %s张' % contract_amount)
                order_price = float('%.2f' % (short_bids[0][0] *
                                              (1 - self.slippage)))
                try:
                    order = self.client.place_order(self.symbol,
                                                    self.short_contract_type,
                                                    order_price,
                                                    contract_amount,
                                                    '3',
                                                    '0',
                                                    lever_rate=self.lever_rate)
                except Exception as e:
                    self.logger.error('订单异常: %s' % e)
                    continue
                print order

                orderid = order['order_id']
                try:
                    order_info = self.client.order_info(
                        self.symbol, self.short_contract_type, 0, orderid, 1,
                        5)
                except Exception as e:
                    self.logger.error('获取订单信息异常: %s, 程序终止' % e)
                    break
                print order_info

                order_info = order_info['orders'][0]
                self.logger.info('order status: %s' % order_info['status'])
                # 等待成交或未成交
                if order_info['status'] == 0 or order_info['status'] == 1:
                    self.logger.info('撤销未完成委托')
                    try:
                        self.client.cancel(self.symbol,
                                           self.short_contract_type, orderid)
                    except Exception as e:
                        self.logger.error('撤销异常: %s' % e)
                    else:
                        self.logger.info('撤销成功')

                    self.logger.info('更新订单状态')
                    # status:-1:已撤销  0:未成交  1:部分成交  2:完全成交 4:撤单处理中
                    times = 0
                    while times < 10:
                        self.logger.info('第%s次查询订单状态' % (times + 1))
                        try:
                            order_info = self.client.order_info(
                                self.symbol, self.short_contract_type, 0,
                                orderid, 1, 5)
                            print order_info
                            order_info = order_info['orders'][0]
                            self.logger.info('订单状态: %s' % order_info['status'])

                            if order_info['status'] == 2 or order_info[
                                    'status'] == -1:
                                break
                        except Exception as e:
                            self.logger.error('查询订单信息异常: %s' % e)
                        times += 1

                        if times == 9 and order_info['status'] == 4:
                            self.logger.info('撤单处理中...')
                            time.sleep(3)
                    if times == 10:
                        self.logger.error('未知错误,程序终止')
                        break
                deal_contract_amount = order_info['deal_amount']
                deal_price = order_info['price_avg']
                if deal_contract_amount == 0:
                    self.logger.info('未完成任何委托')
                    continue
                self.short_bull_amount -= deal_contract_amount
                self.logger.info('short_deal_contract: %d\tprice: %s' %
                                 (deal_contract_amount, deal_price))

                self.logger.info('远期平空: %s张' % deal_contract_amount)
                order_price = float('%.2f' % (long_asks[0][0] * (1 + 0.002)))
                try:
                    order = self.client.place_order(self.symbol,
                                                    self.long_contract_type,
                                                    order_price,
                                                    deal_contract_amount,
                                                    '4',
                                                    '0',
                                                    lever_rate=self.lever_rate)
                except Exception as e:
                    self.logger.error('订单异常: %s' % e)
                    break
                print order

                orderid = order['order_id']
                # status:-1:已撤销  0:未成交  1:部分成交  2:完全成交 4:撤单处理中
                deal_contract_amount = 0
                deal_price = 0
                times = 0
                while times < 20:
                    self.logger.info('第%s次查询订单状态' % (times + 1))
                    try:
                        order_info = self.client.order_info(
                            self.symbol, self.long_contract_type, 0, orderid,
                            1, 5)
                        print order_info
                        order_info = order_info['orders'][0]
                        self.logger.info('订单状态: %s' % order_info['status'])
                        if order_info['status'] == 2:
                            deal_contract_amount = order_info['deal_amount']
                            deal_price = order_info['price_avg']
                            self.long_bear_amount -= deal_contract_amount
                            break
                    except Exception as e:
                        self.logger.error('查询订单信息异常: %s' % e)
                    times += 1

                    if times == 9:
                        time.sleep(1)
                    if times == 19:
                        time.sleep(15)
                if times == 20:
                    self.logger.error('未知错误,程序终止')
                    break
                self.logger.info('long_deal_contract: %d\tprice: %s' %
                                 (deal_contract_amount, deal_price))
                if self.short_bull_amount == 0:
                    time.sleep(15)
                    self.update_future_account()

            if self.short_bear_amount > 0 and (
                    self.debug_type == 4
                    or short_asks[0][0] - long_bids[0][0] < 0):
                # 近期平空,远期平多
                contract_amount = min(
                    self.max_contract_exchange_amount, self.short_bear_amount,
                    self.calc_exchange_contract_amount(short_asks, long_bids))
                if contract_amount == 0:
                    time.sleep(3)
                    continue
                self.logger.info('近期平空: %s张' % contract_amount)
                order_price = float('%.2f' % (short_bids[0][0] *
                                              (1 + self.slippage)))
                try:
                    order = self.client.place_order(self.symbol,
                                                    self.short_contract_type,
                                                    order_price,
                                                    contract_amount,
                                                    '4',
                                                    '0',
                                                    lever_rate=self.lever_rate)
                except Exception as e:
                    self.logger.error('订单异常: %s' % e)
                    continue
                print order

                orderid = order['order_id']
                try:
                    order_info = self.client.order_info(
                        self.symbol, self.short_contract_type, 0, orderid, 1,
                        5)
                except Exception as e:
                    self.logger.error('获取订单信息异常: %s, 程序终止' % e)
                    break
                print order_info

                order_info = order_info['orders'][0]
                self.logger.info('order status: %s' % order_info['status'])
                # 等待成交或未成交
                if order_info['status'] == 0 or order_info['status'] == 1:
                    self.logger.info('撤销未完成委托')
                    try:
                        self.client.cancel(self.symbol,
                                           self.short_contract_type, orderid)
                    except Exception as e:
                        self.logger.error('撤销异常: %s' % e)
                    else:
                        self.logger.info('撤销成功')

                    self.logger.info('更新订单状态')
                    # status:-1:已撤销  0:未成交  1:部分成交  2:完全成交 4:撤单处理中
                    times = 0
                    while times < 10:
                        self.logger.info('第%s次查询订单状态' % (times + 1))
                        try:
                            order_info = self.client.order_info(
                                self.symbol, self.short_contract_type, 0,
                                orderid, 1, 5)
                            print order_info
                            order_info = order_info['orders'][0]
                            self.logger.info('订单状态: %s' % order_info['status'])

                            if order_info['status'] == 2 or order_info[
                                    'status'] == -1:
                                break
                        except Exception as e:
                            self.logger.error('查询订单信息异常: %s' % e)
                        times += 1

                        if times == 9 and order_info['status'] == 4:
                            self.logger.info('撤单处理中...')
                            time.sleep(3)
                    if times == 10:
                        self.logger.error('未知错误,程序终止')
                        break
                deal_contract_amount = order_info['deal_amount']
                deal_price = order_info['price_avg']

                if deal_contract_amount == 0:
                    self.logger.info('未完成任何委托')
                    continue
                self.short_bear_amount -= deal_contract_amount
                self.logger.info('short_deal_contract: %d\tprice: %s' %
                                 (deal_contract_amount, deal_price))

                self.logger.info('远期平多: %s张' % deal_contract_amount)
                order_price = float('%.2f' % (long_asks[0][0] * (1 - 0.002)))
                try:
                    order = self.client.place_order(self.symbol,
                                                    self.long_contract_type,
                                                    order_price,
                                                    deal_contract_amount,
                                                    '3',
                                                    '0',
                                                    lever_rate=self.lever_rate)
                except Exception as e:
                    self.logger.error('订单异常: %s' % e)
                    break
                print order

                orderid = order['order_id']
                # status:-1:已撤销  0:未成交  1:部分成交  2:完全成交 4:撤单处理中
                deal_contract_amount = 0
                deal_price = 0
                times = 0
                while times < 20:
                    self.logger.info('第%s次查询订单状态' % (times + 1))
                    try:
                        order_info = self.client.order_info(
                            self.symbol, self.long_contract_type, 0, orderid,
                            1, 5)
                        print order_info
                        order_info = order_info['orders'][0]
                        self.logger.info('订单状态: %s' % order_info['status'])
                        if order_info['status'] == 2:
                            deal_contract_amount = order_info['deal_amount']
                            deal_price = order_info['price_avg']
                            self.long_bull_amount -= deal_contract_amount
                            break
                    except Exception as e:
                        self.logger.error('查询订单信息异常: %s' % e)
                    times += 1

                    if times == 9:
                        time.sleep(1)
                    if times == 19:
                        time.sleep(15)
                if times == 20:
                    self.logger.error('未知错误,程序终止')
                    break
                self.logger.info('long_deal_contract: %d\tprice: %s' %
                                 (deal_contract_amount, deal_price))

                if self.short_bear_amount == 0:
                    time.sleep(15)
                    self.update_future_account()

            time.sleep(1)
示例#5
0
class TermArbitrage:
    # 正表示期货价格高于现货
    # TODO
    # 增加动态调整
    open_positive_thd = 25
    close_positive_thd = -5

    open_negative_thd = -25
    close_negetive_thd = 5

    # 风险控制
    margin_thd = 150
    risk_thd = 0.5

    margin_coefficient = 0.5
    min_contract_amount = 1

    symbol = 'btc_usdt'
    contract_type = 'quarter'

    # 每次最大合约交易量
    max_contract_exchange_amount = 5

    # 1 开多 2 开空 3 平多 4 平空
    debug_type = 0

    #
    trend_10_thd = 5
    trend_30_thd = 3

    def __init__(self):
        key_dict = {}
        # 读取配置文件
        with open('config', 'r') as f:
            for line in f.readlines():
                splited = line.split('=')
                if len(splited) == 2:
                    key_dict[splited[0].strip()] = splited[1].strip()

        self.huobi_client = HuobiSpot(key_dict['HUOBI_ACCESS_KEY3'],
                                      key_dict['HUOBI_SECRET_KEY3'])
        self.future_client = OkexFutureClient(key_dict['OKEX_ACCESS_KEY'],
                                              key_dict['OKEX_SECRET_KEY'])

        # 空头合约数量
        self.bear_amount = 0

        # 多头合约数量
        self.bull_amount = 0

        #
        self.buy_available = 0
        self.sell_available = 0
        self.buy_profit_lossratio = 0
        self.sell_profit_lossratio = 0
        self.sell_price_avg = 0
        self.buy_price_avg = 0

        # 保证金
        self.keep_deposit = 0
        self.risk_rate = 0
        self.future_rights = 0
        self.profit_real = 0
        self.profit_unreal = 0
        # 指数
        self.future_index = 0

        self.spot_free_btc = 0
        self.spot_free_usdt = 0
        self.spot_freezed_btc = 0
        self.spot_freezed_usdt = 0

        # 统计收益

        # config logging
        self.logger = logging.getLogger("Future")

        # 指定logger输出格式
        formatter = logging.Formatter(
            '%(asctime)s %(levelname)-8s: %(message)s')

        # 文件日志
        file_handler = logging.FileHandler("term.log")
        file_handler.setFormatter(formatter)

        # 控制台日志
        console_handler = logging.StreamHandler(sys.stdout)
        console_handler.formatter = formatter

        # 为logger添加的日志处理器
        self.logger.addHandler(file_handler)
        self.logger.addHandler(console_handler)

        # 指定日志的最低输出级别,默认为WARN级别
        self.logger.setLevel(logging.INFO)

        #
        # 5s
        self.avg_line_1m = []
        self.avg_line_3m = []
        self.avg_line_5m = []
        # 10s
        self.avg_line_10m = []
        # 30s
        self.avg_line_30m = []

        self.avg_1m = 0
        self.avg_5m = 0
        self.avg_10m_prev = 0
        self.avg_10m_post = 0

        self.avg_30m_prev = 0
        self.avg_30m_post = 0

    def trend_test(self):
        while True:
            try:
                future_depth = self.future_client.depth(
                    self.symbol, self.contract_type, 10)
            except Exception as e:
                self.logger.error('获取期货市场深度错误: %s' % e)
                time.sleep(3)
                continue
            future_bids = future_depth['bids']
            future_asks = future_depth['asks'][::-1]

            spot_depth = self.huobi_client.get_depth('btcusdt', 'step5')
            if spot_depth['status'] == 'ok':
                spot_bids = spot_depth['tick']['bids']
                spot_asks = spot_depth['tick']['asks']
            else:
                time.sleep(3)
                continue

            d1 = future_bids[0][0] - spot_asks[0][0]
            d1 = float('%.2f' % d1)

            timestamp = int(time.time())

            # 10m
            last_item_10m = self.avg_line_10m[-1] if len(
                self.avg_line_10m) > 0 else (0, 0)
            if timestamp - last_item_10m[0] >= 10:
                self.avg_line_10m.append((timestamp, d1))
                if len(self.avg_line_10m) > 90:
                    self.avg_line_10m.pop(0)
                    self.avg_10m_prev = np.mean(self.avg_line_10m[:60],
                                                axis=0)[1]
                    self.avg_10m_post = np.mean(self.avg_line_10m[60:],
                                                axis=0)[1]
                    print d1, self.avg_10m_prev, self.avg_10m_post
            last_item_30m = self.avg_line_30m[-1] if len(
                self.avg_line_30m) > 0 else (0, 0)
            if timestamp - last_item_30m >= 30:
                self.avg_line_30m.append((timestamp, d1))
                if len(self.avg_line_30m) > 90:
                    self.avg_line_30m.pop(0)
                    self.avg_30m_prev = np.mean(self.avg_line_30m[:60],
                                                axis=0)[1]
                    self.avg_30m_post = np.mean(self.avg_line_30m[60:],
                                                axis=0)[1]

    def update_future_position(self):
        self.logger.info('全仓用户持仓查询')
        try:
            info = self.future_client.position(self.symbol, self.contract_type)
        except Exception as e:
            self.logger.error('全仓用户持仓查询异常: %s' % e)
        else:
            print info
            if info['result']:
                holding = info['holding']
                if len(holding) > 0:
                    # hold是数组
                    self.bull_amount = holding[0]['buy_amount']
                    self.bear_amount = holding[0]['sell_amount']
                    self.buy_available = holding[0]['buy_available']
                    self.sell_available = holding[0]['sell_available']
                    self.buy_price_avg = holding[0]['buy_price_avg']
                    self.sell_price_avg = holding[0]['sell_price_avg']
                    if self.bull_amount == 0 and self.bear_amount == 0:
                        self.logger.info('用户未持仓')
                        # self.asset_balance()
                    else:
                        self.logger.info('多仓: %s\t空仓: %s' %
                                         (self.bull_amount, self.bear_amount))
                # 暂定认为len(holding)一定大于0
                # else:
                #     self.logger.info('用户未持仓')
                #     self.asset_balance()
            else:
                self.logger.info('postion_4fix result error')

    def update_account_info(self):
        self.logger.info('获取Future全仓账户信息')
        try:
            future_info = self.future_client.userinfo()
        except Exception as e:
            self.logger.error('获取Future全仓账户信息异常: %s' % e)
        else:
            if future_info['result']:
                btc_info = future_info['info']['btc']
                self.keep_deposit = btc_info['keep_deposit']
                self.risk_rate = btc_info['risk_rate']
                self.future_rights = btc_info['account_rights']
                self.profit_real = btc_info['profit_real']
                self.profit_unreal = btc_info['profit_unreal']
                self.logger.info('bond:%s\trights:%s' %
                                 (self.keep_deposit, self.future_rights))
        self.logger.info('获取SPOT账户信息')
        # update huobi
        r = self.huobi_client.get_balance()
        if r['status'] == 'ok':
            for item in r['data']['list']:
                if item['currency'] == 'btc' and item['type'] == 'trade':
                    self.spot_free_btc = float(item['balance'])
                elif item['currency'] == 'btc' and item['type'] == 'frozen':
                    self.spot_freezed_btc = float(item['balance'])
                elif item['currency'] == 'usdt' and item['type'] == 'trade':
                    self.spot_free_usdt = float(item['balance'])
                elif item['currency'] == 'usdt' and item['type'] == 'frozen':
                    self.spot_freezed_usdt = float(item['balance'])
            self.logger.info('spot_btc:%s\tspot_usdt:%s' %
                             (self.spot_free_btc, self.spot_free_usdt))
        else:
            print r
            if 'fail' == r['status']:
                self.logger.error('Huobi get_balance error: %s' % r['msg'])
            else:
                self.logger.error('Huobi get_balance error: %s' % r['err-msg'])

    def calc_available_contract(self, price):
        available_btc = self.future_rights * self.margin_coefficient - self.keep_deposit
        return int(available_btc * price / 10) if available_btc > 0 else 0

    def go(self):
        while True:
            self.logger.info('获取深度')
            try:
                future_depth = self.future_client.depth(
                    self.symbol, self.contract_type, 10)
            except Exception as e:
                self.logger.error('获取期货市场深度错误: %s' % e)
                time.sleep(3)
                continue
            future_bids = future_depth['bids']
            future_asks = future_depth['asks'][::-1]

            spot_depth = self.huobi_client.get_depth('btcusdt', 'step5')
            # print spot_depth
            if spot_depth['status'] == 'ok':
                spot_bids = spot_depth['tick']['bids']
                spot_asks = spot_depth['tick']['asks']
            else:
                time.sleep(3)
                continue

            d1 = future_bids[0][0] - spot_asks[0][0]
            d1 = float('%.2f' % d1)

            timestamp = int(time.time())

            last_item_10m = self.avg_line_10m[-1] if len(
                self.avg_line_10m) > 0 else (0, 0)
            if timestamp - last_item_10m[0] >= 10:
                self.avg_line_10m.append((timestamp, d1))
                if len(self.avg_line_10m) > 90:
                    self.avg_line_10m.pop(0)
                    self.avg_10m_prev = np.mean(self.avg_line_10m[:60],
                                                axis=0)[1]
                    self.avg_10m_post = np.mean(self.avg_line_10m[60:],
                                                axis=0)[1]
                    print d1, self.avg_10m_prev, self.avg_10m_post

            last_item_30m = self.avg_line_30m[-1] if len(
                self.avg_line_30m) > 0 else (0, 0)
            if timestamp - last_item_30m >= 30:
                self.avg_line_30m.append((timestamp, d1))
                if len(self.avg_line_30m) > 90:
                    self.avg_line_30m.pop(0)
                    self.avg_30m_prev = np.mean(self.avg_line_30m[:60],
                                                axis=0)[1]
                    self.avg_30m_post = np.mean(self.avg_line_30m[60:],
                                                axis=0)[1]

            # 开空
            if self.debug_type == 2 or future_bids[0][0] - spot_asks[0][
                    0] > self.open_positive_thd:
                self.logger.info('期货开空,现货买入')
                self.logger.info('期货价格: %s,现货价格: %s' %
                                 (future_bids[0][0], spot_asks[0][0]))

                available_bear_amount = self.calc_available_contract(
                    future_bids[0][0])

                if available_bear_amount == 0:
                    self.logger.info('可开合约数量不足')
                    continue
                self.logger.info('可开合约数量为: %s' % available_bear_amount)

                spot_sum = np.sum(spot_asks[:3], axis=0)
                spot_std = np.std(spot_asks[:3], axis=0)
                if spot_sum[1] < 0.5 or spot_std[0] > 5:
                    self.logger.info('现货btc不足或标准差过大')
                    time.sleep(1)
                    continue
                future_contract_amount = min(
                    future_bids[0][1], self.max_contract_exchange_amount,
                    int(spot_sum[1] * spot_asks[0][0] / 200),
                    available_bear_amount)

                spot_usdt_amount = 100 * future_contract_amount
                if spot_usdt_amount > self.spot_free_usdt:
                    self.logger.info('现货USDT数量:%s, 不足:%s,此单取消' %
                                     (self.spot_free_usdt, spot_usdt_amount))
                    continue
                # 限价购买期货合约
                self.logger.info('期货开空:%s' % future_contract_amount)
                price = future_bids[0][0]
                try:
                    future_order = self.future_client.place_order(
                        self.symbol, self.contract_type, price,
                        future_contract_amount, '2', '0')
                except Exception as e:
                    self.logger.error('Future订单异常: %s' % e)
                    continue
                print future_order

                orderid = future_order['order_id']
                try:
                    order_info = self.future_client.order_info(
                        self.symbol, self.contract_type, 0, orderid, 1, 5)
                except Exception as e:
                    self.logger.error('获取订单信息异常: %s,程序终止' % e)
                    break
                print order_info

                order_info = order_info['orders'][0]
                self.logger.info('order status: %s' % order_info['status'])
                # 等待成交或未成交
                if order_info['status'] == 0 or order_info['status'] == 1:
                    self.logger.info('撤销未完成委托')
                    try:
                        self.future_client.cancel(self.symbol,
                                                  self.contract_type, orderid)
                    except Exception as e:
                        self.logger.error('撤销异常: %s' % e)
                        self.logger.info('更新订单状态')
                        times = 0
                        while times < 10:
                            try:
                                order_info = self.future_client.order_info(
                                    self.symbol, self.contract_type, 0,
                                    orderid, 1, 5)
                                order_info = order_info['orders'][0]
                                self.logger.info('订单状态: %s' %
                                                 order_info['status'])
                                # 若撤销失败,状态必定为完全成交
                                if order_info['status'] == 2:
                                    break
                            except Exception as e:
                                self.logger.error('查询订单信息异常: %s' % e)
                                times += 1
                                continue
                            times += 1
                        if times == 10:
                            self.logger.error('未知错误,程序终止')
                            break
                    else:
                        self.logger.info('撤销成功')
                        self.logger.info('更新订单状态')
                        try:
                            order_info = self.future_client.order_info(
                                self.symbol, self.contract_type, 0, orderid, 1,
                                5)
                            order_info = order_info['orders'][0]
                            self.logger.info('订单状态: %s' % order_info['status'])
                        except Exception as e:
                            self.logger.error('查询订单状态异常: %s,程序终止' % e)
                            break

                print order_info

                future_deal_contract_amount = order_info['deal_amount']
                # 未完成任何委托
                if future_deal_contract_amount == 0:
                    continue

                future_deal_price = order_info['price_avg']
                # fee精度是8位,fee默认是负数,改成习惯的正数
                future_deal_fee = -1 * float('%.8f' % order_info['fee'])
                # btc精度是8
                future_deal_btc_amount = float(
                    '%.8f' %
                    (future_deal_contract_amount * 10 / future_deal_price))

                self.bear_amount += future_deal_contract_amount

                self.keep_deposit -= future_deal_btc_amount
                self.keep_deposit -= future_deal_fee

                self.logger.info(
                    'future_deal:contract:%d\tbtc:%s\tfee:%s\tprice:%s' %
                    (future_deal_contract_amount, future_deal_btc_amount,
                     future_deal_fee, future_deal_price))

                # 市价买入现货
                self.logger.info('现货买入%s USDT' % spot_usdt_amount)

                spot_order = self.huobi_client.send_order(
                    spot_usdt_amount, 'api', 'btcusdt', 'buy-market')
                if spot_order['status'] != 'ok':
                    if spot_order['status'] == 'fail':
                        self.logger.error('spot buy failed : %s' %
                                          spot_order['msg'])
                    else:
                        self.logger.error('spot buy failed : %s' %
                                          spot_order['err-msg'])
                    # TODO
                    self.logger.info('开始回滚')
                    self.logger.info('终止程序')
                    break
                orderid = spot_order['data']

                times = 0
                while times < 20:
                    self.logger.info('第%s次确认订单信息' % (times + 1))
                    order_info = self.huobi_client.order_info(orderid)
                    print order_info
                    if order_info['status'] == 'ok' and order_info['data'][
                            'state'] == 'filled':
                        self.logger.info('spot buy filled, orderId: %s' %
                                         orderid)
                        field_cash_amount = order_info['data'][
                            'field-cash-amount']
                        field_amount = order_info['data']['field-amount']
                        field_fees = order_info['data']['field-fees']
                        break
                    times += 1
                    if times == 19:
                        time.sleep(15)

                if times == 20:
                    self.logger.info('现货买入错误, 终止程序')
                    break

                self.logger.info(
                    'spot_field_amount:%.8f\tspot_field_cash_amount:%.8f' %
                    (float(field_amount), float(field_cash_amount)))

                # self.update_account_info()
            # 开多
            if self.debug_type == 1 or future_asks[0][0] - spot_bids[0][
                    0] < self.open_negative_thd:
                self.logger.info('期货开多,现货卖出')
                self.logger.info('期货价格: %s,现货价格 %s' %
                                 (future_asks[0][0], spot_bids[0][0]))
                available_bull_amount = self.calc_available_contract(
                    future_asks[0][0])
                if available_bull_amount == 0:
                    self.logger.info('可开合约数量不足')
                    continue
                self.logger.info('可开合约数量: %s' % available_bull_amount)
                # print spot_bids
                spot_sum = np.sum(spot_bids[:3], axis=0)
                spot_std = np.std(spot_bids[:3], axis=0)
                self.logger.info('BIDS:\tsum:%10.4f\tstd:%10.4f' %
                                 (spot_sum[1], spot_std[0]))
                if spot_sum[1] < 0.5 or spot_std[0] > 5:
                    self.logger.info('标准差过大')
                    time.sleep(1)
                    continue
                future_contract_amount = min(
                    self.max_contract_exchange_amount,
                    available_bull_amount, future_asks[0][1],
                    int(spot_sum[1] * spot_bids[0][0] / 200))

                spot_limited_price = spot_bids[0][0] - 20
                spot_btc_amount = float(
                    '%.4f' %
                    (100 * future_contract_amount / spot_limited_price))

                if spot_btc_amount > self.spot_free_btc:
                    self.logger.info('现货BTC数量%s, 不足%s,交易取消' %
                                     (self.spot_free_btc, spot_btc_amount))
                    continue
                self.logger.info('期货开多:%s' % future_contract_amount)
                # 限价购买期货合约
                price = future_asks[0][0] + 1
                try:
                    future_order = self.future_client.place_order(
                        self.symbol, self.contract_type, price,
                        future_contract_amount, '1', '0')
                except Exception as e:
                    self.logger.error('Future订单异常: %s' % e)
                    continue
                print future_order

                orderid = future_order['order_id']
                try:
                    order_info = self.future_client.order_info(
                        self.symbol, self.contract_type, 0, orderid, 1, 5)
                except Exception as e:
                    self.logger.error('获取订单信息异常: %s, 程序终止' % e)
                    break
                print order_info

                order_info = order_info['orders'][0]
                self.logger.info('order status: %s' % order_info['status'])
                # 等待成交或未成交
                if order_info['status'] == 0 or order_info['status'] == 1:
                    self.logger.info('撤销未完成委托')
                    try:
                        self.future_client.cancel(self.symbol,
                                                  self.contract_type, orderid)
                    except Exception as e:
                        self.logger.error('撤销异常: %s' % e)
                        self.logger.info('更新订单状态')
                        times = 0
                        while times < 10:
                            try:
                                order_info = self.future_client.order_info(
                                    self.symbol, self.contract_type, 0,
                                    orderid, 1, 5)
                                order_info = order_info['orders'][0]
                                self.logger.info('订单状态: %s' %
                                                 order_info['status'])
                                # 若撤销失败,状态必定为完全成交
                                if order_info['status'] == 2:
                                    break
                            except Exception as e:
                                self.logger.error('查询订单信息异常: %s' % e)
                                times += 1
                                continue
                            times += 1
                        if times == 10:
                            self.logger.error('未知错误,程序终止')
                            break
                    else:
                        self.logger.info('撤销成功')
                        self.logger.info('更新订单状态')
                        try:
                            order_info = self.future_client.order_info(
                                self.symbol, self.contract_type, 0, orderid, 1,
                                5)
                            order_info = order_info['orders'][0]
                            self.logger.info('订单状态: %s' % order_info['status'])
                        except Exception as e:
                            self.logger.error('查询订单状态异常: %s,程序终止' % e)
                            break

                print order_info

                future_deal_contract_amount = order_info['deal_amount']
                # 未完成任何委托
                if future_deal_contract_amount == 0:
                    continue

                future_deal_price = order_info['price_avg']
                # fee精度是8位,fee默认是负数,改成习惯的正数
                future_deal_fee = -1 * float('%.8f' % order_info['fee'])
                # btc精度是8
                future_deal_btc_amount = float(
                    '%.8f' %
                    (future_deal_contract_amount * 10 / future_deal_price))

                self.bull_amount += future_deal_contract_amount
                self.keep_deposit -= future_deal_btc_amount
                self.keep_deposit -= future_deal_fee

                self.logger.info(
                    'future_deal_contract:%d\tbtc:%s\tfee:%s\tprice:%s' %
                    (future_deal_contract_amount, future_deal_btc_amount,
                     future_deal_fee, future_deal_price))
                # 市价卖出现货
                self.logger.info('现货卖出')
                spot_limited_price = spot_bids[0][0] - 20
                spot_btc_amount = float(
                    '%.4f' %
                    (100 * future_deal_contract_amount / spot_limited_price))

                spot_order = self.huobi_client.send_order(
                    spot_btc_amount, 'api', 'btcusdt', 'sell-limit',
                    spot_limited_price)
                print spot_order

                if spot_order['status'] != 'ok':
                    if spot_order['status'] == 'fail':
                        self.logger.error('spot sell failed : %s' %
                                          spot_order['msg'])
                    else:
                        self.logger.error('spot sell failed : %s' %
                                          spot_order['err-msg'])
                    self.logger.info('开始回滚')
                    self.logger.info('终止程序')
                    break

                orderid = spot_order['data']
                times = 0
                while times < 20:
                    self.logger.info('第%s次确认订单信息' % (times + 1))
                    order_info = self.huobi_client.order_info(orderid)
                    print order_info
                    if order_info['status'] == 'ok' and order_info['data'][
                            'state'] == 'filled':
                        self.logger.info('spot sell filled, orderId: %s' %
                                         orderid)
                        field_amount = float(
                            '%.8f' % float(order_info['data']['field-amount']))
                        field_cash_amount = float(
                            '%.8f' %
                            float(order_info['data']['field-cash-amount']))
                        field_fees = float('%.8f' %
                                           (field_cash_amount * 0.002))
                        break
                    times += 1
                    if times == 19:
                        time.sleep(15)

                if times == 20:
                    self.logger.info('现货卖出错误, 终止程序')

                self.logger.info(
                    'spot_field_amount:%.8f\tspot_field_cash_amount:%.8f' %
                    (field_amount, field_cash_amount))

                # self.update_account_info()
            # 平空
            if self.debug_type == 4 or future_asks[0][0] - spot_bids[0][
                    0] < self.close_positive_thd:
                if self.bear_amount == 0:
                    continue
                self.logger.info('期货平空,现货卖出')
                self.logger.info('期货价格: %s,现货价格 %s' %
                                 (future_asks[0][0], spot_bids[0][0]))
                self.logger.info('当前持空仓: %s' % self.bear_amount)

                spot_sum = np.sum(spot_bids[:3], axis=0)
                spot_std = np.std(spot_bids[:3], axis=0)
                if spot_sum[1] < 0.5 or spot_std[0] > 5:
                    self.logger.info('现货btc不足或标准差过大')
                    time.sleep(1)
                    continue
                future_contract_amount = min(
                    self.bear_amount, future_asks[0][1],
                    spot_sum[1] * spot_bids[0][0] / 200)

                spot_limited_price = float(spot_asks[0][0]) - 20
                spot_btc_amount = float(
                    '%.4f' %
                    (100 * future_contract_amount / spot_limited_price))
                if spot_btc_amount > self.spot_free_btc:
                    self.logger.info('现货BTC数量:%s, 不足:%s,本单取消' %
                                     (self.spot_free_btc, spot_btc_amount))
                    continue
                self.logger.info('期货平空%s' % future_contract_amount)

                price = future_asks[0][0]
                try:
                    future_order = self.future_client.place_order(
                        self.symbol, self.contract_type, price,
                        future_contract_amount, '4', '0')
                except Exception as e:
                    self.logger.error('Future订单异常: %s' % e)
                    continue
                print future_order

                orderid = future_order['order_id']
                try:
                    order_info = self.future_client.order_info(
                        self.symbol, self.contract_type, 0, orderid, 1, 5)
                except Exception as e:
                    self.logger.error('查询订单信息异常:%s, 程序终止' % e)
                    break

                order_info = order_info['orders'][0]
                self.logger.info('order status: %s' % order_info['status'])
                # 等待成交或未成交
                if order_info['status'] == 0 or order_info['status'] == 1:
                    self.logger.info('撤销未完成委托')
                    try:
                        self.future_client.cancel(self.symbol,
                                                  self.contract_type, orderid)
                    except Exception as e:
                        self.logger.error('撤销异常: %s' % e)
                        self.logger.info('更新订单状态')
                        times = 0
                        while times < 10:
                            try:
                                order_info = self.future_client.order_info(
                                    self.symbol, self.contract_type, 0,
                                    orderid, 1, 5)
                                order_info = order_info['orders'][0]
                                self.logger.info('订单状态: %s' %
                                                 order_info['status'])
                                # 若撤销失败,状态必定为完全成交
                                if order_info['status'] == 2:
                                    break
                            except Exception as e:
                                self.logger.error('查询订单信息异常: %s' % e)
                                times += 1
                                continue
                            times += 1
                        if times == 10:
                            self.logger.error('未知错误,程序终止')
                            break
                    else:
                        self.logger.info('撤销成功')
                        self.logger.info('更新订单状态')
                        try:
                            order_info = self.future_client.order_info(
                                self.symbol, self.contract_type, 0, orderid, 1,
                                5)
                            order_info = order_info['orders'][0]
                            self.logger.info('订单状态: %s' % order_info['status'])
                        except Exception as e:
                            self.logger.error('查询订单信息异常: %s' % e)
                            break

                print order_info

                future_deal_contract_amount = order_info['deal_amount']
                # 未完成任何委托
                if future_deal_contract_amount == 0:
                    continue
                future_deal_price = order_info['price_avg']
                # fee精度是8位,fee默认是负数,改成习惯的正数
                future_deal_fee = -1 * float('%.8f' % order_info['fee'])
                # btc精度是8
                future_deal_btc_amount = float(
                    '%.8f' %
                    (future_deal_contract_amount * 10 / future_deal_price))

                self.bear_amount -= future_deal_contract_amount
                self.keep_deposit += future_deal_btc_amount
                self.keep_deposit -= future_deal_fee

                self.logger.info(
                    'future_deal:contract:%d\tbtc:%s\tfee:%s\tprice:%s' %
                    (future_deal_contract_amount, future_deal_btc_amount,
                     future_deal_fee, future_deal_price))

                spot_limited_price = spot_bids[0][0] - 20
                spot_btc_amount = float(
                    '%.4f' %
                    (100 * future_deal_contract_amount / spot_limited_price))
                self.logger.info('现货卖出: %s' % spot_btc_amount)

                spot_order = self.huobi_client.send_order(
                    spot_btc_amount, 'api', 'btcusdt', 'sell-limit',
                    spot_limited_price)
                print spot_order

                if spot_order['status'] != 'ok':
                    if spot_order['status'] == 'fail':
                        self.logger.error('spot sell failed : %s' %
                                          spot_order['msg'])
                    else:
                        self.logger.error('spot sell failed : %s' %
                                          spot_order['err-msg'])
                    self.logger.info('开始回滚')
                    self.logger.info('终止程序')
                    break

                orderid = spot_order['data']
                times = 0
                while times < 20:
                    self.logger.info('第%s次确认订单信息' % (times + 1))
                    order_info = self.huobi_client.order_info(orderid)
                    print order_info
                    if order_info['status'] == 'ok' and order_info['data'][
                            'state'] == 'filled':
                        self.logger.info('spot sell filled, orderId: %s' %
                                         orderid)
                        field_amount = float(
                            '%.8f' % float(order_info['data']['field-amount']))
                        field_cash_amount = float(
                            '%.8f' %
                            float(order_info['data']['field-cash-amount']))
                        field_fees = float('%.8f' %
                                           (field_cash_amount * 0.002))
                        break
                    times += 1
                    if times == 19:
                        time.sleep(15)

                if times == 20:
                    self.logger.info('现货卖出错误, 终止程序')
                    break

                self.logger.info('field_amount:%.8f\tfield_cash_amount:%.8f' %
                                 (field_amount, field_cash_amount))

                # self.update_account_info()
                # 计算当前收益,并短信通知
                if self.bear_amount == 0:
                    pass

            # 平多
            if self.debug_type == 3 or future_bids[0][0] - spot_asks[0][
                    0] > self.close_negetive_thd:
                if self.bull_amount == 0:
                    continue
                self.logger.info('期货平多,现货买入')
                self.logger.info('期货价格: %s,现货价格 %s' %
                                 (future_bids[0][0], spot_asks[0][0]))
                self.logger.info('当前持多仓: %s' % self.bull_amount)

                spot_sum = np.sum(spot_asks[:3], axis=0)
                spot_std = np.std(spot_asks[:3], axis=0)
                if spot_sum[1] < 0.10 or spot_std[0] > 5:
                    self.logger.info('现货btc不足或标准差过大')
                    time.sleep(1)
                    continue
                future_contract_amount = min(
                    self.bull_amount, future_bids[0][1],
                    spot_sum[1] * spot_asks[0][0] / 200)

                spot_usdt_amount = 100 * future_contract_amount
                if spot_usdt_amount > self.spot_free_usdt:
                    self.logger.info('现货USDT数量:%s, 不足:%s,本单取消' %
                                     (self.spot_free_usdt, spot_usdt_amount))
                    continue
                self.logger.info('期货平多%s' % future_contract_amount)
                # 限价购买期货合约
                price = future_bids[0][0]
                try:
                    future_order = self.future_client.place_order(
                        self.symbol, self.contract_type, price,
                        future_contract_amount, '3', 0)
                except Exception as e:
                    self.logger.error('Future订单异常: %s' % e)
                    continue
                print future_order

                orderid = future_order['order_id']
                try:
                    order_info = self.future_client.order_info(
                        self.symbol, self.contract_type, 0, orderid, 1, 5)
                except Exception as e:
                    self.logger.error('查询订单信息异常:%s,程序终止' % e)
                    break

                order_info = order_info['orders'][0]
                self.logger.info('order status: %s' % order_info['status'])
                # 等待成交或未成交
                if order_info['status'] == 0 or order_info['status'] == 1:
                    self.logger.info('撤销未完成委托')
                    try:
                        self.future_client.cancel(self.symbol,
                                                  self.contract_type, orderid)
                    except Exception as e:
                        self.logger.error('撤销异常: %s' % e)
                        self.logger.info('更新订单状态')
                        times = 0
                        while times < 10:
                            try:
                                order_info = self.future_client.order_info(
                                    self.symbol, self.contract_type, 0,
                                    orderid, 1, 5)
                                order_info = order_info['orders'][0]
                                self.logger.info('订单状态: %s' %
                                                 order_info['status'])
                                # 若撤销失败,状态必定为完全成交
                                if order_info['status'] == 2:
                                    break
                            except Exception as e:
                                self.logger.error('查询订单信息异常: %s' % e)
                                times += 1
                                continue
                            times += 1
                        if times == 10:
                            self.logger.error('未知错误,程序终止')
                            break
                    else:
                        self.logger.info('撤销成功')
                        self.logger.info('更新订单状态')
                        try:
                            order_info = self.future_client.order_info(
                                self.symbol, self.contract_type, 0, orderid, 1,
                                5)
                            order_info = order_info['orders'][0]
                            self.logger.info('订单状态: %s' % order_info['status'])
                        except Exception as e:
                            self.logger.error('查询订单信息异常: %s' % e)
                            break

                print order_info

                future_deal_contract_amount = order_info['deal_amount']
                # 未完成任何委托
                if future_deal_contract_amount == 0:
                    continue
                future_deal_price = order_info['price_avg']
                # fee精度是8位,fee默认是负数,改成习惯的正数
                future_deal_fee = -1 * float('%.8f' % order_info['fee'])
                # btc精度是8
                future_deal_btc_amount = float(
                    '%.8f' %
                    (future_deal_contract_amount * 10 / future_deal_price))

                self.bull_amount -= future_deal_contract_amount
                self.keep_deposit += future_deal_btc_amount
                self.keep_deposit -= future_deal_fee

                self.logger.info(
                    'future_deal:contract:%d\tbtc:%s\tfee:%s\tprice:%s' %
                    (future_deal_contract_amount, future_deal_btc_amount,
                     future_deal_fee, future_deal_price))

                self.logger.info('现货买入%s USDT' % spot_usdt_amount)

                spot_order = self.huobi_client.send_order(
                    spot_usdt_amount, 'api', 'btcusdt', 'buy-market')
                if spot_order['status'] != 'ok':
                    if spot_order['status'] == 'fail':
                        self.logger.error('spot buy failed : %s' %
                                          spot_order['msg'])
                    else:
                        self.logger.error('spot buy failed : %s' %
                                          spot_order['err-msg'])

                    self.logger.info('开始回滚')
                    self.logger.info('终止程序')
                    break
                orderid = spot_order['data']

                times = 0
                while times < 20:
                    self.logger.info('第%s次确认订单信息' % (times + 1))
                    order_info = self.huobi_client.order_info(orderid)
                    print order_info
                    if order_info['status'] == 'ok' and order_info['data'][
                            'state'] == 'filled':
                        self.logger.info('huobi buy filled, orderId: %s' %
                                         orderid)
                        field_cash_amount = order_info['data'][
                            'field-cash-amount']
                        field_amount = order_info['data']['field-amount']
                        field_fees = order_info['data']['field-fees']
                        break
                    times += 1
                    if times == 19:
                        time.sleep(15)

                if times == 20:
                    self.logger.info('现货买入错误, 终止程序')
                    break

                self.logger.info(
                    'spot_field_amount:%.8f\tspot_field_cash_amount:%.8f' %
                    (float(field_amount), float(field_cash_amount)))

                # self.update_account_info()
                # 计算当前收益,并短信通知
                if self.bull_amount == 0:
                    pass

            time.sleep(1)
示例#6
0
    def __init__(self):
        key_dict = {}
        # 读取配置文件
        with open('config', 'r') as f:
            for line in f.readlines():
                splited = line.split('=')
                if len(splited) == 2:
                    key_dict[splited[0].strip()] = splited[1].strip()

        self.output = open('history', 'a+')
        self.huobiSpot = HuobiSpot(key_dict['HUOBI_ACCESS_KEY2'], key_dict['HUOBI_SECRET_KEY2'])
        self.binanceClient = BinanceSpot(key_dict['BINANCE_ACCESS_KEY'], key_dict['BINANCE_SECRET_KEY'])

        self.btc_mat = "BTC :\tfree:{:<20.8f}locked:{:<20.8f}"
        self.usdt_mat = "USDT:\tfree:{:<20.8f}locked:{:<20.8f}"
        self.total_format = "BTC:{:<20.8f}USDT:{:<20.8f}"

        # config logging
        self.logger = logging.getLogger("Robot")

        # 指定logger输出格式
        formatter = logging.Formatter('%(asctime)s %(levelname)-8s: %(message)s')

        # 文件日志
        file_handler = logging.handlers.TimedRotatingFileHandler('log', when='midnight')
        # 设置日志文件后缀,以当前时间作为日志文件后缀名。
        file_handler.suffix = "%Y-%m-%d"
        # 可以通过setFormatter指定输出格式
        file_handler.setFormatter(formatter)

        # 控制台日志
        console_handler = logging.StreamHandler(sys.stdout)
        console_handler.formatter = formatter  # 也可以直接给formatter赋值

        # 为logger添加的日志处理器
        self.logger.addHandler(file_handler)
        self.logger.addHandler(console_handler)

        # 指定日志的最低输出级别,默认为WARN级别
        self.logger.setLevel(logging.INFO)

        # 用于记录huobi连续响应过长时间的次数,超过两次,就退出
        self.huobi_timeout = 0

        # 收益统计
        self.huobi_usdt_inc = 0
        self.huobi_usdt_dec = 0
        self.binance_usdt_inc = 0
        self.binance_usdt_dec = 0

        self.huobi_usdt_total_change = 0
        self.binance_usdt_total_change = 0

        # 账户余额
        self.huobi_trade_btc = 0
        self.huobi_trade_usdt = 0
        self.binance_trade_btc = 0
        self.binance_trade_usdt = 0

        # 成交量统计
        self.usdt_exchange_amount = 0
        self.btc_exchange_amount = 0

        self.last_deal_time = 0

        # 由于数量小于0.001而未能成交
        # >0 表示需要卖出的
        self.untreated_btc = 0
示例#7
0
class ArbitrageStratety:
    # 手续费率
    huobi_fee_rate = 0.0012
    binance_fee_rate = 0.0005
    bnb_price = 10.8159
    # 盈利率
    huobi_profit_rate = 0.0003
    binance_profit_rate = 0.0003
    # btc每次最大交易量
    btc_exchange_min = 0.001
    usdt_exchange_min = 10
    # 程序里有3处需要同时更改
    btc_exchange_max = 0.065
    # HUOBI API最大连续超时次数
    huobi_max_timeout = 3
    # 深度参数阈值
    STD_THD = 5
    SUM_THD = 0.5

    def __init__(self):
        key_dict = {}
        # 读取配置文件
        with open('config', 'r') as f:
            for line in f.readlines():
                splited = line.split('=')
                if len(splited) == 2:
                    key_dict[splited[0].strip()] = splited[1].strip()

        self.output = open('history', 'a+')
        self.huobiSpot = HuobiSpot(key_dict['HUOBI_ACCESS_KEY2'], key_dict['HUOBI_SECRET_KEY2'])
        self.binanceClient = BinanceSpot(key_dict['BINANCE_ACCESS_KEY'], key_dict['BINANCE_SECRET_KEY'])

        self.btc_mat = "BTC :\tfree:{:<20.8f}locked:{:<20.8f}"
        self.usdt_mat = "USDT:\tfree:{:<20.8f}locked:{:<20.8f}"
        self.total_format = "BTC:{:<20.8f}USDT:{:<20.8f}"

        # config logging
        self.logger = logging.getLogger("Robot")

        # 指定logger输出格式
        formatter = logging.Formatter('%(asctime)s %(levelname)-8s: %(message)s')

        # 文件日志
        file_handler = logging.handlers.TimedRotatingFileHandler('log', when='midnight')
        # 设置日志文件后缀,以当前时间作为日志文件后缀名。
        file_handler.suffix = "%Y-%m-%d"
        # 可以通过setFormatter指定输出格式
        file_handler.setFormatter(formatter)

        # 控制台日志
        console_handler = logging.StreamHandler(sys.stdout)
        console_handler.formatter = formatter  # 也可以直接给formatter赋值

        # 为logger添加的日志处理器
        self.logger.addHandler(file_handler)
        self.logger.addHandler(console_handler)

        # 指定日志的最低输出级别,默认为WARN级别
        self.logger.setLevel(logging.INFO)

        # 用于记录huobi连续响应过长时间的次数,超过两次,就退出
        self.huobi_timeout = 0

        # 收益统计
        self.huobi_usdt_inc = 0
        self.huobi_usdt_dec = 0
        self.binance_usdt_inc = 0
        self.binance_usdt_dec = 0

        self.huobi_usdt_total_change = 0
        self.binance_usdt_total_change = 0

        # 账户余额
        self.huobi_trade_btc = 0
        self.huobi_trade_usdt = 0
        self.binance_trade_btc = 0
        self.binance_trade_usdt = 0

        # 成交量统计
        self.usdt_exchange_amount = 0
        self.btc_exchange_amount = 0

        self.last_deal_time = 0

        # 由于数量小于0.001而未能成交
        # >0 表示需要卖出的
        self.untreated_btc = 0

    @staticmethod
    def sms_notify(msg):
        url = 'http://221.228.17.88:8080/sendmsg/send'
        params = {
            'phonenum': '18118999630',
            'msg': msg
        }
        requests.get(url, params=params)

    def update_profit_rate(self):
        self.logger.info('更新盈利率')
        huobi_btc_percent = float('%.2f' % (self.huobi_trade_btc / (self.huobi_trade_btc + self.binance_trade_btc)))
        binance_btc_percent = 1 - huobi_btc_percent

        self.logger.info('Huobi: %s\tBinance:%s' % (huobi_btc_percent, binance_btc_percent))
        if huobi_btc_percent < 0.1:
            self.huobi_profit_rate = 0.001
        elif huobi_btc_percent < 0.2:
            self.huobi_profit_rate = 0.0007
        elif huobi_btc_percent > 0.9:
            self.binance_profit_rate = 0.001
        elif huobi_btc_percent > 0.8:
            self.binance_profit_rate = 0.0007
        else:
            self.huobi_profit_rate = 0.0003
            self.binance_profit_rate = 0.0003
        self.logger.info('huobi_profit_rate: %s\t, binance_profit_rate: %s' % (
            self.huobi_profit_rate, self.binance_profit_rate))

    def update_account_info(self):
        # update binance
        self.logger.info('|--------------------------------------------------')
        self.logger.info('|' + '更新账户信息')
        try:
            account_info = self.binanceClient.get_account()
        except Exception as e:
            self.logger.error('Binance get_account error: %s' % e)
        else:
            freezed_btc = 0
            freezed_usdt = 0
            for info in account_info['balances']:
                if info['asset'] == 'BTC':
                    self.binance_trade_btc = float(info['free'])
                    freezed_btc = float(info['locked'])
                elif info['asset'] == 'USDT':
                    self.binance_trade_usdt = float(info['free'])
                    freezed_usdt = float(info['locked'])
            self.logger.info('|' + 'Binance:')
            self.logger.info('|' + self.btc_mat.format(self.binance_trade_btc, freezed_btc))
            self.logger.info('|' + self.usdt_mat.format(self.binance_trade_usdt, freezed_usdt))

            # update huobi
            # 修复了float进位的问题
            json_r = self.huobiSpot.get_balance()
            if json_r['status'] == 'ok':
                for item in json_r['data']['list']:
                    if item['currency'] == 'btc' and item['type'] == 'trade':
                        self.huobi_trade_btc = ArbitrageStratety.cut2_float(item['balance'], 8)
                        # self.huobi_trade_btc = float(item['balance'])
                    elif item['currency'] == 'btc' and item['type'] == 'frozen':
                        freezed_btc = float(item['balance'])
                    elif item['currency'] == 'usdt' and item['type'] == 'trade':
                        self.huobi_trade_usdt = ArbitrageStratety.cut2_float(item['balance'], 8)
                    elif item['currency'] == 'usdt' and item['type'] == 'frozen':
                        freezed_usdt = float(item['balance'])
                self.logger.info('|' + 'Huobi:')
                self.logger.info('|' + self.btc_mat.format(self.huobi_trade_btc, freezed_btc))
                self.logger.info('|' + self.usdt_mat.format(self.huobi_trade_usdt, freezed_usdt))

                self.logger.info('|' + 'Total:')
                self.logger.info('|' + self.total_format.format(self.binance_trade_btc + self.huobi_trade_btc,
                                                                self.binance_trade_usdt + self.huobi_trade_usdt))
                self.logger.info('|' + 'Untreated: %s' % self.untreated_btc)
                self.update_profit_rate()
            else:
                print json_r
                if 'fail' == json_r['status']:
                    self.logger.error('Huobi get_balance error: %s' % json_r['msg'])
                else:
                    self.logger.error('Huobi get_balance error: %s' % json_r['err-msg'])
            self.logger.info('|--------------------------------------------------')

    @staticmethod
    def merge_depth(depth):
        new_depth = []
        for d in depth:
            price = ArbitrageStratety.cut2_float(d[0], 1)
            # price = float(re.match('(\d+\.\d)\d*', '{:.8f}'.format(float(d[0]))).group(1))
            amount = float(d[1])
            if len(new_depth) == 0:
                new_depth.append([price, amount])
            else:
                if new_depth[-1][0] == price:
                    new_depth[-1] = [price, new_depth[-1][1] + amount]
                else:
                    new_depth.append([price, amount])
        return new_depth[:5]

    @staticmethod
    def cut2_float(s, n):
        if isinstance(s, float):
            s = '{:.8f}'.format(s)
        pattern = re.compile(r'(\d+\.\d{1,%d})\d*' % n)
        return float(pattern.match(s).group(1))

    def go(self):
        while True:
            # get depth info
            print '获取深度信息'
            h_depth = self.huobiSpot.get_depth('btcusdt', 'step5')
            if h_depth['status'] == 'ok':
                h_bids = h_depth['tick']['bids']
                h_asks = h_depth['tick']['asks']
            else:
                time.sleep(3)
                continue

            try:
                b_depth = self.binanceClient.get_order_book(symbol='BTCUSDT')
            except Exception as e:
                self.logger.error(u'获取Binance市场深度错误: %s' % e)
                time.sleep(3)
                continue
            # print b_depth
            # 需要合并深度
            b_bids = ArbitrageStratety.merge_depth(b_depth['bids'])
            b_asks = ArbitrageStratety.merge_depth(b_depth['asks'])
            print b_asks

            # huobi sell
            if h_bids[0][0] * 1.0 / float(
                    b_asks[0][0]) > 1 + self.huobi_fee_rate + self.binance_fee_rate + self.huobi_profit_rate:
                self.logger.info('binance买入,huobi卖出,')
                # print h_bids
                h_sum = np.sum(h_bids[:3], axis=0)
                h_std = np.std(h_bids[:3], axis=0)
                h_avg = np.mean(h_bids[:3], axis=0)

                a = [(float(i[0]), float(i[1])) for i in b_asks]
                b_sum = np.sum(a[:3], axis=0)
                b_std = np.std(a[:3], axis=0)
                b_avg = np.mean(a[:3], axis=0)
                # print a
                self.logger.info('ASKS:\tsum:%10.4f\tstd:%10.4f\tavg:%10.4f' % (h_sum[1], h_std[0], h_avg[0]))
                self.logger.info('BIDS:\tsum:%10.4f\tstd:%10.4f\tavg:%10.4f' % (b_sum[1], b_std[0], b_avg[0]))

                self.logger.info(
                    '卖出价:%s, 买入价:%s, 比例:%s' % (h_bids[0][0], float(b_asks[0][0]), h_bids[0][0] * 1.0 / float(
                        b_asks[0][0])))

                if h_std[0] > self.STD_THD or h_sum[1] < self.SUM_THD:
                    self.logger.info('标准差过大,本单取消')
                    time.sleep(0.1)
                    continue

                btc_amount = float(
                    '%.4f' % min(h_bids[0][1], float(b_asks[0][1]), self.btc_exchange_max))

                # Binance btc-amount 精度是6位,需要截取前4位,不能产生进位
                if btc_amount > float(b_asks[0][1]):
                    # btc_amount = float(re.match('(\d+\.\d{4})\d*', '{:.8f}'.format(float(b_asks[0][1]))).group(1))
                    btc_amount = ArbitrageStratety.cut2_float(b_asks[0][1], 4)

                if btc_amount > self.huobi_trade_btc:
                    # btc_amount = float(
                    #     re.match('(\d+\.\d{4})\d*', '{:.8f}'.format(self.huobi_trade_btc)).group(1))
                    btc_amount = ArbitrageStratety.cut2_float(self.huobi_trade_btc, 4)

                order_price = float(b_asks[0][0]) + 1.1
                usdt_amount = float('%.4f' % (btc_amount * order_price))
                self.logger.info('本次交易量:%s BTC, %s USDT' % (btc_amount, usdt_amount))

                if btc_amount < self.btc_exchange_min:
                    self.logger.info('BTC交易数量不足: %s, 本单取消' % self.btc_exchange_min)
                    time.sleep(1)
                    continue
                if usdt_amount < self.usdt_exchange_min:
                    self.logger.info('USDT交易数量不足: %s, 本单取消' % self.usdt_exchange_min)
                    time.sleep(1)
                    continue
                if usdt_amount > self.binance_trade_usdt - 5:
                    self.logger.info('Binance USDT 数量: %s, 不足:%s, 本单取消' % (self.binance_trade_usdt, usdt_amount))
                    time.sleep(1)
                    continue

                # 限价买
                self.logger.info('开始限价买入')
                try:
                    buy_order = self.binanceClient.order_limit_buy(symbol='BTCUSDT', quantity=btc_amount,
                                                                   price=order_price, newOrderRespType='FULL')
                except Exception as e:
                    self.logger.error(u'Binance买入错误: %s' % e)
                    time.sleep(3)
                    continue
                print buy_order

                buy_order_id = buy_order['orderId']
                self.output.write('\n' + str(buy_order_id))
                self.output.flush()
                self.logger.info('binance buy orderId: %s, state: %s' % (buy_order_id, buy_order['status']))
                field_cash_amount = 0
                field_amount = 0
                if buy_order['status'] == 'NEW' or buy_order['status'] == 'PARTIALLY_FILLED':
                    self.logger.info('撤消未完成委托')
                    try:
                        cancel_r = self.binanceClient.cancel_order(symbol='BTCUSDT', orderId=buy_order_id)
                        print cancel_r
                    # get_my_trades有超时问题,暂时不用此函数

                    except Exception as e:
                        self.logger.error(u'撤销错误: %s' % e)
                    else:
                        self.logger.info('撤销成功')
                    # 有可能会出现撤销成功,但是撤销完成的过程中,又完成了部分委托,需要更新实际成交量
                    self.logger.info('更新成交量')
                    times = 0
                    while times < 10:
                        self.logger.info(u'第%s次查询Binance订单状态' % (times + 1))
                        try:
                            order = self.binanceClient.get_order(symbol='BTCUSDT', orderId=buy_order_id)
                            self.logger.info(u'当前订单状态为: %s', order['status'])
                            # 撤销成功,状态必定为CANCELED, 撤销成功则为FILLED
                            if order['status'] == 'CANCELED' or order['status'] == 'FILLED':
                                field_amount = float(order['executedQty'])
                                price = float(order['price'])
                                field_cash_amount = float('%.8f' % (field_amount * price))
                                break
                        except Exception as e:
                            self.logger.error(u'Binance get order error: %s' % e)
                        times += 1

                    if times == 10:
                        self.logger.info('未知错误,程序终止')
                        break
                # filled
                elif buy_order['status'] == 'FILLED':
                    fills = buy_order['fills']
                    for f in fills:
                        price = float('%.2f' % float(f['price']))
                        qty = float('%.8f' % float(f['qty']))
                        field_amount += qty
                        field_cash_amount += price * qty
                else:
                    self.logger.info('订单状态异常: %s' % buy_order['status'])

                if field_amount == 0:
                    self.logger.info('未完成任何委托')
                    continue

                self.logger.info('field_amount:%.8f\tfield_cash_amount:%.8f' % (
                    float(field_amount), float(field_cash_amount)))

                self.binance_trade_btc += field_amount
                self.binance_trade_usdt -= field_cash_amount

                self.binance_usdt_dec = field_cash_amount

                self.binance_usdt_total_change -= self.binance_usdt_dec

                # 市价卖
                self.logger.info('开始市价卖出')
                btc_amount = float('%.4f' % field_amount)

                # 记录总共损失的BTC数目,达到0.001时候,进行补全
                if btc_amount < self.btc_exchange_min:
                    self.logger.info('BTC卖出数量低于 %s', self.btc_exchange_min)
                    self.logger.info('本次交易终止')
                    self.untreated_btc += field_amount
                    # self.rollback_binance_order(buy_order_id)
                    time.sleep(3)
                    continue
                # 买入卖出由于精度不同,会存在一定的偏差,这里进行统计调整
                else:
                    self.untreated_btc += field_amount - btc_amount

                sell_order = self.huobiSpot.send_order(btc_amount, 'api', 'btcusdt', 'sell-market')
                if sell_order['status'] != 'ok':
                    if sell_order['status'] == 'fail':
                        self.logger.error('sell failed : %s' % sell_order['msg'])
                    else:
                        self.logger.error('sell failed : %s' % sell_order['err-msg'])

                    self.logger.info('开始回滚')
                    self.rollback_binance_order(buy_order_id)
                    self.logger.info('终止程序')
                    break
                sell_order_id = sell_order['data']

                self.output.write(':' + str(sell_order_id))
                self.output.flush()
                times = 0
                while times < 20:
                    self.logger.info('第%s次确认订单信息' % (times + 1))
                    sell_order_info = self.huobiSpot.order_info(sell_order_id)
                    print sell_order_info
                    if sell_order_info['status'] == 'ok' and sell_order_info['data']['state'] == 'filled':
                        self.logger.info('huobi sell filled, orderId: %s' % sell_order_id)
                        field_cash_amount = sell_order_info['data']['field-cash-amount']
                        field_amount = sell_order_info['data']['field-amount']
                        break
                    times += 1
                    if times == 19:
                        time.sleep(15)

                if times == 20:
                    self.huobi_timeout += 1
                    if self.huobi_timeout == self.huobi_max_timeout:
                        self.logger.info('连续%s次超时,终止程序' % self.huobi_timeout)
                        break
                    else:
                        self.logger.info('连续%s次超时,继续程序' % self.huobi_timeout)
                        if self.huobi_timeout == 1:
                            time.sleep(60)
                        else:
                            time.sleep(600)
                        self.btc_exchange_max /= 2
                        continue
                else:
                    self.huobi_timeout = 0
                    self.btc_exchange_max = 0.065

                self.logger.info('field_amount:%.8f\tfield_cash_amount:%.8f' % (
                    float(field_amount), float(field_cash_amount)))

                self.huobi_trade_btc -= float(field_amount)
                self.huobi_trade_usdt += float(field_cash_amount)
                # 更新交易量统计
                self.usdt_exchange_amount += float(field_cash_amount)
                self.btc_exchange_amount += float(field_amount)

                self.huobi_usdt_inc = float(field_cash_amount)

                self.huobi_usdt_total_change += self.huobi_usdt_inc

                usdt_inc = self.huobi_usdt_inc - self.binance_usdt_dec
                thistime_earnings = usdt_inc
                thistime_earnings_rate = thistime_earnings * 1.0 / float(field_cash_amount)

                total_usdt_earnings = self.huobi_usdt_total_change + self.binance_usdt_total_change

                self.logger.info('本次交易量: %s BTC, 盈利: %s USDT, 盈利率: %s' % (
                    float(field_amount), thistime_earnings, thistime_earnings_rate))
                self.logger.info(
                    '总BTC成交量: %s, 盈利: %s USDT, 盈利率: %s' % (
                        self.btc_exchange_amount, total_usdt_earnings,
                        total_usdt_earnings * 1.0 / self.usdt_exchange_amount))
                self.logger.info('|--------------------------------------------------')
                self.logger.info('|' + ' BTC:\tTOTAL:{:<20.8f}HUOBI:{:<20.8f}BINANCE:{:<20.8f}'.format(
                    (self.huobi_trade_btc + self.binance_trade_btc), self.huobi_trade_btc, self.binance_trade_btc))
                self.logger.info(
                    '|' + 'USDT:\tTOTAL:{:<20.8f}HUOBI:{:<20.8f}BINANCE:{:<20.8f}'.format(
                        (self.huobi_trade_usdt + self.binance_trade_usdt), self.huobi_trade_usdt,
                        self.binance_trade_usdt))
                self.logger.info('|--------------------------------------------------')
                self.update_profit_rate()

                self.last_deal_time = int(time.time())

            # binance sell
            elif float(b_bids[0][0]) / h_asks[0][
                0] > 1 + self.huobi_fee_rate + self.binance_fee_rate + self.binance_profit_rate:
                self.logger.info('binance 卖出, huobi买入')

                b = [(float(i[0]), float(i[1])) for i in b_bids]
                print b
                b_sum = np.sum(b[:3], axis=0)
                b_std = np.std(b[:3], axis=0)
                b_avg = np.mean(b[:3], axis=0)

                print h_asks
                h_sum = np.sum(h_asks[:3], axis=0)
                h_std = np.std(h_asks[:3], axis=0)
                h_avg = np.mean(h_asks[:3], axis=0)

                self.logger.info('ASKS:\tsum:%10.4f\tstd:%10.4f\tavg:%10.4f' % (b_sum[1], b_std[0], b_avg[0]))
                self.logger.info('BIDS:\tsum:%10.4f\tstd:%10.4f\tavg:%10.4f' % (h_sum[1], h_std[0], h_avg[0]))

                self.logger.info(
                    '卖出价:%s, 买入价:%s, 比例:%s' % (float(b_bids[0][0]), h_asks[0][0], float(b_bids[0][0]) / h_asks[0][0]))
                if h_std[0] > self.STD_THD or h_sum[1] < self.SUM_THD:
                    self.logger.info('标准差过大,本单取消')
                    time.sleep(0.1)
                    continue
                order_price = b_bids[0][0] - 1.1

                btc_amount = float('%.4f' % min(float(b_bids[0][1]), h_asks[0][1],
                                                self.btc_exchange_max))
                if btc_amount > float(b_bids[0][1]):
                    # = float(re.match('(\d+\.\d{4})\d*', '{:.8f}'.format(float(b_bids[0][1]))).group(1))
                    btc_amount = ArbitrageStratety.cut2_float(b_bids[0][1], 4)

                if btc_amount > self.binance_trade_btc:
                    # btc_amount = float(
                    #     re.match('(\d+\.\d{4})\d*', '{:.8f}'.format(self.binance_trade_btc)).group(1))
                    btc_amount = ArbitrageStratety.cut2_float(self.binance_trade_btc, 4)

                usdt_amount = float('%.4f' % (btc_amount * h_asks[0][0]))

                self.logger.info('本次交易量:%s BTC, %s USDT' % (btc_amount, usdt_amount))
                if btc_amount < self.btc_exchange_min:
                    self.logger.info('BTC交易数量不足: %s, 本单取消' % self.btc_exchange_min)
                    time.sleep(1)
                    continue
                if float('%.4f' % (btc_amount * order_price)) < self.usdt_exchange_min:
                    self.logger.info('USDT交易数量不足: %s, 本单取消' % self.usdt_exchange_min)
                    time.sleep(1)
                    continue
                if usdt_amount > self.huobi_trade_usdt - 5:
                    self.logger.info('Huobi USDT 数量: %s, 不足:%s, 本单取消' % (self.huobi_trade_usdt, usdt_amount))
                    time.sleep(1)
                    continue

                # 限价卖
                self.logger.info('开始限价卖出')
                try:
                    sell_r = self.binanceClient.order_limit_sell(symbol='BTCUSDT', quantity=btc_amount,
                                                                 price=order_price, newOrderRespType='FULL')
                except Exception as e:
                    self.logger.error(u'Binance卖出错误: %s' % e)
                    time.sleep(3)
                    continue
                print sell_r
                sell_order_id = sell_r['orderId']
                self.output.write('\n' + str(sell_order_id))
                self.output.flush()
                self.logger.info('binance sell orderId: %s, state: %s' % (sell_order_id, sell_r['status']))
                field_cash_amount = 0
                field_amount = 0
                if sell_r['status'] == 'NEW' or sell_r['status'] == 'PARTIALLY_FILLED':
                    # 撤销未完成订单
                    self.logger.info('撤消未完成委托')
                    try:
                        cancel_r = self.binanceClient.cancel_order(symbol='BTCUSDT', orderId=sell_order_id)
                        print cancel_r
                    except Exception as e:
                        self.logger.error(u'撤销错误: %s' % e)
                    else:
                        self.logger.info('撤销成功')
                    self.logger.info('更新成交量')
                    times = 0
                    while times < 10:
                        self.logger.info(u'第%s次查询Binance订单状态' % (times + 1))
                        try:
                            order = self.binanceClient.get_order(symbol='BTCUSDT', orderId=sell_order_id)
                            print order
                            self.logger.info(u'当前订单状态为: %s', order['status'])
                            # 撤销成功,状态必定为CANCELED, 撤销成功则为FILLED
                            if order['status'] == 'CANCELED' or order['status'] == 'FILLED':
                                field_amount = float(order['executedQty'])
                                price = float(order['price'])
                                field_cash_amount = float('%.8f' % (field_amount * price))
                                break
                        except Exception as e:
                            self.logger.error(u'Binance get order error: %s' % e)
                        times += 1

                    if times == 10:
                        self.logger.info('未知错误,程序终止')
                        break
                # filled
                elif sell_r['status'] == 'FILLED':
                    fills = sell_r['fills']
                    for f in fills:
                        price = float('%.2f' % float(f['price']))
                        qty = float('%.8f' % float(f['qty']))
                        field_amount += qty
                        field_cash_amount += price * qty
                else:
                    self.logger.info('订单状态异常: %s' % sell_r['status'])

                if field_amount == 0:
                    self.logger.info('未完成任何委托')
                    continue

                # 更新统计数据
                self.btc_exchange_amount += float(field_amount)
                self.usdt_exchange_amount += float(field_cash_amount)

                # update income
                self.binance_usdt_inc = field_cash_amount

                self.binance_usdt_total_change += self.binance_usdt_inc

                self.logger.info('field_amount:%.8f\tfield_cash_amount:%.8f' % (
                    field_amount, field_cash_amount))
                self.binance_trade_btc -= field_amount
                self.binance_trade_usdt += field_cash_amount

                # 限价买,使买价高于市价,最后会已市价成交
                self.logger.info('开始伪市价(限价)买入')
                buy_price = h_asks[0][0] + 20
                btc_amount = float('%.4f' % field_amount)

                if btc_amount < self.btc_exchange_min:
                    self.logger.error('BTC交易数量低于 %s' % self.btc_exchange_min)
                    self.logger.info('本次交易终止,开始回滚')
                    self.untreated_btc -= field_amount
                    time.sleep(3)
                    continue
                else:
                    self.untreated_btc -= field_amount - btc_amount

                buy_r = self.huobiSpot.send_order(btc_amount, 'api', 'btcusdt', 'buy-limit', buy_price)
                print buy_r

                if buy_r['status'] != 'ok':
                    if buy_r['status'] == 'fail':
                        self.logger.error('buy failed : %s' % buy_r['msg'])
                    else:
                        self.logger.error('buy failed : %s' % buy_r['err-msg'])
                    self.logger.info('开始回滚')
                    self.rollback_binance_order(sell_order_id)
                    self.logger.info('终止程序')
                    break

                buy_order_id = buy_r['data']
                self.output.write(':' + str(buy_order_id))
                self.output.flush()
                times = 0
                while times < 20:
                    self.logger.info('第%s次确认订单信息' % (times + 1))
                    buy_order_result = self.huobiSpot.order_info(buy_order_id)
                    print buy_order_result
                    if buy_order_result['status'] == 'ok' and buy_order_result['data']['state'] == 'filled':
                        self.logger.info('huobi buy filled, orderId: %s' % buy_order_id)
                        field_amount = float('%.8f' % float(buy_order_result['data']['field-amount']))
                        field_cash_amount = float('%.8f' % float(buy_order_result['data']['field-cash-amount']))
                        break
                    times += 1
                    if times == 19:
                        time.sleep(15)

                if times == 20:
                    self.huobi_timeout += 1
                    if self.huobi_timeout == self.huobi_max_timeout:
                        self.logger.info('连续%s次超时,终止程序' % self.huobi_timeout)
                        break
                    else:
                        self.logger.info('连续%s次超时,继续程序' % self.huobi_timeout)
                        if self.huobi_timeout == 1:
                            time.sleep(60)
                        else:
                            time.sleep(600)
                        #
                        self.btc_exchange_max /= 2
                        continue
                else:
                    self.huobi_timeout = 0
                    self.btc_exchange_max = 0.065

                self.logger.info('field_amount:%.8f\tfield_cash_amount:%.8f' % (
                    field_amount, field_cash_amount))
                # update income
                self.huobi_usdt_dec = field_cash_amount

                self.huobi_usdt_total_change -= self.huobi_usdt_dec

                self.huobi_trade_btc += field_amount
                self.huobi_trade_usdt -= field_cash_amount
                # total
                usdt_inc = self.binance_usdt_inc - self.huobi_usdt_dec
                thistime_earnings = usdt_inc
                thistime_earnings_rate = thistime_earnings * 1.0 / field_cash_amount

                # total_btc_earnings = self.huobi_btc_total_change + self.binance_btc_total_change
                total_usdt_earnings = self.huobi_usdt_total_change + self.binance_usdt_total_change

                self.logger.info('本次交易量: %s BTC, 盈利: %s USDT, 盈利率: %s' % (
                    float(field_amount), thistime_earnings, thistime_earnings_rate))
                self.logger.info(
                    '总BTC成交量: %s, 盈利: %s USDT, 盈利率: %s' % (
                        self.btc_exchange_amount, total_usdt_earnings,
                        total_usdt_earnings * 1.0 / self.usdt_exchange_amount))
                self.logger.info('|--------------------------------------------------')
                self.logger.info('|' + ' BTC:\tTOTAL:{:<20.8f}HUOBI:{:<20.8f}BINANCE:{:<20.8f}'.format(
                    (self.huobi_trade_btc + self.binance_trade_btc), self.huobi_trade_btc,
                    self.binance_trade_btc))
                self.logger.info(
                    '|' + 'USDT:\tTOTAL:{:<20.8f}HUOBI:{:<20.8f}BINANCE:{:<20.8f}'.format(
                        (self.huobi_trade_usdt + self.binance_trade_usdt), self.huobi_trade_usdt,
                        self.binance_trade_usdt))
                self.logger.info('|--------------------------------------------------')
                self.update_profit_rate()

                self.last_deal_time = int(time.time())
            time.sleep(0.1)
            nowtime = time.strftime('%H:%M:%S', time.localtime(time.time()))
            if nowtime.startswith('08:30'):
                self.order_statistics()

                self.usdt_exchange_amount = 0
                self.btc_exchange_amount = 0
                self.huobi_usdt_total_change = 0
                self.binance_usdt_total_change = 0
                time.sleep(60)

            # 每5分钟没有交易就更新账户信息
            if self.last_deal_time > 0 and int(time.time()) - self.last_deal_time > 300:
                orderid = 0
                if self.untreated_btc > 0.001:
                    sell_amount = float('%.4f' % self.untreated_btc)
                    self.logger.info('平衡账户资产,Huobi卖出: %s BTC' % sell_amount)
                    sell_r = self.huobiSpot.send_order(sell_amount, 'api', 'btcusdt', 'sell-market')
                    if sell_r['status'] != 'ok':
                        if sell_r['status'] == 'fail':
                            self.logger.error('sell failed : %s' % sell_r['msg'])
                        else:
                            self.logger.error('sell failed : %s' % sell_r['err-msg'])
                        break
                    else:
                        orderid = sell_r['data']
                        self.untreated_btc -= sell_amount
                elif self.untreated_btc < -0.001:
                    buy_price = h_asks[0][0] + 20
                    buy_amount = float('%.4f' % self.untreated_btc)
                    self.logger.info('平衡账户资产,Huobi买入: %s BTC' % buy_amount)
                    buy_r = self.huobiSpot.send_order(-1 * buy_amount, 'api', 'btcusdt', 'buy-limit', buy_price)
                    if buy_r['status'] != 'ok':
                        if buy_r['status'] == 'fail':
                            self.logger.error('sell failed : %s' % buy_r['msg'])
                        else:
                            self.logger.error('sell failed : %s' % buy_r['err-msg'])
                        break
                    else:
                        orderid = buy_r['data']
                        self.untreated_btc -= buy_amount
                print orderid
                if orderid:
                    times = 0
                    while times < 20:
                        self.logger.info('第%s次确认订单信息' % (times + 1))
                        order_result = self.huobiSpot.order_info(orderid)
                        print order_result
                        if order_result['status'] == 'ok' and order_result['data']['state'] == 'filled':
                            self.logger.info('order filled, orderId: %s' % orderid)
                            field_amount = float('%.8f' % float(order_result['data']['field-amount']))
                            field_cash_amount = float('%.8f' % float(order_result['data']['field-cash-amount']))
                            if 'buy' in order_result['data']['type']:
                                self.huobi_trade_btc += field_amount
                                self.huobi_trade_usdt -= field_cash_amount
                            else:
                                self.huobi_trade_btc -= field_amount
                                self.huobi_trade_usdt += field_cash_amount
                            break
                        times += 1

                        if times == 9:
                            time.sleep(10)
                        if times == 19:
                            time.sleep(300)

                    if times == 20:
                        self.logger.error('未知错误,程序终止')
                        break

                total_btc_amount_before = self.binance_trade_btc + self.huobi_trade_btc
                self.logger.info('before: binance:%s\thuobi: %s' % (self.binance_trade_btc, self.huobi_trade_btc))
                self.update_account_info()
                self.logger.info('after : binance:%s\thuobi: %s' % (self.binance_trade_btc, self.huobi_trade_btc))
                total_btc_amount_after = self.binance_trade_btc + self.huobi_trade_btc
                if abs(total_btc_amount_after - total_btc_amount_before) > 0.001:
                    self.logger.info('账户BTC总量发生异常,程序终止')
                    break
                self.last_deal_time = 0

    def rollback_binance_order(self, orderid):
        order_info = self.binanceClient.get_order(symbol='BTCUSDT', orderId=orderid)
        side = order_info['side'].upper()
        field_amount = float('%.6f' % float(order_info['executedQty']))
        price = float('%.2f' % float(order_info['price']))
        if side == 'BUY':
            try:
                order = self.binanceClient.order_limit_sell(symbol='BTCUSDT', quantity=field_amount,
                                                            price=price - 5, newOrderRespType='FULL')
                print order
            except Exception as e:
                self.logger.error(u'Binance卖出错误: %s, 回滚失败' % e)
        else:
            try:
                order = self.binanceClient.order_limit_buy(symbol='BTCUSDT', quantity=field_amount,
                                                           price=price + 5, newOrderRespType='FULL')
                print order
            except Exception as e:
                self.logger.error(u'Binance买入错误: %s, 回滚失败' % e)

    # HUOBI API 提供的只能获取100条,此函数为扩展,提取500条
    def get_huobi_orders(self):
        result = []
        last_order_id = None
        for i in range(5):
            orders_list = self.huobiSpot.orders_list('btcusdt', '', _from=last_order_id, size=100)
            if orders_list['status'] != 'ok':
                print ('获取火币历史委托错误')
                print orders_list
                return []
            else:
                if i == 0:
                    for item in orders_list['data']:
                        result.append(item)
                else:
                    for item in orders_list['data'][1:]:
                        result.append(item)
                last_order_id = orders_list['data'][-1]['id']
        return result

    def order_statistics(self, start=None, end=None):
        huobi_order_list = []
        binance_order_list = []
        # Huobi
        for order in self.get_huobi_orders():
            dict = {
                'id': order['id'],
                'state': order['state'].upper(),
                'amount': float('%.8f' % float(order['amount'])),
                'field-cash-amount': float('%.4f' % float(order['field-cash-amount'])),
                'field-amount': float('%.8f' % float(order['field-amount'])),
                'created-at': order['created-at'],
                'finished-at': order['finished-at'],
                'canceled-at': order['canceled-at'],
                'type': u'LIMIT' if 'limit' in order['type'] else u'MARKET',
                'side': u'BUY' if 'buy' in order['type'] else u'SELL',
            }
            # print dict
            if dict['finished-at'] == 0:
                continue
            if dict['field-amount'] > 0:
                dict['price'] = float('%.2f' % (dict['field-cash-amount'] / dict['field-amount']))
            huobi_order_list.append(dict)
        print('获取币安历史委托数据')
        binance_orders = self.binanceClient.get_all_orders(symbol='BTCUSDT')
        binance_trades = self.binanceClient.get_my_trades(symbol='BTCUSDT', recvWindow=130000)
        # print binance_trades
        for order in binance_orders:
            dict = {
                'id': order['orderId'],
                'state': order['status'].upper(),
                'amount': float('%.8f' % float(order['origQty'])),
                'created-at': order['time'],
                'finished-at': '',
                'canceled-at': '',
                'type': order['type'].upper(),
                'side': order['side'].upper(),
                'commission': 0,
                'field-amount': 0,
                'field-cash-amount': 0
            }
            binance_order_list.append(dict)
        for item in binance_order_list:
            id = item['id']
            for trade in binance_trades:
                if trade['orderId'] == id:
                    item['commission'] += float('%.8f' % float(trade['commission']))
                    item['field-amount'] += float('%.8f' % float(trade['qty']))
                    item['price'] = float('%.2f' % float(trade['price']))
                    item['field-cash-amount'] += float('%.8f' % (float(trade['qty']) * item['price']))
        huobi_order_list = sorted(huobi_order_list, key=lambda x: x['created-at'], reverse=True)
        binance_order_list = sorted(binance_order_list, key=lambda x: x['created-at'], reverse=True)

        # print huobi_order_list
        # print binance_order_list

        yestoday = datetime.date.today() + datetime.timedelta(days=-1)
        timearray = time.strptime(str(yestoday) + ' 8:30:00', "%Y-%m-%d %H:%M:%S")
        timestamp = int(round(time.mktime(timearray)) * 1000)
        print timestamp
        workbook = xlsxwriter.Workbook('output.xlsx')
        worksheet = workbook.add_worksheet(u'成功')
        worksheet2 = workbook.add_worksheet(u'失败')
        worksheet3 = workbook.add_worksheet(u'总计')
        date_format_str = 'yy/mm/dd/ hh:mm:ss'
        binance_common_format = workbook.add_format({'align': 'left', 'font_name': 'Consolas'})
        binance_date_format = workbook.add_format({'num_format': date_format_str,
                                                   'align': 'left', 'font_name': 'Consolas'})

        huobi_common_format = workbook.add_format({'align': 'left', 'font_name': 'Consolas', 'bg_color': 'yellow'})
        huobi_date_format = workbook.add_format({'num_format': date_format_str,
                                                 'align': 'left', 'font_name': 'Consolas', 'bg_color': 'yellow'})
        merged_format = workbook.add_format({'align': 'center', 'valign': 'vcenter', 'font_name': 'Consolas'})
        row_1 = 0
        row_2 = 0
        row_3 = 0
        col = 0
        header = [u'委托时间', u'方向', u'类型', u'价格', u'委托数量', u'成交数量', u'成交金额', u'状态',
                  u'盈利(USDT)', u'盈利率']

        total_usdt_earnings = 0
        total_huobi_usdt_trade = 0
        total_binance_commission = 0
        total_btc_exchange = 0
        i = 0
        for h in header:
            worksheet.write(row_1, col + i, h)
            worksheet2.write(row_2, col + i, h)
            i += 1
        row_1 += 1
        row_2 += 1
        with open('history', 'r') as f:
            for line in f.readlines():
                if len(line) < 5:
                    continue
                splited = line.strip().split(':')
                huobi_id = 0
                if len(splited) == 2:
                    binance_id = int(splited[0])
                    huobi_id = int(splited[1])
                elif len(splited) == 1:
                    binance_id = int(splited[0])
                else:
                    continue
                # print binance_id, huobi_id
                if binance_id > 0 and huobi_id > 0:
                    binance_order = None
                    huobi_order = None
                    for order in binance_order_list:
                        if order['created-at'] < timestamp:
                            continue
                        if order['id'] == binance_id:
                            binance_order = order
                            break
                    for order in huobi_order_list:
                        if order['created-at'] < timestamp:
                            continue
                        if order['id'] == huobi_id:
                            huobi_order = order
                            break
                    if not binance_order or not huobi_order:
                        continue
                    # print binance_id, huobi_id
                    total_huobi_usdt_trade += float('%.8f' % float(huobi_order['field-cash-amount']))
                    total_binance_commission += float('%.8f' % float(binance_order['commission']))
                    total_btc_exchange += float('%.8f' % float(binance_order['field-amount']))

                    order = huobi_order
                    worksheet.write(row_1, col, datetime.datetime.fromtimestamp(
                        float(str(order['created-at'])[0:-3] + '.' + str(order['created-at'])[-3:0])),
                                    huobi_date_format)
                    worksheet.write(row_1, col + 1, order['side'], huobi_common_format)
                    worksheet.write(row_1, col + 2, order['type'], huobi_common_format)
                    worksheet.write(row_1, col + 3, '%.2f' % float(order['price']), huobi_common_format)
                    worksheet.write(row_1, col + 4, '%.4f' % float(order['amount']), huobi_common_format)
                    worksheet.write(row_1, col + 5, '%.8f' % float(order['field-amount']), huobi_common_format)
                    worksheet.write(row_1, col + 6, '%.8f' % float(order['field-cash-amount']), huobi_common_format)
                    worksheet.write(row_1, col + 7, order['state'], huobi_common_format)
                    row_1 += 1

                    order = binance_order
                    worksheet.write_datetime(row_1, col, datetime.datetime.fromtimestamp(
                        float(str(order['created-at'])[0:-3] + '.' + str(order['created-at'])[-3:0])),
                                             binance_date_format)
                    worksheet.write(row_1, col + 1, order['side'], binance_common_format)
                    worksheet.write(row_1, col + 2, order['type'], binance_common_format)
                    worksheet.write(row_1, col + 3, '%.2f' % float(order['price']), binance_common_format)
                    worksheet.write(row_1, col + 4, '%.4f' % float(order['amount']), binance_common_format)
                    worksheet.write(row_1, col + 5, '%.8f' % float(order['field-amount']), binance_common_format)
                    worksheet.write(row_1, col + 6, '%.8f' % float(order['field-cash-amount']),
                                    binance_common_format)
                    worksheet.write(row_1, col + 7, order['state'], binance_common_format)

                    earnings = abs(float(huobi_order['field-cash-amount']) - float(binance_order['field-cash-amount']))
                    earning_rate = earnings / binance_order['field-cash-amount']

                    total_usdt_earnings += earnings

                    worksheet.merge_range(row_1 - 1, 8, row_1, 8, '%.8f' % earnings, merged_format)
                    worksheet.merge_range(row_1 - 1, 9, row_1, 9, '%.8f' % earning_rate, merged_format)
                    row_1 += 1
                else:
                    for order in binance_order_list:
                        # print order['created-at']
                        if order['created-at'] < timestamp:
                            continue
                        if order['id'] == binance_id and order['field-amount'] > 0:
                            print order
                        if order['id'] == binance_id and 'CANCELED' in order['state']:
                            worksheet2.write_datetime(row_2, col, datetime.datetime.fromtimestamp(
                                float(str(order['created-at'])[0:-3] + '.' + str(order['created-at'])[-3:0])),
                                                      binance_date_format)
                            worksheet2.write(row_2, col + 1, order['side'], binance_common_format)
                            worksheet2.write(row_2, col + 2, order['type'], binance_common_format)
                            worksheet2.write(row_2, col + 3, 0, binance_common_format)
                            worksheet2.write(row_2, col + 4, '%.4f' % float(order['amount']), binance_common_format)
                            worksheet2.write(row_2, col + 5, 0, binance_common_format)
                            worksheet2.write(row_2, col + 6, 0, binance_common_format)
                            worksheet2.write(row_2, col + 7, order['state'], binance_common_format)
                            row_2 += 1
                            break
            total_huobi_commission = total_huobi_usdt_trade * 0.002
            total_binance_commission = total_binance_commission * 10.8159
            # print total_huobi_commission, total_binance_commission
            header = [u'BTC总量', u'BTC成交量', u'USDT总量' u'USDT盈亏', u'HUOBI手续费', u'BINANCE手续费']
            i = 0
            for h in header:
                worksheet3.write(row_3, i, h)
                i += 1
            row_3 += 1
            worksheet3.write(row_3, 0, total_btc_exchange)
            worksheet3.write(row_3, 1, total_usdt_earnings)
            worksheet3.write(row_3, 2, total_huobi_commission)
            worksheet3.write(row_3, 3, total_binance_commission)
        workbook.close()
        # 发送邮件
        self.logger.info('邮件通知')
        ArbitrageStratety.send_mail_with_attachment()

    @staticmethod
    def send_mail_with_attachment():
        from email import encoders
        from email.header import Header
        from email.mime.base import MIMEBase
        from email.mime.multipart import MIMEMultipart
        from email.utils import parseaddr, formataddr
        from email.mime.text import MIMEText
        import smtplib

        def _format_addr(s):
            name, addr = parseaddr(s)
            return formataddr(( \
                Header(name, 'utf-8').encode(), \
                addr.encode('utf-8') if isinstance(addr, unicode) else addr))

        from_addr = '*****@*****.**'
        username = '******'
        password = '******'
        to_addr = '*****@*****.**'
        smtp_server = 'smtp.163.com'

        print 'sending mail to [email protected]'
        msg = MIMEMultipart()
        msg['From'] = _format_addr(from_addr)
        msg['To'] = _format_addr(to_addr)
        msg['Subject'] = u'收益情况(%s)' % (time.strftime('%Y-%m-%d', time.localtime(time.time())))
        msg.attach(MIMEText('send with file...', 'plain', 'utf-8'))

        # add file:
        with open('output.xlsx', 'rb') as f:
            mime = MIMEBase('text', 'txt', filename='output.xlsx')
            mime.add_header('Content-Disposition', 'attachment', filename='output.xlsx')
            mime.add_header('Content-ID', '<0>')
            mime.add_header('X-Attachment-Id', '0')
            mime.set_payload(f.read())
            encoders.encode_base64(mime)
            msg.attach(mime)

        server = smtplib.SMTP()
        server.connect(smtp_server)
        server.login(username, password)
        server.sendmail(from_addr, [to_addr], msg.as_string())
        server.quit()