2026-06-23 22:09:06 +08:00
|
|
|
package com.quantai.trader.domain;
|
|
|
|
|
|
2026-06-26 21:53:22 +08:00
|
|
|
import static com.quantai.trader.util.TraderNumbers.*;
|
2026-06-23 22:09:06 +08:00
|
|
|
|
2026-06-26 21:53:22 +08:00
|
|
|
import com.quantai.trader.enums.PositionSide;
|
|
|
|
|
import com.quantai.trader.enums.TraderActionType;
|
2026-06-23 22:09:06 +08:00
|
|
|
import java.math.BigDecimal;
|
|
|
|
|
import java.util.Map;
|
2026-06-26 21:53:22 +08:00
|
|
|
import java.util.Objects;
|
2026-06-23 22:09:06 +08:00
|
|
|
|
|
|
|
|
public record TraderAction(
|
2026-06-26 21:53:22 +08:00
|
|
|
String actionId,
|
2026-06-23 22:09:06 +08:00
|
|
|
String runId,
|
|
|
|
|
String cycleId,
|
2026-06-26 21:53:22 +08:00
|
|
|
String modelOutputId,
|
|
|
|
|
String pmDecisionId,
|
|
|
|
|
String riskDecisionId,
|
2026-06-23 22:09:06 +08:00
|
|
|
TraderActionType actionType,
|
|
|
|
|
String symbol,
|
2026-06-26 21:53:22 +08:00
|
|
|
PositionSide side,
|
|
|
|
|
String pricePlanId,
|
|
|
|
|
String pricePlanConfigHash,
|
|
|
|
|
BigDecimal positionRatio,
|
2026-06-23 22:09:06 +08:00
|
|
|
BigDecimal quantity,
|
2026-06-26 21:53:22 +08:00
|
|
|
BigDecimal stopPrice,
|
|
|
|
|
BigDecimal targetPrice,
|
|
|
|
|
boolean reduceOnly,
|
|
|
|
|
String idempotencyKey,
|
2026-06-23 22:09:06 +08:00
|
|
|
String reason,
|
2026-06-26 21:53:22 +08:00
|
|
|
Map<String, Object> actionContextJson
|
2026-06-23 22:09:06 +08:00
|
|
|
) {
|
|
|
|
|
public TraderAction {
|
2026-06-26 21:53:22 +08:00
|
|
|
actionId = requiredText(actionId, "actionId");
|
|
|
|
|
runId = requiredText(runId, "runId");
|
|
|
|
|
cycleId = requiredText(cycleId, "cycleId");
|
|
|
|
|
modelOutputId = requiredText(modelOutputId, "modelOutputId");
|
|
|
|
|
pmDecisionId = requiredText(pmDecisionId, "pmDecisionId");
|
|
|
|
|
riskDecisionId = requiredText(riskDecisionId, "riskDecisionId");
|
|
|
|
|
actionType = Objects.requireNonNull(actionType, "actionType is required");
|
|
|
|
|
symbol = requiredText(symbol, "symbol");
|
|
|
|
|
side = Objects.requireNonNull(side, "side is required");
|
|
|
|
|
idempotencyKey = requiredText(idempotencyKey, "idempotencyKey");
|
|
|
|
|
reason = requiredText(reason, "reason");
|
|
|
|
|
actionContextJson = Map.copyOf(actionContextJson == null ? Map.of() : actionContextJson);
|
|
|
|
|
if (actionType.increasesExposure()) {
|
|
|
|
|
pricePlanId = requiredText(pricePlanId, "pricePlanId");
|
|
|
|
|
pricePlanConfigHash = requiredText(pricePlanConfigHash, "pricePlanConfigHash");
|
|
|
|
|
positionRatio = positive(positionRatio, "positionRatio");
|
|
|
|
|
}
|
|
|
|
|
if (actionType.reducesExposure() && !reduceOnly) {
|
|
|
|
|
throw new IllegalArgumentException("reduce/close action must be reduceOnly");
|
|
|
|
|
}
|
2026-06-23 22:09:06 +08:00
|
|
|
}
|
|
|
|
|
}
|