Files
injoyai-tdx/protocol/model_trade.go

234 lines
5.1 KiB
Go
Raw Permalink 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 (
"errors"
"fmt"
"github.com/injoyai/base/types"
"github.com/injoyai/conv"
"time"
)
type TradeResp struct {
Count uint16
List Trades
}
// Trade 分时成交todo 时间没有到秒,客户端上也没有,东方客户端能显示秒
type Trade struct {
Time time.Time //时间, 09:30
Price Price //价格
Volume int //成交量,手
Status int //0是买1是卖2中性/汇总 中途也可能出现2,例20241115(sz000001)的14:56
Number int //单数,历史数据该字段无效
}
func (this *Trade) String() string {
return fmt.Sprintf("%s \t%-6s \t%-6s \t%-6d(手) \t%-4d(单) \t%-4s",
this.Time, this.Price, this.Amount(), this.Volume, this.Number, this.StatusString())
}
// Amount 成交额
func (this *Trade) Amount() Price {
return this.Price * Price(this.Volume*100)
}
func (this *Trade) StatusString() string {
switch this.Status {
case 0:
return "买入"
case 1:
return "卖出"
default:
return ""
}
}
// AvgVolume 平均每单成交量
func (this *Trade) AvgVolume() float64 {
return float64(this.Volume) / float64(this.Number)
}
// AvgPrice 平均每单成交金额
func (this *Trade) AvgPrice() Price {
return Price(this.AvgVolume() * float64(this.Price) * 100)
}
// IsBuy 是否是买单
func (this *Trade) IsBuy() bool {
return this.Status == 0
}
// IsSell 是否是卖单
func (this *Trade) IsSell() bool {
return this.Status == 1
}
type trade struct{}
func (trade) Frame(code string, start, count uint16) (*Frame, error) {
exchange, number, err := DecodeCode(code)
if err != nil {
return nil, err
}
codeBs := []byte(number)
codeBs = append(codeBs, Bytes(start)...)
codeBs = append(codeBs, Bytes(count)...)
return &Frame{
Control: Control01,
Type: TypeMinuteTrade,
Data: append([]byte{exchange.Uint8(), 0x0}, codeBs...),
}, nil
}
func (trade) Decode(bs []byte, c TradeCache) (*TradeResp, error) {
_, code, err := DecodeCode(c.Code)
if err != nil {
return nil, err
}
if len(bs) < 2 {
return nil, errors.New("数据长度不足")
}
resp := &TradeResp{
Count: Uint16(bs[:2]),
}
bs = bs[2:]
lastPrice := Price(0)
for i := uint16(0); i < resp.Count; i++ {
timeStr := GetHourMinute([2]byte(bs[:2]))
t, err := time.Parse("2006010215:04", c.Date+timeStr)
if err != nil {
return nil, err
}
mt := &Trade{Time: t}
var sub Price
bs, sub = GetPrice(bs[2:])
lastPrice += sub * 10 //把分转换成厘
mt.Price = lastPrice / basePrice(code)
bs, mt.Volume = CutInt(bs)
bs, mt.Number = CutInt(bs)
bs, mt.Status = CutInt(bs)
bs, _ = CutInt(bs) //这个得到的是0不知道是啥
resp.List = append(resp.List, mt)
}
return resp, nil
}
type Trades []*Trade
// Klines 合并分时成交成k线
func (this Trades) Klines() Klines {
//按天分割
m := make(types.SortMap[int64, Trades])
for _, v := range this {
//获取当天零点的时间戳
unix := time.Date(v.Time.Year(), v.Time.Month(), v.Time.Day(), 0, 0, 0, 0, v.Time.Location()).Unix()
m[unix] = append(m[unix], v)
}
//按天排序
mKline := types.SortMap[int64, Klines]{}
for date, v := range m {
//生成一分钟k线
t := time.Unix(date, 0)
mKline[date] = v.klinesForDay(t)
}
//按时间排序
lss := mKline.Sort()
ls := Klines{}
for _, v := range lss {
ls = append(ls, v...)
}
return ls
}
// Kline 合并分时成交成1个k线,注意分时成交时间保持一致
func (this Trades) Kline(t time.Time, last Price) *Kline {
k := &Kline{
Time: t,
Last: last,
Open: last,
High: last,
Low: last,
Close: last,
}
first := 0
for _, v := range this {
if v.Price <= 0 {
continue
}
switch first {
case 0:
k.Open = v.Price
k.High = v.Price
k.Low = v.Price
k.Close = v.Price
default:
k.High = conv.Select(k.High < v.Price, v.Price, k.High)
k.Low = conv.Select(k.Low > v.Price, v.Price, k.Low)
}
k.Close = v.Price
k.Volume += int64(v.Volume)
k.Order += v.Number
k.Amount += v.Price * Price(v.Volume) * 100
first++
}
return k
}
// kline1 生成一分钟k线,一天
func (this Trades) klinesForDay(date time.Time) Klines {
_930 := 570 //9:30 的分钟
_1130 := 690 //11:30 的分钟
_1300 := 780 //13:00 的分钟
_1500 := 900 //15:00 的分钟
keys := []int(nil)
//早上
m := map[int]Trades{}
for i := 1; i <= 120; i++ {
keys = append(keys, _930+i)
m[_930+i] = []*Trade{}
}
//下午
for i := 1; i <= 120; i++ {
keys = append(keys, _1300+i)
m[_1300+i] = []*Trade{}
}
//获取开盘价,有可能前几分钟没有数据,先遍历一遍
var open Price
for _, v := range this {
if v.Price > 0 {
open = v.Price
break
}
}
//分组,按
for _, v := range this {
ms := minutes(v.Time)
t := conv.Select(ms < _930, _930, ms)
t++
t = conv.Select(t > _1130 && t <= _1300, _1130, t)
t = conv.Select(t > _1500, _1500, t)
m[t] = append(m[t], v)
}
//合并
ls := []*Kline(nil)
for _, v := range keys {
k := m[v].Kline(time.Date(date.Year(), date.Month(), date.Day(), v/60, v%60, 0, 0, date.Location()), open)
open = k.Close
ls = append(ls, k)
}
return ls
}
type TradeCache struct {
Date string //日期
Code string //计算倍数
}