diff --git a/db/canonical-baseline/R__database_runtime_grants.sql b/db/canonical-baseline/R__database_runtime_grants.sql index fc09df5..b24d445 100644 --- a/db/canonical-baseline/R__database_runtime_grants.sql +++ b/db/canonical-baseline/R__database_runtime_grants.sql @@ -336,7 +336,7 @@ GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE "identity"."password_reset_request GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE "identity"."policy_documents" TO idea2strategy_backend; GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE "identity"."recovery_code_sets" TO idea2strategy_backend; GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE "identity"."recovery_codes" TO idea2strategy_backend; -GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE "identity"."sessions" TO idea2strategy_backend; +GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE "identity"."refresh_token_families" TO idea2strategy_backend; GRANT SELECT ON TABLE "market_data"."corporate_actions" TO idea2strategy_backend; GRANT SELECT ON TABLE "market_data"."dataset_lineage" TO idea2strategy_backend; GRANT SELECT ON TABLE "market_data"."dataset_manifests" TO idea2strategy_backend; @@ -454,11 +454,11 @@ GRANT SELECT ON TABLE "backtest"."monthly_judgment_summaries" TO idea2strategy_b GRANT SELECT ON TABLE "backtest"."performance_summaries" TO idea2strategy_batch; GRANT SELECT ON TABLE "backtest"."run_attempts" TO idea2strategy_batch; GRANT SELECT ON TABLE "backtest"."run_input_pins" TO idea2strategy_batch; -GRANT SELECT ON TABLE "backtest"."runs" TO idea2strategy_batch; +GRANT SELECT, INSERT ON TABLE "backtest"."runs" TO idea2strategy_batch; GRANT SELECT ON TABLE "bot"."bot_events" TO idea2strategy_batch; GRANT SELECT ON TABLE "bot"."bot_partitions" TO idea2strategy_batch; -GRANT SELECT ON TABLE "bot"."bots" TO idea2strategy_batch; -GRANT SELECT ON TABLE "bot"."continuation_deadlines" TO idea2strategy_batch; +GRANT SELECT, UPDATE ON TABLE "bot"."bots" TO idea2strategy_batch; +GRANT SELECT, INSERT ON TABLE "bot"."continuation_deadlines" TO idea2strategy_batch; GRANT SELECT ON TABLE "bot"."evaluation_runs" TO idea2strategy_batch; GRANT SELECT ON TABLE "bot"."flow_feature_requirements" TO idea2strategy_batch; GRANT SELECT ON TABLE "bot"."flow_instruments" TO idea2strategy_batch; @@ -474,20 +474,20 @@ GRANT SELECT ON TABLE "competition"."backtest_evaluation_periods" TO idea2strate GRANT SELECT ON TABLE "competition"."backtest_evaluation_plans" TO idea2strategy_batch; GRANT SELECT ON TABLE "competition"."backtest_period_datasets" TO idea2strategy_batch; GRANT SELECT ON TABLE "competition"."backtest_period_feature_materializations" TO idea2strategy_batch; -GRANT SELECT ON TABLE "competition"."backtest_period_runs" TO idea2strategy_batch; +GRANT SELECT, INSERT ON TABLE "competition"."backtest_period_runs" TO idea2strategy_batch; GRANT SELECT ON TABLE "competition"."leaderboard_entries" TO idea2strategy_batch; GRANT SELECT ON TABLE "competition"."leaderboard_snapshots" TO idea2strategy_batch; -GRANT SELECT ON TABLE "competition"."live_evaluation_segments" TO idea2strategy_batch; +GRANT SELECT, INSERT ON TABLE "competition"."live_evaluation_segments" TO idea2strategy_batch; GRANT SELECT ON TABLE "competition"."live_room_rules" TO idea2strategy_batch; -GRANT SELECT ON TABLE "competition"."participation_events" TO idea2strategy_batch; -GRANT SELECT ON TABLE "competition"."participations" TO idea2strategy_batch; +GRANT SELECT, INSERT ON TABLE "competition"."participation_events" TO idea2strategy_batch; +GRANT SELECT, UPDATE ON TABLE "competition"."participations" TO idea2strategy_batch; GRANT SELECT ON TABLE "competition"."room_evaluation_account_results" TO idea2strategy_batch; -GRANT SELECT ON TABLE "competition"."room_events" TO idea2strategy_batch; +GRANT SELECT, INSERT ON TABLE "competition"."room_events" TO idea2strategy_batch; GRANT SELECT ON TABLE "competition"."room_final_access_grants" TO idea2strategy_batch; GRANT SELECT ON TABLE "competition"."room_invitations" TO idea2strategy_batch; GRANT SELECT ON TABLE "competition"."room_rules" TO idea2strategy_batch; GRANT SELECT ON TABLE "competition"."room_schedules" TO idea2strategy_batch; -GRANT SELECT ON TABLE "competition"."rooms" TO idea2strategy_batch; +GRANT SELECT, UPDATE ON TABLE "competition"."rooms" TO idea2strategy_batch; GRANT SELECT ON TABLE "competition"."scoring_template_versions" TO idea2strategy_batch; GRANT SELECT ON TABLE "identity"."account_closure_readiness" TO idea2strategy_batch; GRANT SELECT ON TABLE "identity"."account_closure_runs" TO idea2strategy_batch; @@ -525,7 +525,7 @@ GRANT SELECT ON TABLE "identity"."password_reset_requests" TO idea2strategy_batc GRANT SELECT ON TABLE "identity"."policy_documents" TO idea2strategy_batch; GRANT SELECT ON TABLE "identity"."recovery_code_sets" TO idea2strategy_batch; GRANT SELECT ON TABLE "identity"."recovery_codes" TO idea2strategy_batch; -GRANT SELECT ON TABLE "identity"."sessions" TO idea2strategy_batch; +GRANT SELECT ON TABLE "identity"."refresh_token_families" TO idea2strategy_batch; GRANT SELECT ON TABLE "market_data"."corporate_actions" TO idea2strategy_batch; GRANT SELECT ON TABLE "market_data"."dataset_lineage" TO idea2strategy_batch; GRANT SELECT ON TABLE "market_data"."dataset_manifests" TO idea2strategy_batch; @@ -755,6 +755,8 @@ GRANT SELECT, INSERT, UPDATE ON TABLE "backtest"."performance_summaries" TO idea GRANT SELECT, INSERT, UPDATE ON TABLE "backtest"."run_attempts" TO idea2strategy_backtest; GRANT SELECT, INSERT, UPDATE ON TABLE "backtest"."run_input_pins" TO idea2strategy_backtest; GRANT SELECT, INSERT, UPDATE ON TABLE "backtest"."runs" TO idea2strategy_backtest; +GRANT SELECT ON TABLE "bot"."bots" TO idea2strategy_backtest; +GRANT SELECT ON TABLE "bot"."launch_contract_plans" TO idea2strategy_backtest; GRANT SELECT ON TABLE "market_data"."corporate_actions" TO idea2strategy_backtest; GRANT SELECT ON TABLE "market_data"."dataset_lineage" TO idea2strategy_backtest; GRANT SELECT ON TABLE "market_data"."dataset_manifests" TO idea2strategy_backtest; @@ -771,6 +773,8 @@ GRANT SELECT ON TABLE "market_data"."providers" TO idea2strategy_backtest; GRANT SELECT ON TABLE "market_data"."quality_incidents" TO idea2strategy_backtest; GRANT SELECT ON TABLE "market_data"."stream_watermarks" TO idea2strategy_backtest; GRANT SELECT ON TABLE "market_data"."trading_sessions" TO idea2strategy_backtest; +GRANT SELECT, INSERT, UPDATE ON TABLE "operations"."outbox_consumer_receipts" TO idea2strategy_backtest; +GRANT SELECT ON TABLE "operations"."outbox_messages" TO idea2strategy_backtest; GRANT SELECT, INSERT ON TABLE "storage"."objects" TO idea2strategy_backtest; GRANT SELECT ON TABLE "strategy"."compiled_flow_plans" TO idea2strategy_backtest; GRANT SELECT ON TABLE "strategy"."element_catalog_versions" TO idea2strategy_backtest; @@ -784,7 +788,9 @@ GRANT SELECT ON TABLE "strategy"."template_versions" TO idea2strategy_backtest; GRANT SELECT ON TABLE "strategy"."templates" TO idea2strategy_backtest; GRANT SELECT ON TABLE "strategy"."validation_runs" TO idea2strategy_backtest; GRANT USAGE ON SCHEMA "backtest" TO idea2strategy_backtest; +GRANT USAGE ON SCHEMA "bot" TO idea2strategy_backtest; GRANT USAGE ON SCHEMA "market_data" TO idea2strategy_backtest; +GRANT USAGE ON SCHEMA "operations" TO idea2strategy_backtest; GRANT USAGE ON SCHEMA "storage" TO idea2strategy_backtest; GRANT USAGE ON SCHEMA "strategy" TO idea2strategy_backtest; GRANT SELECT, INSERT, UPDATE ON TABLE "market_data"."corporate_actions" TO idea2strategy_pipeline; diff --git a/db/canonical-baseline/V20260805170000__backtest_run_outcome_detail.sql b/db/canonical-baseline/V20260805170000__backtest_run_outcome_detail.sql new file mode 100644 index 0000000..66c9b68 --- /dev/null +++ b/db/canonical-baseline/V20260805170000__backtest_run_outcome_detail.sql @@ -0,0 +1,35 @@ +-- backtest: forward adoption of the approved run outcome projection. +-- +-- The earlier V20260802143000 proposal was authored before newer central Flyway +-- history. It is preserved under fixtures/superseded-proposals for audit, but is +-- deliberately outside the active contribution directory. This globally ordered +-- forward version is the only bundle-eligible outcome migration. +-- +-- Scope is intentionally limited to the three outcome fields already consumed by +-- the runtime. In particular this migration does not restore the retired legacy +-- input-pin fields dataset_manifest_id, dataset_hash, or +-- feature_materialization_version. + +ALTER TABLE "backtest"."runs" + ADD COLUMN "result_manifest_id" uuid, + ADD COLUMN "retryable" boolean, + ADD COLUMN "missing_requirements" jsonb; + +-- backtest.v1 requires UNAVAILABLE.missingRequirements to be a non-empty array +-- of strings. NULL remains valid for every status where the field is not +-- applicable. retryable intentionally has no default: "not failed" and +-- "failed, not retryable" are different facts. +ALTER TABLE "backtest"."runs" + ADD CONSTRAINT "runs_missing_requirements_is_a_non_empty_string_array" + CHECK ( + "missing_requirements" IS NULL + OR ( + jsonb_typeof("missing_requirements") = 'array' + AND jsonb_array_length("missing_requirements") > 0 + AND NOT jsonb_path_exists("missing_requirements", '$[*] ? (@.type() != "string")') + ) + ); + +COMMENT ON COLUMN "backtest"."runs"."result_manifest_id" IS 'COMPLETED resultManifestId linking the run to its immutable result manifest; NULL for other states.'; +COMMENT ON COLUMN "backtest"."runs"."retryable" IS 'FAILED retryable decision; NULL when the run has not failed.'; +COMMENT ON COLUMN "backtest"."runs"."missing_requirements" IS 'UNAVAILABLE missingRequirements as the non-empty ordered string array received from the worker; NULL for other states.'; diff --git a/db/canonical-baseline/V20260806100000__backend_google_oidc_provider.sql b/db/canonical-baseline/V20260806100000__backend_google_oidc_provider.sql new file mode 100644 index 0000000..eadfec1 --- /dev/null +++ b/db/canonical-baseline/V20260806100000__backend_google_oidc_provider.sql @@ -0,0 +1,8 @@ +INSERT INTO identity.auth_providers (id, code, display_name, provider_type, issuer, is_active) +VALUES (3, 'GOOGLE', 'Google', 'OIDC', 'https://accounts.google.com', true) +ON CONFLICT (code) DO UPDATE +SET display_name = EXCLUDED.display_name, + provider_type = EXCLUDED.provider_type, + issuer = EXCLUDED.issuer, + is_active = true, + updated_at = now(); diff --git a/db/canonical-baseline/V20260806120000__pipeline_seed_official_feature_output_feed.sql b/db/canonical-baseline/V20260806120000__pipeline_seed_official_feature_output_feed.sql new file mode 100644 index 0000000..78bb31d --- /dev/null +++ b/db/canonical-baseline/V20260806120000__pipeline_seed_official_feature_output_feed.sql @@ -0,0 +1,69 @@ +-- Forward-only adoption of contract.market-data.publication.v1 revision 2. +-- Existing rows are accepted only when every protected identity field matches. +DO $migration$ +DECLARE + rsi_parameters jsonb := '{"period":14,"price_field":"close","method":"SIMPLE_AVERAGE_BOUNDED_WINDOW","input_adjustment":"SPLIT_DIVIDEND_ADJUSTED","calendar_id":"XNYS"}'::jsonb; +BEGIN + IF EXISTS (SELECT 1 FROM market_data.feature_definitions WHERE id = '0f1b0000-0000-4000-8000-000000000001') THEN + IF NOT EXISTS ( + SELECT 1 FROM market_data.feature_definitions + WHERE id = '0f1b0000-0000-4000-8000-000000000001' + AND element_catalog_version_id = '0f1a0000-0000-4000-8000-000000000001' + AND feature_code = 'RSI_14' AND calculator_version = 'rsi:1.0.0' + AND resolution = '1m' AND normalized_parameters = rsi_parameters + AND output_value_type = 'NUMBER' AND required_history_points = 15 + AND definition_hash = 'sha256:1a7c3e5b9d2f4068a1c3e5b7d9f20416283a5c7e9b1d3f50627496a8c0e2b4d6' + ) THEN + RAISE EXCEPTION 'official RSI_14 definition identity drift'; + END IF; + ELSE + INSERT INTO market_data.feature_definitions ( + id, element_catalog_version_id, feature_code, calculator_version, resolution, + normalized_parameters, output_value_type, required_history_points, definition_hash, created_at + ) VALUES ( + '0f1b0000-0000-4000-8000-000000000001', '0f1a0000-0000-4000-8000-000000000001', + 'RSI_14', 'rsi:1.0.0', '1m', rsi_parameters, 'NUMBER', 15, + 'sha256:1a7c3e5b9d2f4068a1c3e5b7d9f20416283a5c7e9b1d3f50627496a8c0e2b4d6', + TIMESTAMPTZ '2026-08-04 00:00:00+00' + ); + END IF; + + IF EXISTS (SELECT 1 FROM market_data.providers WHERE id = 'b9146ed9-dbb0-5323-93e3-8518f3851236') THEN + IF NOT EXISTS ( + SELECT 1 FROM market_data.providers + WHERE id = 'b9146ed9-dbb0-5323-93e3-8518f3851236' + AND code = 'IDEA2STRATEGY_INTERNAL' + AND display_name = 'Idea2Strategy Derived Data' + AND rights_version = 'internal-derived-v1' AND status = 'ACTIVE' + ) THEN + RAISE EXCEPTION 'IDEA2STRATEGY_INTERNAL provider identity drift'; + END IF; + ELSE + INSERT INTO market_data.providers (id, code, display_name, rights_version, status, created_at) + VALUES ('b9146ed9-dbb0-5323-93e3-8518f3851236', 'IDEA2STRATEGY_INTERNAL', + 'Idea2Strategy Derived Data', 'internal-derived-v1', 'ACTIVE', + TIMESTAMPTZ '2026-08-06 12:00:00+00'); + END IF; + + IF EXISTS (SELECT 1 FROM market_data.feeds WHERE id = '063f8f27-5c6a-5348-b2bb-abc3c634149c') THEN + IF NOT EXISTS ( + SELECT 1 FROM market_data.feeds + WHERE id = '063f8f27-5c6a-5348-b2bb-abc3c634149c' + AND provider_id = 'b9146ed9-dbb0-5323-93e3-8518f3851236' + AND code = 'FEATURE_RSI_14_1M_RSI_1_0_0' AND data_kind = 'FEATURE_SERIES' + AND resolution = '1m' AND timezone_name = 'UTC' + AND feed_version = 'rsi-1.0.0+feature-series.parquet.v1' AND retired_at IS NULL + ) THEN + RAISE EXCEPTION 'official RSI_14 feature feed identity drift'; + END IF; + ELSE + INSERT INTO market_data.feeds ( + id, provider_id, code, data_kind, resolution, timezone_name, feed_version, created_at, retired_at + ) VALUES ( + '063f8f27-5c6a-5348-b2bb-abc3c634149c', 'b9146ed9-dbb0-5323-93e3-8518f3851236', + 'FEATURE_RSI_14_1M_RSI_1_0_0', 'FEATURE_SERIES', '1m', 'UTC', + 'rsi-1.0.0+feature-series.parquet.v1', TIMESTAMPTZ '2026-08-06 12:00:00+00', NULL + ); + END IF; +END +$migration$; diff --git a/db/canonical-baseline/V20260807120000__backend_customer_refresh_token_families.sql b/db/canonical-baseline/V20260807120000__backend_customer_refresh_token_families.sql new file mode 100644 index 0000000..91ac3cc --- /dev/null +++ b/db/canonical-baseline/V20260807120000__backend_customer_refresh_token_families.sql @@ -0,0 +1,87 @@ +CREATE TABLE identity.refresh_token_families ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + account_id uuid NOT NULL, + authenticated_by_login_identity_id uuid NOT NULL, + auth_epoch_at_issue bigint NOT NULL, + credential_version_at_issue bigint, + current_token_digest varchar(128) NOT NULL UNIQUE, + digest_key_version smallint NOT NULL, + issued_at timestamptz NOT NULL DEFAULT now(), + last_rotated_at timestamptz NOT NULL, + expires_at timestamptz NOT NULL, + revoked_at timestamptz, + revoke_reason_code varchar(80), + CONSTRAINT refresh_token_family_auth_epoch_positive CHECK (auth_epoch_at_issue > 0), + CONSTRAINT refresh_token_family_credential_version_positive + CHECK (credential_version_at_issue IS NULL OR credential_version_at_issue > 0), + CONSTRAINT refresh_token_family_digest_key_version_positive CHECK (digest_key_version > 0), + CONSTRAINT refresh_token_family_time_order_valid + CHECK (last_rotated_at >= issued_at AND expires_at > last_rotated_at), + CONSTRAINT refresh_token_family_login_identity_fk + FOREIGN KEY (account_id, authenticated_by_login_identity_id) + REFERENCES identity.login_identities (account_id, id) +); + +CREATE INDEX refresh_token_families_account_expiry_idx + ON identity.refresh_token_families (account_id, expires_at); +CREATE INDEX refresh_token_families_account_revoked_idx + ON identity.refresh_token_families (account_id, revoked_at); + +INSERT INTO identity.refresh_token_families ( + id, + account_id, + authenticated_by_login_identity_id, + auth_epoch_at_issue, + credential_version_at_issue, + current_token_digest, + digest_key_version, + issued_at, + last_rotated_at, + expires_at, + revoked_at, + revoke_reason_code +) +SELECT + id, + account_id, + authenticated_by_login_identity_id, + auth_epoch_at_issue, + credential_version_at_issue, + token_digest, + digest_key_version, + issued_at, + last_seen_at, + expires_at, + revoked_at, + revoke_reason_code +FROM identity.sessions; + +ALTER TABLE strategy.strategy_edit_leases + ADD COLUMN account_id uuid; + +UPDATE strategy.strategy_edit_leases lease +SET account_id = session.account_id +FROM identity.sessions session +WHERE lease.session_id = session.id; + +ALTER TABLE strategy.strategy_edit_leases + DROP CONSTRAINT strategy_edit_leases_session_id_fkey, + DROP CONSTRAINT strategy_edit_lease_exactly_one_editor, + DROP COLUMN session_id, + ADD CONSTRAINT strategy_edit_lease_exactly_one_editor + CHECK ( + (account_id IS NOT NULL AND delegated_credential_id IS NULL) + OR (account_id IS NULL AND delegated_credential_id IS NOT NULL) + ), + ADD CONSTRAINT strategy_edit_leases_account_id_fkey + FOREIGN KEY (account_id) REFERENCES identity.accounts (id); + +DROP TABLE identity.sessions; + +ALTER TABLE identity.account_security_states + RENAME COLUMN sessions_revoked_before TO credentials_revoked_before; + +COMMENT ON TABLE identity.refresh_token_families IS + 'Minimal server state for rotating refresh JWT reuse detection. It is not a device session registry and has no concurrent-login policy.'; +COMMENT ON TABLE strategy.strategy_edit_leases IS + 'Allows exactly one active editor. Customer leases are account-owned and independently protected by the lease token; refresh token families are not editor identities.'; diff --git a/db/canonical-baseline/V20260807130000__backend_publish_full_basic_element_catalog.sql b/db/canonical-baseline/V20260807130000__backend_publish_full_basic_element_catalog.sql new file mode 100644 index 0000000..016fc1e --- /dev/null +++ b/db/canonical-baseline/V20260807130000__backend_publish_full_basic_element_catalog.sql @@ -0,0 +1,175 @@ +-- Publishes the complete Basic editor catalog. The previous catalog remains immutable and is +-- retired at the instant this replacement becomes available, so every released bot continues to +-- pin the element meanings it was compiled with. + +UPDATE strategy.element_catalog_versions +SET retired_at = '2026-08-07T13:00:00+00' +WHERE id = '0f1a0000-0000-4000-8000-000000000001' + AND retired_at IS NULL; + +INSERT INTO strategy.element_catalog_versions ( + id, language_version, schema_version, catalog_version, data_requirement_version, + definition_hash, published_at) +VALUES ( + '0f2a0000-0000-4000-8000-000000000001', + 'basic/v1', + 'basic-semantic/v1', + 'basic-elements:2026-08-07', + 'alpaca-sip/v1', + 'sha256:92a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301', + '2026-08-07T13:00:00+00'); + +WITH definitions( + id, element_code, element_kind, parameter_schema, containers, operation, arguments, + input_ports, output_ports, terminal, definition_hash +) AS (VALUES + ( + '0f2c0000-0000-4000-8000-000000000001'::uuid, + 'BASIC_PRICE_COMPARE', 'CONDITION', + '{"type":"object","properties":{"resolution":{"type":"string"},"operator":{"type":"string","enum":["LT","LTE","GT","GTE","EQ","NEQ"]},"reference":{"type":"string","enum":["PREVIOUS_CLOSE","SESSION_OPEN","AVERAGE_ENTRY_PRICE","SMA_5","SMA_20","SMA_60","HIGH_5","HIGH_20","HIGH_60","LOW_5","LOW_20","LOW_60"]}},"required":["resolution","operator","reference"]}'::jsonb, + '["BUY","SELL"]'::jsonb, 'PRICE_COMPARE', + '{"resolution":"$resolution","operator":"$operator","reference":"$reference"}'::jsonb, + '{"passed":{"type":"boolean"}}'::jsonb, '{"passed":{"type":"boolean"}}'::jsonb, + false, 'sha256:01a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301' + ), + ( + '0f2c0000-0000-4000-8000-000000000002'::uuid, + 'BASIC_PRICE_CHANGE_PERCENT', 'CONDITION', + '{"type":"object","properties":{"resolution":{"type":"string"},"base":{"type":"string","enum":["PREVIOUS_CLOSE","SESSION_OPEN","AVERAGE_ENTRY_PRICE"]},"direction":{"type":"string","enum":["UP","DOWN"]},"thresholdPercent":{"type":"string","minLength":1}},"required":["resolution","base","direction","thresholdPercent"]}'::jsonb, + '["BUY","SELL"]'::jsonb, 'PRICE_CHANGE_PERCENT', + '{"resolution":"$resolution","base":"$base","direction":"$direction","thresholdPercent":"$thresholdPercent"}'::jsonb, + '{"passed":{"type":"boolean"}}'::jsonb, '{"passed":{"type":"boolean"}}'::jsonb, + false, 'sha256:02a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301' + ), + ( + '0f2c0000-0000-4000-8000-000000000003'::uuid, + 'BASIC_VOLUME_COMPARE', 'CONDITION', + '{"type":"object","properties":{"resolution":{"type":"string"},"operator":{"type":"string","enum":["LT","LTE","GT","GTE","EQ","NEQ"]},"reference":{"type":"string","enum":["PREVIOUS_VOLUME","AVERAGE_VOLUME"]},"period":{"type":"string","enum":["1","5","20","60"]},"multiplier":{"type":"string","enum":["1","2","3"]}},"required":["resolution","operator","reference","period","multiplier"]}'::jsonb, + '["BUY","SELL"]'::jsonb, 'VOLUME_COMPARE', + '{"resolution":"$resolution","operator":"$operator","reference":"$reference","period":"$period","multiplier":"$multiplier"}'::jsonb, + '{"passed":{"type":"boolean"}}'::jsonb, '{"passed":{"type":"boolean"}}'::jsonb, + false, 'sha256:03a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301' + ), + ( + '0f2c0000-0000-4000-8000-000000000004'::uuid, + 'BASIC_STREAK', 'CONDITION', + '{"type":"object","properties":{"resolution":{"type":"string"},"direction":{"type":"string","enum":["UP","DOWN"]},"bars":{"type":"string","enum":["2","3","5","10","20","30"]}},"required":["resolution","direction","bars"]}'::jsonb, + '["BUY","SELL"]'::jsonb, 'STREAK', + '{"resolution":"$resolution","direction":"$direction","bars":"$bars"}'::jsonb, + '{"passed":{"type":"boolean"}}'::jsonb, '{"passed":{"type":"boolean"}}'::jsonb, + false, 'sha256:04a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301' + ), + ( + '0f2c0000-0000-4000-8000-000000000005'::uuid, + 'BASIC_SMA_CROSS', 'CONDITION', + '{"type":"object","properties":{"resolution":{"type":"string"},"direction":{"type":"string","enum":["UP","DOWN"]},"shortPeriod":{"type":"string","enum":["5","20","60"]},"longPeriod":{"type":"string","enum":["20","60","120"]}},"required":["resolution","direction","shortPeriod","longPeriod"]}'::jsonb, + '["BUY","SELL"]'::jsonb, 'SMA_CROSS', + '{"resolution":"$resolution","direction":"$direction","shortPeriod":"$shortPeriod","longPeriod":"$longPeriod"}'::jsonb, + '{"passed":{"type":"boolean"}}'::jsonb, '{"passed":{"type":"boolean"}}'::jsonb, + false, 'sha256:05a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301' + ), + ( + '0f2c0000-0000-4000-8000-000000000006'::uuid, + 'BASIC_RSI_CROSS', 'CONDITION', + '{"type":"object","properties":{"resolution":{"type":"string"},"direction":{"type":"string","enum":["UP","DOWN"]},"period":{"type":"string","enum":["14"]},"threshold":{"type":"string","minLength":1}},"required":["resolution","direction","period","threshold"]}'::jsonb, + '["BUY","SELL"]'::jsonb, 'RSI_CROSS', + '{"resolution":"$resolution","direction":"$direction","period":"$period","threshold":"$threshold"}'::jsonb, + '{"passed":{"type":"boolean"}}'::jsonb, '{"passed":{"type":"boolean"}}'::jsonb, + false, 'sha256:06a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301' + ), + ( + '0f2c0000-0000-4000-8000-000000000007'::uuid, + 'BASIC_MACD_CROSS', 'CONDITION', + '{"type":"object","properties":{"resolution":{"type":"string"},"direction":{"type":"string","enum":["UP","DOWN"]},"fastPeriod":{"type":"string","enum":["12"]},"slowPeriod":{"type":"string","enum":["26"]},"signalPeriod":{"type":"string","enum":["9"]}},"required":["resolution","direction","fastPeriod","slowPeriod","signalPeriod"]}'::jsonb, + '["BUY","SELL"]'::jsonb, 'MACD_CROSS', + '{"resolution":"$resolution","direction":"$direction","fastPeriod":"$fastPeriod","slowPeriod":"$slowPeriod","signalPeriod":"$signalPeriod"}'::jsonb, + '{"passed":{"type":"boolean"}}'::jsonb, '{"passed":{"type":"boolean"}}'::jsonb, + false, 'sha256:07a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301' + ), + ( + '0f2c0000-0000-4000-8000-000000000008'::uuid, + 'BASIC_BOLLINGER_REVERSAL', 'CONDITION', + '{"type":"object","properties":{"resolution":{"type":"string"},"direction":{"type":"string","enum":["UP","DOWN"]},"period":{"type":"string","enum":["20"]},"deviations":{"type":"string","enum":["2"]}},"required":["resolution","direction","period","deviations"]}'::jsonb, + '["BUY","SELL"]'::jsonb, 'BOLLINGER_REVERSAL', + '{"resolution":"$resolution","direction":"$direction","period":"$period","deviations":"$deviations"}'::jsonb, + '{"passed":{"type":"boolean"}}'::jsonb, '{"passed":{"type":"boolean"}}'::jsonb, + false, 'sha256:08a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301' + ), + ( + '0f2c0000-0000-4000-8000-000000000009'::uuid, + 'BASIC_POSITION_RETURN', 'CONDITION', + '{"type":"object","properties":{"direction":{"type":"string","enum":["PROFIT","LOSS"]},"thresholdPercent":{"type":"string","minLength":1}},"required":["direction","thresholdPercent"]}'::jsonb, + '["SELL"]'::jsonb, 'POSITION_RETURN', + '{"direction":"$direction","thresholdPercent":"$thresholdPercent"}'::jsonb, + '{"passed":{"type":"boolean"}}'::jsonb, '{"passed":{"type":"boolean"}}'::jsonb, + false, 'sha256:09a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301' + ), + ( + '0f2c0000-0000-4000-8000-000000000010'::uuid, + 'BASIC_HOLDING_PERIOD', 'CONDITION', + '{"type":"object","properties":{"unit":{"type":"string","enum":["SESSION_CLOSE","BAR","TRADING_DAY"]},"amount":{"type":"string","enum":["0","1","5","20"]},"resolution":{"type":"string"}},"required":["unit","amount","resolution"]}'::jsonb, + '["SELL"]'::jsonb, 'HOLDING_PERIOD', + '{"unit":"$unit","amount":"$amount","resolution":"$resolution"}'::jsonb, + '{"passed":{"type":"boolean"}}'::jsonb, '{"passed":{"type":"boolean"}}'::jsonb, + false, 'sha256:10a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301' + ), + ( + '0f2c0000-0000-4000-8000-000000000011'::uuid, + 'BASIC_PEAK_RETURN', 'CONDITION', + '{"type":"object","properties":{"operator":{"type":"string","enum":["LT","LTE","GT","GTE","EQ","NEQ"]},"thresholdPercent":{"type":"string","minLength":1}},"required":["operator","thresholdPercent"]}'::jsonb, + '["SELL"]'::jsonb, 'PEAK_RETURN', + '{"operator":"$operator","thresholdPercent":"$thresholdPercent"}'::jsonb, + '{"passed":{"type":"boolean"}}'::jsonb, '{"passed":{"type":"boolean"}}'::jsonb, + false, 'sha256:11a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301' + ), + ( + '0f2c0000-0000-4000-8000-000000000012'::uuid, + 'BASIC_DRAWDOWN_FROM_PEAK', 'CONDITION', + '{"type":"object","properties":{"operator":{"type":"string","enum":["LT","LTE","GT","GTE","EQ","NEQ"]},"thresholdPercent":{"type":"string","minLength":1}},"required":["operator","thresholdPercent"]}'::jsonb, + '["SELL"]'::jsonb, 'DRAWDOWN_FROM_PEAK', + '{"operator":"$operator","thresholdPercent":"$thresholdPercent"}'::jsonb, + '{"passed":{"type":"boolean"}}'::jsonb, '{"passed":{"type":"boolean"}}'::jsonb, + false, 'sha256:12a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301' + ), + ( + '0f2c0000-0000-4000-8000-000000000013'::uuid, + 'BASIC_SCHEDULE', 'TRIGGER', + '{"type":"object","properties":{"cycle":{"type":"string","enum":["EVERY_TRADING_DAY","WEEK_FIRST_TRADING_DAY","MONTH_FIRST_TRADING_DAY","MONTH_LAST_TRADING_DAY","EVERY_N_TRADING_DAYS"]},"interval":{"type":"string","minLength":1},"resolution":{"type":"string"}},"required":["cycle","interval","resolution"]}'::jsonb, + '["BUY"]'::jsonb, 'SCHEDULE', + '{"cycle":"$cycle","interval":"$interval","resolution":"$resolution"}'::jsonb, + '{"passed":{"type":"boolean"}}'::jsonb, '{"passed":{"type":"boolean"}}'::jsonb, + false, 'sha256:13a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301' + ), + ( + '0f2c0000-0000-4000-8000-000000000014'::uuid, + 'BASIC_EQUAL_ALLOCATION_ORDER', 'ACTION', + '{"type":"object","properties":{"orderPercent":{"type":"string","minLength":1},"executionMode":{"type":"string"},"waitMode":{"type":"string"},"waitInterval":{"type":"string","minLength":1},"maxExecutions":{"type":"string","minLength":1}},"required":["orderPercent","executionMode","waitMode","waitInterval","maxExecutions"]}'::jsonb, + '["BUY","SELL"]'::jsonb, 'EMIT_ORDER_CANDIDATE', + '{"allocation":"EQUAL","orderType":"MARKET","timeInForce":"DAY","side":"$container","orderPercent":"$orderPercent","executionMode":"$executionMode","waitMode":"$waitMode","waitInterval":"$waitInterval","maxExecutions":"$maxExecutions"}'::jsonb, + '{"passed":{"type":"boolean"}}'::jsonb, '{}'::jsonb, + true, 'sha256:14a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301' + ) +) +INSERT INTO strategy.element_definitions ( + id, element_catalog_version_id, element_code, element_kind, + parameter_schema, input_port_schema, output_port_schema, execution_contract, definition_hash) +SELECT + id, + '0f2a0000-0000-4000-8000-000000000001'::uuid, + element_code, + element_kind, + parameter_schema, + input_ports, + output_ports, + jsonb_build_object( + 'deterministic', true, + 'terminal', terminal, + 'containers', containers, + 'runtime', jsonb_build_object('operation', operation, 'arguments', arguments), + 'backtest', jsonb_build_object( + 'supported', true, + 'feeds', CASE WHEN terminal THEN '[]'::jsonb ELSE '[{"feed":"ADJUSTED_BAR","resolution":"1m"}]'::jsonb END, + 'features', '[]'::jsonb), + 'reviewTemplates', jsonb_build_object('ko-KR', element_code)), + definition_hash +FROM definitions; diff --git a/db/canonical-baseline/V20260808000000__backend_publish_live_strategy_timeframes.sql b/db/canonical-baseline/V20260808000000__backend_publish_live_strategy_timeframes.sql new file mode 100644 index 0000000..6e88854 --- /dev/null +++ b/db/canonical-baseline/V20260808000000__backend_publish_live_strategy_timeframes.sql @@ -0,0 +1,43 @@ +-- Publishes the first Basic catalog whose live resolutions match the strategy worker. +-- Display-only 1m/5m/15m bars are intentionally excluded: live strategies evaluate only on +-- finalized 30m, 1h, 4h, and 1d candles. + +UPDATE strategy.element_catalog_versions +SET retired_at = '2026-08-07T15:00:00+00' +WHERE id = '0f2a0000-0000-4000-8000-000000000001' + AND retired_at IS NULL; + +INSERT INTO strategy.element_catalog_versions ( + id, language_version, schema_version, catalog_version, data_requirement_version, + definition_hash, published_at) +VALUES ( + '0f3a0000-0000-4000-8000-000000000001', + 'basic/v1', + 'basic-semantic/v1', + 'basic-elements:2026-08-08-live-bars', + 'alpaca-sip/v1', + 'sha256:30a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301', + '2026-08-07T15:00:00+00'); + +INSERT INTO strategy.element_definitions ( + id, element_catalog_version_id, element_code, element_kind, + parameter_schema, input_port_schema, output_port_schema, execution_contract, definition_hash) +SELECT + ('0f3c' || substring(id::text from 5))::uuid, + '0f3a0000-0000-4000-8000-000000000001'::uuid, + element_code, + element_kind, + CASE + WHEN parameter_schema #> '{properties,resolution}' IS NULL THEN parameter_schema + ELSE jsonb_set( + parameter_schema, + '{properties,resolution}', + '{"type":"string","enum":["30m","1h","4h","1d"]}'::jsonb) + END, + input_port_schema, + output_port_schema, + execution_contract, + 'sha256:' || md5(definition_hash || ':live-timeframes') + || md5('live-timeframes:' || definition_hash) +FROM strategy.element_definitions +WHERE element_catalog_version_id = '0f2a0000-0000-4000-8000-000000000001'; diff --git a/db/canonical-baseline/V20260808120000__backend_publish_production_backtest_resolutions.sql b/db/canonical-baseline/V20260808120000__backend_publish_production_backtest_resolutions.sql new file mode 100644 index 0000000..23b5fe1 --- /dev/null +++ b/db/canonical-baseline/V20260808120000__backend_publish_production_backtest_resolutions.sql @@ -0,0 +1,80 @@ +-- Replaces the live-timeframe catalog with the production backtest resolution contract. +-- Earlier catalogs remain immutable so already released strategies keep their meaning. + +UPDATE strategy.element_catalog_versions +SET retired_at = '2026-08-07T16:00:00+00' +WHERE id = '0f3a0000-0000-4000-8000-000000000001' + AND retired_at IS NULL; + +INSERT INTO strategy.element_catalog_versions ( + id, language_version, schema_version, catalog_version, data_requirement_version, + definition_hash, published_at) +VALUES ( + '0f4a0000-0000-4000-8000-000000000001', + 'basic/v1', + 'basic-semantic/v1', + 'basic-elements:2026-08-08', + 'alpaca-sip/v1', + 'sha256:a46b05ff472bb14f011d288900804142e385520f8c9c448b9bf4da8ea6f755da', + '2026-08-07T16:00:00+00'); + +WITH replacement_ids(element_code, id, definition_hash) AS (VALUES + ('BASIC_PRICE_COMPARE', '0f4c0000-0000-4000-8000-000000000001'::uuid, 'sha256:71a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301'), + ('BASIC_PRICE_CHANGE_PERCENT', '0f4c0000-0000-4000-8000-000000000002'::uuid, 'sha256:72a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301'), + ('BASIC_VOLUME_COMPARE', '0f4c0000-0000-4000-8000-000000000003'::uuid, 'sha256:73a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301'), + ('BASIC_STREAK', '0f4c0000-0000-4000-8000-000000000004'::uuid, 'sha256:74a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301'), + ('BASIC_SMA_CROSS', '0f4c0000-0000-4000-8000-000000000005'::uuid, 'sha256:75a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301'), + ('BASIC_RSI_CROSS', '0f4c0000-0000-4000-8000-000000000006'::uuid, 'sha256:76a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301'), + ('BASIC_MACD_CROSS', '0f4c0000-0000-4000-8000-000000000007'::uuid, 'sha256:77a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301'), + ('BASIC_BOLLINGER_REVERSAL', '0f4c0000-0000-4000-8000-000000000008'::uuid, 'sha256:78a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301'), + ('BASIC_POSITION_RETURN', '0f4c0000-0000-4000-8000-000000000009'::uuid, 'sha256:79a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301'), + ('BASIC_HOLDING_PERIOD', '0f4c0000-0000-4000-8000-000000000010'::uuid, 'sha256:80a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301'), + ('BASIC_PEAK_RETURN', '0f4c0000-0000-4000-8000-000000000011'::uuid, 'sha256:81a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301'), + ('BASIC_DRAWDOWN_FROM_PEAK', '0f4c0000-0000-4000-8000-000000000012'::uuid, 'sha256:82a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301'), + ('BASIC_SCHEDULE', '0f4c0000-0000-4000-8000-000000000013'::uuid, 'sha256:83a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301'), + ('BASIC_EQUAL_ALLOCATION_ORDER', '0f4c0000-0000-4000-8000-000000000014'::uuid, 'sha256:84a8aa1db1ec89acd824d270df6df53f652f58ad75a7688f59efb20bf86b4301') +) +INSERT INTO strategy.element_definitions ( + id, element_catalog_version_id, element_code, element_kind, + parameter_schema, input_port_schema, output_port_schema, execution_contract, definition_hash) +SELECT + replacement.id, + '0f4a0000-0000-4000-8000-000000000001'::uuid, + previous.element_code, + previous.element_kind, + CASE + WHEN previous.parameter_schema #> '{properties,resolution}' IS NULL + THEN previous.parameter_schema + ELSE jsonb_set( + previous.parameter_schema, + '{properties,resolution,enum}', + '["30m","1h","4h","1d"]'::jsonb, + true) + END, + previous.input_port_schema, + previous.output_port_schema, + jsonb_set( + previous.execution_contract #- '{backtest,feeds}', + '{backtest,features}', + CASE WHEN previous.element_code = 'BASIC_RSI_CROSS' + THEN '["RSI_14"]'::jsonb + ELSE '[]'::jsonb + END, + true), + replacement.definition_hash +FROM strategy.element_definitions previous +JOIN replacement_ids replacement USING (element_code) +WHERE previous.element_catalog_version_id = '0f3a0000-0000-4000-8000-000000000001'::uuid; + +UPDATE strategy.element_definitions +SET parameter_schema = jsonb_set( + jsonb_set( + parameter_schema, + '{properties,executionMode,enum}', + '["1회만","주기마다","대기 후 재진입","대기 후 재실행"]'::jsonb, + true), + '{properties,waitMode,enum}', + '["조건 재충족","N봉 이후","N거래일 이후"]'::jsonb, + true) +WHERE element_catalog_version_id = '0f4a0000-0000-4000-8000-000000000001'::uuid + AND element_code = 'BASIC_EQUAL_ALLOCATION_ORDER'; diff --git a/db/canonical-baseline/V20260808120100__pipeline_seed_production_rsi_timeframes.sql b/db/canonical-baseline/V20260808120100__pipeline_seed_production_rsi_timeframes.sql new file mode 100644 index 0000000..66ac3f3 --- /dev/null +++ b/db/canonical-baseline/V20260808120100__pipeline_seed_production_rsi_timeframes.sql @@ -0,0 +1,82 @@ +-- Proposed production replacement for the legacy RSI_14@1m output. +-- New strategy releases select exactly one of 30m, 1h, 4h, or 1d and pin the +-- definition and deterministic output feed at that same resolution. +DO $migration$ +DECLARE + expected record; + rsi_parameters jsonb := '{"period":14,"price_field":"close","method":"SIMPLE_AVERAGE_BOUNDED_WINDOW","input_adjustment":"SPLIT_DIVIDEND_ADJUSTED","calendar_id":"XNYS"}'::jsonb; +BEGIN + IF EXISTS (SELECT 1 FROM market_data.providers WHERE id = 'b9146ed9-dbb0-5323-93e3-8518f3851236') THEN + IF NOT EXISTS ( + SELECT 1 FROM market_data.providers + WHERE id = 'b9146ed9-dbb0-5323-93e3-8518f3851236' + AND code = 'IDEA2STRATEGY_INTERNAL' + AND display_name = 'Idea2Strategy Derived Data' + AND rights_version = 'internal-derived-v1' AND status = 'ACTIVE' + ) THEN + RAISE EXCEPTION 'IDEA2STRATEGY_INTERNAL provider identity drift'; + END IF; + ELSE + INSERT INTO market_data.providers (id, code, display_name, rights_version, status, created_at) + VALUES ('b9146ed9-dbb0-5323-93e3-8518f3851236', 'IDEA2STRATEGY_INTERNAL', + 'Idea2Strategy Derived Data', 'internal-derived-v1', 'ACTIVE', + TIMESTAMPTZ '2026-08-08 12:01:00+00'); + END IF; + + FOR expected IN + SELECT * FROM (VALUES + ('30m', '4b1c6801-0259-5176-a857-0e5ea923d898'::uuid, '363f534dc77c6af0ebfe58f35be4fd2aa208906b1eaa36b550b17e9acb8692e4', '57794d8c-2254-53e4-966e-44f97edd9e6a'::uuid, 'FEATURE_RSI_14_30M_RSI_1_0_0'), + ('1h', '2e18c093-5d4e-5d9a-bd22-b7e5679f1a3e'::uuid, '9b8512c0502ca80e1804711ac624eb4a3b4e294a875dac2364e3510e284cc8b9', '28012549-4f45-56d3-8bb6-329e4c7a9d77'::uuid, 'FEATURE_RSI_14_1H_RSI_1_0_0'), + ('4h', '1b2785bd-20f0-50a2-ae96-6a1f7bad74b9'::uuid, 'da3aff028a1fdef861abb1d68852e2ba3a91ed3917f7c7196e2d43ef48176b2c', 'e1d7d508-aaf1-5ae9-8098-c4af870f6fa4'::uuid, 'FEATURE_RSI_14_4H_RSI_1_0_0'), + ('1d', 'eddfb2d4-8586-5260-8fc9-9c8125990270'::uuid, '0cf646eb9cacf5826d26f7dcb982bf7cec9213cc438b99716ac47883aa04ba04', '6d2647f8-5caf-55ee-8821-869dc693f68a'::uuid, 'FEATURE_RSI_14_1D_RSI_1_0_0') + ) AS definitions(resolution, definition_id, definition_hash, feed_id, feed_code) + LOOP + IF EXISTS (SELECT 1 FROM market_data.feature_definitions WHERE id = expected.definition_id) THEN + IF NOT EXISTS ( + SELECT 1 FROM market_data.feature_definitions + WHERE id = expected.definition_id + AND element_catalog_version_id = '0f4a0000-0000-4000-8000-000000000001' + AND feature_code = 'RSI_14' AND calculator_version = 'rsi:1.0.0' + AND resolution = expected.resolution AND normalized_parameters = rsi_parameters + AND output_value_type = 'NUMBER' AND required_history_points = 15 + AND definition_hash = expected.definition_hash + ) THEN + RAISE EXCEPTION 'production RSI_14 definition identity drift at %', expected.resolution; + END IF; + ELSE + INSERT INTO market_data.feature_definitions ( + id, element_catalog_version_id, feature_code, calculator_version, resolution, + normalized_parameters, output_value_type, required_history_points, definition_hash, created_at + ) VALUES ( + expected.definition_id, '0f4a0000-0000-4000-8000-000000000001', + 'RSI_14', 'rsi:1.0.0', expected.resolution, rsi_parameters, 'NUMBER', 15, + expected.definition_hash, TIMESTAMPTZ '2026-08-08 12:01:00+00' + ); + END IF; + + IF EXISTS (SELECT 1 FROM market_data.feeds WHERE id = expected.feed_id) THEN + IF NOT EXISTS ( + SELECT 1 FROM market_data.feeds + WHERE id = expected.feed_id + AND provider_id = 'b9146ed9-dbb0-5323-93e3-8518f3851236' + AND code = expected.feed_code AND data_kind = 'FEATURE_SERIES' + AND resolution = expected.resolution AND timezone_name = 'UTC' + AND feed_version = 'rsi-1.0.0+feature-series.parquet.v1' + AND retired_at IS NULL + ) THEN + RAISE EXCEPTION 'production RSI_14 feed identity drift at %', expected.resolution; + END IF; + ELSE + INSERT INTO market_data.feeds ( + id, provider_id, code, data_kind, resolution, timezone_name, + feed_version, created_at, retired_at + ) VALUES ( + expected.feed_id, 'b9146ed9-dbb0-5323-93e3-8518f3851236', + expected.feed_code, 'FEATURE_SERIES', expected.resolution, 'UTC', + 'rsi-1.0.0+feature-series.parquet.v1', + TIMESTAMPTZ '2026-08-08 12:01:00+00', NULL + ); + END IF; + END LOOP; +END +$migration$; diff --git a/db/canonical-baseline/baseline.manifest b/db/canonical-baseline/baseline.manifest index 2c7475a..2d6583e 100644 --- a/db/canonical-baseline/baseline.manifest +++ b/db/canonical-baseline/baseline.manifest @@ -1,5 +1,5 @@ idea2strategy-canonical-baseline-v1 -R__database_runtime_grants.sql 9075477009cc487c355ffb433d6035f9c9d40eda859aa9cba7e841dcce415978 +R__database_runtime_grants.sql 254d7a693e810225a3ac51f90481a30df40f34c766c05b74ff5bc342f58a4597 V1__initial_schema.sql 333a39cb2fe1bb01e93b487e29a50c27ec9b9937cbef952974f385a9af9707ce V20260801112341__backend_identity_email_auth.sql d483ed0c22904929f533ea122d3e77907f5b8084163b377797b10166984ae9cb V20260801153000__backend_bot_continuation_deadlines.sql d4dcb770ffdbc5e004b1feb587d53f20ee772ae024f20a8c9f846037055904be @@ -44,3 +44,11 @@ V20260804160100__backtest_runtime_ownership_constrain.sql ec1ce83eda58025656617b V20260805010000__pipeline_upgrade_legacy_market_schema.sql 961c4ff61d3dc0754d6d09cf244d887c8ad0e82746da81366c16a264ec1ff23b V20260805130000__backtest_run_input_pins.sql 0311a2767e99ead8910afa5f5b5f578e6ca1e0fd7a41f1c321888a61eac0a022 V20260805153000__trading_add_candidate_batch_processing.sql 09f304bc0f4e972684665f0370468cf25c2a1ae2314980cd8aa9feb116e9b767 +V20260805170000__backtest_run_outcome_detail.sql 8cc6ba086e1f7072051058540a9c91997452338ebd204a05cbbea2093d2ef731 +V20260806100000__backend_google_oidc_provider.sql 3e63766bdaa38995bbefa6b541a73eab8037dd93ef54f3e8e481d2a8c8ed0b71 +V20260806120000__pipeline_seed_official_feature_output_feed.sql a28002b33dc35b427fadfa51d4f0138c3da8d8568d9f1ada66a7ddd5f2749b6d +V20260807120000__backend_customer_refresh_token_families.sql 7878182aa4e2910946f2ff435a09d5dc853c91cf61105c79187ca3ee850bada1 +V20260807130000__backend_publish_full_basic_element_catalog.sql 7d974acc1575cd40e2df7c219fd569dffa7c393d7bf5d0617fac24c6eb1369f4 +V20260808000000__backend_publish_live_strategy_timeframes.sql 87392d260f13a8e2ad90e025bb3afd6fd95dc7abdc2c223afbfbdfaa046cb6c7 +V20260808120000__backend_publish_production_backtest_resolutions.sql 94ae572554a7cef8b0b9f91a24a6fd14b7c2ad93feccd283da7047bcb3239f30 +V20260808120100__pipeline_seed_production_rsi_timeframes.sql 40b691b98fb4b28c58741173bb9db91f61cef1ec69c4c693583645bcc6c01487 diff --git a/db/canonical-baseline/baseline.sha256 b/db/canonical-baseline/baseline.sha256 index d9e24ec..1694996 100644 --- a/db/canonical-baseline/baseline.sha256 +++ b/db/canonical-baseline/baseline.sha256 @@ -1 +1 @@ -303ff2c9fc635880f42c5d85f5ffe977799ed46604be2883ea972fe2df71560c +50ff03a93be8c690b500b6f3085fde7fc102bdccfc9f75404a1d5e8ca7ac8cd3