Files
quant-trader-service/src/main/java/com/quantai/trader/domain/RiskOutput.java
T

70 lines
2.8 KiB
Java

package com.quantai.trader.domain;
import static com.quantai.trader.util.TraderNumbers.*;
import com.quantai.trader.enums.PositionSide;
import java.math.BigDecimal;
import java.util.Map;
import java.util.Set;
public record RiskOutput(
BigDecimal marketRiskProb,
BigDecimal longPositionRiskProb,
BigDecimal shortPositionRiskProb,
BigDecimal marketPathRiskBps,
BigDecimal longPositionPathRiskBps,
BigDecimal shortPositionPathRiskBps,
Map<String, BigDecimal> riskReasonScores
) {
private static final Set<String> REQUIRED_REASON_KEYS = Set.of(
"market_drawdown_prob", "volatility_expansion_prob", "spike_prob",
"liquidity_deterioration_prob", "position_drawdown_prob");
public RiskOutput {
marketRiskProb = probability(marketRiskProb, "risk.marketRiskProb");
longPositionRiskProb = probability(longPositionRiskProb, "risk.longPositionRiskProb");
shortPositionRiskProb = probability(shortPositionRiskProb, "risk.shortPositionRiskProb");
marketPathRiskBps = nonNegative(marketPathRiskBps, "risk.marketPathRiskBps");
longPositionPathRiskBps = nonNegative(longPositionPathRiskBps, "risk.longPositionPathRiskBps");
shortPositionPathRiskBps = nonNegative(shortPositionPathRiskBps, "risk.shortPositionPathRiskBps");
riskReasonScores = checkedProbabilities(riskReasonScores, "risk.riskReasonScores");
}
public BigDecimal reasonScore(String key) {
return riskReasonScores.get(requiredText(key, "risk reason key"));
}
public BigDecimal sideRiskProbFor(PositionSide side) {
if (side == PositionSide.LONG) {
return longPositionRiskProb;
}
if (side == PositionSide.SHORT) {
return shortPositionRiskProb;
}
throw new IllegalArgumentException("position risk requires LONG or SHORT side");
}
public BigDecimal positionPathRiskBpsFor(PositionSide side) {
if (side == PositionSide.LONG) {
return longPositionPathRiskBps;
}
if (side == PositionSide.SHORT) {
return shortPositionPathRiskBps;
}
throw new IllegalArgumentException("position path risk requires LONG or SHORT side");
}
private static Map<String, BigDecimal> checkedProbabilities(Map<String, BigDecimal> scores, String field) {
Map<String, BigDecimal> source = scores == null ? Map.of() : scores;
if (!source.keySet().containsAll(REQUIRED_REASON_KEYS)) {
throw new IllegalArgumentException(field + " must contain " + REQUIRED_REASON_KEYS);
}
source.forEach((key, value) -> {
requiredText(key, field + ".key");
probability(value, field + "." + key);
});
return Map.copyOf(source);
}
}