Exemplo n.º 1
0
    def sell(self, symbol, price=0, amount=0, volume=0):

        symbol = symbol.lower()

        if amount == 0 and volume == 0:
            raise TraderAPIError('do you want to sell amount/volume 0 ?')

        if volume == 0:
            hq = self.hq(symbol)
            price = hq.loc[symbol, 'lasttrade']
            volume = price * amount

        portfolio = self.portfolio
        # 计算一下当前持有该证券的市值
        current_market_value = 0
        if symbol in portfolio.index:
            current_market_value = portfolio.loc[symbol, 'market_value']

        target_volume = current_market_value - volume
        if target_volume < 0:
            raise TraderAPIError('Not enough %s for sell' % symbol)

        target_percent = target_volume / portfolio['market_value'].sum()
        return self._trade_api(symbol=symbol,
                               target_percent=target_percent,
                               portfolio=portfolio)
Exemplo n.º 2
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
Exemplo n.º 3
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
Exemplo n.º 4
0
    def login(self):
        self.pre_login()

        login_params = {
            'username': '',
            'areacode': '86',
            'telephone': self._account,
            'remember_me': '0',
            'password': self._password
        }

        # 提交登录信息
        r = self._session.post(url='https://xueqiu.com/user/login',
                               data=login_params)
        r.raise_for_status()
        login_info = r.json()

        # 错误检查,并处理
        if 'error_description' in login_info.keys():
            self._expire_at = 0
            logger.warning('Login Error: %s' % login_info['error_description'])
            raise TraderAPIError('Login Error: %s' %
                                 login_info['error_description'])

        # 登录成功
        logger.info('Login Success. uid: %s, expire_at: %s' %
                    (login_info['uid'], login_info['expires_in']))
        return
Exemplo n.º 5
0
    def buy(self, symbol, price=0, amount=0, volume=0):

        symbol = symbol.lower()

        if amount == 0 and volume == 0:
            raise TraderAPIError('do you want to buy amount/volume 0 ?')

        if volume == 0:
            hq = self.hq(symbol)
            price = hq.loc[symbol, 'lasttrade']
            volume = price * amount

        portfolio = self.portfolio
        net_asset = portfolio['market_value'].sum()

        # 计算交易股票的持仓目标比例
        if symbol in portfolio.index:
            target_percent = (portfolio.loc[symbol, 'market_value'] +
                              volume) / net_asset
        else:
            target_percent = volume / net_asset

        return self._trade_api(symbol=symbol,
                               target_percent=target_percent,
                               portfolio=portfolio)
Exemplo n.º 6
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'])
Exemplo n.º 7
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)
Exemplo n.º 8
0
    def _trade_api(self, **kwargs):
        url = 'https://trade.gf.com.cn/entry'
        resq = self.client.post(url, params=kwargs)
        data = resq.json()
        if data.get('success', False) == 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
Exemplo n.º 9
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
Exemplo n.º 10
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])
Exemplo n.º 11
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']
Exemplo n.º 12
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']
Exemplo n.º 13
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