feat: 自动候选筛选与股票研究工作台
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
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"`
|
||||
}
|
||||
Reference in New Issue
Block a user