3 Commits

4 changed files with 103 additions and 7 deletions
@@ -59,8 +59,8 @@ public final class AlarmConfigurationParser {
BigDecimal target = parseTarget(cursor.nextToken("target")); BigDecimal target = parseTarget(cursor.nextToken("target"));
AlarmTrigger trigger = AlarmTrigger.valueOf( TriggerConfiguration triggerConfiguration = parseTrigger(
cursor.nextToken("trigger").toUpperCase(Locale.ROOT) cursor.nextToken("trigger")
); );
AlarmSeverity severity = AlarmSeverity.valueOf( AlarmSeverity severity = AlarmSeverity.valueOf(
@@ -81,7 +81,8 @@ public final class AlarmConfigurationParser {
asset, asset,
direction, direction,
target, target,
trigger, triggerConfiguration.trigger(),
triggerConfiguration.gracePeriod(),
severity, severity,
note note
); );
@@ -109,6 +110,50 @@ public final class AlarmConfigurationParser {
return target; return target;
} }
private static TriggerConfiguration parseTrigger(String triggerText) {
String normalized = triggerText.toUpperCase(Locale.ROOT);
if (normalized.equals("ONETIME")) {
return new TriggerConfiguration(AlarmTrigger.ONETIME, 0);
}
if (normalized.equals("PERSISTENT")) {
return new TriggerConfiguration(AlarmTrigger.PERSISTENT, 0);
}
if (normalized.startsWith("PERSISTENT:")) {
String graceText = normalized.substring("PERSISTENT:".length());
if (graceText.isEmpty()) {
throw new IllegalArgumentException("Missing persistent grace period: " + triggerText);
}
ΩsecondsΩ gracePeriodSeconds;
try {
gracePeriodSeconds = Integer.parseInt(graceText);
} catch (NumberFormatException exception) {
throw new IllegalArgumentException(
"Invalid persistent grace period: " + triggerText,
exception
);
}
if (gracePeriodSeconds < 0) {
throw new IllegalArgumentException(
"Persistent grace period cannot be negative: " + triggerText
);
}
return new TriggerConfiguration(AlarmTrigger.PERSISTENT, gracePeriodSeconds);
}
if (normalized.startsWith("ONETIME:")) {
throw new IllegalArgumentException("ONETIME cannot have a grace period: " + triggerText);
}
throw new IllegalArgumentException("Unknown trigger: " + triggerText);
}
private static void validateTarget(BigDecimal target, String originalTargetStr) { private static void validateTarget(BigDecimal target, String originalTargetStr) {
if (target.compareTo(BigDecimal.ZERO) < 0) { if (target.compareTo(BigDecimal.ZERO) < 0) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
@@ -274,6 +319,12 @@ public final class AlarmConfigurationParser {
} }
} }
private record TriggerConfiguration(
AlarmTrigger trigger,
ΩsecondsΩ gracePeriod
) {
}
private AlarmConfigurationParser() { private AlarmConfigurationParser() {
} }
@@ -1,5 +1,7 @@
package com.r35157.jupiterperpsalarm.impl.ref; package com.r35157.jupiterperpsalarm.impl.ref;
import java.time.Instant;
public final class PriceAlarm { public final class PriceAlarm {
public PriceAlarm(PriceAlarmDefinition definition, AlarmAction action) { public PriceAlarm(PriceAlarmDefinition definition, AlarmAction action) {
@@ -25,16 +27,27 @@ public final class PriceAlarm {
previousReached = reached; previousReached = reached;
if (!enteredTriggeredSide) { if (!reached) {
return; return;
} }
if (definition.trigger() == AlarmTrigger.ONETIME && triggerCount > 0) { if (definition.trigger() == AlarmTrigger.ONETIME) {
if (!enteredTriggeredSide || triggerCount > 0) {
return;
}
trigger(price);
return; return;
} }
triggerCount++; if (definition.trigger() == AlarmTrigger.PERSISTENT) {
action.trigger(price, definition); if (enteredTriggeredSide || persistentGracePeriodHasPassed()) {
trigger(price);
}
return;
}
throw new IllegalStateException("Unsupported alarm trigger: " + definition.trigger());
} }
public PriceAlarmDefinition definition() { public PriceAlarmDefinition definition() {
@@ -45,9 +58,32 @@ public final class PriceAlarm {
return triggerCount; return triggerCount;
} }
private boolean persistentGracePeriodHasPassed() {
if (lastTriggeredAt == null) {
return true;
}
ΩsecondsΩ gracePeriod = definition.triggerGracePeriod();
if (gracePeriod == 0) {
return true;
}
return !Instant.now().isBefore(
lastTriggeredAt.plusSeconds(gracePeriod)
);
}
private void trigger(OraclePrice price) {
triggerCount++;
lastTriggeredAt = Instant.now();
action.trigger(price, definition);
}
private final PriceAlarmDefinition definition; private final PriceAlarmDefinition definition;
private final AlarmAction action; private final AlarmAction action;
private Instant lastTriggeredAt;
private Boolean previousReached; private Boolean previousReached;
private long triggerCount; private long triggerCount;
} }
@@ -11,6 +11,7 @@ public record PriceAlarmDefinition(
PriceDirection direction, PriceDirection direction,
BigDecimal target, BigDecimal target,
AlarmTrigger trigger, AlarmTrigger trigger,
ΩsecondsΩ triggerGracePeriod,
AlarmSeverity severity, AlarmSeverity severity,
String note String note
) { ) {
@@ -24,5 +25,11 @@ public record PriceAlarmDefinition(
if (target.signum() < 0) { if (target.signum() < 0) {
throw new IllegalArgumentException("Target price cannot be negative (was: " + target.signum() + ")!"); throw new IllegalArgumentException("Target price cannot be negative (was: " + target.signum() + ")!");
} }
if (triggerGracePeriod < 0) {
throw new IllegalArgumentException(
"Trigger grace period cannot be negative: " + triggerGracePeriod
);
}
} }
} }
@@ -0,0 +1,2 @@
package com.r35157.jupiterperpsalarm.impl.ref;