dev 3
This commit is contained in:
@@ -6,15 +6,40 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"big-qmt/go-client/config"
|
||||
"big-qmt/go-client/sdk"
|
||||
)
|
||||
|
||||
const (
|
||||
pendingNone = ""
|
||||
pendingBaseOpening = "base_opening"
|
||||
pendingAdd = "add"
|
||||
pendingSellAdd = "sell_add"
|
||||
pendingSellBase = "sell_base"
|
||||
)
|
||||
|
||||
type SymbolState struct {
|
||||
Code string `json:"code"`
|
||||
BaseQty int `json:"base_qty"`
|
||||
BaseCost float64 `json:"base_cost"`
|
||||
AddQty int `json:"add_qty"`
|
||||
AddCost float64 `json:"add_cost"`
|
||||
Pending string `json:"pending"`
|
||||
Code string `json:"code"`
|
||||
BaseQty int `json:"base_qty"`
|
||||
BaseCost float64 `json:"base_cost"`
|
||||
AddQty int `json:"add_qty"`
|
||||
AddCost float64 `json:"add_cost"`
|
||||
Pending string `json:"pending"`
|
||||
PendingOrderID string `json:"pending_order_id,omitempty"`
|
||||
OrderStatus string `json:"order_status,omitempty"`
|
||||
}
|
||||
|
||||
func setPending(item *SymbolState, pending, orderID string) {
|
||||
item.Pending = pending
|
||||
item.PendingOrderID = orderID
|
||||
item.OrderStatus = "submitted"
|
||||
}
|
||||
|
||||
func clearPending(item *SymbolState) {
|
||||
item.Pending = pendingNone
|
||||
item.PendingOrderID = ""
|
||||
item.OrderStatus = ""
|
||||
}
|
||||
|
||||
type filePayload struct {
|
||||
@@ -26,6 +51,7 @@ type ZTState struct {
|
||||
path string
|
||||
Items map[string]*SymbolState
|
||||
LoadError string
|
||||
fresh bool
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
@@ -34,14 +60,34 @@ var (
|
||||
states = map[string]*ZTState{}
|
||||
)
|
||||
|
||||
func getState(cfg Config) *ZTState {
|
||||
// BootstrapState 在状态文件首次不存在时,将启动前已有持仓登记为底仓。
|
||||
func BootstrapState(positions []sdk.Position) {
|
||||
state := getState()
|
||||
if !state.Fresh() {
|
||||
return
|
||||
}
|
||||
for _, pos := range positions {
|
||||
code := pos.StockCode
|
||||
if code == "" || pos.Volume <= 0 || pos.OpenPrice <= 0 {
|
||||
continue
|
||||
}
|
||||
item := state.Ensure(code)
|
||||
item.BaseQty, item.BaseCost = pos.Volume, pos.OpenPrice
|
||||
logf("WARNING", "[ZT][状态] %s 首次接管为底仓 数量=%d 成本=%.2f", code, pos.Volume, pos.OpenPrice)
|
||||
}
|
||||
state.completeBootstrap()
|
||||
state.Save()
|
||||
}
|
||||
|
||||
func getState() *ZTState {
|
||||
statesMu.Lock()
|
||||
defer statesMu.Unlock()
|
||||
if s, ok := states[cfg.AccountID]; ok {
|
||||
accountID := config.Account.AccountID
|
||||
if s, ok := states[accountID]; ok {
|
||||
return s
|
||||
}
|
||||
s := loadZTState(cfg.DataDir, cfg.AccountID)
|
||||
states[cfg.AccountID] = s
|
||||
s := loadZTState(config.Global.QMTDataDir, accountID)
|
||||
states[accountID] = s
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -53,19 +99,23 @@ func loadZTState(dataDir, accountID string) *ZTState {
|
||||
raw, err := os.ReadFile(st.path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
st.fresh = true
|
||||
return st
|
||||
}
|
||||
st.rebuild(err)
|
||||
st.LoadError = err.Error()
|
||||
logf("ERROR", "[ZT][状态] 读取状态文件失败: %v", err)
|
||||
return st
|
||||
}
|
||||
var payload filePayload
|
||||
if err := json.Unmarshal(raw, &payload); err != nil || payload.Version != 1 {
|
||||
st.rebuild(fmt.Errorf("状态文件版本无效"))
|
||||
st.LoadError = "状态文件版本无效"
|
||||
logf("ERROR", "[ZT][状态] %s", st.LoadError)
|
||||
return st
|
||||
}
|
||||
data := payload.Data
|
||||
if data == nil {
|
||||
st.rebuild(fmt.Errorf("状态文件内容无效"))
|
||||
st.LoadError = "状态文件内容无效"
|
||||
logf("ERROR", "[ZT][状态] %s", st.LoadError)
|
||||
return st
|
||||
}
|
||||
symbolsAny, _ := data["symbols"]
|
||||
@@ -91,19 +141,16 @@ func loadZTState(dataDir, accountID string) *ZTState {
|
||||
return st
|
||||
}
|
||||
|
||||
func (s *ZTState) rebuild(err error) {
|
||||
if err := os.Remove(s.path); err != nil && !os.IsNotExist(err) {
|
||||
s.LoadError = err.Error()
|
||||
logf("ERROR", "[ZT][状态] 状态文件重建失败: %s", s.LoadError)
|
||||
return
|
||||
}
|
||||
s.Items = map[string]*SymbolState{}
|
||||
if saveErr := s.saveUnlocked(); saveErr != nil {
|
||||
s.LoadError = fmt.Sprintf("%v;重建失败: %v", err, saveErr)
|
||||
logf("ERROR", "[ZT][状态] 状态文件重建失败: %s", s.LoadError)
|
||||
return
|
||||
}
|
||||
logf("WARNING", "[ZT][状态] 状态文件损坏,已删除并重建: %v", err)
|
||||
func (s *ZTState) Fresh() bool {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.fresh
|
||||
}
|
||||
|
||||
func (s *ZTState) completeBootstrap() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.fresh = false
|
||||
}
|
||||
|
||||
func (s *ZTState) Get(code string) *SymbolState {
|
||||
@@ -146,7 +193,9 @@ func (s *ZTState) Save() {
|
||||
return
|
||||
}
|
||||
if err := s.saveUnlocked(); err != nil {
|
||||
s.LoadError = err.Error()
|
||||
logf("ERROR", "[ZT][状态] 保存失败: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user