Beispiel #1
0
    def _trade_api(self, **kwargs):

        # 确保已经正确登录了融资融券账号
        self._ensure_margin_flags()

        url = 'https://trade.gf.com.cn/entry'
        resq = self.client.post(url, params=kwargs)
        if len(resq.text) == 0:
            self.client.reset()
            resq = self.client.post(url, params=kwargs)

        data = resq.json()
        logger.debug('_trade_api() return: %s' % data)

        trade_status = data.pop('success', False)
        if trade_status == False:
            logger.error(data)
            error_info = data.get('error_info', data)
            raise TraderAPIError(error_info)

        df = pd.DataFrame(data['data'])

        df.rename(columns=RENAME_DICT, inplace=True)
        if 'symbol' in df.columns:
            df['symbol'] = df['symbol'].apply(code_to_symbols)

        # 去字段的并集,提高效率
        cols = list(set(FLOAT_COLUMNS).intersection(set(df.columns)))

        for col in cols:
            df[col] = pd.to_numeric(df[col], errors='ignore')
        return df
Beispiel #2
0
    def portfolio(self):

        logger.debug('call mystock_403 and mystock_405')
        p = self._worker.apply_async(self._trade_api,
                                     kwds={'request_id': 'mystock_403'})
        b = self._worker.apply_async(self._trade_api,
                                     kwds={'request_id': 'mystock_405'})
        p = p.get().copy()
        p.set_index('symbol', inplace=True)
        p = p[[
            'symbol_name', 'current_amount', 'enable_amount', 'lasttrade',
            'market_value'
        ]]

        b = b.get().copy()
        money_type = b['money_type'].iloc[0]
        current_amount = b['enable_balance'].iloc[0]

        p.loc['cash', 'symbol_name'] = money_type
        p.loc['cash', 'current_amount'] = current_amount
        p.loc['cash', 'enable_amount'] = current_amount
        p.loc['cash', 'lasttrade'] = 1.0
        p.loc['cash', 'market_value'] = current_amount

        p['weight'] = p['market_value'] / b['asset_balance'].iloc[0]
        p['weight'] = p['weight'].round(4)
        p = p.dropna(axis=0)

        return p
Beispiel #3
0
    def pre_login(self):
        '''
        初始化session,以及需要的headers
        :return:
        '''

        # session
        gfheader = {
            'Accept': '*/*',
            'Accept-Encoding': 'gzip, deflate',
            'Accept-Language': 'zh-Hans-CN, zh-Hans; q=0.5',
            'Connection': 'Keep-Alive',
            'Content-Type': 'application/x-www-form-urlencoded',
            'User-Agent':
            'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko',
            'X-Requested-With': 'XMLHttpRequest'
        }

        session = requests.session()
        session.headers.update(gfheader)
        resq = session.get('https://trade.gf.com.cn/')
        resq.raise_for_status()
        logger.debug('get trade home pages sucess.')

        self._expire_at = 0
        self._session = session
        return
Beispiel #4
0
    def _ensure_margin_flags(self):
        '''确保已经登录了融资融券账户'''
        if self.client.margin_flags == False:
            margin_login_params = {
                'classname': 'com.gf.etrade.control.RZRQUF2Control',
                'method': 'ValidataLogin'
            }

            r = self.client.post(url='https://trade.gf.com.cn/entry',
                                 params=margin_login_params)

            data = r.json()
            logger.debug('ensure_margin_flags: %s' % data)

            trade_status = data.pop('success', False)
            if trade_status == False:
                logger.error(data)
                error_info = data.get('error_info', data)
                raise TraderAPIError(error_info)

            stockholders = data.get('stockholders', [])
            self._exchange_stock_account = {}
            for holders in stockholders:
                self._exchange_stock_account[
                    holders['exchange_type']] = holders['stock_account']

            # 将session 设置为已经登录信用账户的状态
            self.client.margin_flags = True

            return
Beispiel #5
0
 def request(self, method, url, **kwargs):
     '''
     调用session的各类http方法
     '''
     logger.debug('Call params: %s' % kwargs)
     with self:
         resq = self.session.request(method=method, url=url, **kwargs)
         resq.raise_for_status()
         logger.debug('return: %s' % resq.text)
         self._expire_at = time.time() + _TIMEOUT
     return resq
Beispiel #6
0
    def order(self, symbol, amount=0, volume=0, weight=0, portfolio=None):

        symbol = symbol.lower()

        logger.info(
            'order: symbol(%s), amount(%.2f), volume(%.2f), weight(%.4f)' %
            (symbol, amount, volume, weight))

        if amount == 0 and volume == 0 and weight == 0:
            return 0

        if portfolio is None:
            portfolio = self.portfolio

        if amount != 0:
            if symbol in portfolio.index:
                volume = portfolio.loc[symbol, 'lasttrade'] * amount
                target_volume = portfolio.loc[symbol, 'market_value'] + volume
                target_weight = target_volume / portfolio['market_value'].sum()
                weight = target_weight - portfolio.loc[symbol, 'weight']
            else:
                lasttrade = self.hq(symbol).loc[symbol, 'lasttrade']
                target_volume = amount * lasttrade
                target_weight = target_volume / portfolio['market_value'].sum()
                weight = target_weight
        elif volume != 0:
            if symbol in portfolio.index:
                target_volume = portfolio.loc[symbol, 'market_value'] + volume
                target_weight = target_volume / portfolio['market_value'].sum()
                weight = target_weight - portfolio.loc[symbol, 'weight']
            else:
                target_volume = volume
                target_weight = target_volume / portfolio['market_value'].sum()
                weight = target_weight
        else:
            if symbol in portfolio.index:
                target_weight = portfolio.loc[symbol, 'weight'] + weight
            else:
                target_weight = weight

        if target_weight > 1 or target_weight < 0:
            raise AttributeError(
                'order: symbol(%s), amount(%.2f), volume(%.2f), weight(%.4f)' %
                (symbol, amount, volume, weight))

        if weight > 0.001 or weight < -0.001:
            logger.debug('target_weight: %s' % round(target_weight, 4))
            self._trade_api(symbol=symbol,
                            target_percent=round(target_weight, 4),
                            portfolio=portfolio)
        else:
            logger.info('权重变化过小,无需下单: %s' % weight)
        return 0
Beispiel #7
0
    def request(self, method, url, **kwargs):

        with self:
            params = kwargs.get('params', {})
            params.update({'dse_sessionId': self._dse_sessionId})
            kwargs['params'] = params

            logger.debug('Call params: %s' % kwargs)
            resq = self._session.request(method=method, url=url, **kwargs)
            resq.raise_for_status()
            logger.debug('return: %s' % resq.text)
            self._expire_at = time.time() + TIMEOUT
        return resq
Beispiel #8
0
    def keepalive(self, now=0):
        '''
        自动保持连接的函数
        '''
        if now == 0:
            now = time.time()

        logger.debug('keepalive checking. now: %s, expire_at: %s' %
                     (now, self.expire_at))
        if now + 60 > self.expire_at:
            self.portfolio
            logger.info('Reflash the expire time, expire_at timestamp is: %s' %
                        self.expire_at)

        return
Beispiel #9
0
    def login(self):

        self.pre_login()

        login_params = {
            "function_id": 200,
            "login_type": "stock",
            "version": 200,
            "identity_type": "",
            "remember_me": "",
            "input_content": 1,
            "content_type": 0,
            "loginPasswordType": "B64",
            "disk_serial_id": self.disk_serial_id,
            "cpuid": self.cpuid,
            "machinecode": self.machinecode,
            "mac_addr": self.mac_address,
            "account_content": self._account,
            "password": urllib.parse.unquote(self._password),
            "validateCode": self.vcode
        }
        logger.debug('login_params is: %s' % login_params)

        r = self._session.post(
            'https://jy.yongjinbao.com.cn/winner_gj/gjzq/exchange.action',
            params=login_params)
        r.raise_for_status()

        logger.debug('Login respone: %s' % r.text)

        returnJson = r.json()['returnJson']
        data = demjson.decode(returnJson)
        error_msg = dict()
        if data['msg_no'] != '0':
            if 'msg_info' in data.keys() and data['msg_info'] != '':
                error_msg['error_info'] = data['msg_info']
            else:
                error_msg = data[data['error_grids']][1]

            if error_msg['error_info'].find('验证码') != -1:
                logger.warning('vcode error : %s' % error_msg['error_info'])
                raise VerifyCodeError(error_msg['error_info'])
            else:
                logger.error('login Failed :%s' % error_msg['error_info'])
                raise LoginFailedError(error_msg['error_info'])

        return
Beispiel #10
0
    def vcode(self):

        r = self._session.get(
            'https://jy.yongjinbao.com.cn/winner_gj/gjzq/user/extraCode.jsp',
            params={'randomStamp': random.random()})
        r.raise_for_status()

        # 通过内存保存数据
        img_buffer = BytesIO(r.content)
        img = Image.open(img_buffer)
        code = pytesseract.image_to_string(img)
        img.close()
        img_buffer.close()

        if self.code_rule.findall(code) == []:
            raise VerifyCodeError('Wrong verify code: %s' % code)
        else:
            logger.debug('Verify Code is: %s' % code)
            return code
Beispiel #11
0
    def post_login(self):

        if self.margin_flags == True:
            margin_login_params = {
                'classname': 'com.gf.etrade.control.RZRQUF2Control',
                'method': 'ValidataLogin',
                'dse_sessionId': self._dse_sessionId
            }

            r = self._session.post(url='https://trade.gf.com.cn/entry',
                                   params=margin_login_params)

            data = r.json()
            logger.debug('ensure_margin_flags: %s' % data)

            trade_status = data.pop('success', False)
            if trade_status == False:
                logger.error(data)
                error_info = data.get('error_info', data)
                raise TraderAPIError(error_info)
Beispiel #12
0
    def portfolio(self):

        url = 'https://xueqiu.com/p/' + self.portfolio_code
        r = self.client.get(url)
        r.raise_for_status()

        # 查找持仓的字符串段
        html = r.text
        pos_start = html.find('SNB.cubeInfo = ') + len('SNB.cubeInfo = ')
        pos_end = html.find('SNB.cubePieData')
        json_data = to_text(html[pos_start:pos_end - 2])
        logger.debug(json_data)
        p_info = json.decode(json_data, encoding='utf-8')

        # 修复雪球持仓错误
        positions = p_info['last_success_rebalancing']['holdings']
        logger.debug(p_info)

        df = pd.DataFrame(positions)
        df.rename(columns=_RENAME_DICT, inplace=True)
        df['symbol'] = df['symbol'].str.lower()
        df = df.set_index('symbol')
        hq = self.hq(df.index)
        df['lasttrade'] = hq['lasttrade']

        df.loc['cash', 'symbol_name'] = '人民币'
        df.loc['cash',
               'current_amount'] = p_info['view_rebalancing']['cash_value']
        df.loc['cash', 'lasttrade'] = 1.0

        df['current_amount'] = df['current_amount'] * _BASE_MULTIPE
        df['enable_amount'] = df['current_amount']
        df['market_value'] = df['current_amount'] * df['lasttrade']
        net_value = df['market_value'].sum()
        df['weight'] = (df['market_value'] / net_value).round(4)

        return df[[
            'symbol_name', 'current_amount', 'enable_amount', 'lasttrade',
            'market_value', 'weight'
        ]]
Beispiel #13
0
    def _trade_api(self, **kwargs):
        '''
        底层交易接口
        '''

        logger.debug('call params: %s' % kwargs)
        r = self.client.get(
            url=
            'https://jy.yongjinbao.com.cn/winner_gj/gjzq/stock/exchange.action',
            params=kwargs)
        logger.debug('return: %s' % r.text)

        # 解析返回的结果数据
        returnJson = r.json()['returnJson']
        if returnJson is None:
            return None

        data = demjson.decode(returnJson)
        if data['msg_no'] != '0':
            error_msg = data[data['error_grids']][1]
            logger.error('error no: %s,error info: %s' % (error_msg.get(
                'error_no', ''), error_msg.get('error_info', '')))
            raise TraderAPIError(error_msg.get('error_info', ''))

        data = data['Func%s' % data['function_id']]
        df = pd.DataFrame(data[1:])

        # 替换表头的命名
        df.rename(columns=RENAME_DICT, inplace=True)
        # 生成symbol
        if 'symbol' in df.columns:
            df['symbol'] = df['symbol'].apply(code_to_symbols)
        # FLOAT_COLUMNS和 df.columns取交集,以减少调用时间
        cols = list(set(FLOAT_COLUMNS).intersection(set(df.columns)))

        for col in cols:
            df[col] = pd.to_numeric(df[col], errors='ignore')

        return df
Beispiel #14
0
    def login(self):

        # 无论是否登录,都重新创建一个session对象
        # self.pre_login()

        login_params = {
            "authtype": 2,
            "disknum": self.disknum,
            "loginType": 2,
            "origin": "web",
            'mac': self.mac_address,
            'username': self._account,
            'password': self._password,
            'tmp_yzm': self.vcode
        }
        resq = self._session.post(url='https://trade.gf.com.cn/login',
                                  params=login_params)
        resq.raise_for_status()
        logger.debug('login resq: %s' % resq.json())

        data = resq.json()
        if data['success'] == True:
            v = resq.headers
            self._dse_sessionId = v['Set-Cookie'][-32:]
            # 等待服务器准备就绪
            time.sleep(0.1)
            logger.info('Login success: %s' % self._dse_sessionId)
            return
        elif data['success'] == False and 'error_info' not in data.keys():
            logger.warning('当前系统无法登陆')
            raise TraderAPIError(data)
        elif data['error_info'].find('验证码') != -1:
            self.dse_sessionId = None
            logger.warning('VerifyCode Error: %s' % data)
            raise VerifyCodeError(data['error_info'])
        else:
            self.dse_sessionId = None
            logger.warning('API Login Error: %s' % data)
            raise TraderAPIError(data['error_info'])
Beispiel #15
0
    def vcode(self):

        # 获取校验码
        r = self._session.get('https://trade.gf.com.cn/yzm.jpgx')
        r.raise_for_status()

        # 通过内存保存图片,进行识别
        img_buffer = BytesIO(r.content)
        img = Image.open(img_buffer)
        if hasattr(img, "width"):
            width, height = img.width, img.height
        else:
            width, height = img.size
        for x in range(width):
            for y in range(height):
                if img.getpixel((x, y)) < (100, 100, 100):
                    img.putpixel((x, y), (256, 256, 256))

        gray = img.convert('L')
        two = gray.point(lambda x: 0 if 68 < x < 90 else 256)
        min_res = two.filter(ImageFilter.MinFilter)
        med_res = min_res.filter(ImageFilter.MedianFilter)
        for _ in range(1):
            med_res = med_res.filter(ImageFilter.MedianFilter)

        # 通过tesseract-ocr的工具进行校验码识别
        vcode = pytesseract.image_to_string(med_res)
        img.close()
        img_buffer.close()

        vcode = vcode.replace(' ', '')
        if self.code_rule.findall(vcode) != []:
            logger.debug('vcode is: %s' % vcode)
            return vcode
        else:
            raise VerifyCodeError('verify code error: %s' % vcode)
Beispiel #16
0
    def __new__(cls, account, password):
        '''
        创建loginSession类时,如果同一券商的账号密码都一样时,只创建一次
        '''

        logger.debug('LoginType: %s, account: %s, password: %s' %
                     (type(cls), account, password))

        # cls, account, password 是用MD5进行创建关键字
        m = hashlib.md5()
        m.update(str(type(cls)).encode('utf-8'))
        m.update(account.encode('utf-8'))
        m.update(password.encode('utf-8'))
        keyword = m.hexdigest()

        obj = cls._objects.get(keyword, None)
        logger.debug('keyword: %s, obj: %s' % (keyword, obj))
        if obj is None:
            # 如果没有缓存过此对象,就创建,并进行缓存
            logger.debug('缓存内没有对象,重新创建一个对象')
            obj = super(LoginSession, cls).__new__(cls)
            cls._objects[keyword] = obj

        return obj
Beispiel #17
0
    def orderlist(self):

        order_col = [
            'order_no', 'symbol', 'symbol_name', 'trade_side', 'order_price',
            'order_amount', 'business_price', 'business_amount',
            'order_status', 'order_time'
        ]

        p = {"cube_symbol": self.portfolio_code, 'count': 5, 'page': 1}

        resq = self.client.get(
            url='https://xueqiu.com/cubes/rebalancing/history.json',
            params=p,
            headers={})
        logger.debug(resq.text)
        resq.raise_for_status()
        data = resq.json()['list']
        logger.debug('get_entrust raw data: %s' % data)

        order_list = []
        for xq_orders in data:
            status = xq_orders['status']  # 调仓状态
            if status == 'pending':
                status = "已报"
            elif status == 'canceled':
                status = "已撤"
            elif status == 'failed':
                status = "废单"
            elif status == 'success':
                status = "已成"
            else:
                raise TraderAPIError('Unkown order status. %s' % status)

            for order in xq_orders['rebalancing_histories']:
                prev_target_volume = order['prev_target_volume'] if order[
                    'prev_target_volume'] is not None else 0.0
                target_volume = order['target_volume'] if order[
                    'target_volume'] else 0.0
                # 实际上应该是这里通常说得amount
                volume = abs(target_volume -
                             prev_target_volume) * _BASE_MULTIPE
                price = order['price'] if order['price'] else 0.0
                if volume > 0:
                    order_list.append({
                        'order_no':
                        order['id'],
                        'symbol':
                        order['stock_symbol'].lower(),
                        'symbol_name':
                        order['stock_name'],
                        'trade_side':
                        "买入" if target_volume > prev_target_volume else "卖出",
                        'order_status':
                        status,
                        'order_time':
                        time.strftime(
                            "%Y-%m-%d %H:%M:%S",
                            time.localtime(order['updated_at'] / 1000)),
                        'business_amount':
                        volume * price if status == '已成' else 0,
                        'business_price':
                        price if status == '已成' else 0,
                        'order_amount':
                        volume * price,
                        'order_price':
                        price,
                    })
        df = pd.DataFrame(order_list, columns=order_col)
        df = df.set_index('order_no')
        df[['order_price', 'order_amount', 'business_amount', 'business_price']] = \
            df[['order_price', 'order_amount', 'business_amount', 'business_price']].round(3)

        return df
Beispiel #18
0
    def cancel(self, order_no=0):

        portfolio = self.portfolio

        comment = '撤单\n 来自vxTrader'

        stock_infos = self._worker.imap(
            self._get_stock_info,
            portfolio.loc[portfolio.index != 'cash'].index)

        holdings = []
        for stock in stock_infos:
            holding = {
                "code":
                stock['code'],
                "flag":
                stock['flag'],
                "type":
                stock['type'],
                "stock_id":
                stock['stock_id'],
                "ind_id":
                stock['ind_id'],
                "ind_name":
                stock['ind_name'],
                "ind_color":
                stock['ind_color'],
                "textname":
                stock['name'],
                "segment_name":
                stock['ind_name'],
                # 注意此处雪球接受的是持仓百分比,例如:30.33
                "weight":
                round(portfolio.loc[stock['code'].lower(), 'weight'] * 100, 2),
                "proactive":
                True,
                "price":
                str(stock['current'])
            }

            holdings.append(holding)

        params = {
            # 注意此处雪球接受的是持仓百分比,例如:30.33
            'cash': round(portfolio.loc['cash', 'weight'] * 100, 2),
            'holdings': str(json.encode(holdings)),
            'cube_symbol': self.portfolio_code,
            'segment': 1,
            'comment': comment
        }

        self.client.headers.update(
            referer='https://xueqiu.com/p/update?action=holdings&symbol=%s' %
            self.portfolio_code)
        logger.debug(self.client.session.headers)

        try:
            r = self.client.post(
                url='https://xueqiu.com/cubes/rebalancing/create.json',
                params=params)
            r.raise_for_status()
        except Exception as err:
            logger.warning('order failed: %s' % err)
            raise TraderAPIError(str(err))

        logger.debug('order success:%s %s' % (holdings, r.json()))
        return r.json()['id']
Beispiel #19
0
    def _trade_api(self, symbol, target_percent, portfolio=None, comment=None):

        # 目标的持仓比例应该大于0
        if target_percent < 0:
            raise TraderAPIError('wrong target_percent: %s' % target_percent)

        # symbol 转换成小写
        symbol = symbol.lower()

        if comment is None:
            comment = '将股票( %s )的仓位调整至 %.2f%% . \n 来自vxTrader' % (
                symbol, target_percent * 100)

        # 如果没有穿portfolio,就更新一下
        if portfolio is None:
            portfolio = self.portfolio

        portfolio.loc[symbol, 'weight'] = target_percent
        market_weight = portfolio.loc[portfolio.index != 'cash',
                                      'weight'].sum()
        if market_weight > 1.0:
            raise TraderAPIError(
                'wrong target_percent: %s, market_weight is: %s' %
                (target_percent, market_weight))

        portfolio.loc['cash', 'weight'] = 1.0 - market_weight

        stock_infos = self._worker.imap(
            self._get_stock_info,
            portfolio.loc[portfolio.index != 'cash'].index)

        holdings = []
        for stock in stock_infos:
            proactive = (stock['code'].lower() == symbol)
            holding = {
                "code":
                stock['code'],
                "flag":
                stock['flag'],
                "type":
                stock['type'],
                "stock_id":
                stock['stock_id'],
                "ind_id":
                stock['ind_id'],
                "ind_name":
                stock['ind_name'],
                "ind_color":
                stock['ind_color'],
                "textname":
                stock['name'],
                "segment_name":
                stock['ind_name'],
                # 注意此处雪球接受的是持仓百分比,例如:30.33
                "weight":
                round(portfolio.loc[stock['code'].lower(), 'weight'] * 100, 2),
                "proactive":
                proactive,
                "price":
                str(stock['current'])
            }

            holdings.append(holding)

        params = {
            # 注意此处雪球接受的是持仓百分比,例如:30.33
            'cash': round(portfolio.loc['cash', 'weight'] * 100, 2),
            'holdings': str(json.encode(holdings)),
            'cube_symbol': self.portfolio_code,
            'segment': 1,
            'comment': comment
        }

        self.client.headers.update(
            referer='https://xueqiu.com/p/update?action=holdings&symbol=%s' %
            self.portfolio_code)
        logger.debug(self.client.session.headers)

        try:
            r = self.client.post(
                url='https://xueqiu.com/cubes/rebalancing/create.json',
                params=params)
            r.raise_for_status()
        except Exception as err:
            logger.warning('order failed: %s' % err)
            raise TraderAPIError(str(err))

        logger.debug('order success:%s %s' % (holdings, r.json()))
        return r.json()['id']
Beispiel #20
0
    def order(self, symbol, amount=0, volume=0, wait=10):
        '''
        按数量下单
        :return: order_no, left
        '''
        logger.debug(
            'order_amount: symbol: %s, amount: %s, volume: %s, wait: %s' %
            (symbol, amount, volume, wait))

        if (amount == 0 and volume == 0):
            raise AttributeError('order_amount amount and volume can not be 0')

        # 下单
        try:
            hq = self.hq(symbol)
            logger.debug('hq: %s' % hq.loc[symbol])
            price = hq.loc[symbol, 'lasttrade']
            amount = amount if amount else round(volume,
                                                 2) // price // 100 * 100
            if amount == 0:
                return 0, 0

            if amount > 0 or volume > 0:
                price = hq.loc[symbol, 'ask']
                order_no = self.buy(symbol, price, amount=amount)
                logger.info('buy order send,order_no: %s' % order_no)
            elif amount < 0 or volume < 0:
                price = hq.loc[symbol, 'bid']
                order_no = self.sell(symbol, price, amount=-amount)
                logger.info('sell order send,order_no: %s' % order_no)

        except TraderError as err:
            logger.debug('Order Error: %s' % err)
            raise err

        # 每隔2秒检查一下成交状态.
        # 如果是已成交,则返回order_no, 0
        # 如果是已报、部成, 则再等2秒钟。
        # 如果是其他状态,就报警
        time.sleep(5)
        for i in range(int((wait + 1) / 2)):
            logger.info('Check Order Status %s times.' % i)
            orderlist = self.orderlist
            if order_no in orderlist.index:
                status = orderlist.loc[order_no, 'order_status']
            else:
                # 如果记录的订单号不在orderlist里面,则认为已经成交了。
                status = '已成'

            if status in ('已成'):
                logger.info('Order Success. %s' % orderlist.loc[order_no])
                return order_no, 0

            elif status in ('已报', '部成', '正常'):
                logger.info('Order not Complete. %s' % orderlist.loc[order_no])
                time.sleep(5)

            elif status in ('未报'):
                logger.info('Not Allow to Send Order. %s' %
                            orderlist.loc[order_no])
                self.cancel(order_no)
                return order_no, amount
            else:
                logger.error('Order Status Invaild. %s' %
                             orderlist.loc[order_no])
                raise TraderAPIError('Order Status Invaild. %s' %
                                     orderlist.loc[order_no])

        # 等待了足够时间,仍未全部成交,则撤单
        try:
            logger.warning('Cancel order: %s' % order_no)
            self.cancel(order_no)
            time.sleep(0.3)
            orderlist = self.orderlist
            status = orderlist.loc[order_no, 'order_status']
            if status in ('已撤', '部撤'):
                orderlist['left'] = orderlist['order_amount'] - orderlist[
                    'business_amount']
                left = orderlist.loc[order_no, 'left']
                if amount < 0:
                    left = -left
                return order_no, left
            else:
                raise TraderAPIError('Order Status Invaild. %s' %
                                     orderlist.loc[order_no])

        except TraderError as err:
            logger.warning(err)
            logger.warning('Order Status Invaild. %s' %
                           orderlist.loc[order_no])