text: # -*- coding:utf-8 -*-
  
  """
  获取基金净值数据接口 
  Created on 2016/04/03
  @author: leo
  @group : lazytech
  @contact: lazytech@sina.cn
  """
  
  from __future__ import division
  import time
  import json
  import re
  import pandas as pd
  import numpy as np
  from tushare.fund import cons as ct
  from tushare.fund import parser
  from tushare.util import dateu as du
  from tushare.util.exception import RequestError, UnkownError, Not200Error, ParseError
  from tushare.util import httputil
  from urllib.request import urlopen, Request
  from urllib.error import URLError
  
  def get_nav_open(fund_type='all'):
      """
          获取开放型基金净值数据
      Parameters
      ------
          type:string
              开放基金类型:
                  1. all 		所有开放基金
                  2. equity	股票型开放基金
                  3. mix 		混合型开放基金
                  4. bond		债券型开放基金
                  5. monetary	货币型开放基金
                  6. qdii		QDII型开放基金
       return
       -------
          DataFrame
              开放型基金净值数据(DataFrame):
                  symbol      基金代码
                  sname       基金名称
                  per_nav     单位净值
                  total_nav   累计净值
                  yesterday_nav  前一日净值
                  nav_a       涨跌额
                  nav_rate    增长率(%)
                  nav_date    净值日期
                  fund_manager 基金经理
                  jjlx        基金类型
                  jjzfe       基金总份额
      """
      if ct._check_nav_oft_input(fund_type) is True:
          ct._write_head()
          nums = _get_fund_num(ct.SINA_NAV_COUNT_URL %
                               (ct.P_TYPE['http'], ct.DOMAINS['vsf'],
                                ct.NAV_OPEN_KEY[fund_type],
                                ct.NAV_OPEN_API[fund_type],
                                ct.NAV_OPEN_T2[fund_type],
                                ct.NAV_OPEN_T3))
  
          pages = 2  # 分两次请求数据
          limit_cnt = int(nums/pages)+1   # 每次取的数量
          fund_dfs = []
          for page in range(1, pages+1):
              fund_df = _parse_fund_data(ct.SINA_NAV_DATA_URL %
                                         (ct.P_TYPE['http'], ct.DOMAINS['vsf'],
                                          ct.NAV_OPEN_KEY[fund_type],
                                          ct.NAV_OPEN_API[fund_type],
                                          page,
                                          limit_cnt,
                                          ct.NAV_OPEN_T2[fund_type],
                                          ct.NAV_OPEN_T3))
              fund_dfs.append(fund_df)
  
          return pd.concat(fund_dfs, ignore_index=True)
  
  
  def get_nav_close(fund_type='all', sub_type='all'):
      """
          获取封闭型基金净值数据
      Parameters
      ------
          type:string
              封闭基金类型:
                  1. all      所有封闭型基金
                  2. fbqy     封闭-权益
                  3. fbzq     封闭债券
  
          sub_type:string
              基金子类型:
  
                  1. type=all sub_type无效
                  2. type=fbqy 封闭-权益
                      *all    全部封闭权益
                      *ct     传统封基
                      *cx     创新封基
  
                  3. type=fbzq  封闭债券
                      *all    全部封闭债券
                      *wj     稳健债券型
                      *jj     激进债券型
                      *cz     纯债债券型
       return
       -------
          DataFrame
              开放型基金净值数据(DataFrame):
                  symbol      基金代码
                  sname       基金名称
                  per_nav     单位净值
                  total_nav   累计净值
                  nav_rate    增长率(%)
                  discount_rate 折溢价率(%)
                  nav_date    净值日期
                  start_date  成立日期
                  end_date    到期日期
                  fund_manager 基金经理
                  jjlx        基金类型
                  jjzfe       基金总份额
      """
      ct._write_head()
      nums = _get_fund_num(ct.SINA_NAV_COUNT_URL %
                           (ct.P_TYPE['http'], ct.DOMAINS['vsf'],
                            ct.NAV_CLOSE_KEY, ct.NAV_CLOSE_API,
                            ct.NAV_CLOSE_T2[fund_type],
                            ct.NAV_CLOSE_T3[sub_type]))
  
      fund_df = _parse_fund_data(ct.SINA_NAV_DATA_URL %
                                 (ct.P_TYPE['http'], ct.DOMAINS['vsf'],
                                  ct.NAV_OPEN_KEY, ct.NAV_CLOSE_API, 
                                  ct.NAV_DEFAULT_PAGE,
                                  nums,
                                  ct.NAV_CLOSE_T2[fund_type],
                                  ct.NAV_CLOSE_T3[sub_type]),
                                 'close')
      return fund_df
  
  
  def get_nav_grading(fund_type='all', sub_type='all'):
      """
          获取分级子基金净值数据
      Parameters
      ------
          type:string
              封闭基金类型:
                  1. all      所有分级基金
                  2. fjgs     分级-固收
                  3. fjgg     分级-杠杆
  
          sub_type:string
              基金子类型(type=all sub_type无效):
                  *all    全部分级债券
                  *wjzq   稳健债券型
                  *czzq   纯债债券型
                  *jjzq   激进债券型
                  *gp     股票型
                  *zs     指数型
       return
       -------
          DataFrame
              开放型基金净值数据(DataFrame):
                  symbol      基金代码
                  sname       基金名称
                  per_nav     单位净值
                  total_nav   累计净值
                  nav_rate    增长率(%)
                  discount_rate 折溢价率(%)
                  nav_date    净值日期
                  start_date  成立日期
                  end_date    到期日期
                  fund_manager 基金经理
                  jjlx        基金类型
                  jjzfe       基金总份额
      """
      ct._write_head()
      nums = _get_fund_num(ct.SINA_NAV_COUNT_URL %
                           (ct.P_TYPE['http'], ct.DOMAINS['vsf'],
                            ct.NAV_GRADING_KEY, ct.NAV_GRADING_API,
                            ct.NAV_GRADING_T2[fund_type],
                            ct.NAV_GRADING_T3[sub_type]))
  
      fund_df = _parse_fund_data(ct.SINA_NAV_DATA_URL %
                                 (ct.P_TYPE['http'], ct.DOMAINS['vsf'],
                                  ct.NAV_GRADING_KEY, ct.NAV_GRADING_API, 
                                  ct.NAV_DEFAULT_PAGE,
                                  nums,
                                  ct.NAV_GRADING_T2[fund_type],
                                  ct.NAV_GRADING_T3[sub_type]),
                                 'grading')
      return fund_df
  
  
  def get_nav_history(code, start=None, end=None, retry_count=3, pause=0.001, timeout=10):
      '''
      获取历史净值数据
      Parameters
      ------
        code:string
                    基金代码 e.g. 000001
        start:string
                    开始日期 format：YYYY-MM-DD 为空时取当前日期
        end:string
                    结束日期 format：YYYY-MM-DD 为空时取去年今日
        retry_count : int, 默认 3
                   如遇网络等问题重复执行的次数
        pause : int, 默认 0
                  重复请求数据过程中暂停的秒数，防止请求间隔时间太短出现的问题
        timeout: int 默认 10s
                  请求大量数据时的网络超时
      return
      -------
        DataFrame
            date 发布日期 (index)
            value 基金净值(股票/混合/QDII型基金) / 年华收益(货币/债券基金)
            total 累计净值(股票/混合/QDII型基金) / 万分收益(货币/债券基金)
            change 净值增长率(股票/混合/QDII型基金)
      '''
      start = du.today_last_year() if start is None else start
      end = du.today() if end is None else end
  
      # 判断基金类型
      ismonetary = False  # 是否是债券型和货币型基金
      df_fund = get_fund_info(code)
  
      fund_type = df_fund.iloc[0]['Type2Name']
      if (fund_type.find(u'债券型') != -1) or (fund_type.find(u'货币型') != -1):
          ismonetary = True
  
      ct._write_head()
      nums = _get_nav_histroy_num(code, start, end, ismonetary)
      data = _parse_nav_history_data(
          code, start, end, nums, ismonetary, retry_count, pause, timeout)
      return data
  
  
  def get_fund_info(code):
      '''
      获取基金基本信息
      Parameters
      ------
        code:string
                    基金代码 e.g. 000001
      return
      -------
        DataFrame
            jjqc      基金全称
            jjjc      基金简称
            symbol    基金代码
            clrq      成立日期
            ssrq      上市日期
            xcr       存续期限
            ssdd      上市地点
            Type1Name 运作方式
            Type2Name 基金类型
            Type3Name 二级分类
            jjgm      基金规模(亿元)
            jjfe      基金总份额(亿份)
            jjltfe    上市流通份额(亿份)
            jjferq    基金份额日期
            quarter   上市季度
            glr       基金管理人
            tgr       基金托管人
      '''
      request = ct.SINA_FUND_INFO_URL % (
          ct.P_TYPE['http'], ct.DOMAINS['ssf'], code)
      text = httputil.get(request)
      text = text.decode('gbk')
      org_js = json.loads(text)
  
      status_code = int(org_js['result']['status']['code'])
      if status_code != 0:
          status = str(org_js['result']['status']['msg'])
          raise Not200Error(status)
      data = org_js['result']['data']
      fund_df = pd.DataFrame(data, columns=ct.FUND_INFO_COLS, index=[0])
      fund_df = fund_df.set_index('symbol')
  
      return fund_df
  
  
  def _parse_fund_data(url, fund_type='open'):
  
      url = url.replace(' ', '')
      text = httputil.get(url)
      result = parser.parse_fund_data(text)
      fund_df = pd.DataFrame(result,
                             columns=ct.NAV_COLUMNS[fund_type])
      fund_df.fillna(0, inplace=True)
      return fund_df
  
  
  
  def _get_fund_num(url):
      """
          获取基金数量
      """
      text = httputil.get(url)
      nums = parser.parse_fund_count(text)
      return nums
  
  
  
  def _get_nav_histroy_num(code, start, end, ismonetary=False):
      """
          获取基金历史净值数量
  
      --------
          货币和证券型基金采用的url不同，需要增加基金类型判断
      """
  
      ct._write_console()
  
      if ismonetary:
          request = Request(ct.SINA_NAV_HISTROY_COUNT_CUR_URL %
                            (ct.P_TYPE['http'], ct.DOMAINS['ssf'],
                             code, start, end))
      else:
          request = Request(ct.SINA_NAV_HISTROY_COUNT_URL %
                            (ct.P_TYPE['http'], ct.DOMAINS['ssf'],
                             code, start, end))
  
      text = httputil.get(request)
      text = text.decode('gbk')
      org_js = json.loads(text)
      status_code = int(org_js['result']['status']['code'])
      if status_code != 0:
          status = str(org_js['result']['status']['msg'])
          raise Not200Error(status)
      nums = org_js['result']['data']['total_num']
  
      return int(nums)
  
  
  def _parse_nav_history_data(code, start, end, nums, ismonetary=False, retry_count=3, pause=0.01, timeout=10):
      if nums == 0:
          return None
  
      for _ in range(retry_count):
          time.sleep(pause)
          # try:
          ct._write_console()
  
          if ismonetary:
              request = Request(ct.SINA_NAV_HISTROY_DATA_CUR_URL %
                                (ct.P_TYPE['http'], ct.DOMAINS['ssf'],
                                 code, start, end, nums))
          else:
              request = Request(ct.SINA_NAV_HISTROY_DATA_URL %
                                (ct.P_TYPE['http'], ct.DOMAINS['ssf'],
                                 code, start, end, nums))
          text = httputil.get(request, timeout=timeout)
          text = text.decode('gbk')
          org_js = json.loads(text)
  
          status_code = int(org_js['result']['status']['code'])
          if status_code != 0:
              status = str(org_js['result']['status']['msg'])
              raise Not200Error(status)
  
          data = org_js['result']['data']['data']
  
          if 'jjjz' in data[0].keys():
              fund_df = pd.DataFrame(data, columns=ct.NAV_HIS_JJJZ)
              fund_df['jjjz'] = fund_df['jjjz'].astype(float)
              fund_df['ljjz'] = fund_df['ljjz'].astype(float)
              fund_df.rename(columns=ct.DICT_NAV_EQUITY, inplace=True)
  
          else:
              fund_df = pd.DataFrame(data, columns=ct.NAV_HIS_NHSY)
              fund_df['nhsyl'] = fund_df['nhsyl'].astype(float)
              fund_df['dwsy'] = fund_df['dwsy'].astype(float)
              fund_df.rename(columns=ct.DICT_NAV_MONETARY, inplace=True)
  
          #fund_df.fillna(0, inplace=True)
  
          if fund_df['date'].dtypes == np.object:
              fund_df['date'] = pd.to_datetime(fund_df['date'])
          fund_df = fund_df.set_index('date')
          fund_df = fund_df.sort_index(ascending=False)
  
          fund_df['pre_value'] = fund_df['value'].shift(-1)
          fund_df['change'] = (fund_df['value'] / fund_df['pre_value'] - 1) * 100
          fund_df = fund_df.drop('pre_value', axis=1)
  
          return fund_df
  
      raise IOError(ct.NETWORK_URL_ERROR_MSG)
  