Files
injoyai-tdx/protocol/unit.go
2025-10-14 13:07:59 +08:00

324 lines
7.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package protocol
import (
"bytes"
"fmt"
"github.com/injoyai/conv"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
"io"
"math"
"strings"
"time"
)
// String 字节先转小端,再转字符
func String(bs []byte) string {
return string(Reverse(bs))
}
// Bytes 任意类型转小端字节
func Bytes(n any) []byte {
return Reverse(conv.Bytes(n))
}
// Reverse 字节倒序
func Reverse(bs []byte) []byte {
x := make([]byte, len(bs))
for i, v := range bs {
x[len(bs)-i-1] = v
}
return x
}
// Uint32 字节通过小端方式转为uint32
func Uint32(bs []byte) uint32 {
return conv.Uint32(Reverse(bs))
}
// Uint16 字节通过小端方式转为uint16
func Uint16(bs []byte) uint16 {
return conv.Uint16(Reverse(bs))
}
func UTF8ToGBK(text []byte) []byte {
r := bytes.NewReader(text)
decoder := transform.NewReader(r, simplifiedchinese.GBK.NewDecoder()) //GB18030
content, _ := io.ReadAll(decoder)
return bytes.ReplaceAll(content, []byte{0x00}, []byte{})
}
func DecodeCode(code string) (Exchange, string, error) {
code = AddPrefix(code)
if len(code) != 8 {
return 0, "", fmt.Errorf("股票代码长度错误,例如:SZ000001")
}
switch strings.ToLower(code[:2]) {
case ExchangeSH.String():
return ExchangeSH, code[2:], nil
case ExchangeSZ.String():
return ExchangeSZ, code[2:], nil
case ExchangeBJ.String():
return ExchangeBJ, code[2:], nil
default:
return 0, "", fmt.Errorf("股票代码错误,例如:SZ000001")
}
}
func FloatUnit(f float64) (float64, string) {
m := []string{"万", "亿"}
unit := ""
for i := 0; f > 1e4 && i < len(m); f /= 1e4 {
unit = m[i]
}
return f, unit
}
func FloatUnitString(f float64) string {
m := []string{"万", "亿", "万亿", "亿亿", "万亿亿", "亿亿亿"}
unit := ""
for i := 0; f > 1e4 && i < len(m); i++ {
unit = m[i]
f /= 1e4
}
if unit == "" {
return conv.String(f)
}
return fmt.Sprintf("%0.2f%s", f, unit)
}
func IntUnitString(n int) string {
return FloatUnitString(float64(n))
}
func Int64UnitString(n int64) string {
return FloatUnitString(float64(n))
}
func GetHourMinute(bs [2]byte) string {
n := Uint16(bs[:])
h := n / 60
m := n % 60
return fmt.Sprintf("%02d:%02d", h, m)
}
func GetTime(bs [4]byte, Type uint8) time.Time {
switch Type {
case TypeKlineMinute, TypeKlineMinute2, TypeKline5Minute, TypeKline15Minute, TypeKline30Minute, TypeKline60Minute:
yearMonthDay := Uint16(bs[:2])
hourMinute := Uint16(bs[2:4])
year := int(yearMonthDay>>11 + 2004)
month := yearMonthDay % 2048 / 100
day := int((yearMonthDay % 2048) % 100)
hour := int(hourMinute / 60)
minute := int(hourMinute % 60)
return time.Date(year, time.Month(month), day, hour, minute, 0, 0, time.Local)
default:
yearMonthDay := Uint32(bs[:4])
year := int(yearMonthDay / 10000)
month := int((yearMonthDay % 10000) / 100)
day := int(yearMonthDay % 100)
return time.Date(year, time.Month(month), day, 15, 0, 0, 0, time.Local)
}
}
func basePrice(code string) Price {
if len(code) < 2 {
return 1
}
switch code[:1] {
case "8":
return 1
}
switch code[:2] {
case "60", "30", "68", "00", "92", "43":
return 1
default:
return 10
}
}
func getVolume(val uint32) (volume float64) {
ivol := int32(val)
logpoint := ivol >> (8 * 3)
//hheax := ivol >> (8 * 3) // [3]
hleax := (ivol >> (8 * 2)) & 0xff // [2]
lheax := (ivol >> 8) & 0xff //[1]
lleax := ivol & 0xff //[0]
//dbl_1 := 1.0
//dbl_2 := 2.0
//dbl_128 := 128.0
dwEcx := logpoint*2 - 0x7f
dwEdx := logpoint*2 - 0x86
dwEsi := logpoint*2 - 0x8e
dwEax := logpoint*2 - 0x96
tmpEax := dwEcx
if dwEcx < 0 {
tmpEax = -dwEcx
} else {
tmpEax = dwEcx
}
dbl_xmm6 := 0.0
dbl_xmm6 = math.Pow(2.0, float64(tmpEax))
if dwEcx < 0 {
dbl_xmm6 = 1.0 / dbl_xmm6
}
dbl_xmm4 := 0.0
dbl_xmm0 := 0.0
if hleax > 0x80 {
tmpdbl_xmm3 := 0.0
//tmpdbl_xmm1 := 0.0
dwtmpeax := dwEdx + 1
tmpdbl_xmm3 = math.Pow(2.0, float64(dwtmpeax))
dbl_xmm0 = math.Pow(2.0, float64(dwEdx)) * 128.0
dbl_xmm0 += float64(hleax&0x7f) * tmpdbl_xmm3
dbl_xmm4 = dbl_xmm0
} else {
if dwEdx >= 0 {
dbl_xmm0 = math.Pow(2.0, float64(dwEdx)) * float64(hleax)
} else {
dbl_xmm0 = (1 / math.Pow(2.0, float64(dwEdx))) * float64(hleax)
}
dbl_xmm4 = dbl_xmm0
}
dbl_xmm3 := math.Pow(2.0, float64(dwEsi)) * float64(lheax)
dbl_xmm1 := math.Pow(2.0, float64(dwEax)) * float64(lleax)
if (hleax & 0x80) > 0 {
dbl_xmm3 *= 2.0
dbl_xmm1 *= 2.0
}
volume = dbl_xmm6 + dbl_xmm4 + dbl_xmm3 + dbl_xmm1
return
}
func getVolume2(val uint32) float64 {
ivol := int32(val)
logpoint := ivol >> 24 // 提取最高字节原8*3移位
hleax := (ivol >> 16) & 0xff // 提取次高字节
lheax := (ivol >> 8) & 0xff // 提取第三字节
lleax := ivol & 0xff // 提取最低字节
dwEcx := logpoint*2 - 0x7f // 基础指数计算
dbl_xmm6 := math.Exp2(float64(dwEcx)) // 核心指数计算仅一次
// 计算dbl_xmm4
var dbl_xmm4 float64
if hleax > 0x80 {
// 高位分支:合并指数计算
dbl_xmm4 = dbl_xmm6 * (64.0 + float64(hleax&0x7f)) / 64.0
} else {
// 低位分支:复用核心指数
dbl_xmm4 = dbl_xmm6 * float64(hleax) / 128.0
}
// 计算缩放因子
scale := 1.0
if (hleax & 0x80) != 0 {
scale = 2.0
}
// 预计算常量的倒数,优化除法
const (
inv32768 = 1.0 / 32768.0 // 2^15
inv8388608 = 1.0 / 8388608.0 // 2^23
)
// 计算低位分量
dbl_xmm3 := dbl_xmm6 * float64(lheax) * inv32768 * scale
dbl_xmm1 := dbl_xmm6 * float64(lleax) * inv8388608 * scale
// 合计最终结果
return dbl_xmm6 + dbl_xmm4 + dbl_xmm3 + dbl_xmm1
}
// IsStock 是否是股票,示例sz000001
func IsStock(code string) bool {
return IsSZStock(code) || IsSHStock(code) || IsBJStock(code)
//if len(code) != 8 {
// 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() &&
// (code[2:3] == "0" || code[2:4] == "30"):
// return true
//}
//return false
}
func IsSZStock(code string) bool {
return len(code) == 8 && strings.ToLower(code[0:2]) == ExchangeSZ.String() && (code[2:3] == "0" || code[2:4] == "30")
}
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:4] == "43" || code[2:3] == "8")
}
// IsETF 是否是基金,示例sz159558
func IsETF(code string) bool {
if len(code) != 8 {
return false
}
code = strings.ToLower(code)
switch {
case code[0:2] == ExchangeSH.String() &&
(code[2:5] == "510" || code[2:5] == "511" || code[2:5] == "512" || code[2:5] == "513" || code[2:5] == "515"):
return true
case code[0:2] == ExchangeSZ.String() &&
(code[2:5] == "159"):
return true
}
return false
}
// AddPrefix 添加股票/基金代码前缀,针对股票/基金生效,例如000001,会增加前缀sz000001(平安银行),而不是sh000001(上证指数)
func AddPrefix(code string) string {
if len(code) == 6 {
switch {
case code[:1] == "6":
//上海股票
code = ExchangeSH.String() + code
case code[:1] == "0":
//深圳股票
code = ExchangeSZ.String() + code
case code[:2] == "30":
//深圳股票
code = ExchangeSZ.String() + code
case code[:3] == "510" || code[:3] == "511" || code[:3] == "512" || code[:3] == "513" || code[:3] == "515":
//上海基金
code = ExchangeSH.String() + code
case code[:3] == "159":
//深圳基金
code = ExchangeSZ.String() + code
case code[:1] == "8" || code[:2] == "92" || code[:2] == "43":
//北京股票
code = ExchangeBJ.String() + code
}
}
return code
}
func minutes(t time.Time) int {
return t.Hour()*60 + t.Minute()
}