2026-08-25 18:59:18 +08:00
|
|
|
|
package logic
|
2026-08-25 16:40:18 +08:00
|
|
|
|
|
|
|
|
|
|
import (
|
2026-08-27 00:27:56 +08:00
|
|
|
|
"strconv"
|
|
|
|
|
|
"strings"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
2026-08-25 22:35:44 +08:00
|
|
|
|
"big-qmt/go-client/config"
|
|
|
|
|
|
"big-qmt/go-client/libs"
|
2026-08-25 16:40:18 +08:00
|
|
|
|
"big-qmt/go-client/sdk"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-08-26 23:27:42 +08:00
|
|
|
|
func openSignal(client *sdk.Client, ticks map[string]sdk.Tick, openSignals []*libs.SignalItem) {
|
2026-08-26 02:10:05 +08:00
|
|
|
|
for _, item := range openSignals {
|
2026-08-27 00:27:56 +08:00
|
|
|
|
// 验证信号配置的时间区间
|
|
|
|
|
|
if !CheckTimezone(item.SignalKey) {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
2026-08-26 12:48:48 +08:00
|
|
|
|
// 是否有锁
|
2026-08-26 16:37:06 +08:00
|
|
|
|
if OrderBook.IsLock("BUY", item.Code) {
|
2026-08-25 16:40:18 +08:00
|
|
|
|
continue
|
|
|
|
|
|
}
|
2026-08-26 12:48:48 +08:00
|
|
|
|
// 验证价格
|
2026-08-26 02:10:05 +08:00
|
|
|
|
price := ticks[item.Code].LastPrice
|
2026-08-25 16:40:18 +08:00
|
|
|
|
if price <= 0 {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
2026-08-26 12:48:48 +08:00
|
|
|
|
// 防止接飞刀
|
|
|
|
|
|
if !OpenWatch.Triggered("开仓", item.Code, price) {
|
2026-08-25 16:40:18 +08:00
|
|
|
|
continue
|
|
|
|
|
|
}
|
2026-08-26 12:48:48 +08:00
|
|
|
|
// 计算开仓数量
|
2026-08-26 02:10:05 +08:00
|
|
|
|
volume := libs.CalcBuyVolume(price, config.Account.BuyValue)
|
2026-08-25 16:40:18 +08:00
|
|
|
|
if volume <= 0 {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
2026-08-26 12:48:48 +08:00
|
|
|
|
// 开仓
|
2026-08-26 16:37:06 +08:00
|
|
|
|
orderID := NewOrderID("base")
|
|
|
|
|
|
if !OrderBook.Place(client, sdk.OpBuy, item.Code, volume, orderID) {
|
2026-08-25 16:40:18 +08:00
|
|
|
|
continue
|
|
|
|
|
|
}
|
2026-08-26 16:37:06 +08:00
|
|
|
|
// 保存状态
|
2026-08-26 12:48:48 +08:00
|
|
|
|
QuantState.Set(&StateItem{Code: item.Code, BaseOrderId: orderID, BaseQty: volume, BaseCost: price, BaseStatus: StatusIng})
|
|
|
|
|
|
if err := QuantState.Save(); err != nil {
|
|
|
|
|
|
logf("ERROR", "%v", err)
|
|
|
|
|
|
}
|
2026-08-26 02:10:05 +08:00
|
|
|
|
logf("INFO", "[ZT][开仓] %s 买入 %d 股", item.Code, volume)
|
2026-08-25 16:40:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-08-27 00:27:56 +08:00
|
|
|
|
|
|
|
|
|
|
// 当前时间区间验证 *代表全时间段,9:30-10:30,13:30-14:30 代表2个时间段
|
|
|
|
|
|
func CheckTimezone(sk string) bool {
|
|
|
|
|
|
timezone := strings.TrimSpace(config.Global.Signals[sk].Timezone)
|
|
|
|
|
|
if timezone == "*" {
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
parse := func(value string) (int, bool) {
|
|
|
|
|
|
parts := strings.Split(strings.TrimSpace(value), ":")
|
|
|
|
|
|
if len(parts) != 2 {
|
|
|
|
|
|
return 0, false
|
|
|
|
|
|
}
|
|
|
|
|
|
hour, errHour := strconv.Atoi(parts[0])
|
|
|
|
|
|
minute, errMinute := strconv.Atoi(parts[1])
|
|
|
|
|
|
if errHour != nil || errMinute != nil || hour < 0 || hour > 23 || minute < 0 || minute > 59 {
|
|
|
|
|
|
return 0, false
|
|
|
|
|
|
}
|
|
|
|
|
|
return hour*60 + minute, true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
current := time.Now()
|
|
|
|
|
|
now := current.Hour()*60 + current.Minute()
|
|
|
|
|
|
for _, section := range strings.Split(timezone, ",") {
|
|
|
|
|
|
bounds := strings.Split(strings.TrimSpace(section), "-")
|
|
|
|
|
|
if len(bounds) != 2 {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
start, startOK := parse(bounds[0])
|
|
|
|
|
|
end, endOK := parse(bounds[1])
|
|
|
|
|
|
if !startOK || !endOK {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
if (start <= end && now >= start && now <= end) ||
|
|
|
|
|
|
(start > end && (now >= start || now <= end)) {
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|