java实现网格交易回测

以下是一个基于Java实现的简单网格交易回测程序框架,以证券ETF(512880)为例。代码包含历史数据加载、网格策略逻辑和基础统计指标:

import java.io.BufferedReader;
import java.io.FileReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

// K线数据对象
class KData {
    Date date;
    double open;
    double high;
    double low;
    double close;
    long volume;

    public KData(String[] data) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        this.date = sdf.parse(data[0]);
        this.open = Double.parseDouble(data[1]);
        this.high = Double.parseDouble(data[2]);
        this.low = Double.parseDouble(data[3]);
        this.close = Double.parseDouble(data[4]);
        this.volume = Long.parseLong(data[5]);
    }
}

// 网格策略回测引擎
class GridStrategyBacktest {
    List<KData> historicalData;
    double initialCapital = 100000;  // 初始资金10万元
    double cash = initialCapital;
    int position = 0;  // 持仓数量
    double gridStep = 0.05;  // 网格间距5%
    int gridLevels = 10;     // 网格层数
    double basePrice;        // 基准价(网格中心)
    
    List<String> trades = new ArrayList<>();  // 交易记录
    double totalReturn = 0;
    int winCount = 0;
    int tradeCount = 0;

    public GridStrategyBacktest(List<KData> data, double basePrice) {
        this.historicalData = data;
        this.basePrice = basePrice;
    }

    // 执行回测
    public void runBacktest() {
        for (KData bar : historicalData) {
            double price = bar.close;
            
            // 计算当前价格对应的网格层级
            int targetLevel = (int) ((price - basePrice) / (basePrice * gridStep));
            
            // 计算应该持有的仓位
            int targetPosition = Math.min(gridLevels, Math.max(-gridLevels, targetLevel)) * 100;
            
            // 执行交易
            int volumeToTrade = targetPosition - position;
            if (volumeToTrade != 0) {
                executeTrade(bar.date, price, volumeToTrade);
            }
        }
        
        // 计算最终收益
        totalReturn = (cash + position * historicalData.get(historicalData.size()-1).close - initialCapital) / initialCapital;
    }

    private void executeTrade(Date date, double price, int volume) {
        double cost = Math.abs(volume) * price * 1.0003;  // 包含0.03%的交易费用
        
        if (volume > 0) {  // 买入
            if (cost > cash) return;
            cash -= cost;
            position += volume;
            trades.add(String.format("%tF - 买入 %d股 @ %.4f", date, volume, price));
        } else {          // 卖出
            if (-volume > position) return;
            cash += (-volume) * price * 0.9997;  // 扣除卖出手续费
            position += volume;
            trades.add(String.format("%tF - 卖出 %d股 @ %.4f", date, -volume, price));
        }
        
        tradeCount++;
        if ((volume > 0 && price < basePrice) || 
            (volume < 0 && price > basePrice)) {
            winCount++;
        }
    }

    // 输出统计结果
    public void printResults() {
        System.out.println("===== 回测结果 =====");
        System.out.printf("初始资金: %.2f\n", initialCapital);
        System.out.printf("最终资产: %.2f\n", cash + position * historicalData.get(historicalData.size()-1).close);
        System.out.printf("总收益率: %.2f%%\n", totalReturn * 100);
        System.out.printf("交易次数: %d\n胜率: %.2f%%\n", 
                          tradeCount, (double)winCount/tradeCount*100);
        System.out.println("\n最后5笔交易记录:");
        trades.subList(Math.max(0, trades.size()-5), trades.size())
              .forEach(System.out::println);
    }
}

public class ETFBacktester {
    public static void main(String[] args) {
        // 1. 加载历史数据(示例数据格式:日期,开盘价,最高价,最低价,收盘价,成交量)
        List<KData> history = loadCSV("512880.csv");  // 需替换为实际数据文件路径
        
        // 2. 设置网格参数
        double basePrice = 1.0;  // 根据历史中位数设定网格基准价
        
        // 3. 运行回测
        GridStrategyBacktest backtester = new GridStrategyBacktest(history, basePrice);
        backtester.runBacktest();
        
        // 4. 输出结果
        backtester.printResults();
    }

    private static List<KData> loadCSV(String filename) {
        List<KData> data = new ArrayList<>();
        try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
            String line;
            br.readLine();  // 跳过标题行
            while ((line = br.readLine()) != null) {
                String[] values = line.split(",");
                data.add(new KData(values));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return data;
    }
}

代码说明及使用步骤:

  1. 数据准备

    • 需要准备CSV格式的历史数据文件(示例文件名:512880.csv)
    • 数据格式(按日期排序):
      日期,开盘价,最高价,最低价,收盘价,成交量
      2023-01-03,1.052,1.068,1.050,1.065,12345678
      ...
      
  2. 核心逻辑

    • 网格生成:以基准价(basePrice)为中心,上下各生成gridLevels层网格
    • 交易触发:当价格突破网格层级时调整仓位
    • 费用计算:包含0.03%的买卖双边交易费用
  3. 关键参数

    double initialCapital = 100000;  // 初始资金
    double gridStep = 0.05;         // 5%网格间距
    int gridLevels = 10;            // 网格层数
    
  4. 输出指标

    • 总收益率
    • 交易次数
    • 胜率(盈利交易占比)
    • 详细交易记录

扩展建议(可根据需求添加):

  1. 增强统计指标

    // 在GridStrategyBacktest类中添加:
    double maxDrawdown = 0;     // 最大回撤
    double peak = initialCapital;
    
    // 在每次交易后更新:
    double currentValue = cash + position * price;
    if (currentValue > peak) {
        peak = currentValue;
    } else {
        double dd = (peak - currentValue)/peak;
        if (dd > maxDrawdown) maxDrawdown = dd;
    }
    
  2. 参数优化功能

    public void optimizeParameters() {
        for (double step = 0.03; step < 0.08; step += 0.01) {
            for (int levels = 5; levels <= 15; levels += 2) {
                GridStrategyBacktest test = new GridStrategyBacktest(history, basePrice);
                test.gridStep = step;
                test.gridLevels = levels;
                test.runBacktest();
                System.out.printf("步长:%.2f 层数:%d 收益:%.2f%%\n", 
                                step, levels, test.totalReturn*100);
            }
        }
    }
    
  3. 可视化输出

    // 使用JFreeChart库生成收益曲线图
    XYSeries series = new XYSeries("净值曲线");
    for (int i = 0; i < historicalData.size(); i++) {
        double value = cash + position * historicalData.get(i).close;
        series.add(i, value / initialCapital);
    }
    

注意事项:

  1. 需要复权价格数据(建议使用后复权)
  2. 实际交易需考虑最小交易单位(A股ETF为100股整数倍)
  3. 可增加止盈止损逻辑:
    // 在executeTrade方法中添加:
    if (totalReturn > 0.3) {  // 收益率超过30%时清仓
        int sellVolume = position;
        executeTrade(date, price, -sellVolume);
    }
    

如需完整实现,建议结合第三方库(如Ta4j用于技术指标计算)和数据库(存储历史数据)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值