gitee.com/quant1x/engine@v1.8.4/datasource/dfcf/shareholder.go (about)

     1  package dfcf
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"gitee.com/quant1x/exchange"
     7  	"gitee.com/quant1x/gox/api"
     8  	"gitee.com/quant1x/gox/http"
     9  	"gitee.com/quant1x/gox/logger"
    10  	"gitee.com/quant1x/pkg/fastjson"
    11  	urlpkg "net/url"
    12  	"strings"
    13  )
    14  
    15  const (
    16  	errorCapitalBase                    = 90000 // 股本异常错误码基础值
    17  	urlEastmoneyGdfxHoldingAnalyse      = "https://datacenter-web.eastmoney.com/api/data/v1/get"
    18  	EastmoneyGdfxHoldingAnalysePageSize = 500
    19  )
    20  
    21  type HoldNumChangeState = int
    22  
    23  const (
    24  	HoldNumDampened       HoldNumChangeState = -1 // 减少
    25  	HoldNumUnChanged      HoldNumChangeState = 0  // 不变
    26  	HoldNumNewlyAdded     HoldNumChangeState = 1  // 新进/新增
    27  	HoldNumIncrease       HoldNumChangeState = 2  // 增加
    28  	HoldNumUnknownChanges HoldNumChangeState = -9 // 未知变化
    29  )
    30  
    31  // CirculatingShareholder Top10CirculatingShareholders
    32  type CirculatingShareholder struct {
    33  	SecurityCode     string  `dataframe:"security_code"`       // 证券代码
    34  	SecurityName     string  `dataframe:"security_name"`       // 证券名称
    35  	EndDate          string  `dataframe:"end_date"`            // 报告日期
    36  	UpdateDate       string  `dataframe:"update_date"`         // 更新日期
    37  	HolderType       string  `dataframe:"holder_type"`         // 股东类型
    38  	HolderName       string  `dataframe:"holder_name"`         // 股东名称
    39  	IsHoldOrg        string  `dataframe:"is_holdorg"`          // 股东是否机构
    40  	HolderRank       int     `dataframe:"holder_rank"`         // 股东排名
    41  	HoldNum          int     `dataframe:"hold_num"`            // 期末持股-数量
    42  	FreeHoldNumRatio float64 `dataframe:"free_hold_num_ratio"` // 期末持股-比例
    43  	HoldNumChange    int     `dataframe:"hold_num_change"`     // 期末持股-持股变动
    44  	HoldChangeName   string  `dataframe:"change_name"`         // 期末持股-变化状态
    45  	HoldChangeState  int     `dataframe:"change_state"`        // 期末持股-变化状态
    46  	HoldChangeRatio  float64 `dataframe:"change_ratio"`        // 期末持股-持股变化比例
    47  	HoldRatio        float64 `dataframe:"hold_ratio"`          // 期末持股-持股变动
    48  	HoldRatioChange  float64 `dataframe:"hold_ratio_change"`   // 期末持股-数量变化比例
    49  }
    50  
    51  // HoldingAnalyse 持股分析
    52  type HoldingAnalyse struct {
    53  	SECUCODE                string  `json:"SECUCODE"`                // "股票代码",
    54  	SECURITY_NAME           string  `json:"SECURITY_NAME_ABBR"`      // "股票简称",
    55  	END_DATE                string  `json:"END_DATE"`                // "报告期",
    56  	UPDATE_DATE             string  `json:"UPDATE_DATE"`             // "公告日",
    57  	HOLDER_TYPE             string  `json:"HOLDER_TYPE"`             // "股东类型",
    58  	HOLDER_NEWTYPE          string  `json:"HOLDER_NEWTYPE"`          // "-",
    59  	HOLDER_NAME             string  `json:"HOLDER_NAME"`             // "股东名称",
    60  	IS_HOLDORG              string  `json:"IS_HOLDORG"`              // "是否机构",
    61  	HOLDER_RANK             int     `json:"HOLDER_RANK"`             // "股东排名",
    62  	HOLD_NUM                int     `json:"HOLD_NUM"`                // "期末持股-数量",
    63  	FREE_HOLDNUM_RATIO      float64 `json:"FREE_HOLDNUM_RATIO"`      // "-",
    64  	HOLD_NUM_CHANGE_NAME    string  `json:"HOLDNUM_CHANGE_NAME"`     // "-",
    65  	XZCHANGE                int     `json:"XZCHANGE"`                // "期末持股-数量变化",
    66  	CHANGE_RATIO            float64 `json:"CHANGE_RATIO"`            // "-",
    67  	HOLDER_STATE            string  `json:"HOLDER_STATE"`            // "-",
    68  	REPORT_DATE_NAME        string  `json:"REPORT_DATE_NAME"`        // ---华丽的分割线---
    69  	HOLDER_MARKET_CAP       float64 `json:"HOLDER_MARKET_CAP"`       // "期末持股-流通市值",
    70  	HOLD_RATIO              float64 `json:"HOLD_RATIO"`              // "-",
    71  	SECURITY_CODE           string  `json:"SECURITY_CODE"`           // "股票代码简写",
    72  	HOLD_CHANGE             string  `json:"HOLD_CHANGE"`             // "-",
    73  	HOLD_RATIO_CHANGE       float64 `json:"HOLD_RATIO_CHANGE"`       // "期末持股-数量变化比例",
    74  	ORG_CODE                string  `json:"ORG_CODE"`                // "-",
    75  	HOLDER_CODE             string  `json:"HOLDER_CODE"`             // "-",
    76  	SECURITY_TYPE_CODE      string  `json:"SECURITY_TYPE_CODE"`      // "-",
    77  	SHARES_TYPE             string  `json:"SHARES_TYPE"`             // "-",
    78  	HOLDER_NEW              string  `json:"HOLDER_NEW"`              // "-",
    79  	FREE_RATIO_QOQ          string  `json:"FREE_RATIO_QOQ"`          // "-",
    80  	HOLDER_STATEE           string  `json:"HOLDER_STATEE"`           // "-",
    81  	IS_REPORT               string  `json:"IS_REPORT"`               // "-",
    82  	HOLDER_CODE_OLD         string  `json:"HOLDER_CODE_OLD"`         // "-",
    83  	IS_MAX_REPORT_DATE      string  `json:"IS_MAX_REPORTDATE"`       // "-",
    84  	COOPERATION_HOLDER_MARK string  `json:"COOPERATION_HOLDER_MARK"` // "-",
    85  	MXID                    string  `json:"MXID"`                    // "-",
    86  	LISTING_STATE           string  `json:"LISTING_STATE"`           // "-",
    87  	NEW_CHANGE_RATIO        string  `json:"NEW_CHANGE_RATIO"`        // "-",
    88  	HOLD_NUM_CHANGE         string  `json:"HOLD_NUM_CHANGE"`         // "期末持股-持股变动",
    89  }
    90  
    91  // FreeHoldingAnalyse 东方财富网-数据中心-股东分析-股东持股明细-十大流通股东
    92  //
    93  //	https://data.eastmoney.com/gdfx/HoldingAnalyse.html
    94  func getFreeHoldingAnalyse(pageNumber ...int) ([]HoldingAnalyse, int, error) {
    95  	pageNo := 1
    96  	pages := 0
    97  	if len(pageNumber) > 0 {
    98  		pageNo = pageNumber[0]
    99  	}
   100  	pageSize := EastmoneyGdfxHoldingAnalysePageSize
   101  	_, qEnd := api.GetQuarterDay(9)
   102  	endDate := exchange.FixTradeDate(qEnd)
   103  	params := urlpkg.Values{
   104  		"sortColumns": {"UPDATE_DATE,SECURITY_CODE,HOLDER_RANK"},
   105  		"sortTypes":   {"-1,1,1"},
   106  		"pageSize":    {fmt.Sprintf("%d", pageSize)},
   107  		"pageNumber":  {fmt.Sprintf("%d", pageNo)},
   108  		"reportName":  {"RPT_F10_EH_FREEHOLDERS"},
   109  		"columns":     {"ALL"},
   110  		"source":      {"WEB"},
   111  		"client":      {"WEB"},
   112  		"filter":      {fmt.Sprintf("(END_DATE>='%s')", endDate)},
   113  	}
   114  	url := urlEastmoneyGdfxHoldingAnalyse + "?" + params.Encode()
   115  	data, err := http.Get(url)
   116  	//fmt.Println(api.Bytes2String(data))
   117  	var holds = []HoldingAnalyse{}
   118  	obj, err := fastjson.ParseBytes(data)
   119  	if err != nil {
   120  		logger.Errorf("%+v\n", err)
   121  		return holds, pages, err
   122  	}
   123  	result := obj.Get("result")
   124  	if result == nil {
   125  		return holds, pages, nil
   126  	}
   127  	pages = result.GetInt("pages")
   128  	tData := result.Get("data")
   129  	if tData == nil {
   130  		return holds, pages, nil
   131  	}
   132  	text := tData.String()
   133  	err = json.Unmarshal(api.String2Bytes(text), &holds)
   134  	for i := 0; i < len(holds); i++ {
   135  		holds[i].END_DATE = exchange.FixTradeDate(holds[i].END_DATE)
   136  		holds[i].UPDATE_DATE = exchange.FixTradeDate(holds[i].UPDATE_DATE)
   137  	}
   138  	return holds, pages, err
   139  }
   140  
   141  func FreeHoldingAnalyse(pageNumber ...int) ([]CirculatingShareholder, int, error) {
   142  	pageNo := 1
   143  	pages := 0
   144  	if len(pageNumber) > 0 {
   145  		pageNo = pageNumber[0]
   146  	}
   147  	shareholders := []CirculatingShareholder{}
   148  	pageSize := EastmoneyGdfxHoldingAnalysePageSize
   149  	_, qEnd := api.GetQuarterDay(9)
   150  	endDate := exchange.FixTradeDate(qEnd)
   151  	params := urlpkg.Values{
   152  		"sortColumns": {"UPDATE_DATE,SECURITY_CODE,HOLDER_RANK"},
   153  		"sortTypes":   {"-1,1,1"},
   154  		"pageSize":    {fmt.Sprintf("%d", pageSize)},
   155  		"pageNumber":  {fmt.Sprintf("%d", pageNo)},
   156  		"reportName":  {"RPT_F10_EH_FREEHOLDERS"},
   157  		"columns":     {"ALL"},
   158  		"source":      {"WEB"},
   159  		"client":      {"WEB"},
   160  		"filter":      {fmt.Sprintf("(END_DATE>='%s')", endDate)},
   161  	}
   162  	url := urlEastmoneyGdfxHoldingAnalyse + "?" + params.Encode()
   163  	data, err := http.Get(url)
   164  	var holds = []HoldingAnalyse{}
   165  	obj, err := fastjson.ParseBytes(data)
   166  	if err != nil {
   167  		logger.Errorf("%+v\n", err)
   168  		return shareholders, pages, err
   169  	}
   170  	result := obj.Get("result")
   171  	if result == nil {
   172  		return shareholders, pages, nil
   173  	}
   174  	pages = result.GetInt("pages")
   175  	tData := result.Get("data")
   176  	if tData == nil {
   177  		return shareholders, pages, nil
   178  	}
   179  	text := tData.String()
   180  	err = json.Unmarshal(api.String2Bytes(text), &holds)
   181  	for i := 0; i < len(holds); i++ {
   182  		holds[i].END_DATE = exchange.FixTradeDate(holds[i].END_DATE)
   183  		holds[i].UPDATE_DATE = exchange.FixTradeDate(holds[i].UPDATE_DATE)
   184  	}
   185  	for _, v := range holds {
   186  		shareholder := CirculatingShareholder{
   187  			//SecurityCode   string `dataframe:"security_code"`    // 证券代码
   188  			SecurityCode: v.SECUCODE,
   189  			//SecurityName   string `dataframe:"security_name"`    // 证券名称
   190  			SecurityName: v.SECURITY_NAME,
   191  			//EndDate        string `dataframe:"end_date"`         // 报告日期
   192  			EndDate: v.END_DATE,
   193  			//UpdateDate     string `dataframe:"update_date"`      // 更新日期
   194  			UpdateDate: v.UPDATE_DATE,
   195  			//HolderType     string `dataframe:"holder_type"`      // 股东类型
   196  			HolderType: v.HOLDER_NEWTYPE,
   197  			//HolderName     string `dataframe:"holder_name"`      // 股东名称
   198  			HolderName: v.HOLDER_NAME,
   199  			//IsHolderOrg    string `dataframe:"is_holder_org"`    // 股东是否机构
   200  			IsHoldOrg: v.IS_HOLDORG,
   201  			//HolderRank     int    `dataframe:"holder_rank"`      // 股东排名
   202  			HolderRank: v.HOLDER_RANK,
   203  			//HoldNum        int    `dataframe:"hold_num"`         // 期末持股-数量
   204  			HoldNum: v.HOLD_NUM,
   205  			//FreeHoldNumRatio float64 `dataframe:"hold_num_ratio"`  // 期末持股-比例
   206  			FreeHoldNumRatio: v.FREE_HOLDNUM_RATIO,
   207  			//HoldNumChange  string `dataframe:"hold_num_change"`  // 期末持股-持股变动
   208  			HoldNumChange: v.XZCHANGE,
   209  			//HoldChangeName string `dataframe:"hold_change_name"` // 期末持股-变化状态
   210  			HoldChangeName: v.HOLD_NUM_CHANGE_NAME,
   211  			//HoldChangeRatio  string  `dataframe:"change_ratio"`    // 期末持股-变化比例
   212  			HoldChangeRatio: v.CHANGE_RATIO,
   213  			//HoldRatio        float64 `dataframe:"hold_ratio"`          // 期末持股-持股变动
   214  			HoldRatio: v.HOLD_RATIO,
   215  			//HoldRatioChange  float64 `dataframe:"hold_ratio_change"`   // "期末持股-数量变化比例",
   216  			HoldRatioChange: v.HOLD_RATIO_CHANGE,
   217  		}
   218  		// 修订证券代码
   219  		_, mfalg, mcode := exchange.DetectMarket(shareholder.SecurityCode)
   220  		shareholder.SecurityCode = mfalg + mcode
   221  		//HoldChangeState  int     `dataframe:"change_state"`        // 期末持股-变化状态
   222  		switch v.HOLD_NUM_CHANGE_NAME {
   223  		case "新进":
   224  			shareholder.HoldChangeState = HoldNumNewlyAdded
   225  		case "增加":
   226  			shareholder.HoldChangeState = HoldNumIncrease
   227  		case "减少":
   228  			shareholder.HoldChangeState = HoldNumDampened
   229  		case "不变":
   230  			shareholder.HoldChangeState = HoldNumUnChanged
   231  		default: // 未知变化报警
   232  			shareholder.HoldChangeState = HoldNumUnknownChanges
   233  			warning := fmt.Sprintf("%s: %s, 变化状态未知: %s", v.SECURITY_NAME, v.SECUCODE, v.HOLD_NUM_CHANGE_NAME)
   234  			logger.Warnf(warning)
   235  		}
   236  		shareholders = append(shareholders, shareholder)
   237  	}
   238  	return shareholders, pages, err
   239  }
   240  
   241  // FreeHoldingDetail 拉取近期的
   242  func freeHoldingDetail() []CirculatingShareholder {
   243  	pageNo := 1
   244  	holds := []CirculatingShareholder{}
   245  	for {
   246  		list, pages, err := getFreeHoldingAnalyse(pageNo)
   247  		if err != nil || pages < 1 {
   248  			logger.Error(err)
   249  			break
   250  		}
   251  		count := len(list)
   252  		if count == 0 {
   253  			break
   254  		}
   255  		for _, v := range list {
   256  			shareholder := CirculatingShareholder{
   257  				//SecurityCode   string `dataframe:"security_code"`    // 证券代码
   258  				SecurityCode: strings.TrimSpace(v.SECUCODE),
   259  				//SecurityName   string `dataframe:"security_name"`    // 证券名称
   260  				SecurityName: strings.TrimSpace(v.SECURITY_NAME),
   261  				//EndDate        string `dataframe:"end_date"`         // 报告日期
   262  				EndDate: strings.TrimSpace(v.END_DATE),
   263  				//UpdateDate     string `dataframe:"update_date"`      // 更新日期
   264  				UpdateDate: strings.TrimSpace(v.UPDATE_DATE),
   265  				//HolderType     string `dataframe:"holder_type"`      // 股东类型
   266  				HolderType: v.HOLDER_NEWTYPE,
   267  				//HolderName     string `dataframe:"holder_name"`      // 股东名称
   268  				HolderName: v.HOLDER_NAME,
   269  				//IsHolderOrg    string `dataframe:"is_holder_org"`    // 股东是否机构
   270  				IsHoldOrg: v.IS_HOLDORG,
   271  				//HolderRank     int    `dataframe:"holder_rank"`      // 股东排名
   272  				HolderRank: v.HOLDER_RANK,
   273  				//HoldNum        int    `dataframe:"hold_num"`         // 期末持股-数量
   274  				HoldNum: v.HOLD_NUM,
   275  				//FreeHoldNumRatio float64 `dataframe:"hold_num_ratio"`  // 期末持股-比例
   276  				FreeHoldNumRatio: v.FREE_HOLDNUM_RATIO,
   277  				//HoldNumChange  string `dataframe:"hold_num_change"`  // 期末持股-持股变动
   278  				HoldNumChange: v.XZCHANGE,
   279  				//HoldChangeName string `dataframe:"hold_change_name"` // 期末持股-变化状态
   280  				HoldChangeName: v.HOLD_NUM_CHANGE_NAME,
   281  				//HoldChangeRatio  string  `dataframe:"change_ratio"`    // 期末持股-变化比例
   282  				HoldChangeRatio: v.CHANGE_RATIO,
   283  				//HoldRatio        float64 `dataframe:"hold_ratio"`          // 期末持股-持股变动
   284  				HoldRatio: v.HOLD_RATIO,
   285  				//HoldRatioChange  float64 `dataframe:"hold_ratio_change"`   // "期末持股-数量变化比例",
   286  				HoldRatioChange: v.HOLD_RATIO_CHANGE,
   287  			}
   288  			// 修订证券代码
   289  			_, mfalg, mcode := exchange.DetectMarket(shareholder.SecurityCode)
   290  			shareholder.SecurityCode = mfalg + mcode
   291  			//HoldChangeState  int     `dataframe:"change_state"`        // 期末持股-变化状态
   292  			switch v.HOLD_NUM_CHANGE_NAME {
   293  			case "新进":
   294  				shareholder.HoldChangeState = HoldNumNewlyAdded
   295  			case "增加":
   296  				shareholder.HoldChangeState = HoldNumIncrease
   297  			case "减少":
   298  				shareholder.HoldChangeState = HoldNumDampened
   299  			case "不变":
   300  				shareholder.HoldChangeState = HoldNumUnChanged
   301  			default: // 未知变化报警
   302  				shareholder.HoldChangeState = HoldNumUnknownChanges
   303  				warning := fmt.Sprintf("%s: %s, 变化状态未知: %s", v.SECURITY_NAME, v.SECUCODE, v.HOLD_NUM_CHANGE_NAME)
   304  				logger.Warnf(warning)
   305  			}
   306  			holds = append(holds, shareholder)
   307  		}
   308  		if count < EastmoneyGdfxHoldingAnalysePageSize {
   309  			break
   310  		}
   311  		pageNo += 1
   312  		break
   313  	}
   314  	return holds
   315  }