Subversion Repositories Integrator Subversion

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
778 blopes 1
package br.com.kronus.core;
2
 
3
import br.com.kronus.core.Candle;
4
import br.com.kronus.core.SinalDeTrade;
5
import br.com.kronus.strategy.Strategy;
6
 
7
import java.util.ArrayList;
8
import java.util.List;
9
 
10
public class BacktestEngine {
11
 
12
    /**
13
     * Roda o backtest: gera sinais com a estratégia e simula trade por trade.
14
     */
15
    public BacktestSummary runBacktest(List<Candle> candles, Strategy strategy) {
16
        BacktestSummary summary = new BacktestSummary();
17
 
18
        if (candles == null || candles.isEmpty()) {
19
            return summary;
20
        }
21
 
22
        List<SinalDeTrade> sinais = strategy.gerarSinais(candles);
23
 
24
        for (SinalDeTrade sinal : sinais) {
25
            BacktestTradeResult result = simularTrade(candles, sinal);
26
            summary.addTrade(result);
27
        }
28
 
29
        summary.calcularResumo();
30
        return summary;
31
    }
32
 
33
    /**
34
     * Simula um trade a partir de um sinal de compra/venda.
35
     * Aqui implementado para COMPRA; para VENDA é só adaptar as condições (mirror).
36
     */
37
    private BacktestTradeResult simularTrade(List<Candle> candles, SinalDeTrade sinal) {
38
        BacktestTradeResult result = new BacktestTradeResult(sinal);
39
 
40
        // Só implementei compra aqui – se precisar de venda também, dá pra expandir
41
        if (sinal.getDirecao() == SinalDeTrade.Direcao.COMPRA) {
42
            simularCompra(candles, sinal, result);
43
        } else {
44
            // TODO: implementar lógica espelhada para venda, se você usar
45
            result.setExitReason(BacktestTradeResult.ExitReason.NAO_EXECUTADO);
46
        }
47
 
48
        return result;
49
    }
50
 
51
    private void simularCompra(List<Candle> candles, SinalDeTrade sinal, BacktestTradeResult result) {
52
        double precoEntrada = sinal.getPrecoEntrada();
53
        double stop = sinal.getStopLoss();
54
        double alvo = sinal.getAlvo();
55
 
56
        // 1) Descobrir a partir de qual índice de candle começar (>= horário do sinal)
57
        int startIdx = encontrarIndiceInicial(candles, sinal);
58
 
59
        if (startIdx == -1) {
60
            // Não há candles após o sinal
61
            result.setExitReason(BacktestTradeResult.ExitReason.NAO_EXECUTADO);
62
            return;
63
        }
64
 
65
        // 2) Procurar candle onde a entrada é acionada (touch na faixa [min, max])
66
        int idxEntrada = -1;
67
        for (int i = startIdx; i < candles.size(); i++) {
68
            Candle c = candles.get(i);
69
            if (c.getMinima() <= precoEntrada && c.getMaxima() >= precoEntrada) {
70
                idxEntrada = i;
71
                break;
72
            }
73
        }
74
 
75
        if (idxEntrada == -1) {
76
            // Preço nunca tocou na entrada
77
            result.setExitReason(BacktestTradeResult.ExitReason.NAO_EXECUTADO);
78
            return;
79
        }
80
 
81
        // Marca entrada
82
        Candle candleEntrada = candles.get(idxEntrada);
83
        result.setExecutado(true);
84
        result.setEntryPrice(precoEntrada);
85
        result.setEntryCandle(candleEntrada);
86
 
87
        // 3) A partir do candle de entrada, checar stop/alvo
88
        for (int i = idxEntrada; i < candles.size(); i++) {
89
            Candle c = candles.get(i);
90
 
91
            boolean tocouStop = c.getMinima() <= stop;
92
            boolean tocouAlvo = c.getMaxima() >= alvo;
93
 
94
            if (tocouStop && tocouAlvo) {
95
                // Mesma barra: por conservadorismo, considerar que o STOP veio primeiro
96
                result.setExitPrice(stop);
97
                result.setPnl(stop - precoEntrada);
98
                result.setExitCandle(c);
99
                result.setExitReason(BacktestTradeResult.ExitReason.STOP);
100
                return;
101
            } else if (tocouStop) {
102
                result.setExitPrice(stop);
103
                result.setPnl(stop - precoEntrada);
104
                result.setExitCandle(c);
105
                result.setExitReason(BacktestTradeResult.ExitReason.STOP);
106
                return;
107
            } else if (tocouAlvo) {
108
                result.setExitPrice(alvo);
109
                result.setPnl(alvo - precoEntrada);
110
                result.setExitCandle(c);
111
                result.setExitReason(BacktestTradeResult.ExitReason.ALVO);
112
                return;
113
            }
114
        }
115
 
116
        // 4) Se chegou aqui, nem stop nem alvo foram atingidos: encerra no último candle (expirado)
117
        Candle ultimo = candles.get(candles.size() - 1);
118
        double exitPrice = ultimo.getFechamento();
119
        result.setExitPrice(exitPrice);
120
        result.setPnl(exitPrice - precoEntrada);
121
        result.setExitCandle(ultimo);
122
        result.setExitReason(BacktestTradeResult.ExitReason.EXPIRADO);
123
    }
124
 
125
    /**
126
     * Procura primeiro candle com horário >= horário do sinal.
127
     */
128
    private int encontrarIndiceInicial(List<Candle> candles, SinalDeTrade sinal) {
129
        for (int i = 0; i < candles.size(); i++) {
130
            if (!candles.get(i).getTime().isBefore(sinal.getTime())) {
131
                return i;
132
            }
133
        }
134
        return -1;
135
    }
136
}