package com.quantai.trader.config; import com.quantai.trader.enums.TraderExecutionMode; import com.quantai.trader.enums.TraderRunMode; import org.springframework.boot.context.properties.ConfigurationProperties; import java.math.BigDecimal; import static com.quantai.trader.util.TraderNumbers.requiredText; @ConfigurationProperties(prefix = "trader") public record TraderProperties( String serviceName, TraderRunMode runMode, String symbol, Artifact artifact, Feedback feedback, Execution execution, Runtime runtime, Outbox outbox, Release release, Risk risk, PositionManager positionManager ) { public TraderProperties { serviceName = defaultText(serviceName, "quant-trader-service"); runMode = runMode == null ? TraderRunMode.SHADOW : runMode; symbol = defaultText(symbol, "BTC-USDT-PERP"); artifact = artifact == null ? new Artifact("trader-v4-btc-p0", "cal-v4-btc-p0", "pm-v4-btc-p0", ".") : artifact; feedback = feedback == null ? new Feedback(false) : feedback; execution = execution == null ? new Execution(TraderExecutionMode.SHADOW, 3, 1500) : execution; runtime = runtime == null ? new Runtime("trader:v4", true, false) : runtime; outbox = outbox == null ? new Outbox(true, 5) : outbox; release = release == null ? new Release(true, true, true) : release; risk = risk == null ? new Risk(new BigDecimal("200"), BigDecimal.ONE, new BigDecimal("500")) : risk; positionManager = positionManager == null ? new PositionManager(BigDecimal.ONE, BigDecimal.ONE) : positionManager; } private static String defaultText(String value, String defaultValue) { return value == null || value.isBlank() ? defaultValue : value; } public record Artifact( String modelBundleVersion, String calibrationBundleVersion, String pmConfigVersion, String artifactRoot ) { public Artifact { modelBundleVersion = requiredText(modelBundleVersion, "artifact.modelBundleVersion"); calibrationBundleVersion = requiredText(calibrationBundleVersion, "artifact.calibrationBundleVersion"); pmConfigVersion = requiredText(pmConfigVersion, "artifact.pmConfigVersion"); artifactRoot = requiredText(artifactRoot, "artifact.artifactRoot"); } } public record Feedback(boolean httpEnabled) { } public record Execution(TraderExecutionMode mode, int maxApiErrorCount, long maxExchangeLatencyMs) { public Execution { mode = mode == null ? TraderExecutionMode.SHADOW : mode; } } public record Runtime(String redisKeyPrefix, boolean requireRedisForOpenAdd, boolean tradingEnabled) { public Runtime { redisKeyPrefix = defaultText(redisKeyPrefix, "trader:v4"); } } public record Outbox(boolean enabled, int maxRetryCount) { } public record Release(boolean requireReviewForPaper, boolean requireReviewForLiveProbe, boolean activePointerCheckEnabled) { } public record Risk(BigDecimal maxDailyLossBps, BigDecimal maxTotalExposureRatio, BigDecimal minLiquidationBufferBps) { public Risk { maxDailyLossBps = maxDailyLossBps == null ? new BigDecimal("200") : maxDailyLossBps; maxTotalExposureRatio = maxTotalExposureRatio == null ? BigDecimal.ONE : maxTotalExposureRatio; minLiquidationBufferBps = minLiquidationBufferBps == null ? new BigDecimal("500") : minLiquidationBufferBps; } } public record PositionManager(BigDecimal maxSingleLegRatio, BigDecimal maxTotalPositionRatio) { public PositionManager { maxSingleLegRatio = maxSingleLegRatio == null ? BigDecimal.ONE : maxSingleLegRatio; maxTotalPositionRatio = maxTotalPositionRatio == null ? BigDecimal.ONE : maxTotalPositionRatio; } } }