From b07ecbed29fda2f9133590f7cb9b651635553388 Mon Sep 17 00:00:00 2001 From: Ryan Min Date: Sun, 9 Aug 2026 12:24:09 +0900 Subject: [PATCH] chore: re-vendor the canonical baseline from the current central bundle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vendored copy had drifted eight migrations behind the assembled bundle, and its grants file predated three grant fixes. CanonicalBaselineContractTest was failing on any branch here, which blocked this repository's CI regardless of what the change was. Re-vendored per db/canonical-baseline/README.md: the assembled bundle copied verbatim, baseline.manifest and baseline.sha256 regenerated. Nothing was hand-edited. The result is checkable rather than asserted: the regenerated baseline.sha256 is 50ff03a9…, which equals canonical_baseline_sha256 already recorded in the root bundle's source-revisions.json at root develop 8be256a. Both sides now agree on the same digest, computed independently. What actually differed, after normalising CRLF the way the manifest does: - eight migrations absent here entirely, from backtest run outcome detail through the production RSI timeframe seeds - R__database_runtime_grants.sql, which had none of backend #241, #246 or #249 — the grants for the batch role's writes and the backtest role's operations and bot reads Worth recording because it misled me first: a raw byte comparison reported all 45 shared files as different. That was line endings alone. The manifest normalises CRLF to LF for exactly this reason, and comparing the same way reduced the real difference to one file plus the eight absent ones. I had told the trading-engine issue that a single file needed copying; that was wrong, and this commit is the correction. Verified: CanonicalBaselineContractTest passes, and the full ./gradlew test suite passes in 7m22s. This repository moved to kcrmin in the 2026-08-09 two-owner split, which is why the re-vendor no longer waits on another owner. Co-Authored-By: Claude Opus 5 --- .../R__database_runtime_grants.sql | 28 +-- ...805170000__backtest_run_outcome_detail.sql | 35 ++++ ...06100000__backend_google_oidc_provider.sql | 8 + ...line_seed_official_feature_output_feed.sql | 69 +++++++ ...ackend_customer_refresh_token_families.sql | 87 +++++++++ ...end_publish_full_basic_element_catalog.sql | 175 ++++++++++++++++++ ...ckend_publish_live_strategy_timeframes.sql | 43 +++++ ...ublish_production_backtest_resolutions.sql | 80 ++++++++ ...ipeline_seed_production_rsi_timeframes.sql | 82 ++++++++ db/canonical-baseline/baseline.manifest | 10 +- db/canonical-baseline/baseline.sha256 | 2 +- 11 files changed, 606 insertions(+), 13 deletions(-) create mode 100644 db/canonical-baseline/V20260805170000__backtest_run_outcome_detail.sql create mode 100644 db/canonical-baseline/V20260806100000__backend_google_oidc_provider.sql create mode 100644 db/canonical-baseline/V20260806120000__pipeline_seed_official_feature_output_feed.sql create mode 100644 db/canonical-baseline/V20260807120000__backend_customer_refresh_token_families.sql create mode 100644 db/canonical-baseline/V20260807130000__backend_publish_full_basic_element_catalog.sql create mode 100644 db/canonical-baseline/V20260808000000__backend_publish_live_strategy_timeframes.sql create mode 100644 db/canonical-baseline/V20260808120000__backend_publish_production_backtest_resolutions.sql create mode 100644 db/canonical-baseline/V20260808120100__pipeline_seed_production_rsi_timeframes.sql 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