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

     1  package dfcf
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"gitee.com/quant1x/engine/cache"
     7  	"gitee.com/quant1x/exchange"
     8  	"gitee.com/quant1x/gox/api"
     9  	"gitee.com/quant1x/gox/http"
    10  	"gitee.com/quant1x/gox/logger"
    11  	urlpkg "net/url"
    12  )
    13  
    14  // 前十大流通股东 https://emweb.securities.eastmoney.com/PC_HSF10/ShareholderResearch/Index?type=web&code=sh600822#sdltgd-0
    15  // 数据接口 https://emweb.securities.eastmoney.com/PC_HSF10/ShareholderResearch/PageAjax?code=SH600822
    16  //
    17  // 十大股东 https://datacenter-web.eastmoney.com/api/data/v1/get?callback=jQuery112308928698030561204_1687518793792&sortColumns=RANK&sortTypes=1&pageSize=10&pageNumber=1&reportName=RPT_DMSK_HOLDERS&columns=ALL&source=WEB&client=WEB&filter=(SECURITY_CODE%3D%22600115%22)(END_DATE%3D%272023-03-31%27)
    18  // callback: jQuery112308928698030561204_1687518793792
    19  //sortColumns: RANK
    20  //sortTypes: 1
    21  //pageSize: 10
    22  //pageNumber: 1
    23  //reportName: RPT_DMSK_HOLDERS
    24  //columns: ALL
    25  //source: WEB
    26  //client: WEB
    27  //filter: (SECURITY_CODE="600115")(END_DATE='2023-03-31')
    28  
    29  type rawStockHolder struct {
    30  	Version string `json:"version"`
    31  	Result  struct {
    32  		Pages int `json:"pages"`
    33  		Data  []struct {
    34  			SECUCODE                string  `json:"SECUCODE"`
    35  			SECURITY_CODE           string  `json:"SECURITY_CODE"`
    36  			ORG_CODE                string  `json:"ORG_CODE"`
    37  			END_DATE                string  `json:"END_DATE"`
    38  			HOLDER_NAME             string  `json:"HOLDER_NAME"`
    39  			HOLD_NUM                int64   `json:"HOLD_NUM"`
    40  			FREE_HOLDNUM_RATIO      float64 `json:"FREE_HOLDNUM_RATIO"`
    41  			HOLD_NUM_CHANGE         string  `json:"HOLD_NUM_CHANGE"`
    42  			CHANGE_RATIO            float64 `json:"CHANGE_RATIO"`
    43  			IS_HOLDORG              string  `json:"IS_HOLDORG"`
    44  			HOLDER_RANK             int     `json:"HOLDER_RANK"`
    45  			SECURITY_NAME_ABBR      string  `json:"SECURITY_NAME_ABBR"`
    46  			HOLDER_CODE             string  `json:"HOLDER_CODE"`
    47  			SECURITY_TYPE_CODE      string  `json:"SECURITY_TYPE_CODE"`
    48  			HOLDER_STATE            string  `json:"HOLDER_STATE"`
    49  			HOLDER_MARKET_CAP       float64 `json:"HOLDER_MARKET_CAP"`
    50  			HOLD_RATIO              float64 `json:"HOLD_RATIO"`
    51  			HOLD_CHANGE             string  `json:"HOLD_CHANGE"`
    52  			HOLD_RATIO_CHANGE       float64 `json:"HOLD_RATIO_CHANGE"`
    53  			HOLDER_TYPE             string  `json:"HOLDER_TYPE"`
    54  			SHARES_TYPE             string  `json:"SHARES_TYPE"`
    55  			UPDATE_DATE             string  `json:"UPDATE_DATE"`
    56  			REPORTDATENAME          string  `json:"REPORT_DATE_NAME"`
    57  			REPORT_DATE_NAME        string  `json:"HOLDER_NEW"`
    58  			FREE_RATIO_QOQ          string  `json:"FREE_RATIO_QOQ"`
    59  			HOLDER_STATEE           string  `json:"HOLDER_STATEE"`
    60  			IS_REPORT               string  `json:"IS_REPORT"`
    61  			HOLDER_CODE_OLD         string  `json:"HOLDER_CODE_OLD"`
    62  			HOLDER_NEWTYPE          string  `json:"HOLDER_NEWTYPE"`
    63  			HOLDNUM_CHANGE_NAME     string  `json:"HOLDNUM_CHANGE_NAME"`
    64  			IS_MAX_REPORTDATE       string  `json:"IS_MAX_REPORTDATE"`
    65  			COOPERATION_HOLDER_MARK string  `json:"COOPERATION_HOLDER_MARK"`
    66  			MXID                    string  `json:"MXID"`
    67  			LISTING_STATE           string  `json:"LISTING_STATE"`
    68  			XZCHANGE                int     `json:"XZCHANGE"`
    69  			NEW_CHANGE_RATIO        string  `json:"NEW_CHANGE_RATIO"`
    70  		} `json:"data"`
    71  		Count int `json:"count"`
    72  	} `json:"result"`
    73  	Success bool   `json:"success"`
    74  	Message string `json:"message"`
    75  	Code    int    `json:"code"`
    76  }
    77  
    78  // 前十大流通股东 https://data.eastmoney.com/gdfx/stock/600115.html
    79  // 数据接口 https://datacenter-web.eastmoney.com/api/data/v1/get
    80  //callback: jQuery112308928698030561204_1687518793796
    81  //sortColumns: HOLDER_RANK
    82  //sortTypes: 1
    83  //pageSize: 10
    84  //pageNumber: 1
    85  //reportName: RPT_F10_EH_FREEHOLDERS
    86  //columns: ALL
    87  //source: WEB
    88  //client: WEB
    89  //filter: (SECURITY_CODE="600115")(END_DATE='2023-03-31')
    90  
    91  const (
    92  	urlTop10ShareHolder = "https://datacenter-web.eastmoney.com/api/data/v1/get"
    93  )
    94  
    95  func ShareHolder(securityCode, date string, diffQuarters ...int) (list []CirculatingShareholder) {
    96  	_, _, code := exchange.DetectMarket(securityCode)
    97  	quarterEndDate := exchange.FixTradeDate(date)
    98  	_, _, qEnd := api.GetQuarterByDate(date, diffQuarters...)
    99  	quarterEndDate = exchange.FixTradeDate(qEnd)
   100  	params := urlpkg.Values{
   101  		"sortColumns": {"HOLDER_RANK"},
   102  		"sortTypes":   {"1"},
   103  		"pageSize":    {"10"},
   104  		"pageNumber":  {"1"},
   105  		"reportName":  {"RPT_F10_EH_FREEHOLDERS"},
   106  		"columns":     {"ALL"},
   107  		"source":      {"WEB"},
   108  		"client":      {"WEB"},
   109  		"filter":      {fmt.Sprintf("(SECURITY_CODE=\"%s\")(END_DATE='%s')", code, quarterEndDate)},
   110  	}
   111  
   112  	url := urlTop10ShareHolder + "?" + params.Encode()
   113  	data, err := http.Get(url)
   114  	//fmt.Println(api.Bytes2String(data))
   115  	if err != nil {
   116  		return
   117  	}
   118  	var raw rawStockHolder
   119  	err = json.Unmarshal(data, &raw)
   120  	if err != nil || raw.Result.Count == 0 || len(raw.Result.Data) == 0 {
   121  		return
   122  	}
   123  	for _, v := range raw.Result.Data {
   124  		shareholder := CirculatingShareholder{
   125  			SecurityCode:     v.SECUCODE,
   126  			SecurityName:     v.SECURITY_NAME_ABBR,
   127  			EndDate:          exchange.FixTradeDate(v.END_DATE),
   128  			UpdateDate:       exchange.FixTradeDate(v.UPDATE_DATE),
   129  			HolderType:       v.HOLDER_NEWTYPE,
   130  			HolderName:       v.HOLDER_NAME,
   131  			IsHoldOrg:        v.IS_HOLDORG,
   132  			HolderRank:       v.HOLDER_RANK,
   133  			HoldNum:          int(v.HOLD_NUM),
   134  			FreeHoldNumRatio: v.FREE_HOLDNUM_RATIO,
   135  			HoldNumChange:    v.XZCHANGE,
   136  			HoldChangeName:   v.HOLDNUM_CHANGE_NAME,
   137  			HoldChangeRatio:  v.CHANGE_RATIO,
   138  			HoldRatio:        v.HOLD_RATIO,
   139  			HoldRatioChange:  v.HOLD_RATIO_CHANGE,
   140  		}
   141  		// 修订证券代码
   142  		_, mfalg, mcode := exchange.DetectMarket(shareholder.SecurityCode)
   143  		shareholder.SecurityCode = mfalg + mcode
   144  		//HoldChangeState  int     `dataframe:"change_state"`        // 期末持股-变化状态
   145  		switch v.HOLDNUM_CHANGE_NAME {
   146  		case "新进":
   147  			shareholder.HoldChangeState = HoldNumNewlyAdded
   148  		case "增加":
   149  			shareholder.HoldChangeState = HoldNumIncrease
   150  		case "减少":
   151  			shareholder.HoldChangeState = HoldNumDampened
   152  		case "不变":
   153  			shareholder.HoldChangeState = HoldNumUnChanged
   154  		default: // 未知变化报警
   155  			shareholder.HoldChangeState = HoldNumUnknownChanges
   156  			warning := fmt.Sprintf("%s: %s, 变化状态未知: %s", v.SECURITY_NAME_ABBR, v.SECUCODE, v.HOLDNUM_CHANGE_NAME)
   157  			logger.Warnf(warning)
   158  		}
   159  		list = append(list, shareholder)
   160  	}
   161  	api.SliceSort(list, func(a, b CirculatingShareholder) bool {
   162  		return a.HolderRank < b.HolderRank
   163  	})
   164  	return
   165  }
   166  
   167  // cacheShareHolder 获取流动股东数据
   168  func cacheShareHolder(securityCode, date string, diffQuarters ...int) (list []CirculatingShareholder) {
   169  	diff := 1
   170  	if len(diffQuarters) > 0 {
   171  		diff = diffQuarters[0]
   172  	}
   173  	_, _, last := api.GetQuarterByDate(date, diff)
   174  	filename := cache.Top10HoldersFilename(securityCode, last)
   175  	if api.FileExist(filename) {
   176  		err := api.CsvToSlices(filename, &list)
   177  		if err == nil && len(list) > 0 {
   178  			return
   179  		}
   180  	}
   181  	tmpList := ShareHolder(securityCode, last)
   182  	if len(tmpList) > 0 {
   183  		list = tmpList
   184  	}
   185  	if len(list) > 0 {
   186  		_ = api.SlicesToCsv(filename, list)
   187  	}
   188  	return
   189  }
   190  
   191  // GetCacheShareHolder 获取流动股东数据
   192  func GetCacheShareHolder(securityCode, date string, diffQuarters ...int) (list []CirculatingShareholder) {
   193  	diff := 1
   194  	if len(diffQuarters) > 0 {
   195  		diff = diffQuarters[0]
   196  	}
   197  	for ; diff < 4; diff++ {
   198  		tmpList := cacheShareHolder(securityCode, date, diff)
   199  		if len(tmpList) == 0 {
   200  			continue
   201  		}
   202  		list = tmpList
   203  		break
   204  	}
   205  	return
   206  }