├── .gitignore ├── src ├── main │ ├── java │ │ └── org │ │ │ └── ambulando │ │ │ └── strategy │ │ │ └── ta4j │ │ │ └── parser │ │ │ ├── ErrorListener.java │ │ │ ├── ParserException.java │ │ │ ├── ParserError.java │ │ │ ├── TA4JStrategyParser.java │ │ │ ├── TA4JStrategyListenerImpl.java │ │ │ └── TA4JIndicatorsStrategyListener.java │ └── antlr4 │ │ └── org │ │ └── ambulando │ │ └── strategy │ │ └── ta4j │ │ └── parser │ │ └── Strategy.g4 └── test │ ├── resources │ ├── indicators.properties │ └── strategies.properties │ └── java │ └── org │ └── ambulando │ └── strategy │ └── ta4j │ └── parser │ ├── TA4JIndicatorsStrategyListenerTest.java │ └── TA4JStrategyParserTest.java ├── LICENSE ├── pom.xml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .idea 3 | *.iml 4 | -------------------------------------------------------------------------------- /src/main/java/org/ambulando/strategy/ta4j/parser/ErrorListener.java: -------------------------------------------------------------------------------- 1 | package org.ambulando.strategy.ta4j.parser; 2 | 3 | import org.antlr.v4.runtime.BaseErrorListener; 4 | import org.antlr.v4.runtime.RecognitionException; 5 | import org.antlr.v4.runtime.Recognizer; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class ErrorListener extends BaseErrorListener { 11 | 12 | private List errors = new ArrayList<>(); 13 | 14 | @Override 15 | public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int position, String msg, RecognitionException e) { 16 | errors.add(new ParserError(line, position, msg)); 17 | } 18 | 19 | 20 | public List getErrors() 21 | { 22 | return errors; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/ambulando/strategy/ta4j/parser/ParserException.java: -------------------------------------------------------------------------------- 1 | package org.ambulando.strategy.ta4j.parser; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | 7 | public class ParserException extends Exception { 8 | 9 | private List errors; 10 | 11 | public ParserException(String message, List errors) { 12 | super(message); 13 | this.errors = errors; 14 | } 15 | 16 | public ParserException(Throwable e) { 17 | super("Parse Errors", e); 18 | this.errors = new ArrayList<>(); 19 | this.errors.add(new ParserError(e.getMessage())); 20 | } 21 | 22 | public ParserException(List errors) { 23 | this("Parse Errors", errors); 24 | } 25 | 26 | public List getErrors() 27 | { 28 | return errors; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/ambulando/strategy/ta4j/parser/ParserError.java: -------------------------------------------------------------------------------- 1 | package org.ambulando.strategy.ta4j.parser; 2 | 3 | public class ParserError { 4 | 5 | int line; 6 | int charPositionInLine; 7 | String msg; 8 | 9 | 10 | public ParserError(String msg) 11 | { 12 | this(0, 0, msg); 13 | } 14 | public ParserError(int line, int charPositionInLine, String msg) 15 | { 16 | this.line = line; 17 | this.charPositionInLine = charPositionInLine; 18 | this.msg = msg; 19 | } 20 | 21 | 22 | public int getLine() 23 | { 24 | return line; 25 | } 26 | 27 | 28 | public int getCharPositionInLine() 29 | { 30 | return charPositionInLine; 31 | } 32 | 33 | 34 | public String getMsg() 35 | { 36 | return msg; 37 | } 38 | 39 | 40 | @Override public String toString() 41 | { 42 | StringBuilder builder = new StringBuilder("ParseError["); 43 | if (line>0) builder.append(line).append(","); 44 | if (charPositionInLine>0) builder.append(charPositionInLine).append(","); 45 | if (msg!=null) builder.append(msg); 46 | builder.append("]"); 47 | return builder.toString(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Massimiliano Gerardi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/test/resources/indicators.properties: -------------------------------------------------------------------------------- 1 | SMA = SMA[15] >= 10 [GO_LONG] ; SMA[15] >= 10 [GO_SHORT] 2 | EMA = EMA[15] >= 10 [GO_LONG] ; EMA[15] >= 10 [GO_SHORT] 3 | DEMA = DEMA[15] >= 10 [GO_LONG] ; DEMA[15] >= 10 [GO_SHORT] 4 | TEMA = TEMA[15] >= 10 [GO_LONG] ; TEMA[15] >= 10 [GO_SHORT] 5 | WILLIAMS = WILLIAMS[15] >= 10 [GO_LONG] ; WILLIAMS[15] >= 10 [GO_SHORT] 6 | WMA = WMA[15] >= 10 [GO_LONG] ; WMA[15] >= 10 [GO_SHORT] 7 | CCI = CCI[15] >= 10 [GO_LONG] ; CCI[15] >= 10 [GO_SHORT] 8 | RSI = RSI[15] >= 10 [GO_LONG] ; RSI[15] >= 10 [GO_SHORT] 9 | KAMA = KAMA[15, 10, 5] >= 10 [GO_LONG] ; KAMA[15, 10, 5] >= 10 [GO_SHORT] 10 | VWAP = VWAP[15] >= 10 [GO_LONG] ; VWAP[15] >= 10 [GO_SHORT] 11 | CMF = CMF[15] >= 10 [GO_LONG] ; CMF[15] >= 10 [GO_SHORT] 12 | ROCV = ROCV[15] >= 10[GO_LONG] ; ROCV[15] >= 10 [GO_SHORT] 13 | MACD = MACD[10,15] >= 10 [GO_LONG] ; MACD[10,15] >= 10 [GO_SHORT] 14 | COI = COI[15,10] >= 10 [GO_LONG] ; COI[15,10] >= 10 [GO_SHORT] 15 | MVWAP = MVWAP[15,10] >= 10 [GO_LONG] ; MVWAP[15,10] >= 10 [GO_SHORT] 16 | CPRICE = CPRICE >= 10[GO_LONG] ; CPRICE >= 10 [GO_SHORT] 17 | VOLUME = VOLUME >= 10[GO_LONG] ; VOLUME >= 10 [GO_SHORT] 18 | ADI = ADI >= 10 [GO_LONG] ; ADI >= 10 [GO_SHORT] 19 | III = III >= 10[GO_LONG] ; III >= 10 [GO_SHORT] 20 | NVI = NVI >= 10[GO_LONG] ; NVI >= 10 [GO_SHORT] 21 | OBV = OBV >= 10[GO_LONG] ; OBV >= 10 [GO_SHORT] 22 | PVI = PVI >= 10[GO_LONG] ; PVI >= 10 [GO_SHORT] 23 | 24 | -------------------------------------------------------------------------------- /src/test/resources/strategies.properties: -------------------------------------------------------------------------------- 1 | STRATEGY_1 = ((EMA[30] >= WMA[87]) OR (CCI[5] <= SMA[87])) [GO_LONG] ; (CCI[725] <= 2.6) [GO_SHORT] 2 | STRATEGY_2 = (EMA[30] >= WMA[87]) [GO_LONG] ; ((CCI[5] <= 2.6) OR (CCI[725] >= SMA[87])) [GO_SHORT] 3 | STRATEGY_3 = ((UI[30] >= WMA[87]) OR (CCI[5] <= SMA[87])) [GO_LONG] ; (CCI[5] <= 2.6) [GO_SHORT] 4 | STRATEGY_4 = (((SMA[30] >= EMA[5]) AND (CCI[38] <= 8)) OR ((((((CCI[30] >= 0.5) OR ((CPRICE <= 0.1) AND (DEMA[17] <= 4))) AND ((WILLIAMS[85] >= 0.9) OR (CCI[30] >= 0.5))) OR ((CPRICE <= 0.1) AND (DEMA[17] <= 4))) OR ((WILLIAMS[85] >= 0.9) OR (CCI[30] >= 0.5))) OR ((CPRICE <= 0.1) AND (DEMA[17] <= 4)))) [GO_LONG] ; ((WILLIAMS[85] >= 0.9) OR (CCI[30] >= 0.5)) [GO_SHORT]; 5 | STRATEGY_5 = (ADX[30] >= WMA[87]) [GO_LONG] ; (CCI[5] <= 2.6) [GO_SHORT] 6 | STRATEGY_6 = (HMA[30] >= WMA[87]) [GO_LONG] ; (CCI[5] <= 2.6) [GO_SHORT] 7 | STRATEGY_7 = (ZLEMA[30] >= WMA[87]) [GO_LONG] ; (CCI[5] <= 2.6) [GO_SHORT] 8 | STRATEGY_8 = (KAMA[10, 20, 30] >= WMA[87]) [GO_LONG] ; (CCI[5] <= 2.6) [GO_SHORT] 9 | STRATEGY_9 = (MACD[10, 20] >= WMA[87]) [GO_LONG] ; (CCI[5] <= 2.6) [GO_SHORT] 10 | STRATEGY_10 = (PPO[10, 20] >= WMA[87]) [GO_LONG] ; (CCI[5] <= 2.6) [GO_SHORT] 11 | STRATEGY_11 = (EMA[30] >= 2) [GO_LONG] ; (((WILLIAMS[5] <= ADX[5]) AND ((PPO[5,30] <= CPRICE) OR ((WILLIAMS[30] >= CPRICE) AND (WILLIAMS[61] <= 0.9)))) AND (SMA[5] <= 0.2)) [GO_SHORT] 12 | STRATEGY_12 = ((VOLUME <= 7) OR (((SMA[5] >= SMA[5]) OR (VOLUME >= WMA[30])) AND (TEMA[30] >= 5))) [GO_LONG] ; (WMA[30] <= WILLIAMS[5964]) [GO_SHORT] 13 | STRATEGY_13 = (KAMA[10, 20, 30] >= WMA[87]) [GO_LONG] ; (CCI[5] >= 2.6) [GO_SHORT] 14 | -------------------------------------------------------------------------------- /src/test/java/org/ambulando/strategy/ta4j/parser/TA4JIndicatorsStrategyListenerTest.java: -------------------------------------------------------------------------------- 1 | package org.ambulando.strategy.ta4j.parser; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.ta4j.core.BaseBarSeries; 7 | import org.ta4j.core.Strategy; 8 | 9 | import java.io.File; 10 | import java.io.FileInputStream; 11 | import java.util.ArrayList; 12 | import java.util.Enumeration; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | import java.util.Properties; 16 | 17 | public class TA4JIndicatorsStrategyListenerTest { 18 | 19 | private Properties indicators = new Properties(); 20 | 21 | private TA4JStrategyParser parser = new TA4JStrategyParser(); 22 | 23 | @Before 24 | public void before() throws Exception { 25 | indicators.load(new FileInputStream(new File("src/test/resources/indicators.properties"))); 26 | } 27 | 28 | @Test 29 | public void testAll() throws Exception { 30 | Enumeration keys = indicators.propertyNames(); 31 | Map errors = new HashMap<>(); 32 | 33 | while (keys.hasMoreElements()) { 34 | String key = (String) keys.nextElement(); 35 | testIndicator(key, errors); 36 | } 37 | Assert.assertTrue(errors.toString(), errors.isEmpty()); 38 | } 39 | 40 | private void testIndicator(String key, Map errors) throws Exception { 41 | String input = indicators.getProperty(key); 42 | try { 43 | Strategy strategy = parser.parse(input, new BaseBarSeries(new ArrayList<>())); 44 | if (strategy == null) { 45 | errors.put(key, null); 46 | } 47 | } catch (ParserException e) { 48 | errors.put(key, e.getErrors().toString()); 49 | } 50 | } 51 | 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/ambulando/strategy/ta4j/parser/TA4JStrategyParser.java: -------------------------------------------------------------------------------- 1 | package org.ambulando.strategy.ta4j.parser; 2 | 3 | import org.antlr.v4.runtime.CharStreams; 4 | import org.antlr.v4.runtime.CodePointCharStream; 5 | import org.antlr.v4.runtime.CommonTokenStream; 6 | import org.antlr.v4.runtime.tree.ParseTreeWalker; 7 | import org.apache.commons.collections.CollectionUtils; 8 | import org.ta4j.core.BarSeries; 9 | import org.ta4j.core.Strategy; 10 | 11 | import java.util.List; 12 | 13 | public class TA4JStrategyParser 14 | { 15 | 16 | public Strategy parse(String strategy, BarSeries timeSeries) throws ParserException 17 | { 18 | ErrorListener errorListener = new ErrorListener(); 19 | 20 | CodePointCharStream stream = CharStreams.fromString(strategy); 21 | StrategyLexer lexer = new StrategyLexer(stream); 22 | lexer.removeErrorListeners(); 23 | lexer.addErrorListener(errorListener); 24 | 25 | CommonTokenStream tokenStream = new CommonTokenStream(lexer); 26 | 27 | StrategyParser taParser = new StrategyParser(tokenStream); 28 | 29 | taParser.removeErrorListeners(); 30 | taParser.addErrorListener(errorListener); 31 | 32 | try 33 | { 34 | StrategyParser.StrategyContext context = taParser.strategy(); 35 | if (!errorListener.getErrors().isEmpty()) 36 | { 37 | throw new ParserException(errorListener.getErrors()); 38 | } 39 | 40 | TA4JStrategyListenerImpl listener = new TA4JIndicatorsStrategyListener(timeSeries); 41 | 42 | ParseTreeWalker walker = new ParseTreeWalker(); 43 | 44 | walker.walk(listener, context); 45 | List errors = listener.getErrors(); 46 | if (CollectionUtils.isNotEmpty(errors)) 47 | { 48 | throw new ParserException(errors); 49 | } 50 | return listener.getStrategy(); 51 | } catch (ParserException e) { 52 | throw e; 53 | } catch (Throwable e) { 54 | throw new ParserException(e); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.ambulando 8 | ta4j-strategy-parser 9 | 1.0.0-SNAPSHOT 10 | 11 | 12 | 1.8 13 | 1.8 14 | 15 | 16 | 17 | 18 | org.ta4j 19 | ta4j-core 20 | 0.13 21 | 22 | 23 | org.antlr 24 | antlr4-runtime 25 | 4.7 26 | 27 | 28 | commons-collections 29 | commons-collections 30 | 3.2.2 31 | 32 | 33 | junit 34 | junit 35 | 4.13 36 | test 37 | 38 | 39 | org.slf4j 40 | slf4j-simple 41 | 1.7.30 42 | 43 | 44 | org.apache.commons 45 | commons-lang3 46 | 3.9 47 | test 48 | 49 | 50 | 51 | 52 | 53 | 54 | org.antlr 55 | antlr4-maven-plugin 56 | 4.7 57 | 58 | 59 | 60 | antlr4 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/main/antlr4/org/ambulando/strategy/ta4j/parser/Strategy.g4: -------------------------------------------------------------------------------- 1 | grammar Strategy; 2 | 3 | strategy 4 | : go_long SEMICOLON go_short 5 | ; 6 | 7 | go_long 8 | : signal GO_LONG 9 | ; 10 | 11 | go_short 12 | : signal GO_SHORT 13 | ; 14 | 15 | signal 16 | : LPAREN? simpleExpression RPAREN? 17 | | LPAREN? constantExpression RPAREN? 18 | | LPAREN logicExpression RPAREN 19 | ; 20 | 21 | logicExpression 22 | : signal logicOp signal 23 | ; 24 | 25 | constantExpression 26 | : indicator op value 27 | ; 28 | 29 | simpleExpression 30 | : indicator op indicator 31 | ; 32 | 33 | indicator 34 | : sma 35 | | ema 36 | | dema 37 | | tema 38 | | williams 39 | | wma 40 | | cci 41 | | rsi 42 | | cprice 43 | | volume 44 | | ui 45 | | adx 46 | | adi 47 | | hma 48 | | zlema 49 | | kama 50 | | macd 51 | | vwap 52 | | mvwap 53 | | cmf 54 | | coi 55 | | iii 56 | | nvi 57 | | obv 58 | | pvi 59 | | rocv 60 | | ppo 61 | ; 62 | 63 | sma 64 | : 'SMA' LBRACKET timeframe RBRACKET 65 | ; 66 | 67 | ema 68 | : 'EMA' LBRACKET timeframe RBRACKET 69 | ; 70 | 71 | dema 72 | : 'DEMA' LBRACKET timeframe RBRACKET 73 | ; 74 | 75 | tema 76 | : 'TEMA' LBRACKET timeframe RBRACKET 77 | ; 78 | 79 | williams 80 | : 'WILLIAMS' LBRACKET timeframe RBRACKET 81 | ; 82 | 83 | wma 84 | : 'WMA' LBRACKET timeframe RBRACKET 85 | ; 86 | 87 | cci 88 | : 'CCI' LBRACKET timeframe RBRACKET 89 | ; 90 | 91 | rsi 92 | : 'RSI' LBRACKET timeframe RBRACKET 93 | ; 94 | 95 | kama 96 | : 'KAMA' LBRACKET timeframe ',' timeframe ',' timeframe RBRACKET 97 | ; 98 | 99 | macd 100 | : 'MACD' LBRACKET timeframe ',' timeframe RBRACKET 101 | ; 102 | 103 | vwap 104 | : 'VWAP' LBRACKET timeframe RBRACKET 105 | ; 106 | 107 | cmf 108 | : 'CMF' LBRACKET timeframe RBRACKET 109 | ; 110 | 111 | coi 112 | : 'COI' LBRACKET timeframe ',' timeframe RBRACKET 113 | ; 114 | 115 | mvwap 116 | : 'MVWAP' LBRACKET timeframe ',' timeframe RBRACKET 117 | ; 118 | 119 | rocv 120 | : 'ROCV' LBRACKET timeframe RBRACKET 121 | ; 122 | 123 | ui 124 | : 'UI' LBRACKET timeframe RBRACKET 125 | ; 126 | 127 | adx 128 | : 'ADX' LBRACKET timeframe RBRACKET 129 | ; 130 | 131 | hma 132 | : 'HMA' LBRACKET timeframe RBRACKET 133 | ; 134 | 135 | zlema 136 | : 'ZLEMA' LBRACKET timeframe RBRACKET 137 | ; 138 | 139 | ppo 140 | : 'PPO' LBRACKET timeframe ',' timeframe RBRACKET 141 | ; 142 | 143 | cprice 144 | : 'CPRICE' 145 | ; 146 | 147 | volume 148 | : 'VOLUME' 149 | ; 150 | 151 | adi 152 | : 'ADI' 153 | ; 154 | 155 | nvi 156 | : 'NVI' 157 | ; 158 | 159 | obv 160 | : 'OBV' 161 | ; 162 | 163 | pvi 164 | : 'PVI' 165 | ; 166 | 167 | iii 168 | : 'III' 169 | ; 170 | 171 | value 172 | : INTEGER 173 | | FLOAT 174 | ; 175 | 176 | timeframe 177 | : INTEGER 178 | ; 179 | 180 | op 181 | : GTE 182 | | LTE 183 | ; 184 | 185 | 186 | logicOp 187 | : 'OR' 188 | | 'AND' 189 | ; 190 | 191 | INTEGER : DIGIT+ ; 192 | 193 | FLOAT : DIGIT+ ('.' DIGIT+)? ; 194 | 195 | SEMICOLON 196 | : ';' 197 | ; 198 | 199 | LBRACKET 200 | : '[' 201 | ; 202 | 203 | RBRACKET 204 | : ']' 205 | ; 206 | 207 | 208 | LPAREN 209 | : '(' 210 | ; 211 | 212 | RPAREN 213 | : ')' 214 | ; 215 | 216 | 217 | GO_LONG 218 | : '[GO_LONG]' 219 | ; 220 | 221 | GO_SHORT 222 | : '[GO_SHORT]' 223 | ; 224 | 225 | GTE 226 | : '>=' 227 | ; 228 | 229 | 230 | LTE 231 | : '<=' 232 | ; 233 | 234 | 235 | WS 236 | : [ \r\n\t] + -> channel (HIDDEN) 237 | ; 238 | 239 | fragment DIGIT : [0-9] ; 240 | -------------------------------------------------------------------------------- /src/main/java/org/ambulando/strategy/ta4j/parser/TA4JStrategyListenerImpl.java: -------------------------------------------------------------------------------- 1 | package org.ambulando.strategy.ta4j.parser; 2 | 3 | import org.antlr.v4.runtime.tree.ErrorNode; 4 | import org.ta4j.core.BaseStrategy; 5 | import org.ta4j.core.Indicator; 6 | import org.ta4j.core.Rule; 7 | import org.ta4j.core.Strategy; 8 | import org.ta4j.core.num.Num; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Deque; 12 | import java.util.LinkedList; 13 | import java.util.List; 14 | 15 | /** 16 | * This class provides an implementation of {@link StrategyListener}, 17 | */ 18 | public abstract class TA4JStrategyListenerImpl extends StrategyBaseListener 19 | { 20 | 21 | protected Deque ruleStack = new LinkedList<>(); 22 | protected Deque> indicatorStack = new LinkedList<>(); 23 | protected Deque timeFrameStack = new LinkedList<>(); 24 | protected Deque operatorStack = new LinkedList<>(); 25 | private Strategy strategy; 26 | private List errors = new ArrayList<>(); 27 | 28 | 29 | protected void addError(ParserError error) 30 | { 31 | errors.add(error); 32 | } 33 | 34 | 35 | public Strategy getStrategy() 36 | { 37 | return strategy; 38 | } 39 | 40 | 41 | public List getErrors() 42 | { 43 | return errors; 44 | } 45 | 46 | 47 | /** 48 | * {@inheritDoc} 49 | *

50 | *

The default implementation does nothing.

51 | */ 52 | @Override 53 | public void exitStrategy(StrategyParser.StrategyContext ctx) 54 | { 55 | try 56 | { 57 | Rule exitRule = ruleStack.pop(); 58 | Rule enterRule = ruleStack.pop(); 59 | strategy = new BaseStrategy(enterRule, exitRule); 60 | } 61 | catch (Exception e) 62 | { 63 | addError(new ParserError("Rules are not defined correctly".concat(e.getMessage() != null ? e.getMessage() : ""))); 64 | } 65 | } 66 | 67 | 68 | @Override 69 | public void exitLogicExpression(StrategyParser.LogicExpressionContext ctx) 70 | { 71 | Rule simpleRule = null; 72 | Rule right = ruleStack.pop(); 73 | Rule left = ruleStack.pop(); 74 | Operator operator = operatorStack.pop(); 75 | switch (operator) 76 | { 77 | case AND: 78 | simpleRule = left.and(right); 79 | break; 80 | case OR: 81 | simpleRule = left.or(right); 82 | break; 83 | } 84 | ruleStack.push(simpleRule); 85 | } 86 | 87 | 88 | @Override 89 | public void exitOp(StrategyParser.OpContext ctx) 90 | { 91 | operatorStack.push(Operator.get(ctx.getText())); 92 | } 93 | 94 | 95 | @Override 96 | public void exitLogicOp(StrategyParser.LogicOpContext ctx) 97 | { 98 | operatorStack.push(Operator.get(ctx.getText())); 99 | } 100 | 101 | 102 | @Override 103 | public void exitTimeframe(StrategyParser.TimeframeContext ctx) 104 | { 105 | Integer timeFrame = Integer.parseInt(ctx.getText()); 106 | timeFrameStack.push(new Integer(timeFrame % 700)); 107 | } 108 | 109 | 110 | @Override 111 | public void visitErrorNode(ErrorNode node) 112 | { 113 | errors.add(new ParserError(node.getText())); 114 | } 115 | 116 | 117 | protected enum Operator 118 | { 119 | GTE(">="), LTE("<="), AND("AND"), OR("OR"), EQ("="); 120 | private String operator; 121 | 122 | 123 | Operator(String op) 124 | { 125 | this.operator = op; 126 | } 127 | 128 | 129 | public static Operator get(String op) 130 | { 131 | switch (op) 132 | { 133 | case ">=": 134 | return GTE; 135 | case "<=": 136 | return LTE; 137 | case "OR": 138 | return OR; 139 | case "AND": 140 | return AND; 141 | default: 142 | throw new IllegalArgumentException(op); 143 | } 144 | } 145 | } 146 | 147 | } 148 | 149 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ta4j Strategy Parser 2 | 3 | **ta4j Strategy Parser** is a library to parse Technical Analysis strategies into [ta4j](https://github.com/ta4j/ta4j) using [antlr4](https://www.antlr.org/). 4 | 5 | ## Strategies 6 | 7 | Strategies are defined as combination of `GO_LONG` and `GO_SHORT` combined signals. 8 | 9 | A signal could be a combination of indicator expressions and logical expressions. 10 | 11 | ``` 12 | (((DEMA[5] >= SMA[45]) OR ((TEMA[30] <= 1.3) OR ((DEMA[74] >= 0.3637) OR (CCI[30] <= 0.5)))) AND ((TEMA[30] <= 1.3) OR ((DEMA[74] >= 0.3637) OR (CCI[30] <= 0.5)))) [GO_LONG] 13 | ; 14 | ((TEMA[30] <= 1.3) OR ((DEMA[74] >= 0.3637) OR (CCII[30] <= 0.5))) [GO_SHORT]", 15 | ``` 16 | 17 | Logical expressions must be inserted between parentheses 18 | 19 | * correct 20 | ``` 21 | ((DEMA[74] >= 0.3637) OR (CCII[30] <= 0.5)) 22 | ``` 23 | 24 | * incorrect 25 | ``` 26 | (DEMA[74] >= 0.3637) OR (CCII[30] <= 0.5) 27 | ``` 28 | 29 | 30 | ## Use 31 | 32 | ```java 33 | 34 | import org.ambulando.strategy.ta4j.parser.*; 35 | 36 | ... 37 | String input = "(RSI[5] <= 7.5) [GO_LONG] ; (CCII[5] >= 7.9) [GO_SHORT]"; 38 | try 39 | { 40 | BarSeries barSeries = getBarSeries(); 41 | Strategy strategy = parser.parse(input, barSeries); 42 | } 43 | catch (ParserException e) 44 | { 45 | List errors = e.getErrors(); 46 | //handle errors 47 | } 48 | ... 49 | ``` 50 | 51 | ## Operators 52 | 53 | * Logic operators between signals 54 | * `OR` 55 | * `AND` 56 | * mathematical operators between indicators: 57 | * `>=` 58 | * `<=` 59 | 60 | 61 | ## Indicators 62 | 63 | Use of the indicator can be found in [ta4j docs](https://oss.sonatype.org/service/local/repositories/releases/archive/org/ta4j/ta4j-core/0.13/ta4j-core-0.13-javadoc.jar/!/index.html) 64 | At now, the following indicators are recognized by the parser: 65 | 66 | |INDICATOR |USE| 67 | |----------|---| 68 | | [Close Price]()|`CPRICE`| 69 | | [Volume]()|`VOLUME`| 70 | | [Simple Moving Average - SMA](https://www.investopedia.com/terms/s/sma.asp)|`SMA[timeframe:int]`| 71 | | [Double Exponential Moving Average - DEMA](https://www.investopedia.com/terms/d/double-exponential-moving-average.asp)|`DEMA[timeframe:int]`| 72 | | [Exponential Moving Average - EMA](https://www.investopedia.com/terms/e/ema.asp)|`EMA[timeframe:int]`| 73 | | [Triple Exponential Moving Average – TEMA](https://www.investopedia.com/terms/t/triple-exponential-moving-average.asp)|`TEMA[timeframe:int]`| 74 | | [Weighted Moving Average WMA](https://www.investopedia.com/articles/technical/060401.asp)|`WMA[timeframe:int]`| 75 | | [William's R - WILLIAMS](https://www.investopedia.com/terms/w/williamsr.asp)|`WILLIAMS[timeframe:int]`| 76 | | [Commodity Channel Index - CCI](https://www.investopedia.com/terms/c/commoditychannelindex.asp)|`CCI[timeframe:int]`| 77 | | [Relative Strength Index - RSI](https://www.investopedia.com/terms/r/rsi.asp)|`RSI[timeframe:int]`| 78 | | [Ulcer Index - UI](https://www.investopedia.com/terms/u/ulcerindex.asp)|`UI[timeframe:int]`| 79 | | [Average Directional Index - ADX](https://www.investopedia.com/terms/a/adx.asp)|`ADX[timeframe:int]`| 80 | | [Hull Moving Average HMA](http://alanhull.com/hull-moving-average)|`HMA[timeframe:int]`| 81 | | [Zero-Lag Exponential Moving Average - ZLEMA](http://www.fmlabs.com/reference/default.htm?url=ZeroLagExpMA.htm)|`ZLEMA[timeframe:int]`| 82 | | [Kaufman's Adaptive Moving Average - KAMA](http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:kaufman_s_adaptive_moving_average)|`KAMA[timeframe:int,timeframe:int,timeframe:int]`| 83 | | [Moving Average Convergence Divergence - MACD](http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:moving_average_convergence_divergence_macd)|`MACD[timeframe:int,timeframe:int]`| 84 | | [Percentage price oscillator - PPO](https://www.investopedia.com/terms/p/ppo.asp)|`PPO[timeframe:int,timeframe:int]`| 85 | | [Volume-Weighted Average Price - VWAP](http://www.investopedia.com/articles/trading/11/trading-with-vwap-mvwap.asp)|`VWAP[timeframe:int]`| 86 | | [Chaikin Oscillator - COI](http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:chaikin_oscillator)|`COI[timeframe:int,timeframe:int]`| 87 | | [Accumulation Distribution - ADI](https://www.investopedia.com/terms/a/accumulationdistribution.asp)|`ADI`| 88 | | [Intraday Intensity Index - III](https://www.investopedia.com/terms/i/intradayintensityindex.asp)|`III`| 89 | | [Chaikin Money Flow - CMF](http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:chaikin_money_flow_cmf)|`CMF[timeframe:int]`| 90 | | [Moving Volume Weighted Average Price - MVWAP](http://www.investopedia.com/articles/trading/11/trading-with-vwap-mvwap.asp)|`MVWAP[timeframe:int,timeframe:int]`| 91 | | [Negative Volume Index - NVI](http://www.investopedia.com/terms/n/nvi.asp)|`NVI`| 92 | | [Rate Of Change of Volume - ROCV](https://www.investopedia.com/articles/technical/02/091002.asp)|`ROCV[timeframe:int]`| 93 | | [Positive Volume Index - PVI](http://www.investopedia.com/terms/p/pvi.asp)|`PVI`| 94 | | [On Balance Volume - OBV](https://www.investopedia.com/terms/o/onbalancevolume.asp)|`OBV`| 95 | 96 | 97 | -------------------------------------------------------------------------------- /src/test/java/org/ambulando/strategy/ta4j/parser/TA4JStrategyParserTest.java: -------------------------------------------------------------------------------- 1 | package org.ambulando.strategy.ta4j.parser; 2 | 3 | import org.apache.commons.lang3.reflect.FieldUtils; 4 | import org.junit.Assert; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | import org.ta4j.core.BarSeries; 8 | import org.ta4j.core.BaseBarSeries; 9 | import org.ta4j.core.Indicator; 10 | import org.ta4j.core.Rule; 11 | import org.ta4j.core.Strategy; 12 | import org.ta4j.core.indicators.CCIIndicator; 13 | import org.ta4j.core.indicators.EMAIndicator; 14 | import org.ta4j.core.indicators.SMAIndicator; 15 | import org.ta4j.core.indicators.WMAIndicator; 16 | import org.ta4j.core.indicators.helpers.ConstantIndicator; 17 | import org.ta4j.core.indicators.helpers.CrossIndicator; 18 | import org.ta4j.core.num.Num; 19 | import org.ta4j.core.trading.rules.OrRule; 20 | import org.ta4j.core.trading.rules.OverIndicatorRule; 21 | import org.ta4j.core.trading.rules.UnderIndicatorRule; 22 | 23 | import java.io.File; 24 | import java.io.FileInputStream; 25 | import java.util.Enumeration; 26 | import java.util.List; 27 | import java.util.Properties; 28 | 29 | import static org.junit.Assert.fail; 30 | 31 | public class TA4JStrategyParserTest { 32 | 33 | private BarSeries barSeries; 34 | 35 | private final String WRONG_STRATEGY = "((EMA[30] >= WMA[87]) OR (CII[5] <= SA[87])) [GO_LONG] ; (CCI[5] <= 2.6) [GO_SHORT]"; 36 | 37 | private final String WRONG_STRATEGY_2 = "(MACD[15,10] >= 10) [GO_LONG] ; (CCI[5] <= 2.6) [GO_SHORT]"; 38 | 39 | private final String WRONG_STRATEGY_3 = "(KAMA[10, 20, 30] = WMA[87]) [GO_LONG] ; (CCI[5] = 2.6) [GO_SHORT]"; 40 | 41 | private TA4JStrategyParser parser = new TA4JStrategyParser(); 42 | 43 | Properties strategies = new Properties(); 44 | 45 | @Before 46 | public void before() throws Exception { 47 | strategies.load(new FileInputStream(new File("src/test/resources/strategies.properties"))); 48 | barSeries = new BaseBarSeries("test"); 49 | } 50 | 51 | @Test 52 | public void testAllStrategies() throws Exception { 53 | Enumeration keys = strategies.propertyNames(); 54 | while (keys.hasMoreElements()) { 55 | String key = (String) keys.nextElement(); 56 | testStrategy(key); 57 | } 58 | } 59 | 60 | private Strategy testStrategy(String key) { 61 | String input = strategies.getProperty(key); 62 | Strategy strategy = null; 63 | try 64 | { 65 | strategy = parser.parse(input, barSeries); 66 | } 67 | catch (ParserException e) 68 | { 69 | fail(e.getErrors().toString()); 70 | } 71 | Assert.assertNotNull(key, strategy); 72 | return strategy; 73 | } 74 | 75 | @Test 76 | public void testParseS11() throws Exception { 77 | testStrategy("STRATEGY_11"); 78 | } 79 | 80 | @Test 81 | public void testParseS1() throws Exception { 82 | Strategy strategy = testStrategy("STRATEGY_1"); 83 | Rule entryRule = strategy.getEntryRule(); 84 | Assert.assertTrue(entryRule instanceof OrRule); 85 | Rule rule1 = (Rule) FieldUtils.readDeclaredField(entryRule, "rule1", true); 86 | checkRule(rule1, OverIndicatorRule.class, "first", EMAIndicator.class, "second", WMAIndicator.class); 87 | Rule rule2 = (Rule) FieldUtils.readDeclaredField(entryRule, "rule2", true); 88 | checkRule(rule2, UnderIndicatorRule.class, "first", CCIIndicator.class, "second", SMAIndicator.class); 89 | Rule exitRule = strategy.getExitRule(); 90 | Indicator crossIndicator = (Indicator) FieldUtils.readDeclaredField(exitRule, "cross", true); 91 | checkRule(crossIndicator, CrossIndicator.class, "up", CCIIndicator.class, "low", ConstantIndicator.class); 92 | } 93 | 94 | private void checkRule(Object rule, Class ruleClass, String firstArgumentName, Class firstArgumentClass, String secondArgumentName, Class secondArgumentClass) throws Exception { 95 | Assert.assertTrue(rule.getClass().getSimpleName(), ruleClass.isAssignableFrom(rule.getClass())); 96 | Indicator first = (Indicator) FieldUtils.readDeclaredField(rule, firstArgumentName, true); 97 | Indicator second = (Indicator) FieldUtils.readDeclaredField(rule, secondArgumentName, true); 98 | Assert.assertTrue(first.getClass().getSimpleName(), firstArgumentClass.isAssignableFrom(first.getClass())); 99 | Assert.assertTrue(second.getClass().getSimpleName(), secondArgumentClass.isAssignableFrom(second.getClass())); 100 | 101 | } 102 | 103 | @Test 104 | public void testParseS2() throws Exception { 105 | Strategy strategy = testStrategy("STRATEGY_2"); 106 | Rule entryRule = strategy.getEntryRule(); 107 | checkRule(entryRule, OverIndicatorRule.class, "first", EMAIndicator.class, "second", WMAIndicator.class); 108 | Rule exitRule = strategy.getExitRule(); 109 | Assert.assertTrue(exitRule instanceof OrRule); 110 | Rule rule1 = (Rule) FieldUtils.readDeclaredField(exitRule, "rule1", true); 111 | Indicator crossIndicator = (Indicator) FieldUtils.readDeclaredField(rule1, "cross", true); 112 | checkRule(crossIndicator, CrossIndicator.class, "up", CCIIndicator.class, "low", ConstantIndicator.class); 113 | Rule rule2 = (Rule) FieldUtils.readDeclaredField(exitRule, "rule2", true); 114 | checkRule(rule2, OverIndicatorRule.class, "first", CCIIndicator.class, "second", SMAIndicator.class); 115 | } 116 | 117 | @Test 118 | public void testParseWrong() { 119 | try { 120 | parser.parse(WRONG_STRATEGY, barSeries); 121 | } catch (ParserException e) { 122 | Assert.assertNotNull(e); 123 | List errors = e.getErrors(); 124 | Assert.assertEquals(5, errors.size()); 125 | Assert.assertTrue(errors.toString(), errors.toString().contains("token recognition error at: 'CI'")); 126 | Assert.assertTrue(errors.toString(), errors.toString().contains("token recognition error at: 'I['")); 127 | Assert.assertTrue(errors.toString(), errors.toString().contains("token recognition error at: 'SA'")); 128 | Assert.assertTrue(errors.toString(), errors.toString().contains("no viable alternative at input '(5'")); 129 | Assert.assertTrue(errors.toString(), errors.toString().contains("extraneous input ')' expecting '[GO_LONG]'")); 130 | return; 131 | } 132 | fail(); 133 | } 134 | 135 | @Test 136 | public void testParseWrong2() { 137 | try { 138 | parser.parse(WRONG_STRATEGY_2, barSeries); 139 | } catch (ParserException e) { 140 | Assert.assertNotNull(e); 141 | List errors = e.getErrors(); 142 | Assert.assertEquals(1, errors.size()); 143 | Assert.assertEquals("Long term period count must be greater than short term period count", errors.get(0).getMsg()); 144 | return; 145 | } 146 | fail(); 147 | } 148 | 149 | @Test 150 | public void testParseWrong3() { 151 | try { 152 | parser.parse(WRONG_STRATEGY_3, barSeries); 153 | } catch (ParserException e) { 154 | Assert.assertNotNull(e); 155 | List errors = e.getErrors(); 156 | Assert.assertEquals(4, errors.size()); 157 | Assert.assertTrue(errors.toString(), errors.toString().contains("token recognition error at: '='")); 158 | Assert.assertTrue(errors.toString(), errors.toString().contains("no viable alternative at input '(KAMA[10, 20, 30] WMA'")); 159 | Assert.assertTrue(errors.toString(), errors.toString().contains("no viable alternative at input '(CCI[5] 2.6'")); 160 | return; 161 | } 162 | fail(); 163 | } 164 | 165 | } 166 | -------------------------------------------------------------------------------- /src/main/java/org/ambulando/strategy/ta4j/parser/TA4JIndicatorsStrategyListener.java: -------------------------------------------------------------------------------- 1 | package org.ambulando.strategy.ta4j.parser; 2 | 3 | import org.ta4j.core.BarSeries; 4 | import org.ta4j.core.Indicator; 5 | import org.ta4j.core.indicators.CCIIndicator; 6 | import org.ta4j.core.indicators.DoubleEMAIndicator; 7 | import org.ta4j.core.indicators.EMAIndicator; 8 | import org.ta4j.core.indicators.HMAIndicator; 9 | import org.ta4j.core.indicators.KAMAIndicator; 10 | import org.ta4j.core.indicators.MACDIndicator; 11 | import org.ta4j.core.indicators.PPOIndicator; 12 | import org.ta4j.core.indicators.RSIIndicator; 13 | import org.ta4j.core.indicators.SMAIndicator; 14 | import org.ta4j.core.indicators.TripleEMAIndicator; 15 | import org.ta4j.core.indicators.UlcerIndexIndicator; 16 | import org.ta4j.core.indicators.WMAIndicator; 17 | import org.ta4j.core.indicators.WilliamsRIndicator; 18 | import org.ta4j.core.indicators.ZLEMAIndicator; 19 | import org.ta4j.core.indicators.adx.ADXIndicator; 20 | import org.ta4j.core.indicators.helpers.ClosePriceIndicator; 21 | import org.ta4j.core.indicators.helpers.ConstantIndicator; 22 | import org.ta4j.core.indicators.helpers.VolumeIndicator; 23 | import org.ta4j.core.indicators.volume.AccumulationDistributionIndicator; 24 | import org.ta4j.core.indicators.volume.ChaikinMoneyFlowIndicator; 25 | import org.ta4j.core.indicators.volume.ChaikinOscillatorIndicator; 26 | import org.ta4j.core.indicators.volume.IIIIndicator; 27 | import org.ta4j.core.indicators.volume.MVWAPIndicator; 28 | import org.ta4j.core.indicators.volume.NVIIndicator; 29 | import org.ta4j.core.indicators.volume.OnBalanceVolumeIndicator; 30 | import org.ta4j.core.indicators.volume.PVIIndicator; 31 | import org.ta4j.core.indicators.volume.ROCVIndicator; 32 | import org.ta4j.core.indicators.volume.VWAPIndicator; 33 | import org.ta4j.core.num.DoubleNum; 34 | import org.ta4j.core.num.Num; 35 | import org.ta4j.core.trading.rules.CrossedDownIndicatorRule; 36 | import org.ta4j.core.trading.rules.CrossedUpIndicatorRule; 37 | import org.ta4j.core.trading.rules.OverIndicatorRule; 38 | import org.ta4j.core.trading.rules.UnderIndicatorRule; 39 | 40 | public class TA4JIndicatorsStrategyListener extends TA4JStrategyListenerImpl { 41 | 42 | private BarSeries barSeries; 43 | private ClosePriceIndicator closePrice; 44 | private VolumeIndicator volume; 45 | 46 | public TA4JIndicatorsStrategyListener(BarSeries barSeries) { 47 | assert barSeries != null : "BarSeries cannot be null"; 48 | this.barSeries = barSeries; 49 | this.closePrice = new ClosePriceIndicator(barSeries); 50 | this.volume = new VolumeIndicator(barSeries); 51 | } 52 | 53 | @Override 54 | public void exitSimpleExpression(StrategyParser.SimpleExpressionContext ctx) { 55 | Indicator right = indicatorStack.pop(); 56 | Indicator left = indicatorStack.pop(); 57 | Operator operator = operatorStack.pop(); 58 | switch (operator) { 59 | case GTE: 60 | ruleStack.push(new OverIndicatorRule(left, right)); 61 | break; 62 | case LTE: 63 | ruleStack.push(new UnderIndicatorRule(left, right)); 64 | break; 65 | } 66 | } 67 | 68 | @Override 69 | public void exitConstantExpression(StrategyParser.ConstantExpressionContext ctx) { 70 | try { 71 | Indicator right = indicatorStack.pop(); 72 | Indicator left = indicatorStack.pop(); 73 | Operator operator = operatorStack.pop(); 74 | switch (operator) { 75 | case GTE: 76 | ruleStack.push(new CrossedUpIndicatorRule(left, right)); 77 | break; 78 | case LTE: 79 | ruleStack.push(new CrossedDownIndicatorRule(left, right)); 80 | break; 81 | } 82 | } catch (Exception e) { 83 | //addError(ParserError.builder().msg(e.getMessage()).build()); 84 | } 85 | } 86 | 87 | 88 | @Override 89 | public void exitEma(StrategyParser.EmaContext ctx) { 90 | Integer timeFrame = timeFrameStack.pop(); 91 | indicatorStack.push(new EMAIndicator(closePrice, timeFrame)); 92 | } 93 | 94 | @Override 95 | public void exitSma(StrategyParser.SmaContext ctx) { 96 | Integer timeFrame = timeFrameStack.pop(); 97 | indicatorStack.push(new SMAIndicator(closePrice, timeFrame)); 98 | } 99 | 100 | @Override 101 | public void exitDema(StrategyParser.DemaContext ctx) { 102 | Integer timeFrame = timeFrameStack.pop(); 103 | indicatorStack.push(new DoubleEMAIndicator(closePrice, timeFrame)); 104 | } 105 | 106 | @Override 107 | public void exitTema(StrategyParser.TemaContext ctx) { 108 | Integer timeFrame = timeFrameStack.pop(); 109 | indicatorStack.push(new TripleEMAIndicator(closePrice, timeFrame)); 110 | } 111 | 112 | @Override 113 | public void exitWilliams(StrategyParser.WilliamsContext ctx) { 114 | Integer timeFrame = timeFrameStack.pop(); 115 | indicatorStack.push(new WilliamsRIndicator(barSeries, timeFrame)); 116 | } 117 | 118 | @Override 119 | public void exitWma(StrategyParser.WmaContext ctx) { 120 | Integer timeFrame = timeFrameStack.pop(); 121 | indicatorStack.push(new WMAIndicator(closePrice, timeFrame)); 122 | } 123 | 124 | @Override 125 | public void exitCci(StrategyParser.CciContext ctx) { 126 | Integer timeFrame = timeFrameStack.pop(); 127 | indicatorStack.push(new CCIIndicator(barSeries, timeFrame)); 128 | 129 | } 130 | 131 | @Override 132 | public void exitRsi(StrategyParser.RsiContext ctx) { 133 | Integer timeFrame = timeFrameStack.pop(); 134 | indicatorStack.push(new RSIIndicator(closePrice, timeFrame)); 135 | } 136 | 137 | @Override 138 | public void exitCprice(StrategyParser.CpriceContext ctx) { 139 | indicatorStack.push(closePrice); 140 | } 141 | 142 | @Override 143 | public void exitVolume(StrategyParser.VolumeContext ctx) { 144 | indicatorStack.push(volume); 145 | } 146 | 147 | @Override 148 | public void exitUi(StrategyParser.UiContext ctx) { 149 | Integer timeFrame = timeFrameStack.pop(); 150 | indicatorStack.push(new UlcerIndexIndicator(closePrice, timeFrame)); 151 | } 152 | 153 | @Override 154 | public void exitAdx(StrategyParser.AdxContext ctx) { 155 | Integer timeFrame = timeFrameStack.pop(); 156 | indicatorStack.push(new ADXIndicator(barSeries, timeFrame)); 157 | } 158 | 159 | @Override 160 | public void exitHma(StrategyParser.HmaContext ctx) { 161 | Integer timeFrame = timeFrameStack.pop(); 162 | indicatorStack.push(new HMAIndicator(closePrice, timeFrame)); 163 | } 164 | 165 | @Override 166 | public void exitZlema(StrategyParser.ZlemaContext ctx) { 167 | Integer timeFrame = timeFrameStack.pop(); 168 | indicatorStack.push(new ZLEMAIndicator(closePrice, timeFrame)); 169 | } 170 | 171 | @Override 172 | public void exitKama(StrategyParser.KamaContext ctx) { 173 | Integer timeFrame3 = timeFrameStack.pop(); 174 | Integer timeFrame2 = timeFrameStack.pop(); 175 | Integer timeFrame1 = timeFrameStack.pop(); 176 | indicatorStack.push(new KAMAIndicator(closePrice, timeFrame1, timeFrame2, timeFrame3)); 177 | } 178 | 179 | @Override 180 | public void exitMacd(StrategyParser.MacdContext ctx) { 181 | Integer timeFrame2 = timeFrameStack.pop(); 182 | Integer timeFrame1 = timeFrameStack.pop(); 183 | indicatorStack.push(new MACDIndicator(closePrice, timeFrame1, timeFrame2)); 184 | } 185 | 186 | @Override 187 | public void exitPpo(StrategyParser.PpoContext ctx) { 188 | Integer timeFrame2 = timeFrameStack.pop(); 189 | Integer timeFrame1 = timeFrameStack.pop(); 190 | indicatorStack.push(new PPOIndicator(closePrice, timeFrame1, timeFrame2)); 191 | } 192 | 193 | @Override 194 | public void exitVwap(StrategyParser.VwapContext ctx) { 195 | Integer timeFrame = timeFrameStack.pop(); 196 | indicatorStack.push(new VWAPIndicator(barSeries, timeFrame)); 197 | } 198 | 199 | @Override 200 | public void exitCoi(StrategyParser.CoiContext ctx) { 201 | Integer timeFrame2 = timeFrameStack.pop(); 202 | Integer timeFrame1 = timeFrameStack.pop(); 203 | indicatorStack.push(new ChaikinOscillatorIndicator(barSeries, timeFrame1, timeFrame2)); 204 | } 205 | 206 | @Override 207 | public void exitAdi(StrategyParser.AdiContext ctx) { 208 | indicatorStack.push(new AccumulationDistributionIndicator(barSeries)); 209 | } 210 | 211 | @Override 212 | public void exitIii(StrategyParser.IiiContext ctx) { 213 | indicatorStack.push(new IIIIndicator(barSeries)); 214 | } 215 | 216 | @Override 217 | public void exitCmf(StrategyParser.CmfContext ctx) { 218 | Integer timeFrame = timeFrameStack.pop(); 219 | indicatorStack.push(new ChaikinMoneyFlowIndicator(barSeries, timeFrame)); 220 | } 221 | 222 | @Override 223 | public void exitMvwap(StrategyParser.MvwapContext ctx) { 224 | Integer timeFrame2 = timeFrameStack.pop(); 225 | Integer timeFrame1 = timeFrameStack.pop(); 226 | VWAPIndicator vwap = new VWAPIndicator(barSeries, timeFrame1); 227 | indicatorStack.push(new MVWAPIndicator(vwap, timeFrame2)); 228 | } 229 | 230 | @Override 231 | public void enterNvi(StrategyParser.NviContext ctx) { 232 | indicatorStack.push(new NVIIndicator(barSeries)); 233 | } 234 | 235 | @Override 236 | public void exitRocv(StrategyParser.RocvContext ctx) { 237 | Integer timeFrame = timeFrameStack.pop(); 238 | indicatorStack.push(new ROCVIndicator(barSeries, timeFrame)); 239 | 240 | } 241 | 242 | @Override 243 | public void exitPvi(StrategyParser.PviContext ctx) { 244 | indicatorStack.push(new PVIIndicator(barSeries)); 245 | } 246 | 247 | @Override 248 | public void exitObv(StrategyParser.ObvContext ctx) { 249 | indicatorStack.push(new OnBalanceVolumeIndicator(barSeries)); 250 | } 251 | 252 | @Override 253 | public void exitValue(StrategyParser.ValueContext ctx) { 254 | indicatorStack.push(new ConstantIndicator<>(barSeries, DoubleNum.valueOf(ctx.getText()))); 255 | } 256 | 257 | } 258 | --------------------------------------------------------------------------------