统一单入口 Go 工程与前端依赖目录,增加后台供应商配置、候选审核、计划预算、成交冲正、持仓复盘和站内通知。接入策略版本、Python 回测、可配置 A 股公开行情及 zap 文件轮转日志,补充迁移、契约与验证。
This commit is contained in:
@@ -48,8 +48,12 @@ func TestMigrationIntegration(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
files, err := migrations.ReadDir("migrations")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var count int
|
||||
if err := pool.QueryRow(ctx, "SELECT count(*) FROM veyra_schema_migration").Scan(&count); err != nil || count != 2 {
|
||||
if err := pool.QueryRow(ctx, "SELECT count(*) FROM veyra_schema_migration").Scan(&count); err != nil || count != len(files) {
|
||||
t.Fatalf("migration ledger count=%d err=%v", count, err)
|
||||
}
|
||||
if _, err := pool.Exec(ctx, "INSERT INTO veyra_user(id,display_name) VALUES($1,$2)", uuid.NewString(), "Test"); err != nil {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
CREATE TABLE veyra_provider_config (
|
||||
id varchar(36) PRIMARY KEY CHECK (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
config_key varchar(32) NOT NULL UNIQUE CHECK (config_key IN ('model_primary','model_backup','market_primary')),
|
||||
name varchar(80) NOT NULL,
|
||||
base_url text NOT NULL DEFAULT '',
|
||||
model_name varchar(120) NOT NULL DEFAULT '',
|
||||
enabled boolean NOT NULL DEFAULT false,
|
||||
timeout_seconds integer NOT NULL DEFAULT 15 CHECK (timeout_seconds BETWEEN 1 AND 30),
|
||||
encrypted_api_key bytea,
|
||||
version bigint NOT NULL CHECK (version > 0),
|
||||
last_test_status varchar(24) NOT NULL DEFAULT 'NOT_TESTED' CHECK (last_test_status IN ('NOT_CONFIGURED','NOT_TESTED','READY','FAILED')),
|
||||
last_tested_at timestamptz,
|
||||
last_test_message varchar(200) NOT NULL DEFAULT '',
|
||||
updated_by varchar(36) NOT NULL REFERENCES veyra_user(id),
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE veyra_provider_config_history (
|
||||
id varchar(36) PRIMARY KEY CHECK (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
config_id varchar(36) NOT NULL REFERENCES veyra_provider_config(id),
|
||||
version bigint NOT NULL CHECK (version > 0),
|
||||
snapshot jsonb NOT NULL,
|
||||
encrypted_api_key bytea,
|
||||
actor_id varchar(36) NOT NULL REFERENCES veyra_user(id),
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
UNIQUE (config_id, version)
|
||||
);
|
||||
|
||||
CREATE TABLE veyra_audit_event (
|
||||
id varchar(36) PRIMARY KEY CHECK (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
actor_id varchar(36) NOT NULL REFERENCES veyra_user(id),
|
||||
action varchar(40) NOT NULL,
|
||||
object_type varchar(40) NOT NULL,
|
||||
object_id varchar(36) NOT NULL CHECK (object_id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
request_id varchar(36) NOT NULL CHECK (request_id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
details jsonb NOT NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX veyra_audit_event_created_at_idx ON veyra_audit_event(created_at DESC, id DESC);
|
||||
@@ -0,0 +1,9 @@
|
||||
ALTER TABLE veyra_provider_config
|
||||
DROP CONSTRAINT veyra_provider_config_config_key_check,
|
||||
ADD CONSTRAINT veyra_provider_config_config_key_check
|
||||
CHECK (config_key IN ('model_primary','model_backup','model_review','market_primary')),
|
||||
ADD COLUMN wire_api varchar(32) NOT NULL DEFAULT 'responses'
|
||||
CHECK (wire_api IN ('responses','chat_completions')),
|
||||
ADD COLUMN model_catalog_url text NOT NULL DEFAULT '',
|
||||
ADD COLUMN disable_response_storage boolean NOT NULL DEFAULT true,
|
||||
ADD COLUMN requires_openai_auth boolean NOT NULL DEFAULT true;
|
||||
@@ -0,0 +1,52 @@
|
||||
CREATE TABLE veyra_candidate (
|
||||
id varchar(36) PRIMARY KEY CHECK (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
latest_revision integer NOT NULL CHECK (latest_revision > 0),
|
||||
published_revision_id varchar(36),
|
||||
created_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE veyra_candidate_revision (
|
||||
id varchar(36) PRIMARY KEY CHECK (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
candidate_id varchar(36) NOT NULL REFERENCES veyra_candidate(id),
|
||||
revision integer NOT NULL CHECK (revision > 0),
|
||||
version bigint NOT NULL DEFAULT 1 CHECK (version > 0),
|
||||
status varchar(24) NOT NULL CHECK (status IN ('DRAFT', 'PENDING_REVIEW', 'PUBLISHED', 'RETURNED', 'WITHDRAWN')),
|
||||
data_mode varchar(16) NOT NULL CHECK (data_mode = 'simulation'),
|
||||
snapshot jsonb NOT NULL CHECK (jsonb_typeof(snapshot) = 'object'),
|
||||
author_id varchar(36) NOT NULL REFERENCES veyra_user(id),
|
||||
observed_at timestamptz NOT NULL,
|
||||
valid_until timestamptz NOT NULL,
|
||||
published_at timestamptz,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now(),
|
||||
CHECK (valid_until > observed_at),
|
||||
UNIQUE (candidate_id, revision),
|
||||
UNIQUE (candidate_id, id)
|
||||
);
|
||||
ALTER TABLE veyra_candidate ADD CONSTRAINT veyra_candidate_published_revision_fk
|
||||
FOREIGN KEY (id, published_revision_id) REFERENCES veyra_candidate_revision(candidate_id, id);
|
||||
CREATE INDEX veyra_candidate_revision_queue_idx ON veyra_candidate_revision(status, created_at DESC, id DESC);
|
||||
|
||||
CREATE TABLE veyra_candidate_review (
|
||||
id varchar(36) PRIMARY KEY CHECK (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
revision_id varchar(36) NOT NULL REFERENCES veyra_candidate_revision(id),
|
||||
reviewer_id varchar(36) NOT NULL REFERENCES veyra_user(id),
|
||||
decision varchar(16) NOT NULL CHECK (decision IN ('SUBMIT', 'PUBLISH', 'RETURN', 'WITHDRAW')),
|
||||
input_version bigint NOT NULL CHECK (input_version > 0),
|
||||
reason text NOT NULL CHECK (length(trim(reason)) BETWEEN 1 AND 1000),
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
UNIQUE (revision_id, input_version)
|
||||
);
|
||||
|
||||
CREATE TABLE veyra_outbox_event (
|
||||
id varchar(36) PRIMARY KEY CHECK (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
aggregate_type varchar(40) NOT NULL,
|
||||
aggregate_id varchar(36) NOT NULL CHECK (aggregate_id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
aggregate_version bigint NOT NULL CHECK (aggregate_version > 0),
|
||||
event_type varchar(64) NOT NULL,
|
||||
payload jsonb NOT NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
processed_at timestamptz,
|
||||
UNIQUE (aggregate_id, aggregate_version, event_type)
|
||||
);
|
||||
CREATE INDEX veyra_outbox_event_pending_idx ON veyra_outbox_event(created_at, id) WHERE processed_at IS NULL;
|
||||
@@ -0,0 +1,28 @@
|
||||
CREATE TABLE veyra_plan_preview (
|
||||
id varchar(36) PRIMARY KEY CHECK (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
account_id varchar(36) NOT NULL REFERENCES veyra_account(id),
|
||||
candidate_revision_id varchar(36) NOT NULL REFERENCES veyra_candidate_revision(id),
|
||||
candidate_version bigint NOT NULL CHECK (candidate_version > 0),
|
||||
quantity bigint NOT NULL CHECK (quantity > 0 AND quantity <= 1000000 AND quantity % 100 = 0),
|
||||
fee_budget_minor bigint NOT NULL CHECK (fee_budget_minor >= 0),
|
||||
reserved_minor bigint NOT NULL CHECK (reserved_minor > 0 AND reserved_minor <= 100000000000),
|
||||
expires_at timestamptz NOT NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE TABLE veyra_trade_plan (
|
||||
id varchar(36) PRIMARY KEY CHECK (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
preview_id varchar(36) NOT NULL UNIQUE REFERENCES veyra_plan_preview(id),
|
||||
account_id varchar(36) NOT NULL REFERENCES veyra_account(id),
|
||||
candidate_revision_id varchar(36) NOT NULL REFERENCES veyra_candidate_revision(id),
|
||||
candidate_version bigint NOT NULL CHECK (candidate_version > 0),
|
||||
quantity bigint NOT NULL CHECK (quantity > 0 AND quantity <= 1000000),
|
||||
filled_quantity bigint NOT NULL DEFAULT 0 CHECK (filled_quantity >= 0 AND filled_quantity <= quantity),
|
||||
fee_budget_minor bigint NOT NULL CHECK (fee_budget_minor >= 0),
|
||||
reserved_minor bigint NOT NULL CHECK (reserved_minor >= 0 AND reserved_minor <= 100000000000),
|
||||
status varchar(24) NOT NULL DEFAULT 'ACTIVE' CHECK (status IN ('ACTIVE','PART_FILLED','COMPLETED','CANCELLED','INVALIDATED')),
|
||||
version bigint NOT NULL DEFAULT 1 CHECK (version > 0),
|
||||
valid_until timestamptz NOT NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX veyra_trade_plan_account_idx ON veyra_trade_plan(account_id,status);
|
||||
@@ -0,0 +1,71 @@
|
||||
ALTER TABLE veyra_cash_entry DROP CONSTRAINT veyra_cash_entry_entry_type_check;
|
||||
ALTER TABLE veyra_cash_entry DROP CONSTRAINT veyra_cash_entry_amount_minor_check;
|
||||
ALTER TABLE veyra_cash_entry DROP CONSTRAINT veyra_cash_entry_account_id_entry_type_key;
|
||||
ALTER TABLE veyra_cash_entry ADD CONSTRAINT veyra_cash_entry_entry_type_check CHECK(entry_type IN ('INITIAL','BUY','SELL','REVERSAL'));
|
||||
ALTER TABLE veyra_cash_entry ADD CONSTRAINT veyra_cash_entry_amount_minor_check CHECK(amount_minor BETWEEN -100000000000 AND 100000000000);
|
||||
CREATE UNIQUE INDEX veyra_cash_entry_initial_idx ON veyra_cash_entry(account_id) WHERE entry_type='INITIAL';
|
||||
CREATE TABLE veyra_trade_fill (
|
||||
id varchar(36) PRIMARY KEY CHECK (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
account_id varchar(36) NOT NULL REFERENCES veyra_account(id),
|
||||
plan_id varchar(36) REFERENCES veyra_trade_plan(id),
|
||||
idempotency_key varchar(36) NOT NULL CHECK (idempotency_key ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
input_hash varchar(64) NOT NULL,
|
||||
posted_sequence bigint GENERATED ALWAYS AS IDENTITY UNIQUE,
|
||||
side varchar(4) NOT NULL CHECK(side IN ('BUY','SELL')),
|
||||
symbol varchar(6) NOT NULL CHECK(symbol ~ '^[0-9]{6}$'),
|
||||
exchange varchar(4) NOT NULL CHECK(exchange IN ('SSE','SZSE')),
|
||||
name varchar(80) NOT NULL,
|
||||
quantity bigint NOT NULL CHECK(quantity BETWEEN 1 AND 1000000),
|
||||
price_minor bigint NOT NULL CHECK(price_minor>0 AND price_minor<=100000000000),
|
||||
fee_minor bigint NOT NULL CHECK(fee_minor>=0 AND fee_minor<=100000000000),
|
||||
cash_delta_minor bigint NOT NULL CHECK(cash_delta_minor BETWEEN -100000000000 AND 100000000000),
|
||||
realized_minor bigint NOT NULL,
|
||||
occurred_at timestamptz NOT NULL,
|
||||
sellable_at timestamptz,
|
||||
note text NOT NULL DEFAULT '',
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
CHECK(side='SELL' OR sellable_at>=occurred_at),
|
||||
UNIQUE(account_id,idempotency_key)
|
||||
);
|
||||
CREATE TABLE veyra_position_lot (
|
||||
id varchar(36) PRIMARY KEY CHECK (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
account_id varchar(36) NOT NULL REFERENCES veyra_account(id),
|
||||
buy_fill_id varchar(36) NOT NULL UNIQUE REFERENCES veyra_trade_fill(id),
|
||||
symbol varchar(6) NOT NULL,
|
||||
exchange varchar(4) NOT NULL,
|
||||
remaining_quantity bigint NOT NULL CHECK(remaining_quantity>=0),
|
||||
remaining_cost_minor bigint NOT NULL CHECK(remaining_cost_minor>=0),
|
||||
sellable_at timestamptz NOT NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX veyra_position_lot_account_idx ON veyra_position_lot(account_id,exchange,symbol,sellable_at);
|
||||
CREATE TABLE veyra_fill_allocation (
|
||||
id varchar(36) PRIMARY KEY CHECK (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
sell_fill_id varchar(36) NOT NULL REFERENCES veyra_trade_fill(id),
|
||||
lot_id varchar(36) NOT NULL REFERENCES veyra_position_lot(id),
|
||||
quantity bigint NOT NULL CHECK(quantity>0),
|
||||
cost_minor bigint NOT NULL CHECK(cost_minor>=0),
|
||||
UNIQUE(sell_fill_id,lot_id)
|
||||
);
|
||||
CREATE TABLE veyra_trade_reversal (
|
||||
id varchar(36) PRIMARY KEY CHECK (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
fill_id varchar(36) NOT NULL UNIQUE REFERENCES veyra_trade_fill(id),
|
||||
actor_id varchar(36) NOT NULL REFERENCES veyra_user(id),
|
||||
reason text NOT NULL CHECK(length(trim(reason)) BETWEEN 1 AND 1000),
|
||||
created_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
ALTER TABLE veyra_cash_entry ADD COLUMN fill_id varchar(36) REFERENCES veyra_trade_fill(id);
|
||||
ALTER TABLE veyra_cash_entry ADD COLUMN reversal_id varchar(36) REFERENCES veyra_trade_reversal(id);
|
||||
CREATE UNIQUE INDEX veyra_cash_entry_fill_idx ON veyra_cash_entry(fill_id) WHERE fill_id IS NOT NULL;
|
||||
CREATE UNIQUE INDEX veyra_cash_entry_reversal_idx ON veyra_cash_entry(reversal_id) WHERE reversal_id IS NOT NULL;
|
||||
|
||||
CREATE FUNCTION veyra_reject_fact_mutation() RETURNS trigger LANGUAGE plpgsql AS $$
|
||||
BEGIN
|
||||
RAISE EXCEPTION 'posted facts are immutable; use a reversal';
|
||||
END;
|
||||
$$;
|
||||
CREATE TRIGGER veyra_trade_fill_immutable BEFORE UPDATE OR DELETE ON veyra_trade_fill FOR EACH ROW EXECUTE FUNCTION veyra_reject_fact_mutation();
|
||||
CREATE TRIGGER veyra_cash_entry_immutable BEFORE UPDATE OR DELETE ON veyra_cash_entry FOR EACH ROW EXECUTE FUNCTION veyra_reject_fact_mutation();
|
||||
CREATE TRIGGER veyra_fill_allocation_immutable BEFORE UPDATE OR DELETE ON veyra_fill_allocation FOR EACH ROW EXECUTE FUNCTION veyra_reject_fact_mutation();
|
||||
CREATE TRIGGER veyra_trade_reversal_immutable BEFORE UPDATE OR DELETE ON veyra_trade_reversal FOR EACH ROW EXECUTE FUNCTION veyra_reject_fact_mutation();
|
||||
CREATE TRIGGER veyra_candidate_review_immutable BEFORE UPDATE OR DELETE ON veyra_candidate_review FOR EACH ROW EXECUTE FUNCTION veyra_reject_fact_mutation();
|
||||
@@ -0,0 +1,32 @@
|
||||
CREATE TABLE veyra_quote_snapshot (
|
||||
id varchar(36) PRIMARY KEY CHECK (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
symbol varchar(6) NOT NULL CHECK (symbol ~ '^[0-9]{6}$'),
|
||||
exchange varchar(4) NOT NULL CHECK (exchange IN ('SSE','SZSE')),
|
||||
data_mode varchar(16) NOT NULL CHECK (data_mode='simulation'),
|
||||
price_minor bigint NOT NULL CHECK (price_minor>0 AND price_minor<=100000000000),
|
||||
observed_at timestamptz NOT NULL,
|
||||
version bigint NOT NULL CHECK (version>0),
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now(),
|
||||
UNIQUE(exchange,symbol,data_mode)
|
||||
);
|
||||
CREATE TABLE veyra_notification (
|
||||
id varchar(36) PRIMARY KEY CHECK (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
user_id varchar(36) NOT NULL REFERENCES veyra_user(id),
|
||||
event_id varchar(36) NOT NULL REFERENCES veyra_outbox_event(id),
|
||||
plan_id varchar(36) REFERENCES veyra_trade_plan(id),
|
||||
title varchar(120) NOT NULL,
|
||||
body text NOT NULL,
|
||||
read_at timestamptz,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
UNIQUE(user_id,event_id,plan_id)
|
||||
);
|
||||
CREATE INDEX veyra_notification_user_idx ON veyra_notification(user_id,created_at DESC);
|
||||
CREATE TABLE veyra_plan_trigger (
|
||||
id varchar(36) PRIMARY KEY CHECK (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
plan_id varchar(36) NOT NULL UNIQUE REFERENCES veyra_trade_plan(id),
|
||||
quote_snapshot_id varchar(36) NOT NULL REFERENCES veyra_quote_snapshot(id),
|
||||
quote_version bigint NOT NULL CHECK(quote_version>0),
|
||||
triggered_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX veyra_notification_unread_idx ON veyra_notification(user_id) WHERE read_at IS NULL;
|
||||
@@ -0,0 +1,25 @@
|
||||
CREATE TABLE veyra_strategy (
|
||||
id varchar(36) PRIMARY KEY CHECK (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
code varchar(64) NOT NULL UNIQUE CHECK (code ~ '^[a-z][a-z0-9_]{2,63}$'),
|
||||
name varchar(80) NOT NULL,
|
||||
status varchar(16) NOT NULL DEFAULT 'PAUSED' CHECK(status IN ('ACTIVE','PAUSED')),
|
||||
latest_revision integer NOT NULL DEFAULT 1 CHECK(latest_revision>0),
|
||||
active_version_id varchar(36),
|
||||
version bigint NOT NULL DEFAULT 1 CHECK(version>0),
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE TABLE veyra_strategy_version (
|
||||
id varchar(36) PRIMARY KEY CHECK (id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
strategy_id varchar(36) NOT NULL REFERENCES veyra_strategy(id),
|
||||
revision integer NOT NULL CHECK(revision>0),
|
||||
algorithm varchar(32) NOT NULL CHECK(algorithm='sma_cross'),
|
||||
parameters jsonb NOT NULL,
|
||||
description text NOT NULL,
|
||||
author_id varchar(36) NOT NULL REFERENCES veyra_user(id),
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
UNIQUE(strategy_id,revision),
|
||||
UNIQUE(strategy_id,id)
|
||||
);
|
||||
ALTER TABLE veyra_strategy ADD CONSTRAINT veyra_strategy_active_version_fk FOREIGN KEY(id,active_version_id) REFERENCES veyra_strategy_version(strategy_id,id);
|
||||
CREATE TRIGGER veyra_strategy_version_immutable BEFORE UPDATE OR DELETE ON veyra_strategy_version FOR EACH ROW EXECUTE FUNCTION veyra_reject_fact_mutation();
|
||||
@@ -0,0 +1,25 @@
|
||||
CREATE TABLE veyra_backtest_run (
|
||||
id varchar(36) PRIMARY KEY CHECK(id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
strategy_version_id varchar(36) NOT NULL REFERENCES veyra_strategy_version(id),
|
||||
status varchar(16) NOT NULL DEFAULT 'PENDING' CHECK(status IN ('PENDING','RUNNING','COMPLETED','FAILED')),
|
||||
input jsonb NOT NULL,
|
||||
input_hash varchar(64) NOT NULL,
|
||||
result jsonb,
|
||||
failure text NOT NULL DEFAULT '',
|
||||
lease_id varchar(36) CHECK(lease_id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
lease_expires_at timestamptz,
|
||||
author_id varchar(36) NOT NULL REFERENCES veyra_user(id),
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
finished_at timestamptz,
|
||||
CHECK((status='RUNNING') = (lease_id IS NOT NULL AND lease_expires_at IS NOT NULL)),
|
||||
CHECK((status IN ('COMPLETED','FAILED')) = (finished_at IS NOT NULL)),
|
||||
CHECK((status='COMPLETED') = (result IS NOT NULL))
|
||||
);
|
||||
CREATE INDEX veyra_backtest_run_queue_idx ON veyra_backtest_run(status,created_at);
|
||||
CREATE FUNCTION veyra_protect_backtest_run() RETURNS trigger LANGUAGE plpgsql AS $$
|
||||
BEGIN
|
||||
IF TG_OP='DELETE' OR OLD.status IN ('COMPLETED','FAILED') THEN RAISE EXCEPTION 'immutable research result'; END IF;
|
||||
IF NEW.id<>OLD.id OR NEW.strategy_version_id<>OLD.strategy_version_id OR NEW.input<>OLD.input OR NEW.input_hash<>OLD.input_hash OR NEW.author_id<>OLD.author_id OR NEW.created_at<>OLD.created_at THEN RAISE EXCEPTION 'immutable research input'; END IF;
|
||||
RETURN NEW;
|
||||
END $$;
|
||||
CREATE TRIGGER veyra_backtest_run_protected BEFORE UPDATE OR DELETE ON veyra_backtest_run FOR EACH ROW EXECUTE FUNCTION veyra_protect_backtest_run();
|
||||
@@ -0,0 +1,24 @@
|
||||
ALTER TABLE veyra_provider_config ADD COLUMN market_adapter varchar(32) NOT NULL DEFAULT '' CHECK(market_adapter IN ('','eastmoney_public')), ADD COLUMN history_base_url text NOT NULL DEFAULT '';
|
||||
CREATE TABLE veyra_instrument (
|
||||
id varchar(36) PRIMARY KEY CHECK(id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
symbol varchar(6) NOT NULL CHECK(symbol ~ '^[0-9]{6}$'),
|
||||
exchange varchar(8) NOT NULL CHECK(exchange IN ('SSE','SZSE')),
|
||||
name varchar(80) NOT NULL,
|
||||
price_cents bigint NOT NULL CHECK(price_cents>=0),
|
||||
observed_at timestamptz NOT NULL,
|
||||
received_at timestamptz NOT NULL DEFAULT now(),
|
||||
source_id varchar(36) NOT NULL REFERENCES veyra_provider_config(id),
|
||||
source_version bigint NOT NULL CHECK(source_version>0),
|
||||
UNIQUE(exchange,symbol)
|
||||
);
|
||||
CREATE TABLE veyra_market_sync (
|
||||
id varchar(36) PRIMARY KEY CHECK(id ~ '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'),
|
||||
source_id varchar(36) NOT NULL REFERENCES veyra_provider_config(id),
|
||||
source_version bigint NOT NULL CHECK(source_version>0),
|
||||
instrument_count integer NOT NULL CHECK(instrument_count>=0),
|
||||
status varchar(16) NOT NULL CHECK(status IN ('COMPLETED','FAILED')),
|
||||
message text NOT NULL DEFAULT '',
|
||||
started_at timestamptz NOT NULL,
|
||||
finished_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX veyra_instrument_symbol_idx ON veyra_instrument(symbol);
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE veyra_provider_config DROP CONSTRAINT veyra_provider_config_market_adapter_check, ADD CONSTRAINT veyra_provider_config_market_adapter_check CHECK(market_adapter IN ('','eastmoney_public','sina_public')), ADD COLUMN quote_base_url text NOT NULL DEFAULT '';
|
||||
Reference in New Issue
Block a user