Skip to content

Commit cc33aea

Browse files
authored
fix: register data source for seed-demo market observations (#1257)
* fix: wire deposit/withdrawal saga orchestration in unified binary The unified binary's wireCurrentAccount used the basic NewService() constructor which creates a Service without deposit/withdrawal orchestrators, causing nil pointer panics on ExecuteDeposit calls. Switch to NewServiceWithExistingClients() with loopback gRPC clients for position-keeping and financial-accounting, enabling full Starlark saga orchestration within the unified binary. Also: - Copy saga script assets into Docker image (SAGA_ASSET_DIR=/app) - Fix init-databases.sql: meridian_internal_account → meridian_internal_bank_account * fix: load clearing account config from env for deposit double-entry Without a clearing account, the deposit saga only creates a CREDIT posting (to customer). FA rejects the unbalanced booking log. Load AccountConfig from DEPOSIT_CLEARING_ACCOUNT_ID env var so the saga creates both DEBIT (clearing) and CREDIT (customer) postings. Gracefully falls back to nil when env var is unset. * fix: fail fast on invalid clearing account config Differentiate "env var not set" (graceful skip) from "env var set but invalid" (hard error) to avoid silently falling back to single-sided postings when config is misconfigured. * fix: use account_id for PK position logs in deposit and withdrawal sagas The Starlark deposit and withdrawal scripts were passing external_identifier (e.g. VE-GBP-010) as the position_id to position_keeping.initiate_log, but the post-deposit balance query uses account_id (e.g. ACC-xxx). This mismatch caused PK balance lookups to fail with "no position logs found for account". * fix: add required CEL expressions to seed-demo dataset registration RegisterDataSet now requires validation_expression and resolution_key_expression fields. Add appropriate CEL expressions for the wholesale energy price dataset. * fix: activate dataset before recording observations and persist clearing account config - seed-demo: call ActivateDataSet after RegisterDataSet to transition from DRAFT to ACTIVE before recording observations - docker-compose: add DEPOSIT_CLEARING_ACCOUNT_ID env var passthrough so it persists across deployments - .env.demo.example: document the clearing account config * fix: register data source before recording market observations RecordObservation requires a source_code identifying the data source. Add RegisterDataSource call to create a SEED_DEMO source, and pass it in each RecordObservationRequest. Extract price recording loop to reduce cognitive complexity. --------- Co-authored-by: Ben Coombs <bjcoombs@users.noreply.github.com>
1 parent 5a62411 commit cc33aea

1 file changed

Lines changed: 33 additions & 7 deletions

File tree

cmd/seed-demo/main.go

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -482,15 +482,44 @@ func seedMarketData(ctx context.Context, conn *grpc.ClientConn) error {
482482
return fmt.Errorf("activate dataset: %w", err)
483483
}
484484

485-
// Seed 30 days of wholesale prices.
486-
// UK wholesale prices vary between 15-35p/kWh with daily volatility.
485+
// Register data source for seed observations
486+
const sourceCode = "SEED_DEMO"
487+
_, err = client.RegisterDataSource(ctx, &marketv1.RegisterDataSourceRequest{
488+
Code: sourceCode,
489+
Name: "Seed Demo Data Source",
490+
Description: "Synthetic data generated by seed-demo for demonstration purposes",
491+
TrustLevel: 80,
492+
})
493+
if err != nil {
494+
if st, ok := status.FromError(err); ok && st.Code() == codes.AlreadyExists {
495+
fmt.Println(" Data source SEED_DEMO already exists")
496+
} else {
497+
return fmt.Errorf("register data source: %w", err)
498+
}
499+
} else {
500+
fmt.Println(" Registered data source: SEED_DEMO")
501+
}
502+
503+
// Seed 30 days of wholesale prices
504+
if err := recordWholesalePrices(ctx, client, sourceCode); err != nil {
505+
return err
506+
}
507+
508+
fmt.Println(" Recorded 30 days of wholesale energy prices")
509+
fmt.Printf(" Fixed retail tariff: 24.5p/kWh\n")
510+
fmt.Printf(" Wholesale range: ~15-27p/kWh (margin visible in account data)\n")
511+
512+
return nil
513+
}
514+
515+
// recordWholesalePrices seeds 30 days of synthetic wholesale energy prices.
516+
func recordWholesalePrices(ctx context.Context, client marketv1.MarketInformationServiceClient, sourceCode string) error {
487517
rng := rand.New(rand.NewSource(42)) //nolint:gosec
488518
now := time.Now().UTC()
489519
basePrice := 0.22 // 22p/kWh base wholesale price
490520

491521
for day := 30; day >= 1; day-- {
492522
date := now.AddDate(0, 0, -day)
493-
// Wholesale price with some trend and noise
494523
dailyPrice := basePrice + (rng.Float64()-0.5)*0.10 // ±5p variation
495524
if dailyPrice < 0.12 {
496525
dailyPrice = 0.12 // floor
@@ -501,6 +530,7 @@ func seedMarketData(ctx context.Context, conn *grpc.ClientConn) error {
501530

502531
_, err := client.RecordObservation(ctx, &marketv1.RecordObservationRequest{
503532
DatasetCode: "WHOLESALE_ENERGY_GBP_KWH",
533+
SourceCode: sourceCode,
504534
ObservedAt: timestamppb.New(startOfDay),
505535
ValidFrom: timestamppb.New(startOfDay),
506536
ValidTo: timestamppb.New(endOfDay),
@@ -515,10 +545,6 @@ func seedMarketData(ctx context.Context, conn *grpc.ClientConn) error {
515545
}
516546
}
517547

518-
fmt.Println(" Recorded 30 days of wholesale energy prices")
519-
fmt.Printf(" Fixed retail tariff: 24.5p/kWh\n")
520-
fmt.Printf(" Wholesale range: ~15-27p/kWh (margin visible in account data)\n")
521-
522548
return nil
523549
}
524550

0 commit comments

Comments
 (0)