80 lines
3.1 KiB
Go
80 lines
3.1 KiB
Go
package model
|
|
|
|
import (
|
|
"encoding/json"
|
|
"time"
|
|
|
|
"git.sundynix.cn/Blizzard/Veyra/internal/apperror"
|
|
)
|
|
|
|
// Screening rules belong to an immutable strategy version, and are opt-in.
|
|
type ScreeningSecurity struct {
|
|
Symbol string `json:"symbol"`
|
|
Exchange string `json:"exchange"`
|
|
}
|
|
type ScreeningParameters struct {
|
|
Securities []ScreeningSecurity `json:"securities"`
|
|
EntryBandBPS int64 `json:"entry_band_bps"`
|
|
StopLossBPS int64 `json:"stop_loss_bps"`
|
|
TargetBPS int64 `json:"target_bps"`
|
|
ValidHours int `json:"valid_hours"`
|
|
}
|
|
|
|
func ValidateScreening(p *ScreeningParameters) error {
|
|
if p == nil {
|
|
return nil
|
|
}
|
|
if len(p.Securities) < 1 || len(p.Securities) > 100 || p.EntryBandBPS < 0 || p.EntryBandBPS > 1000 || p.StopLossBPS <= p.EntryBandBPS || p.StopLossBPS > 5000 || p.TargetBPS <= p.EntryBandBPS || p.TargetBPS > 10000 || p.ValidHours < 1 || p.ValidHours > 168 {
|
|
return apperror.ErrInvalidInput
|
|
}
|
|
seen := map[ScreeningSecurity]bool{}
|
|
for _, v := range p.Securities {
|
|
if !symbolPattern.MatchString(v.Symbol) || (v.Exchange != "SSE" && v.Exchange != "SZSE") || seen[v] {
|
|
return apperror.ErrInvalidInput
|
|
}
|
|
seen[v] = true
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type ScreeningTarget struct {
|
|
StrategyID string
|
|
StrategyName string
|
|
Version StrategyVersion
|
|
Instrument Instrument
|
|
}
|
|
type StrategyScreening struct {
|
|
ID string `db:"id" json:"id"`
|
|
StrategyVersionID string `db:"strategy_version_id" json:"strategy_version_id"`
|
|
StrategyName string `json:"strategy_name"`
|
|
StrategyRevision int `json:"strategy_revision"`
|
|
Symbol string `db:"symbol" json:"symbol"`
|
|
Exchange string `db:"exchange" json:"exchange"`
|
|
BarDate string `db:"bar_date" json:"bar_date"`
|
|
SourceID string `db:"source_id" json:"source_id"`
|
|
SourceVersion int64 `db:"source_version" json:"source_version"`
|
|
SourceAdapter string `db:"source_adapter" json:"source_adapter"`
|
|
ObservedAt time.Time `db:"observed_at" json:"observed_at"`
|
|
ReceivedAt time.Time `db:"received_at" json:"received_at"`
|
|
InputHash string `db:"input_hash" json:"input_hash"`
|
|
Input json.RawMessage `db:"input" json:"input,omitempty"`
|
|
Status string `db:"status" json:"status"`
|
|
CandidateRevisionID *string `db:"candidate_revision_id" json:"candidate_revision_id"`
|
|
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
|
}
|
|
|
|
func (StrategyScreening) TableName() string { return "veyra_strategy_screening" }
|
|
|
|
// Worker state is process-local; durable results remain in StrategyScreening.
|
|
type ScreeningWorkerState struct {
|
|
State string `json:"state"`
|
|
StartedAt *time.Time `json:"started_at"`
|
|
FinishedAt *time.Time `json:"finished_at"`
|
|
Targets int `json:"targets"`
|
|
Completed int `json:"completed"`
|
|
Generated int `json:"generated"`
|
|
Skipped int `json:"skipped"`
|
|
Failed int `json:"failed"`
|
|
Message string `json:"message"`
|
|
}
|