统一单入口 Go 工程与前端依赖目录,增加后台供应商配置、候选审核、计划预算、成交冲正、持仓复盘和站内通知。接入策略版本、Python 回测、可配置 A 股公开行情及 zap 文件轮转日志,补充迁移、契约与验证。
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
type HistoricalBar struct {
|
||||
Date string `json:"date"`
|
||||
Open string `json:"open"`
|
||||
High string `json:"high"`
|
||||
Low string `json:"low"`
|
||||
Close string `json:"close"`
|
||||
}
|
||||
type BacktestInput struct {
|
||||
DataOrigin string `json:"data_origin"`
|
||||
SourceVersion int64 `json:"source_version"`
|
||||
DataReceivedAt *time.Time `json:"data_received_at"`
|
||||
StrategyVersionID string `json:"strategy_version_id"`
|
||||
DatasetName string `json:"dataset_name"`
|
||||
Symbol string `json:"symbol"`
|
||||
InitialCash string `json:"initial_cash"`
|
||||
Bars []HistoricalBar `json:"bars"`
|
||||
}
|
||||
type ResearchInput struct {
|
||||
BacktestInput
|
||||
Parameters StrategyParameters `json:"parameters"`
|
||||
}
|
||||
type BacktestRun struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
StrategyVersionID string `db:"strategy_version_id" json:"strategy_version_id"`
|
||||
Status string `db:"status" json:"status"`
|
||||
Input json.RawMessage `db:"input" json:"input,omitempty"`
|
||||
InputHash string `db:"input_hash" json:"input_hash"`
|
||||
Result json.RawMessage `db:"result" json:"result"`
|
||||
Failure string `db:"failure" json:"failure"`
|
||||
LeaseID *string `db:"lease_id" json:"-"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
FinishedAt *time.Time `db:"finished_at" json:"finished_at"`
|
||||
}
|
||||
|
||||
func (BacktestRun) TableName() string { return "veyra_backtest_run" }
|
||||
@@ -0,0 +1,183 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"git.sundynix.cn/Blizzard/Veyra/internal/apperror"
|
||||
)
|
||||
|
||||
type Candidate struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
LatestRevision int `db:"latest_revision" json:"latest_revision"`
|
||||
PublishedRevisionID *string `db:"published_revision_id" json:"published_revision_id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
}
|
||||
|
||||
func (Candidate) TableName() string { return "veyra_candidate" }
|
||||
|
||||
type CandidateEvidence struct {
|
||||
SourceURL string `json:"source_url"`
|
||||
Locator string `json:"locator"`
|
||||
Summary string `json:"summary"`
|
||||
}
|
||||
type CandidateReferencePlan struct {
|
||||
EntryLow string `json:"entry_low"`
|
||||
EntryHigh string `json:"entry_high"`
|
||||
StopLoss string `json:"stop_loss"`
|
||||
TargetPrice string `json:"target_price"`
|
||||
}
|
||||
type CandidateContent struct {
|
||||
Symbol string `json:"symbol"`
|
||||
Exchange string `json:"exchange"`
|
||||
Name string `json:"name"`
|
||||
StrategyCode string `json:"strategy_code"`
|
||||
Title string `json:"title"`
|
||||
Thesis string `json:"thesis"`
|
||||
Evidence []CandidateEvidence `json:"evidence"`
|
||||
CounterEvidence string `json:"counter_evidence"`
|
||||
Invalidation string `json:"invalidation"`
|
||||
ReferencePlan CandidateReferencePlan `json:"reference_plan"`
|
||||
}
|
||||
|
||||
type CandidateRevision struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
CandidateID string `db:"candidate_id" json:"candidate_id"`
|
||||
Revision int `db:"revision" json:"revision"`
|
||||
Version int64 `db:"version" json:"version"`
|
||||
Status string `db:"status" json:"status"`
|
||||
DataMode string `db:"data_mode" json:"data_mode"`
|
||||
Snapshot CandidateContent `db:"snapshot" json:"content"`
|
||||
AuthorID string `db:"author_id" json:"author_id"`
|
||||
ObservedAt time.Time `db:"observed_at" json:"observed_at"`
|
||||
ValidUntil time.Time `db:"valid_until" json:"valid_until"`
|
||||
PublishedAt *time.Time `db:"published_at" json:"published_at"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
IsLatest bool `json:"is_latest"`
|
||||
WithdrawalReason string `json:"withdrawal_reason"`
|
||||
IsActivePublication bool `json:"is_active_publication"`
|
||||
}
|
||||
|
||||
func (CandidateRevision) TableName() string { return "veyra_candidate_revision" }
|
||||
|
||||
type CandidateReview struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
RevisionID string `db:"revision_id" json:"revision_id"`
|
||||
ReviewerID string `db:"reviewer_id" json:"reviewer_id"`
|
||||
Decision string `db:"decision" json:"decision"`
|
||||
InputVersion int64 `db:"input_version" json:"input_version"`
|
||||
Reason string `db:"reason" json:"reason"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
}
|
||||
|
||||
func (CandidateReview) TableName() string { return "veyra_candidate_review" }
|
||||
|
||||
type CandidateDraftInput struct {
|
||||
Content CandidateContent `json:"content"`
|
||||
ObservedAt time.Time `json:"observed_at"`
|
||||
ValidUntil time.Time `json:"valid_until"`
|
||||
}
|
||||
type CandidateRevisionInput struct {
|
||||
CandidateDraftInput
|
||||
ExpectedVersion int64 `json:"expected_version"`
|
||||
}
|
||||
type CandidateActionInput struct {
|
||||
ExpectedVersion int64 `json:"expected_version"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
type CandidateDetail struct {
|
||||
CandidateRevision
|
||||
Reviews []CandidateReview `json:"reviews"`
|
||||
}
|
||||
|
||||
var symbolPattern = regexp.MustCompile(`^[0-9]{6}$`)
|
||||
|
||||
func textValid(s string, max int) bool {
|
||||
return strings.TrimSpace(s) != "" && utf8.RuneCountInString(s) <= max
|
||||
}
|
||||
|
||||
func ValidateDraft(input CandidateDraftInput, now time.Time) error {
|
||||
c := input.Content
|
||||
if !symbolPattern.MatchString(c.Symbol) || c.Exchange != "SSE" && c.Exchange != "SZSE" || !textValid(c.Name, 80) || !textValid(c.Title, 120) || !textValid(c.Thesis, 2000) || !textValid(c.CounterEvidence, 1000) || !textValid(c.Invalidation, 1000) {
|
||||
return apperror.ErrInvalidInput
|
||||
}
|
||||
switch c.StrategyCode {
|
||||
case "trend_following", "pullback", "event_driven":
|
||||
default:
|
||||
return apperror.ErrInvalidInput
|
||||
}
|
||||
if len(c.Evidence) < 1 || len(c.Evidence) > 8 {
|
||||
return apperror.ErrInvalidInput
|
||||
}
|
||||
for _, e := range c.Evidence {
|
||||
u, err := url.Parse(e.SourceURL)
|
||||
if err != nil || u.Scheme != "https" || u.Hostname() == "" || u.User != nil || len(e.SourceURL) > 2048 || !textValid(e.Locator, 200) || !textValid(e.Summary, 1000) {
|
||||
return apperror.ErrInvalidInput
|
||||
}
|
||||
}
|
||||
low, e1 := ParseMoney(c.ReferencePlan.EntryLow)
|
||||
high, e2 := ParseMoney(c.ReferencePlan.EntryHigh)
|
||||
stop, e3 := ParseMoney(c.ReferencePlan.StopLoss)
|
||||
target, e4 := ParseMoney(c.ReferencePlan.TargetPrice)
|
||||
if e1 != nil || e2 != nil || e3 != nil || e4 != nil || stop <= 0 || stop >= low || low > high || target <= high {
|
||||
return apperror.ErrInvalidInput
|
||||
}
|
||||
if input.ObservedAt.IsZero() || input.ObservedAt.After(now.Add(time.Minute)) || input.ObservedAt.Before(now.Add(-24*time.Hour)) || !input.ValidUntil.After(now) || input.ValidUntil.After(now.Add(7*24*time.Hour)) || !input.ValidUntil.After(input.ObservedAt) {
|
||||
return apperror.ErrInvalidInput
|
||||
}
|
||||
encoded, err := json.Marshal(c)
|
||||
if err != nil || len(encoded) > 12<<10 {
|
||||
return apperror.ErrInvalidInput
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NextStatus is the shared state rule; repositories recheck it under the aggregate lock.
|
||||
func NextStatus(r CandidateRevision, action string, now time.Time) (string, error) {
|
||||
if action == "WITHDRAW" {
|
||||
if r.Status != "PUBLISHED" || !r.IsActivePublication {
|
||||
return "", apperror.ErrConflict
|
||||
}
|
||||
return "WITHDRAWN", nil
|
||||
}
|
||||
if !r.IsLatest {
|
||||
return "", apperror.ErrConflict
|
||||
}
|
||||
switch action {
|
||||
case "SUBMIT":
|
||||
if r.Status != "DRAFT" {
|
||||
return "", apperror.ErrConflict
|
||||
}
|
||||
if err := ValidateDraft(CandidateDraftInput{Content: r.Snapshot, ObservedAt: r.ObservedAt, ValidUntil: r.ValidUntil}, now); err != nil {
|
||||
return "", apperror.ErrInvalidInput
|
||||
}
|
||||
return "PENDING_REVIEW", nil
|
||||
case "PUBLISH":
|
||||
if r.Status != "PENDING_REVIEW" {
|
||||
return "", apperror.ErrConflict
|
||||
}
|
||||
if err := ValidateDraft(CandidateDraftInput{Content: r.Snapshot, ObservedAt: r.ObservedAt, ValidUntil: r.ValidUntil}, now); err != nil {
|
||||
return "", apperror.ErrInvalidInput
|
||||
}
|
||||
return "PUBLISHED", nil
|
||||
case "RETURN":
|
||||
if r.Status != "PENDING_REVIEW" {
|
||||
return "", apperror.ErrConflict
|
||||
}
|
||||
return "RETURNED", nil
|
||||
default:
|
||||
return "", apperror.ErrInvalidInput
|
||||
}
|
||||
}
|
||||
|
||||
func ValidateReason(reason string) error {
|
||||
if !textValid(reason, 1000) {
|
||||
return apperror.ErrInvalidInput
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type Instrument struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
Symbol string `db:"symbol" json:"symbol"`
|
||||
Exchange string `db:"exchange" json:"exchange"`
|
||||
Name string `db:"name" json:"name"`
|
||||
Price string `json:"price"`
|
||||
PriceCents int64 `db:"price_cents" json:"-"`
|
||||
ObservedAt time.Time `db:"observed_at" json:"observed_at"`
|
||||
ReceivedAt time.Time `db:"received_at" json:"received_at"`
|
||||
SourceID string `db:"source_id" json:"source_id"`
|
||||
SourceVersion int64 `db:"source_version" json:"source_version"`
|
||||
}
|
||||
|
||||
func (Instrument) TableName() string { return "veyra_instrument" }
|
||||
|
||||
type MarketSync struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
SourceID string `db:"source_id" json:"source_id"`
|
||||
SourceVersion int64 `db:"source_version" json:"source_version"`
|
||||
InstrumentCount int `db:"instrument_count" json:"instrument_count"`
|
||||
Status string `db:"status" json:"status"`
|
||||
Message string `db:"message" json:"message"`
|
||||
StartedAt time.Time `db:"started_at" json:"started_at"`
|
||||
FinishedAt time.Time `db:"finished_at" json:"finished_at"`
|
||||
}
|
||||
|
||||
func (MarketSync) TableName() string { return "veyra_market_sync" }
|
||||
|
||||
type MarketHistory struct {
|
||||
Symbol string `json:"symbol"`
|
||||
Exchange string `json:"exchange"`
|
||||
Name string `json:"name"`
|
||||
Source string `json:"source"`
|
||||
SourceVersion int64 `json:"source_version"`
|
||||
Adjustment string `json:"adjustment"`
|
||||
ReceivedAt time.Time `json:"received_at"`
|
||||
Bars []HistoricalBar `json:"bars"`
|
||||
}
|
||||
@@ -33,4 +33,12 @@ func ParseMoney(value string) (int64, error) {
|
||||
return amount, nil
|
||||
}
|
||||
|
||||
func FormatMoney(minor int64) string { return fmt.Sprintf("%d.%02d", minor/100, minor%100) }
|
||||
func FormatMoney(minor int64) string {
|
||||
sign := ""
|
||||
absolute := uint64(minor)
|
||||
if minor < 0 {
|
||||
sign = "-"
|
||||
absolute = uint64(-(minor + 1)) + 1
|
||||
}
|
||||
return fmt.Sprintf("%s%d.%02d", sign, absolute/100, absolute%100)
|
||||
}
|
||||
|
||||
@@ -19,3 +19,14 @@ func TestExactMoney(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignedLedgerMoney(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
minor int64
|
||||
want string
|
||||
}{{-1, "-0.01"}, {-100, "-1.00"}, {-1005, "-10.05"}, {0, "0.00"}, {-9223372036854775808, "-92233720368547758.08"}} {
|
||||
if got := FormatMoney(tc.minor); got != tc.want {
|
||||
t.Fatalf("got %s want %s", got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type Notification struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
UserID string `db:"user_id" json:"-"`
|
||||
EventID string `db:"event_id" json:"event_id"`
|
||||
PlanID *string `db:"plan_id" json:"plan_id"`
|
||||
Title string `db:"title" json:"title"`
|
||||
Body string `db:"body" json:"body"`
|
||||
ReadAt *time.Time `db:"read_at" json:"read_at"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
}
|
||||
|
||||
func (Notification) TableName() string { return "veyra_notification" }
|
||||
|
||||
type QuoteSnapshot struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
Symbol string `db:"symbol" json:"symbol"`
|
||||
Exchange string `db:"exchange" json:"exchange"`
|
||||
DataMode string `db:"data_mode" json:"data_mode"`
|
||||
PriceMinor int64 `db:"price_minor" json:"-"`
|
||||
Price string `json:"price"`
|
||||
ObservedAt time.Time `db:"observed_at" json:"observed_at"`
|
||||
Version int64 `db:"version" json:"version"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
func (QuoteSnapshot) TableName() string { return "veyra_quote_snapshot" }
|
||||
|
||||
type PlanTrigger struct {
|
||||
ID string `db:"id"`
|
||||
PlanID string `db:"plan_id"`
|
||||
QuoteSnapshotID string `db:"quote_snapshot_id"`
|
||||
QuoteVersion int64 `db:"quote_version"`
|
||||
TriggeredAt time.Time `db:"triggered_at"`
|
||||
}
|
||||
|
||||
func (PlanTrigger) TableName() string { return "veyra_plan_trigger" }
|
||||
|
||||
type NotificationTask struct {
|
||||
OutboxEvent
|
||||
RecipientCount int `json:"recipient_count"`
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
type OutboxEvent struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
AggregateType string `db:"aggregate_type" json:"aggregate_type"`
|
||||
AggregateID string `db:"aggregate_id" json:"aggregate_id"`
|
||||
AggregateVersion int64 `db:"aggregate_version" json:"aggregate_version"`
|
||||
EventType string `db:"event_type" json:"event_type"`
|
||||
Payload json.RawMessage `db:"payload" json:"payload"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
ProcessedAt *time.Time `db:"processed_at" json:"processed_at"`
|
||||
}
|
||||
|
||||
func (OutboxEvent) TableName() string { return "veyra_outbox_event" }
|
||||
@@ -0,0 +1,59 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ProviderConfig struct {
|
||||
QuoteBaseURL string `db:"quote_base_url" json:"quote_base_url"`
|
||||
MarketAdapter string `db:"market_adapter" json:"market_adapter"`
|
||||
HistoryBaseURL string `db:"history_base_url" json:"history_base_url"`
|
||||
ID string `db:"id" json:"id"`
|
||||
ConfigKey string `db:"config_key" json:"config_key"`
|
||||
Name string `db:"name" json:"name"`
|
||||
BaseURL string `db:"base_url" json:"base_url"`
|
||||
ModelName string `db:"model_name" json:"model_name"`
|
||||
WireAPI string `db:"wire_api" json:"wire_api"`
|
||||
ModelCatalogURL string `db:"model_catalog_url" json:"model_catalog_url"`
|
||||
DisableResponseStorage bool `db:"disable_response_storage" json:"disable_response_storage"`
|
||||
RequiresOpenAIAuth bool `db:"requires_openai_auth" json:"requires_openai_auth"`
|
||||
Enabled bool `db:"enabled" json:"enabled"`
|
||||
TimeoutSeconds int `db:"timeout_seconds" json:"timeout_seconds"`
|
||||
EncryptedAPIKey []byte `db:"encrypted_api_key" json:"-"`
|
||||
HasAPIKey bool `json:"has_api_key"`
|
||||
Version int64 `db:"version" json:"version"`
|
||||
LastTestStatus string `db:"last_test_status" json:"last_test_status"`
|
||||
LastTestedAt *time.Time `db:"last_tested_at" json:"last_tested_at"`
|
||||
LastTestMessage string `db:"last_test_message" json:"last_test_message"`
|
||||
UpdatedBy string `db:"updated_by" json:"updated_by"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
func (ProviderConfig) TableName() string { return "veyra_provider_config" }
|
||||
|
||||
type ProviderConfigHistory struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
ConfigID string `db:"config_id" json:"config_id"`
|
||||
Version int64 `db:"version" json:"version"`
|
||||
Snapshot json.RawMessage `db:"snapshot" json:"snapshot"`
|
||||
EncryptedAPIKey []byte `db:"encrypted_api_key" json:"-"`
|
||||
ActorID string `db:"actor_id" json:"actor_id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
}
|
||||
|
||||
func (ProviderConfigHistory) TableName() string { return "veyra_provider_config_history" }
|
||||
|
||||
type AuditEvent struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
ActorID string `db:"actor_id" json:"actor_id"`
|
||||
Action string `db:"action" json:"action"`
|
||||
ObjectType string `db:"object_type" json:"object_type"`
|
||||
ObjectID string `db:"object_id" json:"object_id"`
|
||||
RequestID string `db:"request_id" json:"request_id"`
|
||||
Details json.RawMessage `db:"details" json:"details"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
}
|
||||
|
||||
func (AuditEvent) TableName() string { return "veyra_audit_event" }
|
||||
@@ -0,0 +1,42 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type StrategyParameters struct {
|
||||
ShortWindow int `json:"short_window"`
|
||||
LongWindow int `json:"long_window"`
|
||||
FeeBPS int `json:"fee_bps"`
|
||||
SlippageBPS int `json:"slippage_bps"`
|
||||
LotSize int64 `json:"lot_size"`
|
||||
}
|
||||
type Strategy struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
Code string `db:"code" json:"code"`
|
||||
Name string `db:"name" json:"name"`
|
||||
Status string `db:"status" json:"status"`
|
||||
LatestRevision int `db:"latest_revision" json:"latest_revision"`
|
||||
ActiveVersionID *string `db:"active_version_id" json:"active_version_id"`
|
||||
Version int64 `db:"version" json:"version"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
func (Strategy) TableName() string { return "veyra_strategy" }
|
||||
|
||||
type StrategyVersion struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
StrategyID string `db:"strategy_id" json:"strategy_id"`
|
||||
Revision int `db:"revision" json:"revision"`
|
||||
Algorithm string `db:"algorithm" json:"algorithm"`
|
||||
Parameters StrategyParameters `db:"parameters" json:"parameters"`
|
||||
Description string `db:"description" json:"description"`
|
||||
AuthorID string `db:"author_id" json:"author_id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
}
|
||||
|
||||
func (StrategyVersion) TableName() string { return "veyra_strategy_version" }
|
||||
|
||||
type StrategyDetail struct {
|
||||
Strategy
|
||||
Versions []StrategyVersion `json:"versions"`
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type TradeFill struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
AccountID string `db:"account_id" json:"account_id"`
|
||||
PlanID *string `db:"plan_id" json:"plan_id"`
|
||||
IdempotencyKey string `db:"idempotency_key" json:"idempotency_key"`
|
||||
InputHash string `db:"input_hash" json:"-"`
|
||||
PostedSequence int64 `db:"posted_sequence" json:"-"`
|
||||
Side string `db:"side" json:"side"`
|
||||
Symbol string `db:"symbol" json:"symbol"`
|
||||
Exchange string `db:"exchange" json:"exchange"`
|
||||
Name string `db:"name" json:"name"`
|
||||
Quantity int64 `db:"quantity" json:"quantity,string"`
|
||||
PriceMinor int64 `db:"price_minor" json:"-"`
|
||||
FeeMinor int64 `db:"fee_minor" json:"-"`
|
||||
CashDeltaMinor int64 `db:"cash_delta_minor" json:"-"`
|
||||
RealizedMinor int64 `db:"realized_minor" json:"-"`
|
||||
Price string `json:"price"`
|
||||
Fee string `json:"fee"`
|
||||
CashDelta string `json:"cash_delta"`
|
||||
RealizedPnL string `json:"realized_pnl"`
|
||||
OccurredAt time.Time `db:"occurred_at" json:"occurred_at"`
|
||||
SellableAt *time.Time `db:"sellable_at" json:"sellable_at"`
|
||||
Note string `db:"note" json:"note"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
Reversed bool `json:"reversed"`
|
||||
}
|
||||
|
||||
func (TradeFill) TableName() string { return "veyra_trade_fill" }
|
||||
|
||||
type TradeReversal struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
FillID string `db:"fill_id" json:"fill_id"`
|
||||
ActorID string `db:"actor_id" json:"actor_id"`
|
||||
Reason string `db:"reason" json:"reason"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
}
|
||||
|
||||
func (TradeReversal) TableName() string { return "veyra_trade_reversal" }
|
||||
|
||||
type PositionLot struct {
|
||||
ID string `db:"id"`
|
||||
AccountID string `db:"account_id"`
|
||||
BuyFillID string `db:"buy_fill_id"`
|
||||
Symbol string `db:"symbol"`
|
||||
Exchange string `db:"exchange"`
|
||||
RemainingQuantity int64 `db:"remaining_quantity"`
|
||||
RemainingCostMinor int64 `db:"remaining_cost_minor"`
|
||||
SellableAt time.Time `db:"sellable_at"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
}
|
||||
|
||||
func (PositionLot) TableName() string { return "veyra_position_lot" }
|
||||
|
||||
type FillAllocation struct {
|
||||
ID string `db:"id"`
|
||||
SellFillID string `db:"sell_fill_id"`
|
||||
LotID string `db:"lot_id"`
|
||||
Quantity int64 `db:"quantity"`
|
||||
CostMinor int64 `db:"cost_minor"`
|
||||
}
|
||||
|
||||
func (FillAllocation) TableName() string { return "veyra_fill_allocation" }
|
||||
|
||||
type PositionSummary struct {
|
||||
Symbol string `json:"symbol"`
|
||||
Exchange string `json:"exchange"`
|
||||
Name string `json:"name"`
|
||||
Quantity string `json:"quantity"`
|
||||
SellableQuantity string `json:"sellable_quantity"`
|
||||
CostBasis string `json:"cost_basis"`
|
||||
MarketValue *string `json:"market_value"`
|
||||
UnrealizedPnL *string `json:"unrealized_pnl"`
|
||||
}
|
||||
type LedgerReview struct {
|
||||
TradeCount int64 `json:"trade_count"`
|
||||
SellCount int64 `json:"sell_count"`
|
||||
ProfitableSellCount int64 `json:"profitable_sell_count"`
|
||||
LosingSellCount int64 `json:"losing_sell_count"`
|
||||
BuyOutflow string `json:"buy_outflow"`
|
||||
SellInflow string `json:"sell_inflow"`
|
||||
TotalFees string `json:"total_fees"`
|
||||
}
|
||||
type PortfolioSummary struct {
|
||||
Review LedgerReview `json:"review"`
|
||||
AccountID string `json:"account_id"`
|
||||
CashBalance string `json:"cash_balance"`
|
||||
RealizedPnL string `json:"realized_pnl"`
|
||||
Positions []PositionSummary `json:"positions"`
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type PlanPreview struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
AccountID string `db:"account_id" json:"account_id"`
|
||||
CandidateRevisionID string `db:"candidate_revision_id" json:"candidate_revision_id"`
|
||||
CandidateVersion int64 `db:"candidate_version" json:"candidate_version"`
|
||||
Quantity int64 `db:"quantity" json:"quantity,string"`
|
||||
FeeBudgetMinor int64 `db:"fee_budget_minor" json:"-"`
|
||||
ReservedMinor int64 `db:"reserved_minor" json:"-"`
|
||||
ReservedAmount string `json:"reserved_amount"`
|
||||
FeeBudget string `json:"fee_budget"`
|
||||
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
}
|
||||
|
||||
func (PlanPreview) TableName() string { return "veyra_plan_preview" }
|
||||
|
||||
type TradePlan struct {
|
||||
ID string `db:"id" json:"id"`
|
||||
PreviewID string `db:"preview_id" json:"preview_id"`
|
||||
AccountID string `db:"account_id" json:"account_id"`
|
||||
CandidateRevisionID string `db:"candidate_revision_id" json:"candidate_revision_id"`
|
||||
CandidateVersion int64 `db:"candidate_version" json:"candidate_version"`
|
||||
Quantity int64 `db:"quantity" json:"quantity,string"`
|
||||
FilledQuantity int64 `db:"filled_quantity" json:"filled_quantity,string"`
|
||||
FeeBudgetMinor int64 `db:"fee_budget_minor" json:"-"`
|
||||
ReservedMinor int64 `db:"reserved_minor" json:"-"`
|
||||
ReservedAmount string `json:"reserved_amount"`
|
||||
Status string `db:"status" json:"status"`
|
||||
Version int64 `db:"version" json:"version"`
|
||||
ValidUntil time.Time `db:"valid_until" json:"valid_until"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
CandidateContent CandidateContent `json:"candidate_content"`
|
||||
}
|
||||
|
||||
func (TradePlan) TableName() string { return "veyra_trade_plan" }
|
||||
|
||||
type PlanBudget struct {
|
||||
AccountID string `json:"account_id"`
|
||||
CashBalance string `json:"cash_balance"`
|
||||
ReservedAmount string `json:"reserved_amount"`
|
||||
AvailableAmount string `json:"available_amount"`
|
||||
}
|
||||
Reference in New Issue
Block a user