mirror of
https://github.com/injoyai/tdx.git
synced 2025-11-26 21:25:35 +08:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47084b1112 | ||
|
|
2566ef5cec | ||
|
|
86404db551 | ||
|
|
3f3438fca8 | ||
|
|
1656b41f02 | ||
|
|
8090cc7216 | ||
|
|
a3169c67da |
18
example/GetBjCodes/main.go
Normal file
18
example/GetBjCodes/main.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/injoyai/logs"
|
||||||
|
"github.com/injoyai/tdx/extend"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ls, err := extend.GetBjCodes()
|
||||||
|
if err != nil {
|
||||||
|
logs.Err(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, v := range ls {
|
||||||
|
logs.Debug(v)
|
||||||
|
}
|
||||||
|
logs.Debug("总数量:", len(ls))
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
common.Test(func(c *tdx.Client) {
|
common.Test(func(c *tdx.Client) {
|
||||||
resp, err := c.GetKlineDay("000001", 0, 10)
|
resp, err := c.GetKlineDay("838971", 0, 20)
|
||||||
logs.PanicErr(err)
|
logs.PanicErr(err)
|
||||||
|
|
||||||
for _, v := range resp.List {
|
for _, v := range resp.List {
|
||||||
|
|||||||
32
example/GetKlineDayFactor/main.go
Normal file
32
example/GetKlineDayFactor/main.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/injoyai/logs"
|
||||||
|
"github.com/injoyai/tdx"
|
||||||
|
"github.com/injoyai/tdx/extend"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
c, err := tdx.DialDefault()
|
||||||
|
logs.PanicErr(err)
|
||||||
|
|
||||||
|
ks, fs, err := extend.GetTHSDayKlineFactorFull("000001", c)
|
||||||
|
logs.PanicErr(err)
|
||||||
|
|
||||||
|
m := map[int64]*extend.THSFactor{}
|
||||||
|
for _, v := range fs {
|
||||||
|
m[v.Date] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range ks[0] {
|
||||||
|
logs.Debugf("%s 不复权:%.2f 前复权:%.2f 后复权:%.2f \n",
|
||||||
|
time.Unix(v.Date, 0).Format(time.DateOnly),
|
||||||
|
v.Close.Float64(),
|
||||||
|
v.Close.Float64()*m[v.Date].QFactor,
|
||||||
|
v.Close.Float64()*m[v.Date].HFactor,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
102
extend/codes_bj.go
Normal file
102
extend/codes_bj.go
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package extend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/injoyai/conv"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// UrlBjCodes 最后跟的是时间戳(ms),但是随便什么时间戳都能请求成功
|
||||||
|
UrlBjCodes = "https://www.bse.cn/nqhqController/nqhq_en.do?callback=jQuery3710848510589806625_%d"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetBjCodes() ([]*BjCode, error) {
|
||||||
|
list := []*BjCode(nil)
|
||||||
|
//这个200预防下bug,除非北京上市公司有4000个
|
||||||
|
for page := 0; page < 200; page++ {
|
||||||
|
ls, done, err := getBjCodes(page)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
list = append(list, ls...)
|
||||||
|
if done {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
<-time.After(time.Millisecond * 100)
|
||||||
|
}
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBjCodes(page int) (_ []*BjCode, last bool, err error) {
|
||||||
|
|
||||||
|
url := fmt.Sprintf(UrlBjCodes, time.Now().UnixMilli())
|
||||||
|
|
||||||
|
bodyStr := "page=" + conv.String(page) + "&type_en=%5B%22B%22%5D&sortfield=hqcjsl&sorttype=desc&xxfcbj_en=%5B2%5D&zqdm="
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(bodyStr))
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
req.Header.Set("X-Requested-With", "XMLHttpRequest")
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
|
||||||
|
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.39 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36")
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
bs, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//处理数据
|
||||||
|
i := bytes.IndexByte(bs, '(')
|
||||||
|
if len(bs) < 1 || len(bs) <= i {
|
||||||
|
return nil, false, errors.New("未知错误: " + string(bs))
|
||||||
|
}
|
||||||
|
|
||||||
|
bs = bs[i+1 : len(bs)-1]
|
||||||
|
|
||||||
|
ls := []*BjCodes(nil)
|
||||||
|
err = json.Unmarshal(bs, &ls)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ls) == 0 {
|
||||||
|
return nil, false, errors.New("未知错误: " + string(bs))
|
||||||
|
}
|
||||||
|
|
||||||
|
return ls[0].Data, ls[0].LastPage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type BjCodes struct {
|
||||||
|
Data []*BjCode `json:"content"`
|
||||||
|
TotalNumber int `json:"totalElements"`
|
||||||
|
TotalPage int `json:"totalPages"`
|
||||||
|
LastPage bool `json:"lastPage"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BjCode struct {
|
||||||
|
Date string `json:"hqjsrq"` //日期
|
||||||
|
Code string `json:"hqzqdm"` //代码
|
||||||
|
Name string `json:"hqzqjc"` //名称
|
||||||
|
LastClose float64 `json:"hqzrsp"` //前一天收盘价
|
||||||
|
Open float64 `json:"hqjrkp"` //开盘价
|
||||||
|
High float64 `json:"hqzgcj"` //最高价
|
||||||
|
Low float64 `json:"hqzdcj"` //最低价
|
||||||
|
Last float64 `json:"hqzjcj"` //最新价/收盘价
|
||||||
|
Volume int `json:"hqcjsl"` //成交量,股
|
||||||
|
Amount float64 `json:"hqcjje"` //成交额,元
|
||||||
|
}
|
||||||
@@ -15,10 +15,36 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
UrlTHSDayKline = "http://d.10jqka.com.cn/v6/line/hs_%s/0%d/all.js"
|
UrlTHSDayKline = "http://d.10jqka.com.cn/v6/line/hs_%s/0%d/all.js"
|
||||||
|
THS_BFQ uint8 = 0 //不复权
|
||||||
THS_QFQ uint8 = 1 //前复权
|
THS_QFQ uint8 = 1 //前复权
|
||||||
THS_HFQ uint8 = 2 //后复权
|
THS_HFQ uint8 = 2 //后复权
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GetTHSDayKlineFactorFull 增加计算复权因子
|
||||||
|
func GetTHSDayKlineFactorFull(code string, c *tdx.Client) ([3][]*Kline, []*THSFactor, error) {
|
||||||
|
ks, err := GetTHSDayKlineFull(code, c)
|
||||||
|
if err != nil {
|
||||||
|
return [3][]*Kline{}, nil, err
|
||||||
|
}
|
||||||
|
mQPrice := make(map[int64]float64)
|
||||||
|
for _, v := range ks[1] {
|
||||||
|
mQPrice[v.Date] = v.Close.Float64()
|
||||||
|
}
|
||||||
|
mHPrice := make(map[int64]float64)
|
||||||
|
for _, v := range ks[2] {
|
||||||
|
mHPrice[v.Date] = v.Close.Float64()
|
||||||
|
}
|
||||||
|
fs := make([]*THSFactor, 0, len(ks[0]))
|
||||||
|
for _, v := range ks[0] {
|
||||||
|
fs = append(fs, &THSFactor{
|
||||||
|
Date: v.Date,
|
||||||
|
QFactor: mQPrice[v.Date] / v.Close.Float64(),
|
||||||
|
HFactor: mHPrice[v.Date] / v.Close.Float64(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return ks, fs, nil
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
GetTHSDayKlineFull
|
GetTHSDayKlineFull
|
||||||
获取[不复权,前复权,后复权]数据,并补充成交金额数据
|
获取[不复权,前复权,后复权]数据,并补充成交金额数据
|
||||||
@@ -70,8 +96,8 @@ GetTHSDayKline
|
|||||||
后复权,和通达信,东方财富都对不上
|
后复权,和通达信,东方财富都对不上
|
||||||
*/
|
*/
|
||||||
func GetTHSDayKline(code string, _type uint8) ([]*Kline, error) {
|
func GetTHSDayKline(code string, _type uint8) ([]*Kline, error) {
|
||||||
if _type != THS_QFQ && _type != THS_HFQ {
|
if _type != THS_BFQ && _type != THS_QFQ && _type != THS_HFQ {
|
||||||
return nil, fmt.Errorf("数据类型错误,例如:前复权1或后复权2")
|
return nil, fmt.Errorf("数据类型错误,例如:不复权0或前复权1或后复权2")
|
||||||
}
|
}
|
||||||
|
|
||||||
code = protocol.AddPrefix(code)
|
code = protocol.AddPrefix(code)
|
||||||
|
|||||||
12
extend/ths-factor.go
Normal file
12
extend/ths-factor.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package extend
|
||||||
|
|
||||||
|
//const (
|
||||||
|
// // UrlTHSFactor https://d.10jqka.com.cn/v6/line/hs_000001/01/2016.js
|
||||||
|
// UrlTHSFactor = "https://d.10jqka.com.cn/v6/line/hs_%s/0%d/%d.js"
|
||||||
|
//)
|
||||||
|
|
||||||
|
type THSFactor struct {
|
||||||
|
Date int64 `json:"date"` //时间
|
||||||
|
QFactor float64 `json:"q_factor"` //前复权因子
|
||||||
|
HFactor float64 `json:"h_factor"` //后复权因子
|
||||||
|
}
|
||||||
2
go.mod
2
go.mod
@@ -5,7 +5,7 @@ go 1.20
|
|||||||
require (
|
require (
|
||||||
github.com/glebarez/go-sqlite v1.22.0
|
github.com/glebarez/go-sqlite v1.22.0
|
||||||
github.com/go-sql-driver/mysql v1.7.0
|
github.com/go-sql-driver/mysql v1.7.0
|
||||||
github.com/injoyai/base v1.2.15
|
github.com/injoyai/base v1.2.17
|
||||||
github.com/injoyai/conv v1.2.5
|
github.com/injoyai/conv v1.2.5
|
||||||
github.com/injoyai/ios v1.2.2
|
github.com/injoyai/ios v1.2.2
|
||||||
github.com/injoyai/logs v1.0.12
|
github.com/injoyai/logs v1.0.12
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -28,6 +28,8 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
|||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/injoyai/base v1.2.15 h1:K/ysPqZl7vgNUAz/jpG1IdDpzdSMWvUfoJL+1gPdM9g=
|
github.com/injoyai/base v1.2.15 h1:K/ysPqZl7vgNUAz/jpG1IdDpzdSMWvUfoJL+1gPdM9g=
|
||||||
github.com/injoyai/base v1.2.15/go.mod h1:NfCQjml3z2pCvQ3J3YcOXtecqXD0xVPKjo4YTsMLhr8=
|
github.com/injoyai/base v1.2.15/go.mod h1:NfCQjml3z2pCvQ3J3YcOXtecqXD0xVPKjo4YTsMLhr8=
|
||||||
|
github.com/injoyai/base v1.2.17 h1:+qYeCSeEMWgmTla+LBC0Ozan9ysS4mV0ne5nfMt9opU=
|
||||||
|
github.com/injoyai/base v1.2.17/go.mod h1:NfCQjml3z2pCvQ3J3YcOXtecqXD0xVPKjo4YTsMLhr8=
|
||||||
github.com/injoyai/conv v1.2.5 h1:G4OCyF0NTZul5W1u9IgXDOhW4/zmIigdPKXFHQGmv1M=
|
github.com/injoyai/conv v1.2.5 h1:G4OCyF0NTZul5W1u9IgXDOhW4/zmIigdPKXFHQGmv1M=
|
||||||
github.com/injoyai/conv v1.2.5/go.mod h1:s05l3fQJQ4mT4VX+KIdbvCWQB0YzZHprmUfUu2uxd1k=
|
github.com/injoyai/conv v1.2.5/go.mod h1:s05l3fQJQ4mT4VX+KIdbvCWQB0YzZHprmUfUu2uxd1k=
|
||||||
github.com/injoyai/ios v1.2.2 h1:fAPWBL6t22DiE2ZEpBgf5bzyVQTcm2ZhLMkM+JFPhZA=
|
github.com/injoyai/ios v1.2.2 h1:fAPWBL6t22DiE2ZEpBgf5bzyVQTcm2ZhLMkM+JFPhZA=
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ func (this Exchange) String() string {
|
|||||||
return "sz"
|
return "sz"
|
||||||
case ExchangeSH:
|
case ExchangeSH:
|
||||||
return "sh"
|
return "sh"
|
||||||
//case ExchangeBJ:
|
case ExchangeBJ:
|
||||||
//return "bj"
|
return "bj"
|
||||||
default:
|
default:
|
||||||
return "unknown"
|
return "unknown"
|
||||||
}
|
}
|
||||||
@@ -33,8 +33,8 @@ func (this Exchange) Name() string {
|
|||||||
return "上海"
|
return "上海"
|
||||||
case ExchangeSZ:
|
case ExchangeSZ:
|
||||||
return "深圳"
|
return "深圳"
|
||||||
//case ExchangeBJ:
|
case ExchangeBJ:
|
||||||
//return "北京"
|
return "北京"
|
||||||
default:
|
default:
|
||||||
return "未知"
|
return "未知"
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,7 @@ func (this Exchange) Name() string {
|
|||||||
const (
|
const (
|
||||||
ExchangeSZ Exchange = iota //深圳交易所
|
ExchangeSZ Exchange = iota //深圳交易所
|
||||||
ExchangeSH //上海交易所
|
ExchangeSH //上海交易所
|
||||||
//ExchangeBJ //北京交易所
|
ExchangeBJ //北京交易所
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ func DecodeCode(code string) (Exchange, string, error) {
|
|||||||
return ExchangeSH, code[2:], nil
|
return ExchangeSH, code[2:], nil
|
||||||
case ExchangeSZ.String():
|
case ExchangeSZ.String():
|
||||||
return ExchangeSZ, code[2:], nil
|
return ExchangeSZ, code[2:], nil
|
||||||
|
case ExchangeBJ.String():
|
||||||
|
return ExchangeBJ, code[2:], nil
|
||||||
default:
|
default:
|
||||||
return 0, "", fmt.Errorf("股票代码错误,例如:SZ000001")
|
return 0, "", fmt.Errorf("股票代码错误,例如:SZ000001")
|
||||||
}
|
}
|
||||||
@@ -237,20 +239,34 @@ func getVolume2(val uint32) float64 {
|
|||||||
|
|
||||||
// IsStock 是否是股票,示例sz000001
|
// IsStock 是否是股票,示例sz000001
|
||||||
func IsStock(code string) bool {
|
func IsStock(code string) bool {
|
||||||
if len(code) != 8 {
|
return IsSZStock(code) || IsSHStock(code) || IsBJStock(code)
|
||||||
return false
|
|
||||||
}
|
|
||||||
code = strings.ToLower(code)
|
|
||||||
switch {
|
|
||||||
case code[0:2] == ExchangeSH.String() &&
|
|
||||||
(code[2:3] == "6"):
|
|
||||||
return true
|
|
||||||
|
|
||||||
case code[0:2] == ExchangeSZ.String() &&
|
//if len(code) != 8 {
|
||||||
(code[2:3] == "0" || code[2:4] == "30"):
|
// return false
|
||||||
return true
|
//}
|
||||||
|
//code = strings.ToLower(code)
|
||||||
|
//switch {
|
||||||
|
//case code[0:2] == ExchangeSH.String() &&
|
||||||
|
// (code[2:3] == "6"):
|
||||||
|
// return true
|
||||||
|
//
|
||||||
|
//case code[0:2] == ExchangeSZ.String() &&
|
||||||
|
// (code[2:3] == "0" || code[2:4] == "30"):
|
||||||
|
// return true
|
||||||
|
//}
|
||||||
|
//return false
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
|
func IsSZStock(code string) bool {
|
||||||
|
return len(code) == 8 && strings.ToLower(code[0:2]) == ExchangeSZ.String() && code[2:3] == "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsSHStock(code string) bool {
|
||||||
|
return len(code) == 8 && strings.ToLower(code[0:2]) == ExchangeSH.String() && code[2:3] == "6"
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsBJStock(code string) bool {
|
||||||
|
return len(code) == 8 && strings.ToLower(code[0:2]) == ExchangeBJ.String() && (code[2:4] == "92" || code[2:3] == "8")
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsETF 是否是基金,示例sz159558
|
// IsETF 是否是基金,示例sz159558
|
||||||
@@ -290,6 +306,9 @@ func AddPrefix(code string) string {
|
|||||||
case code[:3] == "159":
|
case code[:3] == "159":
|
||||||
//深圳基金
|
//深圳基金
|
||||||
code = ExchangeSZ.String() + code
|
code = ExchangeSZ.String() + code
|
||||||
|
case code[:1] == "8" || code[:2] == "92":
|
||||||
|
//北京股票
|
||||||
|
code = ExchangeBJ.String() + code
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return code
|
return code
|
||||||
|
|||||||
Reference in New Issue
Block a user