Subversion Repositories Integrator Subversion

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
776 blopes 1
package br.com.kronus.core;
2
 
3
import java.math.BigDecimal;
4
import java.util.ArrayList;
5
import java.util.List;
6
 
7
import br.com.kronus.core.ResultadoSinalGatilho3.Status;
8
import br.com.sl.domain.dto.robo.SinalTradeGatilho3;
9
import br.com.sl.domain.dto.robo.SinalTradeGatilho3.TipoOperacao;
10
import br.com.sl.domain.model.Candle;
11
import br.com.sl.domain.util.BigDecimalUtils;
12
 
13
/**
14
 * Faz o backtest dos sinais gerados pela EstrategiaGatilhoTipo3Sinais,
15
 * informando para cada sinal se deu alvo/stop/não acionou etc.
16
 *
17
 * Regras principais:
18
 *  - A avaliação começa a partir do candle seguinte ao G3.
19
 *  - Se o primeiro alvo (alvo1) for atingido ANTES de qualquer entrada,
20
 *    a operação é DESCARTADA (Status.DESCARTADO).
21
 *  - Stop é calculado combinando stopMenos100 e stopAlternativo:
22
 *      * COMPRA: stop = max(stopMenos100, stopAlternativo) -> stop mais apertado (mais próximo da entrada)
23
 *      * VENDA:  stop = min(stopMenos100, stopAlternativo) -> stop mais apertado (mais próximo da entrada)
24
 *  - Após a entrada, a posição é encerrada no primeiro evento: STOP, ALVO2 ou ALVO1.
25
 */
26
public class AvaliadorSinaisGatilho3 {
27
 
28
    /**
29
     * Avalia uma lista de sinais sobre uma lista de candles.
30
     */
31
    public List<ResultadoSinalGatilho3> avaliarTodos(List<SinalTradeGatilho3> sinais, List<Candle> candles) {
32
        List<ResultadoSinalGatilho3> resultados = new ArrayList<>();
33
        if (sinais == null || sinais.isEmpty()) {
34
            return resultados;
35
        }
36
 
37
        for (SinalTradeGatilho3 sinal : sinais) {
38
            ResultadoSinalGatilho3 r = avaliarSinal(sinal, candles);
39
            resultados.add(r);
40
        }
41
 
42
        return resultados;
43
    }
44
 
45
    /**
46
     * Avalia UM sinal específico no histórico de candles.
47
     *
48
     * Lógica:
49
     *  - Começa a análise a partir do candle do G3 (gatilho 3) do padrão do sinal.
50
     *  - Primeiro verifica se o ALVO1 é atingido ANTES de qualquer entrada -> DESCARTADO.
51
     *  - Depois, se aciona a entrada (precoEntrada1).
52
     *  - Após a entrada, vê quem vem primeiro: STOP, ALVO2 ou ALVO1.
53
     */
54
    public ResultadoSinalGatilho3 avaliarSinal(SinalTradeGatilho3 sinal, List<Candle> candles) {
55
        if (sinal == null || candles == null || candles.isEmpty()) {
56
            return new ResultadoSinalGatilho3(
57
                    sinal,
58
                    Status.NAO_ACIONADO,
59
                    false,
60
                    -1,
61
                    -1,
62
                    null,
63
                    null
64
            );
65
        }
66
 
67
        // Descobre o índice do candle G3 dentro da lista de candles
68
        Candle candleG3 = sinal.getGatilho3();
69
        int idxG3 = candles.indexOf(candleG3);
70
 
71
        if (idxG3 < 0) {
72
            // Se por algum motivo o candle G3 não estiver na lista, considera que não deu para avaliar
73
            return new ResultadoSinalGatilho3(
74
                    sinal,
75
                    Status.NAO_ACIONADO,
76
                    false,
77
                    -1,
78
                    -1,
79
                    null,
80
                    null
81
            );
82
        }
83
 
84
        // Começamos a avaliar a partir do candle SEGUINTE ao G3
85
        int inicio = idxG3 + 1;
86
        if (inicio >= candles.size()) {
87
            // Não há candles após o G3
88
            return new ResultadoSinalGatilho3(
89
                    sinal,
90
                    Status.NAO_ACIONADO,
91
                    false,
92
                    -1,
93
                    -1,
94
                    null,
95
                    null
96
            );
97
        }
98
 
99
        TipoOperacao tipo = sinal.getTipoOperacao();
100
        BigDecimal precoEntrada = sinal.getPrecoEntrada1(); // usando a 1ª entrada como referência
101
        BigDecimal alvo1 = sinal.getAlvo1();
102
        BigDecimal alvo2 = sinal.getAlvo2();
103
 
104
        // Combinação de stops: stopMenos100 + stopAlternativo
105
        BigDecimal stopMenos100 = sinal.getStopMenos100();
106
        BigDecimal stopAlternativo = sinal.getStopAlternativo();
107
        BigDecimal stop;
108
 
109
        if (tipo == TipoOperacao.COMPRA) {
110
            // Em compra, ambos devem estar abaixo da entrada.
111
            // Stop mais apertado = o MAIOR dos dois (mais próximo da entrada).
112
            if (stopMenos100 != null && stopAlternativo != null) {
113
                stop = stopMenos100.max(stopAlternativo);
114
            } else if (stopMenos100 != null) {
115
                stop = stopMenos100;
116
            } else {
117
                stop = stopAlternativo; // pode ser null -> tratamos mais abaixo
118
            }
119
        } else { // VENDA
120
            // Em venda, ambos devem estar acima da entrada.
121
            // Stop mais apertado = o MENOR dos dois (mais próximo da entrada).
122
            if (stopMenos100 != null && stopAlternativo != null) {
123
                stop = stopMenos100.min(stopAlternativo);
124
            } else if (stopMenos100 != null) {
125
                stop = stopMenos100;
126
            } else {
127
                stop = stopAlternativo;
128
            }
129
        }
130
 
131
        // Se por alguma razão o stop ainda for null, é mais seguro considerar que não há stop definido.
132
        // Neste caso, a operação só encerrará por alvo (ou ficará em ABERTO até o fim do histórico).
133
        boolean temStop = (stop != null);
134
 
135
        boolean entradaAcionada = false;
136
        int idxEntrada = -1;
137
        BigDecimal precoEntradaEfetivo = null;
138
 
139
        // LOOP nos candles após o G3
140
        for (int i = inicio; i < candles.size(); i++) {
141
            Candle c = candles.get(i);
142
            BigDecimal max = c.getMaxima();
143
            BigDecimal min = c.getMinima();
144
 
145
            // ============================
146
            // 1) Antes de entrar na operação
147
            // ============================
148
            if (!entradaAcionada) {
149
 
150
                // 1.1) Regra: se o PRIMEIRO ALVO for atingido ANTES da entrada -> DESCARTAR OPERAÇÃO
151
                if (tipo == TipoOperacao.COMPRA) {
152
                    // Para compra, alvo1 é para cima; se máxima >= alvo1, consideramos tocado
153
                    if (BigDecimalUtils.ehMaiorOuIgualQue(max, alvo1)) {
154
                        return new ResultadoSinalGatilho3(
155
                                sinal,
156
                                Status.DESCARTADO,
157
                                false,
158
                                -1,
159
                                i,
160
                                null,
161
                                alvo1
162
                        );
163
                    }
164
                } else { // VENDA
165
                    // Para venda, alvo1 é para baixo; se mínima <= alvo1, consideramos tocado
166
                    if (BigDecimalUtils.ehMenorOuIgualQue(min, alvo1)) {
167
                        return new ResultadoSinalGatilho3(
168
                                sinal,
169
                                Status.DESCARTADO,
170
                                false,
171
                                -1,
172
                                i,
173
                                null,
174
                                alvo1
175
                        );
176
                    }
177
                }
178
 
179
                // 1.2) Se não descartar, verifica se aciona a entrada
180
                boolean tocaEntrada =
181
                        BigDecimalUtils.ehMenorOuIgualQue(min, precoEntrada) &&
182
                        BigDecimalUtils.ehMaiorOuIgualQue(max, precoEntrada);
183
 
184
                if (tocaEntrada) {
185
                    entradaAcionada = true;
186
                    idxEntrada = i;
187
                    precoEntradaEfetivo = precoEntrada;
188
                }
189
 
190
                // Ainda não entrou -> segue para o próximo candle
191
                continue;
192
            }
193
 
194
            // ============================
195
            // 2) Após a entrada ter sido acionada
196
            // ============================
197
            if (tipo == TipoOperacao.COMPRA) {
198
                // COMPRA: stop é para baixo, alvos para cima
199
 
200
                boolean bateStop = temStop && BigDecimalUtils.ehMenorOuIgualQue(min, stop);
201
                boolean bateAlvo2 = BigDecimalUtils.ehMaiorOuIgualQue(max, alvo2);
202
                boolean bateAlvo1 = BigDecimalUtils.ehMaiorOuIgualQue(max, alvo1);
203
 
204
                // Se, no MESMO candle, tanto stop quanto alvo forem possíveis,
205
                // aqui estamos sendo conservadores: prioriza STOP primeiro.
206
                if (bateStop) {
207
                    return new ResultadoSinalGatilho3(
208
                            sinal,
209
                            Status.STOP,
210
                            false,
211
                            idxEntrada,
212
                            i,
213
                            precoEntradaEfetivo,
214
                            stop
215
                    );
216
                }
217
 
218
                if (bateAlvo2) {
219
                    return new ResultadoSinalGatilho3(
220
                            sinal,
221
                            Status.ALVO2,
222
                            true,
223
                            idxEntrada,
224
                            i,
225
                            precoEntradaEfetivo,
226
                            alvo2
227
                    );
228
                }
229
 
230
                if (bateAlvo1) {
231
                    return new ResultadoSinalGatilho3(
232
                            sinal,
233
                            Status.ALVO1,
234
                            true,
235
                            idxEntrada,
236
                            i,
237
                            precoEntradaEfetivo,
238
                            alvo1
239
                    );
240
                }
241
 
242
            } else { // VENDA
243
 
244
                // VENDA: stop é para cima, alvos para baixo
245
                boolean bateStop = temStop && BigDecimalUtils.ehMaiorOuIgualQue(max, stop);
246
                boolean bateAlvo2 = BigDecimalUtils.ehMenorOuIgualQue(min, alvo2);
247
                boolean bateAlvo1 = BigDecimalUtils.ehMenorOuIgualQue(min, alvo1);
248
 
249
                // Conservador: se stop e alvo possíveis no mesmo candle, prioriza STOP.
250
                if (bateStop) {
251
                    return new ResultadoSinalGatilho3(
252
                            sinal,
253
                            Status.STOP,
254
                            false,
255
                            idxEntrada,
256
                            i,
257
                            precoEntradaEfetivo,
258
                            stop
259
                    );
260
                }
261
 
262
                if (bateAlvo2) {
263
                    return new ResultadoSinalGatilho3(
264
                            sinal,
265
                            Status.ALVO2,
266
                            true,
267
                            idxEntrada,
268
                            i,
269
                            precoEntradaEfetivo,
270
                            alvo2
271
                    );
272
                }
273
 
274
                if (bateAlvo1) {
275
                    return new ResultadoSinalGatilho3(
276
                            sinal,
277
                            Status.ALVO1,
278
                            true,
279
                            idxEntrada,
280
                            i,
281
                            precoEntradaEfetivo,
282
                            alvo1
283
                    );
284
                }
285
            }
286
        }
287
 
288
        // Saiu do loop sem bater alvo/stop
289
        if (!entradaAcionada) {
290
            return new ResultadoSinalGatilho3(
291
                    sinal,
292
                    Status.NAO_ACIONADO,
293
                    false,
294
                    -1,
295
                    -1,
296
                    null,
297
                    null
298
            );
299
        } else {
300
            // Entrada aconteceu, mas nem alvo nem stop foram atingidos até o fim do histórico
301
            return new ResultadoSinalGatilho3(
302
                    sinal,
303
                    Status.ABERTO,
304
                    false,
305
                    idxEntrada,
306
                    candles.size() - 1,
307
                    precoEntradaEfetivo,
308
                    null
309
            );
310
        }
311
    }
312
}