chore(assets): 参赛提交规范红线修复(ASCII 化 + 相对路径)

按《参赛成果物提交规范·赛道一》§6 红线:
- samples/ 目录改名 sample/(git mv,保留历史)
- 10 个中日文样本文件 + docs 参赛手册 PDF 重命名为 ASCII
  (requirements_*/template_*/rules_*/contestant-handbook.pdf)
- tests/test_zh_template.py 硬编码绝对路径 D:\00_project\Genesis 改为相对路径
- 全局更新 21 个活动文件引用;历史日志/审查文档不改(追加说明记录)
全量 pytest 431 passed / 99.15%
This commit is contained in:
lhl
2026-08-26 14:15:52 +08:00
parent 1925ef7239
commit becd3e1f57
398 changed files with 286 additions and 72 deletions
@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.stock</groupId>
<artifactId>stock-trade-system</artifactId>
<version>1.0.0</version>
</parent>
<groupId>com.stock</groupId>
<artifactId>trade-strategy</artifactId>
<packaging>jar</packaging>
<name>trade-strategy</name>
<description>Trading strategy definition and execution module</description>
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- MySQL Connector -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- Hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<!-- trade-common -->
<dependency>
<groupId>com.stock</groupId>
<artifactId>trade-common</artifactId>
<version>${project.parent.version}</version>
</dependency>
<!-- trade-indicator (to use indicators in strategies) -->
<dependency>
<groupId>com.stock</groupId>
<artifactId>trade-indicator</artifactId>
<version>${project.parent.version}</version>
</dependency>
<!-- trade-market-data (to get market data for strategy execution) -->
<dependency>
<groupId>com.stock</groupId>
<artifactId>trade-market-data</artifactId>
<version>${project.parent.version}</version>
</dependency>
<!-- Spring Cloud Nacos Discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Spring Cloud Nacos Config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- Spring Boot Actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<!-- Spring Cloud OpenFeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- SpringDoc OpenAPI -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>
<!-- Optional: Drools or other rule engines for complex strategy logic -->
<!-- <dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>LATEST_VERSION</version>
</dependency> -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,15 @@
package com.stock.strategy.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 策略控制器
*
* @author TraeAI
*/
@RestController
@RequestMapping("/strategy")
public class StrategyController {
// TODO: 实现策略相关接口
}
@@ -0,0 +1,51 @@
package com.stock.strategy.dal.dataobject;
import com.stock.common.dal.dataobject.BaseDO; // 引入公共BaseDO
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* 策略DO
*
* @author TraeAI
*/
@TableName("trade_strategy") // TODO: 确认表名是否正确
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class StrategyDO extends BaseDO {
/**
* ID
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 策略名称
*/
private String strategyName;
/**
* 策略类型
*/
private String strategyType;
/**
* 策略描述
*/
private String description;
/**
* 策略内容 (例如: JSON格式的参数,或者脚本语言)
*/
private String content;
// TODO: 根据实际需求添加更多字段
}
@@ -0,0 +1,15 @@
package com.stock.strategy.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.stock.strategy.dal.dataobject.StrategyDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 策略 Mapper
*
* @author TraeAI
*/
@Mapper
public interface StrategyMapper extends BaseMapper<StrategyDO> {
// TODO: 定义策略相关的数据库操作方法
}
@@ -0,0 +1,10 @@
package com.stock.strategy.service;
/**
* 策略服务接口
*
* @author TraeAI
*/
public interface StrategyService {
// TODO: 定义策略服务相关方法
}
@@ -0,0 +1,14 @@
package com.stock.strategy.service.impl;
import com.stock.strategy.service.StrategyService;
import org.springframework.stereotype.Service;
/**
* 策略服务实现类
*
* @author TraeAI
*/
@Service
public class StrategyServiceImpl implements StrategyService {
// TODO: 实现策略服务相关方法
}
@@ -0,0 +1,111 @@
package com.stock.trade.strategy.controller;
import com.stock.trade.framework.common.pojo.CommonResult;
import com.stock.trade.framework.common.pojo.PageResult;
import com.stock.trade.strategy.controller.vo.StrategyExecutionCreateReqVO;
import com.stock.trade.strategy.controller.vo.StrategyExecutionPageReqVO;
import com.stock.trade.strategy.controller.vo.StrategyExecutionRespVO;
import com.stock.trade.strategy.controller.vo.StrategyExecutionUpdateReqVO;
import com.stock.trade.strategy.convert.StrategyExecutionConvert;
import com.stock.trade.strategy.dal.dataobject.StrategyExecutionDO;
import com.stock.trade.strategy.service.StrategyExecutionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import static com.stock.trade.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 策略执行")
@RestController
@RequestMapping("/trade/strategy-execution")
@Validated
public class StrategyExecutionController {
@Resource
private StrategyExecutionService strategyExecutionService;
@PostMapping("/create")
@Operation(summary = "创建策略执行")
@PreAuthorize("@ss.hasPermission('strategy:execution:create')")
public CommonResult<Long> createStrategyExecution(@Valid @RequestBody StrategyExecutionCreateReqVO createReqVO) {
return success(strategyExecutionService.createStrategyExecution(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新策略执行")
@PreAuthorize("@ss.hasPermission('strategy:execution:update')")
public CommonResult<Boolean> updateStrategyExecution(@Valid @RequestBody StrategyExecutionUpdateReqVO updateReqVO) {
strategyExecutionService.updateStrategyExecution(updateReqVO);
return success(true);
}
@PutMapping("/start/{id}")
@Operation(summary = "启动策略执行")
@Parameter(name = "id", description = "策略执行编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('strategy:execution:start')")
public CommonResult<Boolean> startStrategyExecution(@PathVariable("id") Long id) {
strategyExecutionService.startStrategyExecution(id);
return success(true);
}
@PutMapping("/pause/{id}")
@Operation(summary = "暂停策略执行")
@Parameter(name = "id", description = "策略执行编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('strategy:execution:pause')")
public CommonResult<Boolean> pauseStrategyExecution(@PathVariable("id") Long id) {
strategyExecutionService.pauseStrategyExecution(id);
return success(true);
}
@PutMapping("/stop/{id}")
@Operation(summary = "停止策略执行")
@Parameter(name = "id", description = "策略执行编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('strategy:execution:stop')")
public CommonResult<Boolean> stopStrategyExecution(@PathVariable("id") Long id) {
strategyExecutionService.stopStrategyExecution(id);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除策略执行")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('strategy:execution:delete')")
public CommonResult<Boolean> deleteStrategyExecution(@RequestParam("id") Long id) {
strategyExecutionService.deleteStrategyExecution(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得策略执行")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('strategy:execution:query')")
public CommonResult<StrategyExecutionRespVO> getStrategyExecution(@RequestParam("id") Long id) {
StrategyExecutionDO execution = strategyExecutionService.getStrategyExecution(id);
return success(StrategyExecutionConvert.INSTANCE.convert(execution));
}
@GetMapping("/page")
@Operation(summary = "获得策略执行分页")
@PreAuthorize("@ss.hasPermission('strategy:execution:query')")
public CommonResult<PageResult<StrategyExecutionRespVO>> getStrategyExecutionPage(@Valid StrategyExecutionPageReqVO pageVO) {
PageResult<StrategyExecutionDO> pageResult = strategyExecutionService.getStrategyExecutionPage(pageVO);
return success(StrategyExecutionConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/list-by-definition")
@Operation(summary = "根据策略定义ID获得策略执行列表")
@Parameter(name = "strategyDefinitionId", description = "策略定义编号", required = true, example = "2048")
@PreAuthorize("@ss.hasPermission('strategy:execution:query')")
public CommonResult<List<StrategyExecutionRespVO>> getStrategyExecutionListByDefinitionId(@RequestParam("strategyDefinitionId") Long strategyDefinitionId) {
List<StrategyExecutionDO> list = strategyExecutionService.getStrategyExecutionListByDefinitionId(strategyDefinitionId);
return success(StrategyExecutionConvert.INSTANCE.convertList(list));
}
}
@@ -0,0 +1,33 @@
package com.stock.trade.strategy.enums;
import com.stock.trade.framework.common.exception.ErrorCode;
/**
* Trade Strategy 模块错误码常量
*
* @author AI Assistant
*/
public interface ErrorCodeConstants {
// ========== 策略定义 模块 1-001-000-000 ==========
ErrorCode STRATEGY_DEFINITION_NOT_EXISTS = new ErrorCode(1001000000, "策略定义不存在");
ErrorCode STRATEGY_DEFINITION_NAME_EXISTS = new ErrorCode(1001000001, "策略定义名称已存在");
ErrorCode STRATEGY_DEFINITION_CANNOT_DELETE_WHEN_ACTIVE = new ErrorCode(1001000002, "策略定义处于活动状态,无法删除");
// ========== 策略回测 模块 1-001-001-000 ==========
ErrorCode STRATEGY_BACKTEST_NOT_EXISTS = new ErrorCode(1001001000, "策略回测记录不存在");
ErrorCode STRATEGY_BACKTEST_CANNOT_START = new ErrorCode(1001001001, "当前状态无法启动策略回测");
ErrorCode STRATEGY_BACKTEST_CANNOT_UPDATE_NON_PENDING = new ErrorCode(1001001002, "只有待处理状态的回测才能更新");
ErrorCode STRATEGY_BACKTEST_CANNOT_DELETE_WHEN_RUNNING = new ErrorCode(1001001003, "运行中的策略回测无法删除");
// ========== 策略执行 模块 1-001-002-000 ==========
ErrorCode STRATEGY_EXECUTION_NOT_EXISTS = new ErrorCode(1001002000, "策略执行记录不存在");
ErrorCode STRATEGY_EXECUTION_STATUS_CANNOT_START = new ErrorCode(1001002001, "当前状态无法启动策略执行");
ErrorCode STRATEGY_EXECUTION_STATUS_CANNOT_PAUSE = new ErrorCode(1001002002, "当前状态无法暂停策略执行");
ErrorCode STRATEGY_EXECUTION_STATUS_CANNOT_STOP = new ErrorCode(1001002003, "当前状态无法停止策略执行");
ErrorCode STRATEGY_EXECUTION_STATUS_CANNOT_COMPLETE = new ErrorCode(1001002004, "当前状态无法完成策略执行");
ErrorCode STRATEGY_EXECUTION_CANNOT_DELETE_WHEN_RUNNING = new ErrorCode(1001002005, "运行中的策略执行无法删除");
}
@@ -0,0 +1,205 @@
package com.stock.trade.strategy.service;
import com.stock.trade.framework.common.exception.util.ServiceExceptionUtil;
import com.stock.trade.framework.common.pojo.PageResult;
import com.stock.trade.strategy.controller.vo.StrategyExecutionCreateReqVO;
import com.stock.trade.strategy.controller.vo.StrategyExecutionPageReqVO;
import com.stock.trade.strategy.controller.vo.StrategyExecutionUpdateReqVO;
import com.stock.trade.strategy.convert.StrategyExecutionConvert;
import com.stock.trade.strategy.dal.dataobject.StrategyExecutionDO;
import com.stock.trade.strategy.dal.mysql.StrategyExecutionMapper;
import com.stock.trade.strategy.enums.ErrorCodeConstants;
import com.stock.trade.strategy.enums.StrategyExecutionStatusEnum;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import static com.stock.trade.framework.common.exception.util.ServiceExceptionUtil.exception;
/**
* 策略执行 Service 实现类
*
* @author AI Assistant
*/
@Service
@Validated
public class StrategyExecutionServiceImpl implements StrategyExecutionService {
@Resource
private StrategyExecutionMapper strategyExecutionMapper;
/**
* 创建策略执行记录
*
* @param createReqVO 创建信息
* @return 编号
*/
@Override
public Long createStrategyExecution(StrategyExecutionCreateReqVO createReqVO) {
// 插入
StrategyExecutionDO strategyExecution = StrategyExecutionConvert.INSTANCE.convert(createReqVO);
strategyExecution.setStatus(StrategyExecutionStatusEnum.PENDING.getStatus());
strategyExecution.setCreateTime(LocalDateTime.now());
strategyExecution.setUpdateTime(LocalDateTime.now());
strategyExecutionMapper.insert(strategyExecution);
// 返回
return strategyExecution.getId();
}
/**
* 更新策略执行记录
*
* @param updateReqVO 更新信息
*/
@Override
public void updateStrategyExecution(StrategyExecutionUpdateReqVO updateReqVO) {
// 校验存在
validateStrategyExecutionExists(updateReqVO.getId());
// 更新
StrategyExecutionDO updateDO = StrategyExecutionConvert.INSTANCE.convert(updateReqVO);
updateDO.setUpdateTime(LocalDateTime.now());
strategyExecutionMapper.updateById(updateDO);
}
/**
* 启动策略执行
*
* @param id 策略执行ID
*/
@Override
public void startStrategyExecution(Long id) {
StrategyExecutionDO execution = validateStrategyExecutionExists(id);
if (!Objects.equals(execution.getStatus(), StrategyExecutionStatusEnum.PENDING.getStatus()) &&
!Objects.equals(execution.getStatus(), StrategyExecutionStatusEnum.PAUSED.getStatus())) {
throw exception(ErrorCodeConstants.STRATEGY_EXECUTION_STATUS_CANNOT_START);
}
StrategyExecutionDO updateDO = new StrategyExecutionDO();
updateDO.setId(id);
updateDO.setStatus(StrategyExecutionStatusEnum.RUNNING.getStatus());
updateDO.setStartTime(LocalDateTime.now()); // 记录启动时间
updateDO.setUpdateTime(LocalDateTime.now());
strategyExecutionMapper.updateById(updateDO);
// 此处应有实际的策略启动逻辑,例如与交易API交互,发送到执行引擎等
}
/**
* 暂停策略执行
*
* @param id 策略执行ID
*/
@Override
public void pauseStrategyExecution(Long id) {
StrategyExecutionDO execution = validateStrategyExecutionExists(id);
if (!Objects.equals(execution.getStatus(), StrategyExecutionStatusEnum.RUNNING.getStatus())) {
throw exception(ErrorCodeConstants.STRATEGY_EXECUTION_STATUS_CANNOT_PAUSE);
}
StrategyExecutionDO updateDO = new StrategyExecutionDO();
updateDO.setId(id);
updateDO.setStatus(StrategyExecutionStatusEnum.PAUSED.getStatus());
updateDO.setUpdateTime(LocalDateTime.now());
strategyExecutionMapper.updateById(updateDO);
// 此处应有实际的策略暂停逻辑
}
/**
* 停止策略执行
*
* @param id 策略执行ID
*/
@Override
public void stopStrategyExecution(Long id) {
StrategyExecutionDO execution = validateStrategyExecutionExists(id);
if (Objects.equals(execution.getStatus(), StrategyExecutionStatusEnum.COMPLETED.getStatus()) ||
Objects.equals(execution.getStatus(), StrategyExecutionStatusEnum.FAILED.getStatus()) ||
Objects.equals(execution.getStatus(), StrategyExecutionStatusEnum.STOPPED.getStatus())) {
throw exception(ErrorCodeConstants.STRATEGY_EXECUTION_STATUS_CANNOT_STOP);
}
StrategyExecutionDO updateDO = new StrategyExecutionDO();
updateDO.setId(id);
updateDO.setStatus(StrategyExecutionStatusEnum.STOPPED.getStatus());
updateDO.setEndTime(LocalDateTime.now()); // 记录停止时间
updateDO.setUpdateTime(LocalDateTime.now());
strategyExecutionMapper.updateById(updateDO);
// 此处应有实际的策略停止逻辑
}
/**
* 删除策略执行记录
*
* @param id 编号
*/
@Override
public void deleteStrategyExecution(Long id) {
// 校验存在
StrategyExecutionDO execution = validateStrategyExecutionExists(id);
// 校验是否运行中
if (Objects.equals(execution.getStatus(), StrategyExecutionStatusEnum.RUNNING.getStatus())) {
throw exception(ErrorCodeConstants.STRATEGY_EXECUTION_CANNOT_DELETE_WHEN_RUNNING);
}
// 删除
strategyExecutionMapper.deleteById(id);
}
/**
* 校验策略执行记录是否存在
*
* @param id 策略执行ID
* @return 策略执行实体
*/
private StrategyExecutionDO validateStrategyExecutionExists(Long id) {
StrategyExecutionDO execution = strategyExecutionMapper.selectById(id);
if (execution == null) {
throw exception(ErrorCodeConstants.STRATEGY_EXECUTION_NOT_EXISTS);
}
return execution;
}
/**
* 获取策略执行记录
*
* @param id 编号
* @return 策略执行记录
*/
@Override
public StrategyExecutionDO getStrategyExecution(Long id) {
return strategyExecutionMapper.selectById(id);
}
/**
* 获取策略执行记录列表
*
* @param ids 编号列表
* @return 策略执行记录列表
*/
@Override
public List<StrategyExecutionDO> getStrategyExecutionList(List<Long> ids) {
return strategyExecutionMapper.selectBatchIds(ids);
}
/**
* 获取策略执行记录分页
*
* @param pageReqVO 分页查询
* @return 策略执行记录分页
*/
@Override
public PageResult<StrategyExecutionDO> getStrategyExecutionPage(StrategyExecutionPageReqVO pageReqVO) {
return strategyExecutionMapper.selectPage(pageReqVO);
}
/**
* 根据策略定义ID获取策略执行记录列表
*
* @param strategyDefinitionId 策略定义ID
* @return 策略执行记录列表
*/
@Override
public List<StrategyExecutionDO> getStrategyExecutionListByDefinitionId(Long strategyDefinitionId) {
return strategyExecutionMapper.selectListByStrategyDefinitionId(strategyDefinitionId);
}
}
@@ -0,0 +1,30 @@
package com.trade.strategy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* 策略服务启动类
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients // 如果需要调用其他服务,则开启 Feign
public class StrategyApplication {
public static void main(String[] args) {
SpringApplication.run(StrategyApplication.class, args);
System.out.println("(♥◠‿◠)ノ゙ 策略服务启动成功 ლ(´ڡ`ლ)゙ \n" +
" .-------. ____ __ \n" +
" | _ _ \\ \\ \\ / / \\n" +
" | ( ' ) | \ _. / ' \n" +
" |(_ o _) / _( )_ .' \n" +
" | (_,_).' __ ___(_ o _)' \n" +
" | |\ \ | || |(_,_)' \n" +
" | | \ `' /| `-' / \n" +
" | | \ / \ / \n" +
" ''-' `'-' `-..-' ");
}
}
@@ -0,0 +1,18 @@
package com.trade.strategy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* 交易策略服务启动类
*/
@EnableDiscoveryClient
@SpringBootApplication
public class TradeStrategyApplication {
public static void main(String[] args) {
SpringApplication.run(TradeStrategyApplication.class, args);
}
}
@@ -0,0 +1,8 @@
/**
* 此包包含策略服务模块的切面类 (Aspect Class)。
* <p>
* 切面类用于实现横切关注点,例如日志记录、事务管理、权限校验、性能监控等。
* 通常使用 Spring AOP 或 AspectJ 来定义和织入切面。
* </p>
*/
package com.trade.strategy.aspect;
@@ -0,0 +1,4 @@
/**
* 此包存放策略服务模块的配置类。
*/
package com.trade.strategy.config;
@@ -0,0 +1,8 @@
/**
* 此包包含策略服务模块的常量类 (Constant Class)。
* <p>
* 常量类用于定义在应用程序中广泛使用的静态常量值,例如配置键名、默认参数、固定字符串等。
* 将常量集中管理有助于提高代码的可维护性和一致性。
* </p>
*/
package com.trade.strategy.constant;
@@ -0,0 +1,149 @@
package com.trade.strategy.controller;
import com.trade.strategy.entity.StrategyBacktest;
import com.trade.strategy.service.StrategyBacktestService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* 策略回测API接口
*
* @author AI Assistant
*/
@RestController
@RequestMapping("/api/v1/strategies/backtests")
@Tag(name = "StrategyBacktestController", description = "策略回测管理API")
public class StrategyBacktestController {
@Autowired
private StrategyBacktestService strategyBacktestService;
/**
* 创建新的策略回测任务
*
* @param strategyBacktest 回测任务基本信息
* @return 创建的回测任务
*/
@PostMapping
@Operation(summary = "创建新的策略回测任务")
public ResponseEntity<StrategyBacktest> createBacktest(@RequestBody StrategyBacktest strategyBacktest) {
try {
StrategyBacktest createdBacktest = strategyBacktestService.createBacktest(strategyBacktest);
return new ResponseEntity<>(createdBacktest, HttpStatus.CREATED);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().body(null); // Consider returning error message
}
}
/**
* 启动一个指定ID的回测任务
*
* @param backtestId 回测任务ID
* @return 启动后的回测任务状态
*/
@PostMapping("/{backtestId}/start")
@Operation(summary = "启动一个指定ID的回测任务")
public ResponseEntity<StrategyBacktest> startBacktest(@PathVariable Long backtestId) {
try {
StrategyBacktest startedBacktest = strategyBacktestService.startBacktest(backtestId);
return ResponseEntity.ok(startedBacktest);
} catch (IllegalArgumentException | IllegalStateException e) {
return ResponseEntity.badRequest().body(null); // Consider returning error message
}
}
/**
* (模拟) 更新回测任务的状态和结果 - 实际应由回测引擎回调
*
* @param backtestId 回测任务ID
* @param payload 包含 status 和 resultDetails 的请求体
* @return 更新后的回测任务
*/
@PutMapping("/{backtestId}/update-result") // This endpoint is more for internal/engine callback simulation
@Operation(summary = "(模拟)更新回测任务的状态和结果", description = "实际场景中此接口可能由回测引擎内部调用或通过消息队列触发")
public ResponseEntity<StrategyBacktest> updateBacktestResult(
@PathVariable Long backtestId,
@RequestBody Map<String, String> payload) {
try {
String status = payload.get("status");
String resultDetails = payload.get("resultDetails");
if (status == null || resultDetails == null) {
return ResponseEntity.badRequest().body(null); // Missing parameters
}
StrategyBacktest updatedBacktest = strategyBacktestService.updateBacktestStatusAndResult(backtestId, status, resultDetails);
return ResponseEntity.ok(updatedBacktest);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().body(null);
}
}
/**
* 根据ID查询策略回测记录
*
* @param backtestId 回测任务ID
* @return 回测任务详情
*/
@GetMapping("/{backtestId}")
@Operation(summary = "根据ID查询策略回测记录")
public ResponseEntity<StrategyBacktest> getBacktestById(@PathVariable Long backtestId) {
StrategyBacktest backtest = strategyBacktestService.getBacktestById(backtestId);
if (backtest != null) {
return ResponseEntity.ok(backtest);
} else {
return ResponseEntity.notFound().build();
}
}
/**
* 查询指定策略定义的所有回测记录
*
* @param strategyDefinitionId 策略定义ID
* @return 回测记录列表
*/
@GetMapping("/by-definition/{strategyDefinitionId}")
@Operation(summary = "查询指定策略定义的所有回测记录")
public ResponseEntity<List<StrategyBacktest>> getBacktestsByStrategyDefinitionId(@PathVariable Long strategyDefinitionId) {
List<StrategyBacktest> backtests = strategyBacktestService.getBacktestsByStrategyDefinitionId(strategyDefinitionId);
return ResponseEntity.ok(backtests);
}
/**
* 查询所有策略回测记录
*
* @return 所有回测记录列表
*/
@GetMapping
@Operation(summary = "查询所有策略回测记录")
public ResponseEntity<List<StrategyBacktest>> getAllBacktests() {
List<StrategyBacktest> backtests = strategyBacktestService.getAllBacktests();
return ResponseEntity.ok(backtests);
}
/**
* 根据ID删除策略回测记录
*
* @param backtestId 回测任务ID
* @return 操作结果
*/
@DeleteMapping("/{backtestId}")
@Operation(summary = "根据ID删除策略回测记录")
public ResponseEntity<Void> deleteBacktest(@PathVariable Long backtestId) {
try {
strategyBacktestService.deleteBacktest(backtestId);
return ResponseEntity.noContent().build();
} catch (IllegalArgumentException e) {
return ResponseEntity.notFound().build();
} catch (IllegalStateException e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); // Or another appropriate status for non-deletable state
}
}
}
@@ -0,0 +1,151 @@
package com.trade.strategy.controller;
import com.trade.strategy.entity.StrategyDefinition;
import com.trade.strategy.service.StrategyDefinitionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 交易策略定义API接口
*
* @author AI Assistant
*/
@RestController
@RequestMapping("/api/v1/strategies/definitions")
@Tag(name = "StrategyDefinitionController", description = "交易策略定义管理API")
public class StrategyDefinitionController {
@Autowired
private StrategyDefinitionService strategyDefinitionService;
/**
* 创建新的交易策略定义
*
* @param strategyDefinition 策略定义对象
* @return 创建的策略定义
*/
@PostMapping
@Operation(summary = "创建新的交易策略定义")
public ResponseEntity<StrategyDefinition> createStrategy(@RequestBody StrategyDefinition strategyDefinition) {
try {
StrategyDefinition createdStrategy = strategyDefinitionService.createStrategy(strategyDefinition);
return new ResponseEntity<>(createdStrategy, HttpStatus.CREATED);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().body(null); // 或者返回更详细的错误信息
}
}
/**
* 更新已有的交易策略定义
*
* @param id 策略ID
* @param strategyDefinition 策略定义对象
* @return 更新后的策略定义
*/
@PutMapping("/{id}")
@Operation(summary = "更新已有的交易策略定义")
public ResponseEntity<StrategyDefinition> updateStrategy(@PathVariable Long id, @RequestBody StrategyDefinition strategyDefinition) {
try {
strategyDefinition.setId(id); // 确保ID一致
StrategyDefinition updatedStrategy = strategyDefinitionService.updateStrategy(strategyDefinition);
return ResponseEntity.ok(updatedStrategy);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().body(null); // 或者返回更详细的错误信息
}
}
/**
* 根据ID删除交易策略定义
*
* @param id 策略ID
* @return 操作结果
*/
@DeleteMapping("/{id}")
@Operation(summary = "根据ID删除交易策略定义")
public ResponseEntity<Void> deleteStrategy(@PathVariable Long id) {
try {
strategyDefinitionService.deleteStrategy(id);
return ResponseEntity.noContent().build();
} catch (IllegalArgumentException e) {
return ResponseEntity.notFound().build();
}
}
/**
* 根据ID查询交易策略定义
*
* @param id 策略ID
* @return 策略定义
*/
@GetMapping("/{id}")
@Operation(summary = "根据ID查询交易策略定义")
public ResponseEntity<StrategyDefinition> getStrategyById(@PathVariable Long id) {
StrategyDefinition strategy = strategyDefinitionService.getStrategyById(id);
if (strategy != null) {
return ResponseEntity.ok(strategy);
} else {
return ResponseEntity.notFound().build();
}
}
/**
* 查询所有交易策略定义
*
* @return 策略定义列表
*/
@GetMapping
@Operation(summary = "查询所有交易策略定义")
public ResponseEntity<List<StrategyDefinition>> getAllStrategies() {
List<StrategyDefinition> strategies = strategyDefinitionService.getAllStrategies();
return ResponseEntity.ok(strategies);
}
/**
* 根据策略名称查询交易策略定义
*
* @param name 策略名称
* @return 策略定义
*/
@GetMapping("/by-name")
@Operation(summary = "根据策略名称查询交易策略定义")
public ResponseEntity<StrategyDefinition> getStrategyByName(@RequestParam String name) {
StrategyDefinition strategy = strategyDefinitionService.getStrategyByName(name);
if (strategy != null) {
return ResponseEntity.ok(strategy);
} else {
return ResponseEntity.notFound().build();
}
}
/**
* 根据策略类型查询交易策略定义
*
* @param type 策略类型
* @return 策略定义列表
*/
@GetMapping("/by-type")
@Operation(summary = "根据策略类型查询交易策略定义")
public ResponseEntity<List<StrategyDefinition>> getStrategiesByType(@RequestParam String type) {
List<StrategyDefinition> strategies = strategyDefinitionService.getStrategiesByType(type);
return ResponseEntity.ok(strategies);
}
/**
* 根据策略状态查询交易策略定义
*
* @param status 策略状态
* @return 策略定义列表
*/
@GetMapping("/by-status")
@Operation(summary = "根据策略状态查询交易策略定义")
public ResponseEntity<List<StrategyDefinition>> getStrategiesByStatus(@RequestParam String status) {
List<StrategyDefinition> strategies = strategyDefinitionService.getStrategiesByStatus(status);
return ResponseEntity.ok(strategies);
}
}
@@ -0,0 +1,4 @@
/**
* 此包存放策略服务模块的Controller层,负责处理HTTP请求和响应。
*/
package com.trade.strategy.controller;
@@ -0,0 +1,4 @@
/**
* 此包存放策略服务模块的数据传输对象 (DTO)。
*/
package com.trade.strategy.dto;
@@ -0,0 +1,4 @@
/**
* 此包存放策略执行引擎相关的类,包括策略的加载、执行、事件处理等。
*/
package com.trade.strategy.engine;
@@ -0,0 +1,84 @@
package com.trade.strategy.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* 策略回测记录实体类
*
* @author AI Assistant
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("ts_strategy_backtest")
public class StrategyBacktest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 回测ID (主键, 自增)
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 关联的策略定义ID
*/
private Long strategyDefinitionId;
/**
* 回测名称 (用户自定义)
*/
private String backtestName;
/**
* 回测开始日期
*/
private LocalDate startDate;
/**
* 回测结束日期
*/
private LocalDate endDate;
/**
* 初始资金
*/
private BigDecimal initialCapital;
/**
* 手续费率 (例如: 0.0003 表示万分之三)
*/
private BigDecimal commissionRate;
/**
* 回测状态 (例如: "PENDING", "RUNNING", "COMPLETED", "FAILED")
* 可以考虑使用枚举类定义
*/
private String status;
/**
* 回测结果 (JSON格式,存储回测的各项指标)
* 例如: {"total_return": 0.15, "annualized_return": 0.30, "sharpe_ratio": 1.5, "max_drawdown": 0.10}
*/
private String resultDetails; // 数据库中建议使用TEXT/JSON类型
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间 (例如回测完成或失败时更新)
*/
private LocalDateTime updateTime;
}
@@ -0,0 +1,84 @@
package com.trade.strategy.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 交易策略定义实体类
*
* @author AI Assistant
*/
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("ts_strategy_definition")
public class StrategyDefinition implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 策略ID (主键, 自增)
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 策略名称 (必填, 唯一)
*/
private String name;
/**
* 策略描述
*/
private String description;
/**
* 策略作者
*/
private String author;
/**
* 策略版本
*/
private String version;
/**
* 策略类型 (例如: "TREND_FOLLOWING", "MEAN_REVERSION", "EVENT_DRIVEN")
* 可以考虑使用枚举类定义
*/
private String type;
/**
* 策略脚本内容或配置 (存储策略的具体逻辑或配置)
* 对于复杂策略,可能存储脚本语言代码 (如Python, Groovy) 或JSON/XML配置
*/
private String scriptContent; // 数据库中建议使用TEXT类型
/**
* 策略参数 (JSON格式,存储策略的可配置参数及其默认值)
* 例如: {"MA_short": 5, "MA_long": 20, "stop_loss_pct": 0.05}
*/
private String parameters; // 数据库中建议使用TEXT/JSON类型
/**
* 策略状态 (例如: "DRAFT", "ACTIVE", "INACTIVE", "ARCHIVED")
* 可以考虑使用枚举类定义
*/
private String status;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 更新时间
*/
private LocalDateTime updateTime;
}
@@ -0,0 +1,4 @@
/**
* 此包存放策略服务模块的数据库实体类。
*/
package com.trade.strategy.entity;
@@ -0,0 +1,5 @@
/**
* 此包存放策略服务模块相关的枚举类。
* 例如:策略状态枚举、信号类型枚举等。
*/
package com.trade.strategy.enums;
@@ -0,0 +1,8 @@
/**
* 此包包含策略服务模块的过滤器类 (Filter Class)。
* <p>
* 过滤器类用于在 Servlet 容器处理请求之前或之后执行某些操作,例如请求参数校验、字符编码转换、安全过滤等。
* 它们是 Servlet规范的一部分。
* </p>
*/
package com.trade.strategy.filter;
@@ -0,0 +1,8 @@
/**
* 此包包含策略服务模块的处理器类 (Handler Class)。
* <p>
* 处理器类通常用于处理特定类型的事件、消息或请求,例如消息队列的消费者、WebSocket 处理器、定时任务处理器等。
* 它们封装了对特定输入的响应逻辑。
* </p>
*/
package com.trade.strategy.handler;
@@ -0,0 +1,5 @@
/**
* 此包存放策略中可能用到的技术指标计算相关的类。
* 考虑是否将通用指标计算放到独立的 trade-indicator 模块。
*/
package com.trade.strategy.indicator;
@@ -0,0 +1,8 @@
/**
* 此包包含策略服务模块的拦截器类 (Interceptor Class)。
* <p>
* 拦截器类用于在 Spring MVC 处理请求的过程中拦截和处理请求,例如权限验证、日志记录、性能监控等。
* 它们提供了比过滤器更细粒度的控制,并且可以访问 Spring MVC 的上下文信息。
* </p>
*/
package com.trade.strategy.interceptor;
@@ -0,0 +1,8 @@
/**
* 此包包含策略服务模块的定时任务类 (Job Class)。
* <p>
* 定时任务类用于执行周期性或预定时间的后台任务,例如数据同步、报表生成、缓存刷新等。
* 通常使用 Spring Task 或 Quartz 等框架来实现。
* </p>
*/
package com.trade.strategy.job;
@@ -0,0 +1,4 @@
/**
* 此包存放事件监听器,例如监听行情事件、订单回报事件等,并根据事件驱动策略执行。
*/
package com.trade.strategy.listener;
@@ -0,0 +1,15 @@
package com.trade.strategy.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.trade.strategy.entity.StrategyBacktest;
import org.apache.ibatis.annotations.Mapper;
/**
* 策略回测记录 Mapper 接口
*
* @author AI Assistant
*/
@Mapper
public interface StrategyBacktestMapper extends BaseMapper<StrategyBacktest> {
}
@@ -0,0 +1,15 @@
package com.trade.strategy.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.trade.strategy.entity.StrategyDefinition;
import org.apache.ibatis.annotations.Mapper;
/**
* 交易策略定义 Mapper 接口
*
* @author AI Assistant
*/
@Mapper
public interface StrategyDefinitionMapper extends BaseMapper<StrategyDefinition> {
}
@@ -0,0 +1,4 @@
/**
* 此包存放策略服务模块的Mapper接口,用于数据库操作(例如存储策略配置、回测结果等)。
*/
package com.trade.strategy.mapper;
@@ -0,0 +1,8 @@
/**
* 此包包含策略服务模块的领域模型 (Domain Model) 或实体类 (Entity)。
* <p>
* 这些类通常映射到数据库表结构,代表了系统中的核心业务对象。
* 它们包含了对象的属性以及可能的操作方法。
* </p>
*/
package com.trade.strategy.model;
@@ -0,0 +1,4 @@
/**
* 策略服务模块主包。
*/
package com.trade.strategy;
@@ -0,0 +1,4 @@
/**
* 此包存放风险管理相关的类,例如仓位管理、止盈止损逻辑等。
*/
package com.trade.strategy.risk;
@@ -0,0 +1,4 @@
/**
* 此包存放定时任务相关的类,例如定时执行策略、数据同步等。
*/
package com.trade.strategy.scheduler;
@@ -0,0 +1,74 @@
package com.trade.strategy.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.trade.strategy.entity.StrategyBacktest;
import java.util.List;
/**
* 策略回测服务接口
*
* @author AI Assistant
*/
public interface StrategyBacktestService extends IService<StrategyBacktest> {
/**
* 创建并初始化一个新的策略回测任务
*
* @param strategyBacktest 回测任务基本信息 (strategyDefinitionId, backtestName, startDate, endDate, initialCapital, commissionRate 必须)
* @return 创建成功后的回测任务对象,包含生成的ID和初始状态
*/
StrategyBacktest createBacktest(StrategyBacktest strategyBacktest);
/**
* 启动一个指定ID的回测任务
* (实际回测逻辑可能异步执行)
*
* @param backtestId 回测任务ID
* @return 更新状态后的回测任务对象 (例如: 状态变为RUNNING)
*/
StrategyBacktest startBacktest(Long backtestId);
/**
* 更新回测任务的状态和结果
* (通常在异步回测任务完成后调用)
*
* @param backtestId 回测任务ID
* @param status 新的状态 (例如: "COMPLETED", "FAILED")
* @param resultDetails 回测结果详情 (JSON格式)
* @return 更新后的回测任务对象
*/
StrategyBacktest updateBacktestStatusAndResult(Long backtestId, String status, String resultDetails);
/**
* 根据ID查询策略回测记录
*
* @param backtestId 回测任务ID
* @return 回测任务对象,如果不存在则返回null
*/
StrategyBacktest getBacktestById(Long backtestId);
/**
* 查询指定策略定义的所有回测记录
*
* @param strategyDefinitionId 策略定义ID
* @return 该策略的回测记录列表
*/
List<StrategyBacktest> getBacktestsByStrategyDefinitionId(Long strategyDefinitionId);
/**
* 查询所有策略回测记录
*
* @return 所有回测记录列表
*/
List<StrategyBacktest> getAllBacktests();
/**
* 根据ID删除策略回测记录
* (通常只允许删除未开始或已失败的回测)
*
* @param backtestId 回测任务ID
*/
void deleteBacktest(Long backtestId);
}
@@ -0,0 +1,77 @@
package com.trade.strategy.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.trade.strategy.entity.StrategyDefinition;
import java.util.List;
/**
* 交易策略定义服务接口
*
* @author AI Assistant
*/
public interface StrategyDefinitionService extends IService<StrategyDefinition> {
/**
* 创建新的交易策略定义
*
* @param strategyDefinition 策略定义对象
* @return 创建成功后的策略定义对象,包含生成的ID
*/
StrategyDefinition createStrategy(StrategyDefinition strategyDefinition);
/**
* 更新已有的交易策略定义
*
* @param strategyDefinition 策略定义对象
* @return 更新成功后的策略定义对象
*/
StrategyDefinition updateStrategy(StrategyDefinition strategyDefinition);
/**
* 根据ID删除交易策略定义
*
* @param id 策略ID
*/
void deleteStrategy(Long id);
/**
* 根据ID查询交易策略定义
*
* @param id 策略ID
* @return 策略定义对象,如果不存在则返回null
*/
StrategyDefinition getStrategyById(Long id);
/**
* 查询所有交易策略定义
*
* @return 策略定义列表
*/
List<StrategyDefinition> getAllStrategies();
/**
* 根据策略名称查询交易策略定义
*
* @param name 策略名称
* @return 策略定义对象,如果不存在则返回null
*/
StrategyDefinition getStrategyByName(String name);
/**
* 根据策略类型查询交易策略定义
*
* @param type 策略类型
* @return 符合类型的策略定义列表
*/
List<StrategyDefinition> getStrategiesByType(String type);
/**
* 根据策略状态查询交易策略定义
*
* @param status 策略状态
* @return 符合状态的策略定义列表
*/
List<StrategyDefinition> getStrategiesByStatus(String status);
}
@@ -0,0 +1,179 @@
package com.trade.strategy.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.trade.strategy.entity.StrategyBacktest;
import com.trade.strategy.entity.StrategyDefinition;
import com.trade.strategy.mapper.StrategyBacktestMapper;
import com.trade.strategy.service.StrategyBacktestService;
import com.trade.strategy.service.StrategyDefinitionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.List;
/**
* 策略回测服务实现类
*
* @author AI Assistant
*/
@Service
public class StrategyBacktestServiceImpl extends ServiceImpl<StrategyBacktestMapper, StrategyBacktest> implements StrategyBacktestService {
@Autowired
private StrategyDefinitionService strategyDefinitionService; // 用于校验策略定义是否存在
// 假设有一个异步执行回测的组件,这里仅作演示
// @Autowired
// private BacktestExecutionEngine backtestExecutionEngine;
/**
* 创建并初始化一个新的策略回测任务
*
* @param strategyBacktest 回测任务基本信息
* @return 创建成功后的回测任务对象
*/
@Override
@Transactional
public StrategyBacktest createBacktest(StrategyBacktest strategyBacktest) {
// 参数校验
if (strategyBacktest.getStrategyDefinitionId() == null) {
throw new IllegalArgumentException("策略定义ID不能为空");
}
StrategyDefinition definition = strategyDefinitionService.getStrategyById(strategyBacktest.getStrategyDefinitionId());
if (definition == null) {
throw new IllegalArgumentException("指定的策略定义不存在: ID " + strategyBacktest.getStrategyDefinitionId());
}
if (!StringUtils.hasText(strategyBacktest.getBacktestName())) {
throw new IllegalArgumentException("回测名称不能为空");
}
if (strategyBacktest.getStartDate() == null || strategyBacktest.getEndDate() == null) {
throw new IllegalArgumentException("回测开始或结束日期不能为空");
}
if (strategyBacktest.getStartDate().isAfter(strategyBacktest.getEndDate())) {
throw new IllegalArgumentException("回测开始日期不能晚于结束日期");
}
if (strategyBacktest.getInitialCapital() == null || strategyBacktest.getInitialCapital().doubleValue() <= 0) {
throw new IllegalArgumentException("初始资金必须大于0");
}
// 手续费率可以为0
if (strategyBacktest.getCommissionRate() == null || strategyBacktest.getCommissionRate().doubleValue() < 0) {
strategyBacktest.setCommissionRate(java.math.BigDecimal.ZERO);
}
strategyBacktest.setStatus("PENDING"); // 初始状态为待处理
strategyBacktest.setCreateTime(LocalDateTime.now());
strategyBacktest.setUpdateTime(LocalDateTime.now());
this.save(strategyBacktest);
return strategyBacktest;
}
/**
* 启动一个指定ID的回测任务
*
* @param backtestId 回测任务ID
* @return 更新状态后的回测任务对象
*/
@Override
@Transactional
public StrategyBacktest startBacktest(Long backtestId) {
StrategyBacktest backtest = getBacktestById(backtestId);
if (backtest == null) {
throw new IllegalArgumentException("未找到要启动的回测任务,ID: " + backtestId);
}
if (!"PENDING".equals(backtest.getStatus())) {
throw new IllegalStateException("回测任务当前状态为 " + backtest.getStatus() + ",无法启动。");
}
backtest.setStatus("RUNNING");
backtest.setUpdateTime(LocalDateTime.now());
this.updateById(backtest);
// 实际场景中,这里会触发异步回测引擎执行回测
// backtestExecutionEngine.execute(backtest);
// log.info("策略回测任务 [{}] 已启动", backtestId);
return backtest;
}
/**
* 更新回测任务的状态和结果
*
* @param backtestId 回测任务ID
* @param status 新的状态
* @param resultDetails 回测结果详情
* @return 更新后的回测任务对象
*/
@Override
@Transactional
public StrategyBacktest updateBacktestStatusAndResult(Long backtestId, String status, String resultDetails) {
StrategyBacktest backtest = getBacktestById(backtestId);
if (backtest == null) {
throw new IllegalArgumentException("未找到要更新的回测任务,ID: " + backtestId);
}
backtest.setStatus(status);
backtest.setResultDetails(resultDetails);
backtest.setUpdateTime(LocalDateTime.now());
this.updateById(backtest);
return backtest;
}
/**
* 根据ID查询策略回测记录
*
* @param backtestId 回测任务ID
* @return 回测任务对象
*/
@Override
public StrategyBacktest getBacktestById(Long backtestId) {
return this.getById(backtestId);
}
/**
* 查询指定策略定义的所有回测记录
*
* @param strategyDefinitionId 策略定义ID
* @return 回测记录列表
*/
@Override
public List<StrategyBacktest> getBacktestsByStrategyDefinitionId(Long strategyDefinitionId) {
if (strategyDefinitionId == null) {
return List.of();
}
return this.list(new QueryWrapper<StrategyBacktest>().eq("strategy_definition_id", strategyDefinitionId)
.orderByDesc("create_time")); // 按创建时间降序
}
/**
* 查询所有策略回测记录
*
* @return 所有回测记录列表
*/
@Override
public List<StrategyBacktest> getAllBacktests() {
return this.list(new QueryWrapper<StrategyBacktest>().orderByDesc("create_time"));
}
/**
* 根据ID删除策略回测记录
*
* @param backtestId 回测任务ID
*/
@Override
@Transactional
public void deleteBacktest(Long backtestId) {
StrategyBacktest backtest = getBacktestById(backtestId);
if (backtest == null) {
throw new IllegalArgumentException("未找到要删除的回测任务,ID: " + backtestId);
}
// 通常只允许删除 PENDING 或 FAILED 的回测,或者根据业务需求调整
if ("RUNNING".equals(backtest.getStatus()) || "COMPLETED".equals(backtest.getStatus())) {
// throw new IllegalStateException("无法删除正在运行或已完成的回测任务。");
}
this.removeById(backtestId);
}
}
@@ -0,0 +1,160 @@
package com.trade.strategy.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.trade.strategy.entity.StrategyDefinition;
import com.trade.strategy.mapper.StrategyDefinitionMapper;
import com.trade.strategy.service.StrategyDefinitionService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.List;
/**
* 交易策略定义服务实现类
*
* @author AI Assistant
*/
@Service
public class StrategyDefinitionServiceImpl extends ServiceImpl<StrategyDefinitionMapper, StrategyDefinition> implements StrategyDefinitionService {
/**
* 创建新的交易策略定义
*
* @param strategyDefinition 策略定义对象
* @return 创建成功后的策略定义对象,包含生成的ID
*/
@Override
@Transactional
public StrategyDefinition createStrategy(StrategyDefinition strategyDefinition) {
// 可以在这里添加校验逻辑,例如名称是否唯一等
if (!StringUtils.hasText(strategyDefinition.getName())) {
throw new IllegalArgumentException("策略名称不能为空");
}
StrategyDefinition existing = getStrategyByName(strategyDefinition.getName());
if (existing != null) {
throw new IllegalArgumentException("策略名称已存在: " + strategyDefinition.getName());
}
strategyDefinition.setCreateTime(LocalDateTime.now());
strategyDefinition.setUpdateTime(LocalDateTime.now());
// 默认状态可以设置为 DRAFT
if (!StringUtils.hasText(strategyDefinition.getStatus())){
strategyDefinition.setStatus("DRAFT");
}
this.save(strategyDefinition);
return strategyDefinition;
}
/**
* 更新已有的交易策略定义
*
* @param strategyDefinition 策略定义对象
* @return 更新成功后的策略定义对象
*/
@Override
@Transactional
public StrategyDefinition updateStrategy(StrategyDefinition strategyDefinition) {
if (strategyDefinition.getId() == null) {
throw new IllegalArgumentException("更新策略时,策略ID不能为空");
}
StrategyDefinition existing = getById(strategyDefinition.getId());
if (existing == null) {
throw new IllegalArgumentException("未找到要更新的策略,ID: " + strategyDefinition.getId());
}
// 如果名称被修改,需要检查新名称是否已存在(排除自身)
if (StringUtils.hasText(strategyDefinition.getName()) && !strategyDefinition.getName().equals(existing.getName())){
StrategyDefinition byNewName = getStrategyByName(strategyDefinition.getName());
if(byNewName != null && !byNewName.getId().equals(strategyDefinition.getId())){
throw new IllegalArgumentException("策略名称已存在: " + strategyDefinition.getName());
}
}
strategyDefinition.setUpdateTime(LocalDateTime.now());
this.updateById(strategyDefinition);
return getById(strategyDefinition.getId()); // 返回更新后的完整对象
}
/**
* 根据ID删除交易策略定义
*
* @param id 策略ID
*/
@Override
@Transactional
public void deleteStrategy(Long id) {
if (id == null) {
throw new IllegalArgumentException("删除策略时,策略ID不能为空");
}
if (getById(id) == null) {
throw new IllegalArgumentException("未找到要删除的策略,ID: " + id);
}
this.removeById(id);
}
/**
* 根据ID查询交易策略定义
*
* @param id 策略ID
* @return 策略定义对象,如果不存在则返回null
*/
@Override
public StrategyDefinition getStrategyById(Long id) {
return this.getById(id);
}
/**
* 查询所有交易策略定义
*
* @return 策略定义列表
*/
@Override
public List<StrategyDefinition> getAllStrategies() {
return this.list();
}
/**
* 根据策略名称查询交易策略定义
*
* @param name 策略名称
* @return 策略定义对象,如果不存在则返回null
*/
@Override
public StrategyDefinition getStrategyByName(String name) {
if (!StringUtils.hasText(name)) {
return null;
}
return this.getOne(new QueryWrapper<StrategyDefinition>().eq("name", name));
}
/**
* 根据策略类型查询交易策略定义
*
* @param type 策略类型
* @return 符合类型的策略定义列表
*/
@Override
public List<StrategyDefinition> getStrategiesByType(String type) {
if (!StringUtils.hasText(type)) {
return this.list(); // 如果类型为空,返回所有
}
return this.list(new QueryWrapper<StrategyDefinition>().eq("type", type));
}
/**
* 根据策略状态查询交易策略定义
*
* @param status 策略状态
* @return 符合状态的策略定义列表
*/
@Override
public List<StrategyDefinition> getStrategiesByStatus(String status) {
if (!StringUtils.hasText(status)) {
return this.list(); // 如果状态为空,返回所有
}
return this.list(new QueryWrapper<StrategyDefinition>().eq("status", status));
}
}
@@ -0,0 +1,4 @@
/**
* 此包存放策略服务模块Service接口的实现类。
*/
package com.trade.strategy.service.impl;
@@ -0,0 +1,4 @@
/**
* 此包存放策略服务模块的Service层,负责业务逻辑处理,例如策略的创建、执行、回测等。
*/
package com.trade.strategy.service;
@@ -0,0 +1,8 @@
/**
* 此包包含策略服务模块的工具类 (Utility Class)。
* <p>
* 工具类提供各种通用的辅助方法,例如日期时间处理、字符串操作、加解密、文件操作等。
* 它们通常包含静态方法,方便在应用程序的各个部分调用。
* </p>
*/
package com.trade.strategy.util;
@@ -0,0 +1,4 @@
/**
* 此包存放策略服务模块的工具类。
*/
package com.trade.strategy.utils;
@@ -0,0 +1,4 @@
/**
* 此包存放策略服务模块的视图对象 (VO)。
*/
package com.trade.strategy.vo;
@@ -0,0 +1,118 @@
package com.yudao.module.strategy.controller.admin.execution;
import com.yudao.framework.common.pojo.CommonResult;
import com.yudao.framework.common.pojo.PageResult;
import com.yudao.module.strategy.controller.admin.execution.vo.*;
import com.yudao.module.strategy.convert.execution.StrategyExecutionConvert;
import com.yudao.module.strategy.dal.dataobject.execution.StrategyExecution;
import com.yudao.module.strategy.service.execution.StrategyExecutionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Collection;
import java.util.List;
import static com.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 策略执行")
@RestController
@RequestMapping("/strategy/execution")
@Validated
public class StrategyExecutionController {
@Resource
private StrategyExecutionService strategyExecutionService;
@PostMapping("/create")
@Operation(summary = "创建策略执行")
@PreAuthorize("@ss.hasPermission('strategy:execution:create')")
public CommonResult<Long> createStrategyExecution(@Valid @RequestBody StrategyExecutionCreateReqVO createReqVO) {
return success(strategyExecutionService.createStrategyExecution(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新策略执行")
@PreAuthorize("@ss.hasPermission('strategy:execution:update')")
public CommonResult<Boolean> updateStrategyExecution(@Valid @RequestBody StrategyExecutionUpdateReqVO updateReqVO) {
strategyExecutionService.updateStrategyExecution(updateReqVO);
return success(true);
}
@PutMapping("/start")
@Operation(summary = "启动策略执行")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('strategy:execution:start')")
public CommonResult<Boolean> startStrategyExecution(@RequestParam("id") Long id) {
strategyExecutionService.startStrategyExecution(id);
return success(true);
}
@PutMapping("/pause")
@Operation(summary = "暂停策略执行")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('strategy:execution:pause')")
public CommonResult<Boolean> pauseStrategyExecution(@RequestParam("id") Long id) {
strategyExecutionService.pauseStrategyExecution(id);
return success(true);
}
@PutMapping("/stop")
@Operation(summary = "停止策略执行")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('strategy:execution:stop')")
public CommonResult<Boolean> stopStrategyExecution(@RequestParam("id") Long id) {
strategyExecutionService.stopStrategyExecution(id);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除策略执行")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('strategy:execution:delete')")
public CommonResult<Boolean> deleteStrategyExecution(@RequestParam("id") Long id) {
strategyExecutionService.deleteStrategyExecution(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得策略执行")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('strategy:execution:query')")
public CommonResult<StrategyExecutionRespVO> getStrategyExecution(@RequestParam("id") Long id) {
StrategyExecution execution = strategyExecutionService.getStrategyExecution(id);
return success(StrategyExecutionConvert.INSTANCE.convert(execution));
}
@GetMapping("/list")
@Operation(summary = "获得策略执行列表")
@Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048")
@PreAuthorize("@ss.hasPermission('strategy:execution:query')")
public CommonResult<List<StrategyExecutionRespVO>> getStrategyExecutionList(@RequestParam("ids") Collection<Long> ids) {
List<StrategyExecution> list = strategyExecutionService.getStrategyExecutionList(ids);
return success(StrategyExecutionConvert.INSTANCE.convertList(list));
}
@GetMapping("/page")
@Operation(summary = "获得策略执行分页")
@PreAuthorize("@ss.hasPermission('strategy:execution:query')")
public CommonResult<PageResult<StrategyExecutionRespVO>> getStrategyExecutionPage(@Valid StrategyExecutionPageReqVO pageVO) {
PageResult<StrategyExecution> pageResult = strategyExecutionService.getStrategyExecutionPage(pageVO);
return success(StrategyExecutionConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/list-by-definition")
@Operation(summary = "根据策略定义ID获得策略执行列表")
@Parameter(name = "strategyDefinitionId", description = "策略定义ID", required = true, example = "100")
@PreAuthorize("@ss.hasPermission('strategy:execution:query')")
public CommonResult<List<StrategyExecutionRespVO>> getStrategyExecutionsByDefinitionId(@RequestParam("strategyDefinitionId") Long strategyDefinitionId) {
List<StrategyExecution> list = strategyExecutionService.getStrategyExecutionsByDefinitionId(strategyDefinitionId);
return success(StrategyExecutionConvert.INSTANCE.convertList(list));
}
}
@@ -0,0 +1,40 @@
package com.yudao.module.strategy.controller.admin.execution.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 策略执行创建 Request VO")
@Data
public class StrategyExecutionCreateReqVO {
@Schema(description = "策略定义ID", required = true, example = "1024")
@NotNull(message = "策略定义ID不能为空")
private Long strategyDefinitionId;
@Schema(description = "执行名称", required = true, example = "MA策略实盘执行-20240101")
@NotEmpty(message = "执行名称不能为空")
private String name;
@Schema(description = "执行类型(1:实盘交易,2:模拟交易)", required = true, example = "1")
@NotNull(message = "执行类型不能为空")
private Integer type;
@Schema(description = "初始资金", required = true, example = "100000.00")
@NotNull(message = "初始资金不能为空")
private BigDecimal initialCapital;
@Schema(description = "手续费率", required = true, example = "0.0003")
@NotNull(message = "手续费率不能为空")
private BigDecimal commissionRate;
@Schema(description = "执行参数(JSON格式)", example = "{\"stopLoss\": 0.05}")
private String parameters;
@Schema(description = "备注", example = "测试执行")
private String remark;
}
@@ -0,0 +1,40 @@
package com.yudao.module.strategy.controller.admin.execution.vo;
import com.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static com.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 策略执行分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class StrategyExecutionPageReqVO extends PageParam {
@Schema(description = "策略定义ID", example = "1024")
private Long strategyDefinitionId;
@Schema(description = "执行名称", example = "MA策略")
private String name;
@Schema(description = "执行类型(1:实盘交易,2:模拟交易)", example = "1")
private Integer type;
@Schema(description = "执行状态(0:未开始,1:运行中,2:已暂停,3:已停止,4:已完成,5:执行错误)", example = "1")
private Integer status;
@Schema(description = "开始执行时间范围 - 开始")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] startTime;
@Schema(description = "创建时间范围 - 开始")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}
@@ -0,0 +1,64 @@
package com.yudao.module.strategy.controller.admin.execution.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 策略执行 Response VO")
@Data
public class StrategyExecutionRespVO {
@Schema(description = "执行记录ID", required = true, example = "1")
private Long id;
@Schema(description = "策略定义ID", required = true, example = "1024")
private Long strategyDefinitionId;
@Schema(description = "执行名称", required = true, example = "MA策略实盘执行-20240101")
private String name;
@Schema(description = "执行类型(1:实盘交易,2:模拟交易)", required = true, example = "1")
private Integer type;
@Schema(description = "初始资金", required = true, example = "100000.00")
private BigDecimal initialCapital;
@Schema(description = "当前资金", required = true, example = "105000.00")
private BigDecimal currentCapital;
@Schema(description = "总盈亏金额", required = true, example = "5000.00")
private BigDecimal totalProfitLoss;
@Schema(description = "总盈亏比例", required = true, example = "0.05")
private BigDecimal totalProfitLossRatio;
@Schema(description = "手续费率", required = true, example = "0.0003")
private BigDecimal commissionRate;
@Schema(description = "执行状态(0:未开始,1:运行中,2:已暂停,3:已停止,4:已完成,5:执行错误)", required = true, example = "1")
private Integer status;
@Schema(description = "错误信息", example = "策略执行异常:无法获取行情数据")
private String errorMessage;
@Schema(description = "开始执行时间", example = "2024-01-01 09:30:00")
private LocalDateTime startTime;
@Schema(description = "结束执行时间", example = "2024-01-01 15:00:00")
private LocalDateTime endTime;
@Schema(description = "执行参数(JSON格式)", example = "{\"stopLoss\": 0.05}")
private String parameters;
@Schema(description = "备注", example = "测试执行")
private String remark;
@Schema(description = "创建时间", required = true)
private LocalDateTime createTime;
@Schema(description = "更新时间", required = true)
private LocalDateTime updateTime;
}
@@ -0,0 +1,37 @@
package com.yudao.module.strategy.controller.admin.execution.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 策略执行更新 Request VO")
@Data
public class StrategyExecutionUpdateReqVO {
@Schema(description = "执行记录ID", required = true, example = "1")
@NotNull(message = "执行记录ID不能为空")
private Long id;
@Schema(description = "执行名称", example = "MA策略实盘执行-20240101-V2")
private String name;
@Schema(description = "执行类型(1:实盘交易,2:模拟交易)", example = "1")
private Integer type;
@Schema(description = "初始资金", example = "120000.00")
private BigDecimal initialCapital;
@Schema(description = "手续费率", example = "0.00025")
private BigDecimal commissionRate;
@Schema(description = "执行参数(JSON格式)", example = "{\"stopLoss\": 0.06, \"takeProfit\": 0.1}")
private String parameters;
@Schema(description = "备注", example = "更新执行参数")
private String remark;
// 注意:通常不直接通过API更新执行状态、当前资金、盈亏等,这些应由系统内部逻辑管理
// 如果确实需要手动调整状态(例如管理员干预),可以单独提供接口或在此VO中添加并谨慎处理
}
@@ -0,0 +1,46 @@
package com.yudao.module.strategy.convert.execution;
import com.yudao.framework.common.pojo.PageResult;
import com.yudao.module.strategy.controller.admin.execution.vo.StrategyExecutionCreateReqVO;
import com.yudao.module.strategy.controller.admin.execution.vo.StrategyExecutionRespVO;
import com.yudao.module.strategy.controller.admin.execution.vo.StrategyExecutionUpdateReqVO;
import com.yudao.module.strategy.dal.dataobject.execution.StrategyExecution;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* 策略执行 Convert
*/
@Mapper
public interface StrategyExecutionConvert {
StrategyExecutionConvert INSTANCE = Mappers.getMapper(StrategyExecutionConvert.class);
/**
* 创建请求 VO 转换为 DO
*/
StrategyExecution convert(StrategyExecutionCreateReqVO bean);
/**
* 更新请求 VO 转换为 DO
*/
StrategyExecution convert(StrategyExecutionUpdateReqVO bean);
/**
* DO 转换为响应 VO
*/
StrategyExecutionRespVO convert(StrategyExecution bean);
/**
* DO 列表转换为响应 VO 列表
*/
List<StrategyExecutionRespVO> convertList(List<StrategyExecution> list);
/**
* 分页结果转换
*/
PageResult<StrategyExecutionRespVO> convertPage(PageResult<StrategyExecution> page);
}
@@ -0,0 +1,97 @@
package com.yudao.module.strategy.dal.dataobject.execution;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 策略执行记录 DO
*/
@TableName("strategy_execution")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class StrategyExecution extends BaseDO {
/**
* 执行记录ID
*/
@TableId(type = IdType.ASSIGN_ID)
private Long id;
/**
* 策略定义ID
*/
private Long strategyDefinitionId;
/**
* 执行名称
*/
private String name;
/**
* 执行类型(1:实盘交易,2:模拟交易)
*/
private Integer type;
/**
* 初始资金
*/
private BigDecimal initialCapital;
/**
* 当前资金
*/
private BigDecimal currentCapital;
/**
* 总盈亏金额
*/
private BigDecimal totalProfitLoss;
/**
* 总盈亏比例
*/
private BigDecimal totalProfitLossRatio;
/**
* 手续费率
*/
private BigDecimal commissionRate;
/**
* 执行状态(0:未开始,1:运行中,2:已暂停,3:已停止,4:已完成,5:执行错误)
*/
private Integer status;
/**
* 错误信息
*/
private String errorMessage;
/**
* 开始执行时间
*/
private LocalDateTime startTime;
/**
* 结束执行时间
*/
private LocalDateTime endTime;
/**
* 执行参数(JSON格式)
*/
private String parameters;
/**
* 备注
*/
private String remark;
}
@@ -0,0 +1,15 @@
package com.yudao.module.strategy.dal.mysql.execution;
import com.yudao.framework.mybatis.core.mapper.BaseMapperX;
import com.yudao.module.strategy.dal.dataobject.execution.StrategyExecution;
import org.apache.ibatis.annotations.Mapper;
/**
* 策略执行记录 Mapper
*/
@Mapper
public interface StrategyExecutionMapper extends BaseMapperX<StrategyExecution> {
// 可以在此定义特定的查询方法,例如根据策略定义ID查询执行记录等
}
@@ -0,0 +1,93 @@
package com.yudao.module.strategy.service.execution;
import com.yudao.module.strategy.controller.admin.execution.vo.StrategyExecutionCreateReqVO;
import com.yudao.module.strategy.controller.admin.execution.vo.StrategyExecutionPageReqVO;
import com.yudao.module.strategy.controller.admin.execution.vo.StrategyExecutionUpdateReqVO;
import com.yudao.module.strategy.dal.dataobject.execution.StrategyExecution;
import com.yudao.framework.common.pojo.PageResult;
import javax.validation.Valid;
import java.util.Collection;
import java.util.List;
/**
* 策略执行 Service 接口
*/
public interface StrategyExecutionService {
/**
* 创建策略执行记录
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createStrategyExecution(@Valid StrategyExecutionCreateReqVO createReqVO);
/**
* 更新策略执行记录
*
* @param updateReqVO 更新信息
*/
void updateStrategyExecution(@Valid StrategyExecutionUpdateReqVO updateReqVO);
/**
* 启动策略执行
*
* @param id 策略执行编号
*/
void startStrategyExecution(Long id);
/**
* 暂停策略执行
*
* @param id 策略执行编号
*/
void pauseStrategyExecution(Long id);
/**
* 停止策略执行
*
* @param id 策略执行编号
*/
void stopStrategyExecution(Long id);
/**
* 删除策略执行记录
*
* @param id 编号
*/
void deleteStrategyExecution(Long id);
/**
* 获得策略执行记录
*
* @param id 编号
* @return 策略执行记录
*/
StrategyExecution getStrategyExecution(Long id);
/**
* 获得策略执行记录列表
*
* @param ids 编号列表
* @return 策略执行记录列表
*/
List<StrategyExecution> getStrategyExecutionList(Collection<Long> ids);
/**
* 获得策略执行记录分页
*
* @param pageReqVO 分页查询
* @return 策略执行记录分页
*/
PageResult<StrategyExecution> getStrategyExecutionPage(StrategyExecutionPageReqVO pageReqVO);
/**
* 根据策略定义ID查询执行记录列表
*
* @param strategyDefinitionId 策略定义ID
* @return 策略执行记录列表
*/
List<StrategyExecution> getStrategyExecutionsByDefinitionId(Long strategyDefinitionId);
}
@@ -0,0 +1,192 @@
package com.yudao.module.strategy.service.execution;
import com.yudao.framework.common.pojo.PageResult;
import com.yudao.module.strategy.controller.admin.execution.vo.StrategyExecutionCreateReqVO;
import com.yudao.module.strategy.controller.admin.execution.vo.StrategyExecutionPageReqVO;
import com.yudao.module.strategy.controller.admin.execution.vo.StrategyExecutionUpdateReqVO;
import com.yudao.module.strategy.convert.execution.StrategyExecutionConvert;
import com.yudao.module.strategy.dal.dataobject.execution.StrategyExecution;
import com.yudao.module.strategy.dal.mysql.execution.StrategyExecutionMapper;
import com.yudao.module.strategy.enums.StrategyExecutionStatusEnum;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import static com.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yudao.module.strategy.enums.ErrorCodeConstants.*;
/**
* 策略执行 Service 实现类
*/
@Service
@Validated
public class StrategyExecutionServiceImpl implements StrategyExecutionService {
@Resource
private StrategyExecutionMapper strategyExecutionMapper;
/**
* 创建策略执行记录
*
* @param createReqVO 创建信息
* @return 编号
*/
@Override
public Long createStrategyExecution(StrategyExecutionCreateReqVO createReqVO) {
// 校验策略定义是否存在等前置条件 (根据实际业务补充)
// ...
StrategyExecution strategyExecution = StrategyExecutionConvert.INSTANCE.convert(createReqVO);
strategyExecution.setStatus(StrategyExecutionStatusEnum.NOT_STARTED.getStatus()); // 初始状态为未开始
strategyExecutionMapper.insert(strategyExecution);
return strategyExecution.getId();
}
/**
* 更新策略执行记录
*
* @param updateReqVO 更新信息
*/
@Override
public void updateStrategyExecution(StrategyExecutionUpdateReqVO updateReqVO) {
validateStrategyExecutionExists(updateReqVO.getId());
StrategyExecution updateObj = StrategyExecutionConvert.INSTANCE.convert(updateReqVO);
strategyExecutionMapper.updateById(updateObj);
}
/**
* 启动策略执行
*
* @param id 策略执行编号
*/
@Override
public void startStrategyExecution(Long id) {
StrategyExecution execution = validateStrategyExecutionExists(id);
if (!StrategyExecutionStatusEnum.NOT_STARTED.getStatus().equals(execution.getStatus()) &&
!StrategyExecutionStatusEnum.PAUSED.getStatus().equals(execution.getStatus()) &&
!StrategyExecutionStatusEnum.ERROR.getStatus().equals(execution.getStatus())) {
throw exception(STRATEGY_EXECUTION_CANNOT_START, execution.getStatus());
}
// 实际启动逻辑,可能需要与交易引擎交互
// ...
StrategyExecution updateObj = new StrategyExecution();
updateObj.setId(id);
updateObj.setStatus(StrategyExecutionStatusEnum.RUNNING.getStatus());
updateObj.setStartTime(LocalDateTime.now()); // 记录开始时间
strategyExecutionMapper.updateById(updateObj);
}
/**
* 暂停策略执行
*
* @param id 策略执行编号
*/
@Override
public void pauseStrategyExecution(Long id) {
StrategyExecution execution = validateStrategyExecutionExists(id);
if (!StrategyExecutionStatusEnum.RUNNING.getStatus().equals(execution.getStatus())) {
throw exception(STRATEGY_EXECUTION_CANNOT_PAUSE, execution.getStatus());
}
// 实际暂停逻辑
// ...
StrategyExecution updateObj = new StrategyExecution();
updateObj.setId(id);
updateObj.setStatus(StrategyExecutionStatusEnum.PAUSED.getStatus());
strategyExecutionMapper.updateById(updateObj);
}
/**
* 停止策略执行
*
* @param id 策略执行编号
*/
@Override
public void stopStrategyExecution(Long id) {
StrategyExecution execution = validateStrategyExecutionExists(id);
if (StrategyExecutionStatusEnum.COMPLETED.getStatus().equals(execution.getStatus()) ||
StrategyExecutionStatusEnum.STOPPED.getStatus().equals(execution.getStatus())) {
throw exception(STRATEGY_EXECUTION_ALREADY_STOPPED_OR_COMPLETED, execution.getStatus());
}
// 实际停止逻辑,可能需要进行一些清理工作
// ...
StrategyExecution updateObj = new StrategyExecution();
updateObj.setId(id);
updateObj.setStatus(StrategyExecutionStatusEnum.STOPPED.getStatus());
updateObj.setEndTime(LocalDateTime.now()); // 记录结束时间
strategyExecutionMapper.updateById(updateObj);
}
/**
* 删除策略执行记录
*
* @param id 编号
*/
@Override
public void deleteStrategyExecution(Long id) {
validateStrategyExecutionExists(id);
// 需要考虑是否允许删除运行中的策略等
strategyExecutionMapper.deleteById(id);
}
/**
* 校验策略执行记录是否存在
*
* @param id 策略执行ID
* @return 策略执行记录
*/
private StrategyExecution validateStrategyExecutionExists(Long id) {
StrategyExecution execution = strategyExecutionMapper.selectById(id);
if (execution == null) {
throw exception(STRATEGY_EXECUTION_NOT_EXISTS);
}
return execution;
}
/**
* 获得策略执行记录
*
* @param id 编号
* @return 策略执行记录
*/
@Override
public StrategyExecution getStrategyExecution(Long id) {
return strategyExecutionMapper.selectById(id);
}
/**
* 获得策略执行记录列表
*
* @param ids 编号列表
* @return 策略执行记录列表
*/
@Override
public List<StrategyExecution> getStrategyExecutionList(Collection<Long> ids) {
return strategyExecutionMapper.selectBatchIds(ids);
}
/**
* 获得策略执行记录分页
*
* @param pageReqVO 分页查询
* @return 策略执行记录分页
*/
@Override
public PageResult<StrategyExecution> getStrategyExecutionPage(StrategyExecutionPageReqVO pageReqVO) {
return strategyExecutionMapper.selectPage(pageReqVO, StrategyExecutionConvert.INSTANCE.convert(pageReqVO));
}
/**
* 根据策略定义ID查询执行记录列表
*
* @param strategyDefinitionId 策略定义ID
* @return 策略执行记录列表
*/
@Override
public List<StrategyExecution> getStrategyExecutionsByDefinitionId(Long strategyDefinitionId) {
return strategyExecutionMapper.selectList(StrategyExecution::getStrategyDefinitionId, strategyDefinitionId);
}
}
@@ -0,0 +1,11 @@
server:
port: 8007
spring:
profiles:
active: dev
application:
name: trade-strategy
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
@@ -0,0 +1,11 @@
server:
port: 8007
spring:
profiles:
active: prod
application:
name: trade-strategy
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
@@ -0,0 +1,11 @@
server:
port: 8007
spring:
profiles:
active: test
application:
name: trade-strategy
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
@@ -0,0 +1,103 @@
server:
port: 8083 # 策略服务端口
spring:
application:
name: trade-strategy # 应用名称
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 # Nacos 服务注册中心地址
namespace: trade_dev # Nacos 命名空间
group: DEFAULT_GROUP # Nacos 分组
config:
server-addr: 127.0.0.1:8848 # Nacos 配置中心地址
namespace: trade_dev # Nacos 命名空间
group: DEFAULT_GROUP # Nacos 分组
file-extension: yml # 配置文件格式
shared-configs: # 共享配置
- data-id: application-common.yml # 通用配置
group: DEFAULT_GROUP
refresh: true
- data-id: trade-redis.yml # Redis 配置
group: DEFAULT_GROUP
refresh: true
- data-id: trade-mybatis-plus.yml # MyBatis Plus 配置
group: DEFAULT_GROUP
refresh: true
# # Sentinel 配置 (按需开启)
# sentinel:
# transport:
# dashboard: localhost:8080 # Sentinel 控制台地址
# port: 8719 # Sentinel 客户端与控制台通信端口
# datasource:
# ds1:
# nacos:
# server-addr: ${spring.cloud.nacos.config.server-addr}
# namespace: ${spring.cloud.nacos.config.namespace}
# dataId: ${spring.application.name}-sentinel-rules
# groupId: SENTINEL_GROUP
# data-type: json
# rule-type: flow
# Seata 配置 (按需开启)
#seata:
# tx-service-group: trade_tx_group # 事务组名称,需要与 Seata Server 配置一致
# service:
# vgroup-mapping:
# trade_tx_group: default # 事务组与 Seata Server 实例的映射关系
# registry:
# type: nacos
# nacos:
# server-addr: ${spring.cloud.nacos.discovery.server-addr}
# namespace: ${spring.cloud.nacos.discovery.namespace}
# group: SEATA_GROUP
# config:
# type: nacos
# nacos:
# server-addr: ${spring.cloud.nacos.config.server-addr}
# namespace: ${spring.cloud.nacos.config.namespace}
# group: SEATA_GROUP
# 日志配置
logging:
level:
com.trade.strategy: debug
org.springframework: info
org.apache.ibatis: info
# Actuator 配置
management:
endpoints:
web:
exposure:
include: '*' # 开放所有端点,生产环境建议按需开放
endpoint:
health:
show-details: always # 显示健康检查详情
# MyBatis Plus 配置 (如果 trade-mybatis-plus.yml 中未配置或需要覆盖)
#mybatis-plus:
# mapper-locations: classpath*:/mapper/**/*.xml # Mapper XML 文件位置
# global-config:
# db-config:
# id-type: assign_id # ID 生成策略
# 策略引擎相关配置 (示例)
strategy:
engine:
thread-pool-size: 10 # 策略执行线程池大小
data-source:
type: local # 数据源类型,例如: local, remote_api, message_queue
# ... 其他数据源相关配置
# 开放 API 文档配置 (如果需要)
#springdoc:
# api-docs:
# path: /v3/api-docs
# swagger-ui:
# path: /swagger-ui.html
# display-request-duration: true
# groups-order: DESC
# operations-sorter: alpha
# tags-sorter: alpha
@@ -0,0 +1,24 @@
# Spring Cloud Alibaba Nacos Config
spring:
application:
name: trade-strategy # 应用名称,必须与 Nacos 中的 Data ID 对应
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848 # Nacos Server 地址
namespace: trade_dev # Nacos 命名空间,用于隔离不同环境的配置
group: DEFAULT_GROUP # Nacos 分组,默认为 DEFAULT_GROUP
file-extension: yml # Data ID 配置文件格式,默认为 properties
# 如果配置了共享配置,这里可以不指定 Data ID,或者指定应用特有的配置
# data-ids: ${spring.application.name}.${spring.cloud.nacos.config.file-extension}
# auto-refresh: true # 是否自动刷新配置,默认为 true
# bootstrap: true # 是否在 bootstrap 阶段加载配置,默认为 true
discovery:
server-addr: 127.0.0.1:8848 # Nacos Server 地址
namespace: trade_dev # Nacos 命名空间
group: DEFAULT_GROUP # Nacos 分组
# 配置日志文件名称,确保在 Nacos 配置加载前,日志系统就能正确初始化
logging:
file:
name: logs/${spring.application.name}.log
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<logger name="com.trade.strategy" level="debug"/>
</configuration>