Skip to content

Commit

Permalink
New alert design --backend changes (#8)
Browse files Browse the repository at this point in the history
* wip: support new alert screen design

* build error fix

* minor: save valueText & config
  • Loading branch information
syuraj authored Oct 28, 2021
1 parent bc20fb2 commit e6f693b
Showing 18 changed files with 162 additions and 103 deletions.
27 changes: 14 additions & 13 deletions src/main/java/com/tradiumapp/swingtradealerts/models/Condition.java
Original file line number Diff line number Diff line change
@@ -2,30 +2,31 @@

public class Condition {
public int order;
public Operator operator;
public IndicatorType indicator;
public String timeframe;
public IndicatorType indicator1;
public Operator operator;
public IndicatorType indicator2;
public String value;
public String valueText;
public ValueConfig valueConfig;
public float diff_percent;
public Config config;

public Condition(){}
public Condition() {
}

public Condition(IndicatorType indicator, String value, ValueConfig valueConfig){
this.indicator = indicator;
public Condition(IndicatorType indicator1, Operator operator, IndicatorType indicator2, String value, float diff_percent) {
this.indicator1 = indicator1;
this.operator = operator;
this.value = value;
this.valueConfig = valueConfig;
this.indicator2 = indicator2;
this.diff_percent = diff_percent;
}

public class ValueConfig {
public class Config {
public int length;
public int length2;
public String source;
public float value;
public boolean upDirection;
}

public enum Operator {
And, Not
above, below
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.tradiumapp.swingtradealerts.models;

public enum IndicatorType {
rsi, sma, ema, week52high, week52low, earnings, reddit
price, rsi, sma, ema, week52High, week52Low, earnings, reddit
}
Original file line number Diff line number Diff line change
@@ -79,8 +79,10 @@ public Response updateAlert(final Alert alert) {

private boolean areConditionsSame(List<Condition> conditions1, List<Condition> conditions2) {
for (Condition condition1 : conditions1) {
if (!conditions2.stream().anyMatch(c -> c.indicator == condition1.indicator
&& c.value.equals(condition1.value))) {
if (!conditions2.stream().anyMatch(c -> c.indicator1 == condition1.indicator1
&& c.indicator2 == condition1.indicator2
&& c.operator == condition1.operator
&& c.diff_percent == condition1.diff_percent)) {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -7,7 +7,6 @@
import com.tradiumapp.swingtradealerts.repositories.UserRepository;
import com.tradiumapp.swingtradealerts.scheduledtasks.conditioncheckers.*;
import com.tradiumapp.swingtradealerts.scheduledtasks.helpers.AlertEmailSender;
import org.bson.types.ObjectId;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -137,25 +136,23 @@ private HashMap<String, List<StockHistory.StockPrice>> loadStockHistory(List<Ale
private boolean isConditionMet(Stock stock, Condition condition, BarSeries series) {
ClosePriceIndicator closePrice = new ClosePriceIndicator(series);
ConditionChecker conditionChecker;
if (condition.indicator.equals(IndicatorType.rsi)) {
conditionChecker = new RSIConditionChecker();
} else if (condition.indicator.equals(IndicatorType.sma)) {
conditionChecker = new SMAConditionChecker();
} else if (condition.indicator.equals(IndicatorType.ema)) {
conditionChecker = new EMAConditionChecker();
} else if (condition.indicator.equals(IndicatorType.week52high)) {
if (condition.indicator1.equals(IndicatorType.rsi)) {
conditionChecker = new RSIConditionChecker(stock);
} else if (condition.indicator1.equals(IndicatorType.sma)) {
conditionChecker = new SMAConditionChecker(stock);
} else if (condition.indicator1.equals(IndicatorType.ema)) {
conditionChecker = new EMAConditionChecker(stock);
} else if (condition.indicator1.equals(IndicatorType.week52High)) {
conditionChecker = new Week52HighConditionChecker(stock);
} else if (condition.indicator.equals(IndicatorType.week52low)) {
} else if (condition.indicator1.equals(IndicatorType.week52Low)) {
conditionChecker = new Week52LowConditionChecker(stock);
} else if (condition.indicator.equals(IndicatorType.earnings)) {
} else if (condition.indicator1.equals(IndicatorType.earnings)) {
conditionChecker = new EarningsConditionChecker(stock);
} else {
conditionChecker = new RedditTrendingConditionChecker(stock);
}

boolean result = conditionChecker.checkCondition(condition, closePrice);

return (condition.operator == Condition.Operator.Not) ? !result : result;
return conditionChecker.checkCondition(condition, closePrice);
}

private boolean updateAlertStatus(Alert alert, Alert.AlertStatus status) {
Original file line number Diff line number Diff line change
@@ -3,6 +3,14 @@
import com.tradiumapp.swingtradealerts.models.Condition;
import org.ta4j.core.indicators.helpers.PriceIndicator;

public interface ConditionChecker {
boolean checkCondition(Condition condition, PriceIndicator priceIndicator);
public abstract class ConditionChecker {
public abstract boolean checkCondition(Condition condition, PriceIndicator priceIndicator);

public boolean checkAboveOrBelow(Condition.Operator operator, float value1, float value2, float diff_percent) {
if (operator == Condition.Operator.above) {
return value1 > (1 + diff_percent / 100) * value2;
} else {
return value1 < (1 - diff_percent / 100) * value2;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
package com.tradiumapp.swingtradealerts.scheduledtasks.conditioncheckers;

import com.tradiumapp.swingtradealerts.models.Condition;
import com.tradiumapp.swingtradealerts.models.IndicatorType;
import com.tradiumapp.swingtradealerts.models.Stock;
import org.ta4j.core.indicators.EMAIndicator;
import org.ta4j.core.indicators.SMAIndicator;
import org.ta4j.core.indicators.helpers.PriceIndicator;

import java.util.Arrays;
public class EMAConditionChecker extends ConditionChecker {
private final Stock stock;

public class EMAConditionChecker implements ConditionChecker {
public boolean checkCondition(Condition condition, PriceIndicator priceIndicator) {
float lastValue = priceIndicator.getValue(priceIndicator.getBarSeries().getBarCount() - 1).floatValue();

EMAIndicator indicator = new EMAIndicator(priceIndicator, condition.valueConfig.length);
float emaValue = indicator.getValue(indicator.getBarSeries().getBarCount() - 1).floatValue();
public EMAConditionChecker(Stock stock) {
this.stock = stock;
}

if (Arrays.asList("golden_cross_ema20_ema50", "death_cross_ema20_ema50").contains(condition.value)) {
EMAIndicator indicator2 = new EMAIndicator(priceIndicator, condition.valueConfig.length2);
float emaValue2 = indicator2.getValue(indicator2.getBarSeries().getBarCount() - 1).floatValue();
public boolean checkCondition(Condition condition, PriceIndicator priceIndicator) {
EMAIndicator emaIndicator = new EMAIndicator(priceIndicator, condition.config.length);
float emaValue = emaIndicator.getValue(emaIndicator.getBarSeries().getBarCount() - 1).floatValue();

return condition.valueConfig.upDirection ? emaValue > emaValue2 : emaValue < emaValue2;
}
if (condition.indicator2.equals(IndicatorType.price)) {
float lastValue = priceIndicator.getValue(priceIndicator.getBarSeries().getBarCount() - 1).floatValue();
return checkAboveOrBelow(condition.operator, emaValue, lastValue, condition.diff_percent);
} else if (condition.indicator2.equals(IndicatorType.sma)) {
SMAIndicator smaIndicator = new SMAIndicator(priceIndicator, condition.config.length);
float smaValue = smaIndicator.getValue(smaIndicator.getBarSeries().getBarCount() - 1).floatValue();

if (condition.valueConfig.upDirection && lastValue > (1 + condition.valueConfig.value / 100) * emaValue) {
return true;
} else if (!condition.valueConfig.upDirection && lastValue < (1 - condition.valueConfig.value / 100) * emaValue) {
return true;
return checkAboveOrBelow(condition.operator, emaValue, smaValue, condition.diff_percent);
} else if (condition.indicator2.equals(IndicatorType.week52High)) {
return checkAboveOrBelow(condition.operator, emaValue, stock.week52High, condition.diff_percent);
} else if (condition.indicator2.equals(IndicatorType.week52Low)) {
return checkAboveOrBelow(condition.operator, emaValue, stock.week52Low, condition.diff_percent);
} else {
return false;
}
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@
import org.joda.time.Days;
import org.ta4j.core.indicators.helpers.PriceIndicator;

public class EarningsConditionChecker implements ConditionChecker {
public class EarningsConditionChecker extends ConditionChecker {
private final Stock stock;

public EarningsConditionChecker(Stock stock) {
@@ -16,6 +16,17 @@ public EarningsConditionChecker(Stock stock) {
public boolean checkCondition(Condition condition, PriceIndicator priceIndicator) {
int daysLeft = Days.daysBetween(new DateTime(), new DateTime(stock.nextEarningsDate)).getDays();

return daysLeft > 0 && daysLeft < condition.valueConfig.value;
if (daysLeft < 0) return false;

switch (condition.value) {
case "in_10_days":
return daysLeft < 10;
case "in_20_days":
return daysLeft < 20;
case "in_30_days":
return daysLeft < 30;
}

return false;
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,36 @@
package com.tradiumapp.swingtradealerts.scheduledtasks.conditioncheckers;

import com.tradiumapp.swingtradealerts.models.Condition;
import com.tradiumapp.swingtradealerts.models.IndicatorType;
import com.tradiumapp.swingtradealerts.models.Stock;
import org.ta4j.core.indicators.EMAIndicator;
import org.ta4j.core.indicators.RSIIndicator;
import org.ta4j.core.indicators.SMAIndicator;
import org.ta4j.core.indicators.helpers.PriceIndicator;

public class RSIConditionChecker implements ConditionChecker {
public class RSIConditionChecker extends ConditionChecker {
private final Stock stock;

public RSIConditionChecker(Stock stock) {
this.stock = stock;
}

public boolean checkCondition(Condition condition, PriceIndicator priceIndicator) {
RSIIndicator indicator = new RSIIndicator(priceIndicator, condition.valueConfig.length);
float lastValue = indicator.getValue(indicator.getBarSeries().getBarCount() - 1).floatValue();
RSIIndicator rsiIndicator = new RSIIndicator(priceIndicator, condition.config.length);
float rsiValue = rsiIndicator.getValue(rsiIndicator.getBarSeries().getBarCount() - 1).floatValue();

if (condition.indicator2.equals(IndicatorType.price)) {
float lastValue = priceIndicator.getValue(priceIndicator.getBarSeries().getBarCount() - 1).floatValue();
return checkAboveOrBelow(condition.operator, rsiValue, lastValue, condition.diff_percent);
} else if (condition.indicator2.equals(IndicatorType.sma)) {
SMAIndicator smaIndicator = new SMAIndicator(priceIndicator, condition.config.length);
float smaValue = smaIndicator.getValue(smaIndicator.getBarSeries().getBarCount() - 1).floatValue();

if (condition.valueConfig.upDirection && lastValue > condition.valueConfig.value) {
return true;
} else if (!condition.valueConfig.upDirection && lastValue < condition.valueConfig.value) {
return true;
return checkAboveOrBelow(condition.operator, rsiValue, smaValue, condition.diff_percent);
} else if (condition.indicator2.equals(IndicatorType.week52High)) {
return checkAboveOrBelow(condition.operator, rsiValue, stock.week52High, condition.diff_percent);
} else if (condition.indicator2.equals(IndicatorType.week52Low)) {
return checkAboveOrBelow(condition.operator, rsiValue, stock.week52Low, condition.diff_percent);
} else {
return false;
}
Original file line number Diff line number Diff line change
@@ -5,20 +5,23 @@
import org.ta4j.core.indicators.EMAIndicator;
import org.ta4j.core.indicators.helpers.PriceIndicator;

public class RedditTrendingConditionChecker implements ConditionChecker {
public class RedditTrendingConditionChecker extends ConditionChecker {
private final Stock stock;

public RedditTrendingConditionChecker(Stock stock) {
this.stock = stock;
}

public boolean checkCondition(Condition condition, PriceIndicator priceIndicator) {
if (condition.valueConfig.upDirection && stock.redditRank < condition.valueConfig.value && stock.redditRank > 0) {
return true;
} else if (!condition.valueConfig.upDirection && (stock.redditRank > condition.valueConfig.value || stock.redditRank == 0)) {
return true;
} else {
return false;
switch (condition.value){
case "top10":
return stock.redditRank < 10;
case "top20":
return stock.redditRank < 20;
case "top50":
return stock.redditRank < 50;
}

return false;
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,35 @@
package com.tradiumapp.swingtradealerts.scheduledtasks.conditioncheckers;

import com.tradiumapp.swingtradealerts.models.Condition;
import com.tradiumapp.swingtradealerts.models.IndicatorType;
import com.tradiumapp.swingtradealerts.models.Stock;
import org.ta4j.core.indicators.EMAIndicator;
import org.ta4j.core.indicators.SMAIndicator;
import org.ta4j.core.indicators.helpers.PriceIndicator;

public class SMAConditionChecker implements ConditionChecker {
public class SMAConditionChecker extends ConditionChecker {
private final Stock stock;

public SMAConditionChecker(Stock stock) {
this.stock = stock;
}

public boolean checkCondition(Condition condition, PriceIndicator priceIndicator) {
float lastValue = priceIndicator.getValue(priceIndicator.getBarSeries().getBarCount() - 1).floatValue();
SMAIndicator smaIndicator = new SMAIndicator(priceIndicator, condition.config.length);
float emaValue = smaIndicator.getValue(smaIndicator.getBarSeries().getBarCount() - 1).floatValue();

SMAIndicator indicator = new SMAIndicator(priceIndicator, condition.valueConfig.length);
float smaValue = indicator.getValue(indicator.getBarSeries().getBarCount() - 1).floatValue();
if (condition.indicator2.equals(IndicatorType.price)) {
float lastValue = priceIndicator.getValue(priceIndicator.getBarSeries().getBarCount() - 1).floatValue();
return checkAboveOrBelow(condition.operator, emaValue, lastValue, condition.diff_percent);
} else if (condition.indicator2.equals(IndicatorType.ema)) {
EMAIndicator emaIndicator = new EMAIndicator(priceIndicator, condition.config.length);
float smaValue = emaIndicator.getValue(emaIndicator.getBarSeries().getBarCount() - 1).floatValue();

if (condition.valueConfig.upDirection && lastValue > (1 + condition.valueConfig.value / 100) * smaValue) {
return true;
} else if (!condition.valueConfig.upDirection && lastValue < (1 - condition.valueConfig.value / 100) * smaValue) {
return true;
return checkAboveOrBelow(condition.operator, emaValue, smaValue, condition.diff_percent);
} else if (condition.indicator2.equals(IndicatorType.week52High)) {
return checkAboveOrBelow(condition.operator, emaValue, stock.week52High, condition.diff_percent);
} else if (condition.indicator2.equals(IndicatorType.week52Low)) {
return checkAboveOrBelow(condition.operator, emaValue, stock.week52Low, condition.diff_percent);
} else {
return false;
}
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@
import com.tradiumapp.swingtradealerts.models.Stock;
import org.ta4j.core.indicators.helpers.PriceIndicator;

public class Week52HighConditionChecker implements ConditionChecker {
public class Week52HighConditionChecker extends ConditionChecker {
private final Stock stock;

public Week52HighConditionChecker(Stock stock) {
@@ -13,7 +13,7 @@ public Week52HighConditionChecker(Stock stock) {

public boolean checkCondition(Condition condition, PriceIndicator priceIndicator) {
float lastValue = priceIndicator.getValue(priceIndicator.getBarSeries().getBarCount() - 1).floatValue();
float expectedDrawDown = condition.valueConfig.value;
float expectedDrawDown = condition.diff_percent;
float currentDrawDown = (stock.week52High - lastValue) * 100 / stock.week52High;

return currentDrawDown > expectedDrawDown;
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@
import com.tradiumapp.swingtradealerts.models.Stock;
import org.ta4j.core.indicators.helpers.PriceIndicator;

public class Week52LowConditionChecker implements ConditionChecker {
public class Week52LowConditionChecker extends ConditionChecker {
private final Stock stock;

public Week52LowConditionChecker(Stock stock) {
@@ -13,7 +13,7 @@ public Week52LowConditionChecker(Stock stock) {

public boolean checkCondition(Condition condition, PriceIndicator priceIndicator) {
float lastValue = priceIndicator.getValue(priceIndicator.getBarSeries().getBarCount() - 1).floatValue();
float expectedMarkup = condition.valueConfig.value;
float expectedMarkup = condition.diff_percent;
float currentMarkup = (lastValue - stock.week52Low) * 100 / stock.week52Low;

return currentMarkup > expectedMarkup;
Original file line number Diff line number Diff line change
@@ -35,9 +35,10 @@ public void sendEmail(User user, List<Alert> alerts) throws IOException {
message += (i + 1) + ") " + alert.signal + " " + alert.symbol + ": " + alerts.get(i).title + " <br/> ";

for (Condition condition : alerts.get(i).conditions) {
message += "&nbsp;&nbsp;&nbsp;" + StringUtils.capitalize(condition.timeframe) + " " + condition.indicator.toString().toUpperCase()
+ (condition.operator == Condition.Operator.Not ? " ≠ " : " = ")
+ " '" + condition.valueText + "'. <br/> ";
message += "&nbsp;&nbsp;&nbsp;" + StringUtils.capitalize(condition.timeframe) + " " + condition.indicator1.toString().toUpperCase()
+ (condition.operator != null ? (condition.operator == Condition.Operator.above ? " > " : " < ") : "")
+ (condition.indicator2 != null ? condition.indicator2.toString().toUpperCase() : condition.value)
+ " '" + condition.indicator2 + "'. <br/> ";
}
message += "<br/>";
}
Loading

0 comments on commit e6f693b

Please sign in to comment.