|
|
@@ -12,6 +12,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|
|
import org.springframework.util.StringUtils;
|
|
|
|
|
|
import javax.persistence.criteria.Predicate;
|
|
|
+import java.math.BigDecimal;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.Date;
|
|
|
import java.util.List;
|
|
|
@@ -19,6 +20,16 @@ import java.util.List;
|
|
|
@Service
|
|
|
public class ConfigRobotService extends BaseService {
|
|
|
|
|
|
+ private static final BigDecimal DEFAULT_HOVER_RANGE = new BigDecimal("0.0200");
|
|
|
+ private static final BigDecimal DEFAULT_MIN_CHANGE_RATE = new BigDecimal("0.001000");
|
|
|
+ private static final BigDecimal DEFAULT_MAX_CHANGE_RATE = new BigDecimal("0.005000");
|
|
|
+ private static final BigDecimal DEFAULT_HOVER_MIN_CHANGE_RATE = new BigDecimal("0.000500");
|
|
|
+ private static final BigDecimal DEFAULT_HOVER_MAX_CHANGE_RATE = new BigDecimal("0.002000");
|
|
|
+ private static final BigDecimal DEFAULT_BRIDGE_MIN_RATIO = new BigDecimal("0.0500");
|
|
|
+ private static final BigDecimal DEFAULT_BRIDGE_MAX_RATIO = new BigDecimal("0.1000");
|
|
|
+ private static final BigDecimal DEFAULT_KLINE_MAX_HIGH_RATIO = new BigDecimal("0.0050");
|
|
|
+ private static final BigDecimal DEFAULT_KLINE_MAX_LOW_RATIO = new BigDecimal("0.0050");
|
|
|
+
|
|
|
@Autowired
|
|
|
private ConfigRobotDao configRobotDao;
|
|
|
|
|
|
@@ -48,8 +59,153 @@ public class ConfigRobotService extends BaseService {
|
|
|
return configRobotDao.findAll(spec, pageable);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 校验机器人配置,与 config_robot 表 NOT NULL / 业务规则对齐
|
|
|
+ *
|
|
|
+ * @return 错误信息,通过时返回 null
|
|
|
+ */
|
|
|
+ public String validate(ConfigRobot robot) {
|
|
|
+ if (robot == null) {
|
|
|
+ return "配置不能为空";
|
|
|
+ }
|
|
|
+ if (!StringUtils.hasText(robot.getSymbol())) {
|
|
|
+ return "交易对不能为空";
|
|
|
+ }
|
|
|
+ if (robot.getTrend() == null || robot.getTrend() < 1 || robot.getTrend() > 3) {
|
|
|
+ return "趋势方向必须为 1(涨)、2(跌) 或 3(横盘)";
|
|
|
+ }
|
|
|
+ if (robot.getStatus() == null || robot.getStatus() < 1 || robot.getStatus() > 4) {
|
|
|
+ return "状态必须为 1~4";
|
|
|
+ }
|
|
|
+
|
|
|
+ String err = requirePositive(robot.getOpenPrice(), "起始参考价");
|
|
|
+ if (err != null) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = requirePositive(robot.getHoverRange(), "盘整区间比例");
|
|
|
+ if (err != null) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = requirePositive(robot.getMinChangeRate(), "趋势最小波动率");
|
|
|
+ if (err != null) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ err = requirePositive(robot.getMaxChangeRate(), "趋势最大波动率");
|
|
|
+ if (err != null) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ err = requireMinNotGreaterThanMax(robot.getMinChangeRate(), robot.getMaxChangeRate(),
|
|
|
+ "趋势最小波动率不能大于最大波动率");
|
|
|
+ if (err != null) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = requirePositive(robot.getHoverMinChangeRate(), "盘整最小波动率");
|
|
|
+ if (err != null) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ err = requirePositive(robot.getHoverMaxChangeRate(), "盘整最大波动率");
|
|
|
+ if (err != null) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ err = requireMinNotGreaterThanMax(robot.getHoverMinChangeRate(), robot.getHoverMaxChangeRate(),
|
|
|
+ "盘整最小波动率不能大于最大波动率");
|
|
|
+ if (err != null) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (robot.getBridgeMinRatio() == null) {
|
|
|
+ return "成桥最小比例不能为空";
|
|
|
+ }
|
|
|
+ if (robot.getBridgeMaxRatio() == null) {
|
|
|
+ return "成桥最大比例不能为空";
|
|
|
+ }
|
|
|
+ if (robot.getBridgeMinRatio().compareTo(BigDecimal.ZERO) < 0
|
|
|
+ || robot.getBridgeMaxRatio().compareTo(BigDecimal.ZERO) < 0) {
|
|
|
+ return "成桥比例不能为负数";
|
|
|
+ }
|
|
|
+ err = requireMinNotGreaterThanMax(robot.getBridgeMinRatio(), robot.getBridgeMaxRatio(),
|
|
|
+ "成桥最小比例不能大于最大比例");
|
|
|
+ if (err != null) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = requirePositive(robot.getKlineMaxHighRatio(), "K线最高价上限比例");
|
|
|
+ if (err != null) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ err = requirePositive(robot.getKlineMaxLowRatio(), "K线最低价下限比例");
|
|
|
+ if (err != null) {
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (robot.getIntervalMs() == null || robot.getIntervalMs() < 1) {
|
|
|
+ return "执行间隔不能小于 1ms";
|
|
|
+ }
|
|
|
+
|
|
|
+ if (robot.getTargetPrice() != null && robot.getTargetPrice().compareTo(BigDecimal.ZERO) <= 0) {
|
|
|
+ return "目标价格必须大于 0";
|
|
|
+ }
|
|
|
+ if (robot.getUpProbability() != null) {
|
|
|
+ if (robot.getUpProbability().compareTo(BigDecimal.ZERO) < 0
|
|
|
+ || robot.getUpProbability().compareTo(BigDecimal.ONE) > 0) {
|
|
|
+ return "上涨概率必须在 0~1 之间";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (robot.getMinuteChangeLimit() != null && robot.getMinuteChangeLimit().compareTo(BigDecimal.ZERO) <= 0) {
|
|
|
+ return "分钟涨跌幅限制必须大于 0";
|
|
|
+ }
|
|
|
+ if (robot.getRemark() != null && robot.getRemark().length() > 200) {
|
|
|
+ return "备注不能超过 200 字";
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 写入表默认值(仅对 NOT NULL 且未传值的字段) */
|
|
|
+ public void applyDefaults(ConfigRobot robot) {
|
|
|
+ if (robot.getTrend() == null) {
|
|
|
+ robot.setTrend(1);
|
|
|
+ }
|
|
|
+ if (robot.getHoverRange() == null) {
|
|
|
+ robot.setHoverRange(DEFAULT_HOVER_RANGE);
|
|
|
+ }
|
|
|
+ if (robot.getMinChangeRate() == null) {
|
|
|
+ robot.setMinChangeRate(DEFAULT_MIN_CHANGE_RATE);
|
|
|
+ }
|
|
|
+ if (robot.getMaxChangeRate() == null) {
|
|
|
+ robot.setMaxChangeRate(DEFAULT_MAX_CHANGE_RATE);
|
|
|
+ }
|
|
|
+ if (robot.getHoverMinChangeRate() == null) {
|
|
|
+ robot.setHoverMinChangeRate(DEFAULT_HOVER_MIN_CHANGE_RATE);
|
|
|
+ }
|
|
|
+ if (robot.getHoverMaxChangeRate() == null) {
|
|
|
+ robot.setHoverMaxChangeRate(DEFAULT_HOVER_MAX_CHANGE_RATE);
|
|
|
+ }
|
|
|
+ if (robot.getBridgeMinRatio() == null) {
|
|
|
+ robot.setBridgeMinRatio(DEFAULT_BRIDGE_MIN_RATIO);
|
|
|
+ }
|
|
|
+ if (robot.getBridgeMaxRatio() == null) {
|
|
|
+ robot.setBridgeMaxRatio(DEFAULT_BRIDGE_MAX_RATIO);
|
|
|
+ }
|
|
|
+ if (robot.getIntervalMs() == null) {
|
|
|
+ robot.setIntervalMs(3000);
|
|
|
+ }
|
|
|
+ if (robot.getStatus() == null) {
|
|
|
+ robot.setStatus(1);
|
|
|
+ }
|
|
|
+ if (robot.getKlineMaxHighRatio() == null) {
|
|
|
+ robot.setKlineMaxHighRatio(DEFAULT_KLINE_MAX_HIGH_RATIO);
|
|
|
+ }
|
|
|
+ if (robot.getKlineMaxLowRatio() == null) {
|
|
|
+ robot.setKlineMaxLowRatio(DEFAULT_KLINE_MAX_LOW_RATIO);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public ConfigRobot create(ConfigRobot robot) {
|
|
|
+ applyDefaults(robot);
|
|
|
robot.setCreateTime(new Date());
|
|
|
robot.setUpdateTime(new Date());
|
|
|
return configRobotDao.save(robot);
|
|
|
@@ -65,4 +221,21 @@ public class ConfigRobotService extends BaseService {
|
|
|
public void delete(Long id) {
|
|
|
configRobotDao.delete(id);
|
|
|
}
|
|
|
+
|
|
|
+ private static String requirePositive(BigDecimal value, String label) {
|
|
|
+ if (value == null) {
|
|
|
+ return label + "不能为空";
|
|
|
+ }
|
|
|
+ if (value.compareTo(BigDecimal.ZERO) <= 0) {
|
|
|
+ return label + "必须大于 0";
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String requireMinNotGreaterThanMax(BigDecimal min, BigDecimal max, String message) {
|
|
|
+ if (min != null && max != null && min.compareTo(max) > 0) {
|
|
|
+ return message;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
}
|