feat(交易信号): 持仓状态持久化至 Redis 以保障重启与多实例下均价计算准确

Motivation:
持仓与已处理信号的状态此前仅存于进程内存,服务重启或多实例部署后会丢失,导致加仓均价、仓位大小等计算失真,重复信号也无法跨实例幂等。通过将状态持久化到 Redis,保证持仓跟踪跨重启、跨实例连续一致,提升通知内容的准确性与可靠性。

Changes:

* 新增持仓存储抽象,支持内存与 Redis 两种实现,持仓状态与已处理信号快照按 TTL 持久化
* 持仓变更通过 Redis 事务管道原子提交,保证状态更新与幂等记录一致写入
* 存储写入失败时消息进入重试而非直接确认,避免状态丢失导致通知失真
* 缓存层新增原始值读取与批量事务写入能力,并在订阅器初始化时注入 Redis 依赖
* 补充跨实例持久化、幂等去重与存储失败场景的测试覆盖
This commit is contained in:
2026-08-23 14:43:53 +08:00
parent f8e74738cc
commit b396638438
10 changed files with 404 additions and 46 deletions
+22
View File
@@ -108,6 +108,28 @@ func TestHandleProcessErrorRetryThenDLQ(t *testing.T) {
}
}
func TestHandlePositionStoreErrorRetries(t *testing.T) {
d := HandleMessage(context.Background(), HandleInput{
Body: []byte(`{"action":"OPEN"}`), SourceName: "s", MaxRetry: 3,
}, stubConverter{err: tradesignal.ErrPositionStore},
func(context.Context, string) (*model.Source, error) { return enabledSrc(), nil },
func(context.Context, notify.Request) (notify.Result, error) {
t.Fatal("process should not run")
return notify.Result{}, nil
})
if d != DispositionRetry {
t.Fatalf("%v", d)
}
}
type stubConverter struct {
err error
}
func (s stubConverter) Convert([]byte) (string, map[string]interface{}, error) {
return "", nil, s.err
}
func TestHandleSuccessAckPassesEventAndFormatted(t *testing.T) {
var got notify.Request
d := HandleMessage(context.Background(), HandleInput{