Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import com.idea2strategy.backend.persistence.strategy.ImmutableStrategyReleaseJooqCommandAdapter;
import com.idea2strategy.backend.persistence.backtest.FeatureMaterializationPinResolver;
import com.idea2strategy.backend.persistence.strategy.StrategyDocumentJooqQueryAdapter;
import com.idea2strategy.backend.persistence.strategy.StrategyReleaseInputCatalogJooqQueryAdapter;
import com.idea2strategy.backend.persistence.strategy.StrategyJooqQueryAdapter;
import com.idea2strategy.backend.persistence.strategy.StrategyValidationRunJooqQueryAdapter;
import java.time.Clock;
Expand Down Expand Up @@ -233,13 +234,15 @@ ImmutableStrategyReleaseCommandService roomImmutableStrategyReleaseCommandServic
StrategyValidationRunJooqQueryAdapter validationAdapter,
StrategyJooqQueryAdapter strategyAdapter,
StrategyDocumentJooqQueryAdapter documentAdapter,
StrategyReleaseInputCatalogJooqQueryAdapter releaseInputs,
CurrentPrincipal principal) {
return new ImmutableStrategyReleaseCommandService(
releaseAdapter,
planService,
validationAdapter,
strategyAdapter,
documentAdapter,
releaseInputs,
principal,
Clock.systemUTC());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,23 +55,9 @@ public record ReleaseRequest(
UUID validationRunId,
BigDecimal initialCashAmount,
int budgetCapBps,
String brokerRulesVersion,
String accountingRulesVersion,
String precisionRulesVersion,
UUID feePolicyId,
UUID buyingPowerBufferPolicyId,
UUID datasetManifestId,
String executionPolicyVersion,
Map<String, Object> candidateConflictPolicy) {
ImmutableStrategyReleaseCommand toCommand(UUID releaseId) {
require(initialCashAmount, "initialCashAmount");
require(brokerRulesVersion, "brokerRulesVersion");
require(accountingRulesVersion, "accountingRulesVersion");
require(precisionRulesVersion, "precisionRulesVersion");
require(feePolicyId, "feePolicyId");
require(buyingPowerBufferPolicyId, "buyingPowerBufferPolicyId");
require(datasetManifestId, "datasetManifestId");
require(executionPolicyVersion, "executionPolicyVersion");
require(candidateConflictPolicy, "candidateConflictPolicy");
if (budgetCapBps <= 0 || budgetCapBps > 10_000) {
throw new IllegalArgumentException("budgetCapBps must be in 1..10000");
Expand All @@ -80,13 +66,6 @@ ImmutableStrategyReleaseCommand toCommand(UUID releaseId) {
releaseId,
initialCashAmount,
budgetCapBps,
brokerRulesVersion,
accountingRulesVersion,
precisionRulesVersion,
feePolicyId,
buyingPowerBufferPolicyId,
datasetManifestId,
executionPolicyVersion,
json(candidateConflictPolicy));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@ class StrategyReleaseControllerTest {
private static final UUID STRATEGY_ID = UUID.fromString("20000000-0000-4000-8000-000000000001");
private static final UUID VALIDATION_ID = UUID.fromString("30000000-0000-4000-8000-000000000001");
private static final UUID BOT_ID = UUID.fromString("50000000-0000-4000-8000-000000000002");
private static final UUID FEE_ID = UUID.fromString("80000000-0000-4000-8000-000000000001");
private static final UUID BUFFER_ID = UUID.fromString("90000000-0000-4000-8000-000000000001");
private static final UUID DATASET_ID = UUID.fromString("a0000000-0000-4000-8000-000000000001");

private ImmutableStrategyReleaseCommandService releaseService;
private BasicStrategyCatalogQueryService catalogService;
private MockMvc mvc;
Expand All @@ -57,13 +53,6 @@ void releasesTheValidatedStrategyAndStartsItsOfficialBasicBacktest() throws Exce
"validationRunId":"30000000-0000-4000-8000-000000000001",
"initialCashAmount":100000.00,
"budgetCapBps":10000,
"brokerRulesVersion":"broker/v1",
"accountingRulesVersion":"accounting/v1",
"precisionRulesVersion":"precision/v1",
"feePolicyId":"80000000-0000-4000-8000-000000000001",
"buyingPowerBufferPolicyId":"90000000-0000-4000-8000-000000000001",
"datasetManifestId":"a0000000-0000-4000-8000-000000000001",
"executionPolicyVersion":"backtest-policy-v1",
"candidateConflictPolicy":{"policy":"FIRST_WINS"}
}
"""))
Expand All @@ -77,9 +66,6 @@ void releasesTheValidatedStrategyAndStartsItsOfficialBasicBacktest() throws Exce
assertThat(command.getValue().releaseId())
.isEqualTo(StrategyReleaseController.releaseId(VALIDATION_ID));
assertThat(command.getValue().initialCashAmount()).isEqualByComparingTo("100000.00");
assertThat(command.getValue().feePolicyId()).isEqualTo(FEE_ID);
assertThat(command.getValue().buyingPowerBufferPolicyId()).isEqualTo(BUFFER_ID);
assertThat(command.getValue().datasetManifestId()).isEqualTo(DATASET_ID);
assertThat(command.getValue().candidateConflictPolicy()).isEqualTo("{\"policy\":\"FIRST_WINS\"}");
}

Expand Down Expand Up @@ -126,13 +112,6 @@ private static String validRequest() {
"validationRunId":"30000000-0000-4000-8000-000000000001",
"initialCashAmount":100000.00,
"budgetCapBps":10000,
"brokerRulesVersion":"broker/v1",
"accountingRulesVersion":"accounting/v1",
"precisionRulesVersion":"precision/v1",
"feePolicyId":"80000000-0000-4000-8000-000000000001",
"buyingPowerBufferPolicyId":"90000000-0000-4000-8000-000000000001",
"datasetManifestId":"a0000000-0000-4000-8000-000000000001",
"executionPolicyVersion":"backtest-policy-v1",
"candidateConflictPolicy":{"policy":"FIRST_WINS"}
}
""";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@ void exposesOnlyServerObservedImmutableReleaseInputs() throws Exception {
when(service.getSelectable()).thenReturn(new StrategyReleaseInputCatalog(
List.of(new ExecutionPolicy(
"policy-v1", "market-v1", "accounting-v1", "precision-v1",
feeId, 20, bufferId, 1)),
feeId, 20, bufferId, 1,
LocalDate.parse("2025-01-01"), LocalDate.parse("2026-01-01"),
"market-bars-v2", Instant.parse("2026-08-07T11:00:00Z"))),
List.of(new Dataset(
datasetId, "alpaca-sip", "ADJUSTED", "1m",
LocalDate.parse("2025-01-01"), LocalDate.parse("2026-01-01"), "market-bars-v2")),
LocalDate.parse("2025-01-01"), LocalDate.parse("2026-01-01"), "market-bars-v2",
Instant.parse("2026-08-07T11:30:00Z"))),
Instant.parse("2026-08-07T12:00:00Z")));

MockMvcBuilders.standaloneSetup(new StrategyReleaseInputCatalogController(service))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,10 @@ public record ImmutableStrategyReleaseCommand(
UUID releaseId,
BigDecimal initialCashAmount,
int budgetCapBps,
String brokerRulesVersion,
String accountingRulesVersion,
String precisionRulesVersion,
UUID feePolicyId,
UUID buyingPowerBufferPolicyId,
UUID datasetManifestId,
String executionPolicyVersion,
String candidateConflictPolicy) {
public ImmutableStrategyReleaseCommand {
Objects.requireNonNull(releaseId, "releaseId");
Objects.requireNonNull(initialCashAmount, "initialCashAmount");
Objects.requireNonNull(brokerRulesVersion, "brokerRulesVersion");
Objects.requireNonNull(accountingRulesVersion, "accountingRulesVersion");
Objects.requireNonNull(precisionRulesVersion, "precisionRulesVersion");
Objects.requireNonNull(feePolicyId, "feePolicyId");
Objects.requireNonNull(buyingPowerBufferPolicyId, "buyingPowerBufferPolicyId");
Objects.requireNonNull(datasetManifestId, "datasetManifestId");
if (executionPolicyVersion == null || executionPolicyVersion.isBlank()) {
throw new IllegalArgumentException("executionPolicyVersion must not be blank");
}
candidateConflictPolicy = StrategyDocumentJson.canonicalize(candidateConflictPolicy);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public final class ImmutableStrategyReleaseCommandService {
private final StrategyValidationRunQueryPort validationPort;
private final StrategyQueryPort strategyPort;
private final StrategyDocumentQueryPort documentPort;
private final StrategyReleaseInputCatalogQueryPort releaseInputs;
private final CurrentPrincipal principal;
private final Clock clock;
private final ObjectMapper objectMapper = new ObjectMapper()
Expand All @@ -45,13 +46,15 @@ public ImmutableStrategyReleaseCommandService(
StrategyValidationRunQueryPort validationPort,
StrategyQueryPort strategyPort,
StrategyDocumentQueryPort documentPort,
StrategyReleaseInputCatalogQueryPort releaseInputs,
CurrentPrincipal principal,
Clock clock) {
this.releasePort = Objects.requireNonNull(releasePort, "releasePort");
this.planService = Objects.requireNonNull(planService, "planService");
this.validationPort = Objects.requireNonNull(validationPort, "validationPort");
this.strategyPort = Objects.requireNonNull(strategyPort, "strategyPort");
this.documentPort = Objects.requireNonNull(documentPort, "documentPort");
this.releaseInputs = Objects.requireNonNull(releaseInputs, "releaseInputs");
this.principal = Objects.requireNonNull(principal, "principal");
this.clock = Objects.requireNonNull(clock, "clock");
}
Expand All @@ -61,24 +64,31 @@ public ImmutableStrategyRelease release(
BasicStrategyCatalog catalog,
ImmutableStrategyReleaseCommand command) {
Objects.requireNonNull(command, "command");
var releasedAt = clock.instant();
var inputCatalog = releaseInputs.findSelectableAt(releasedAt);
var policy = OfficialBacktestInputSelector.selectPolicy(inputCatalog);
var preparation = new ImmutableStrategyReleasePreparationCommand(
command.releaseId(),
command.initialCashAmount(),
command.budgetCapBps(),
command.brokerRulesVersion(),
command.accountingRulesVersion(),
command.precisionRulesVersion(),
command.feePolicyId(),
command.buyingPowerBufferPolicyId(),
policy.brokerRulesVersion(),
policy.accountingRulesVersion(),
policy.precisionRulesVersion(),
policy.feePolicyId(),
policy.buyingPowerBufferPolicyId(),
command.candidateConflictPolicy());
var release = prepare(validationRunId, catalog, preparation, clock.instant());
var release = prepare(validationRunId, catalog, preparation, releasedAt);
var selectedInputs = OfficialBacktestInputSelector.select(
release.contractPlan().planDocument(), inputCatalog);
var validation = validationPort.findOwnedById(validationRunId, principal.accountId())
.orElseThrow(() -> new NoSuchElementException("Strategy validation not found"));
// prepare() already compiled the plan and assembled the contract the release publishes, and the
// request now takes its checksum from that contract. Compiling a second time here produced a
// digest of a different artifact and was the cause of root #439.
var backtestRequest = OfficialBacktestRequest.forRelease(
release, command.datasetManifestId(), command.executionPolicyVersion());
release,
selectedInputs.datasets().stream().map(StrategyReleaseInputCatalog.Dataset::id).toList(),
selectedInputs.policy().version());
return releasePort.saveOnce(
release, backtestRequest, validationRunId,
validation.requestedEditSequence(), validation.semanticHash());
Expand Down
Loading