re-enable triggered offers if within trigger price again

This commit is contained in:
woodser 2025-02-14 11:20:57 -05:00
parent 4a82c69507
commit 290a3738b7
6 changed files with 52 additions and 22 deletions

View File

@ -110,6 +110,10 @@ public final class OpenOffer implements Tradable {
@Getter @Getter
@Setter @Setter
transient int numProcessingAttempts = 0; transient int numProcessingAttempts = 0;
@Getter
@Setter
private boolean deactivatedByTrigger;
public OpenOffer(Offer offer) { public OpenOffer(Offer offer) {
this(offer, 0, false); this(offer, 0, false);
} }
@ -141,6 +145,7 @@ public final class OpenOffer implements Tradable {
this.reserveTxHex = openOffer.reserveTxHex; this.reserveTxHex = openOffer.reserveTxHex;
this.reserveTxKey = openOffer.reserveTxKey; this.reserveTxKey = openOffer.reserveTxKey;
this.challenge = openOffer.challenge; this.challenge = openOffer.challenge;
this.deactivatedByTrigger = openOffer.deactivatedByTrigger;
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -158,7 +163,8 @@ public final class OpenOffer implements Tradable {
@Nullable String reserveTxHash, @Nullable String reserveTxHash,
@Nullable String reserveTxHex, @Nullable String reserveTxHex,
@Nullable String reserveTxKey, @Nullable String reserveTxKey,
@Nullable String challenge) { @Nullable String challenge,
boolean deactivatedByTrigger) {
this.offer = offer; this.offer = offer;
this.state = state; this.state = state;
this.triggerPrice = triggerPrice; this.triggerPrice = triggerPrice;
@ -170,6 +176,7 @@ public final class OpenOffer implements Tradable {
this.reserveTxHex = reserveTxHex; this.reserveTxHex = reserveTxHex;
this.reserveTxKey = reserveTxKey; this.reserveTxKey = reserveTxKey;
this.challenge = challenge; this.challenge = challenge;
this.deactivatedByTrigger = deactivatedByTrigger;
// reset reserved state to available // reset reserved state to available
if (this.state == State.RESERVED) setState(State.AVAILABLE); if (this.state == State.RESERVED) setState(State.AVAILABLE);
@ -182,7 +189,8 @@ public final class OpenOffer implements Tradable {
.setTriggerPrice(triggerPrice) .setTriggerPrice(triggerPrice)
.setState(protobuf.OpenOffer.State.valueOf(state.name())) .setState(protobuf.OpenOffer.State.valueOf(state.name()))
.setSplitOutputTxFee(splitOutputTxFee) .setSplitOutputTxFee(splitOutputTxFee)
.setReserveExactAmount(reserveExactAmount); .setReserveExactAmount(reserveExactAmount)
.setDeactivatedByTrigger(deactivatedByTrigger);
Optional.ofNullable(scheduledAmount).ifPresent(e -> builder.setScheduledAmount(scheduledAmount)); Optional.ofNullable(scheduledAmount).ifPresent(e -> builder.setScheduledAmount(scheduledAmount));
Optional.ofNullable(scheduledTxHashes).ifPresent(e -> builder.addAllScheduledTxHashes(scheduledTxHashes)); Optional.ofNullable(scheduledTxHashes).ifPresent(e -> builder.addAllScheduledTxHashes(scheduledTxHashes));
@ -207,7 +215,8 @@ public final class OpenOffer implements Tradable {
ProtoUtil.stringOrNullFromProto(proto.getReserveTxHash()), ProtoUtil.stringOrNullFromProto(proto.getReserveTxHash()),
ProtoUtil.stringOrNullFromProto(proto.getReserveTxHex()), ProtoUtil.stringOrNullFromProto(proto.getReserveTxHex()),
ProtoUtil.stringOrNullFromProto(proto.getReserveTxKey()), ProtoUtil.stringOrNullFromProto(proto.getReserveTxKey()),
ProtoUtil.stringOrNullFromProto(proto.getChallenge())); ProtoUtil.stringOrNullFromProto(proto.getChallenge()),
proto.getDeactivatedByTrigger());
return openOffer; return openOffer;
} }
@ -234,6 +243,14 @@ public final class OpenOffer implements Tradable {
public void setState(State state) { public void setState(State state) {
this.state = state; this.state = state;
stateProperty.set(state); stateProperty.set(state);
if (state == State.AVAILABLE) {
deactivatedByTrigger = false;
}
}
public void deactivate(boolean deactivatedByTrigger) {
this.deactivatedByTrigger = deactivatedByTrigger;
setState(State.DEACTIVATED);
} }
public ReadOnlyObjectProperty<State> stateProperty() { public ReadOnlyObjectProperty<State> stateProperty() {

View File

@ -604,13 +604,14 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
} }
public void deactivateOpenOffer(OpenOffer openOffer, public void deactivateOpenOffer(OpenOffer openOffer,
boolean deactivatedByTrigger,
ResultHandler resultHandler, ResultHandler resultHandler,
ErrorMessageHandler errorMessageHandler) { ErrorMessageHandler errorMessageHandler) {
Offer offer = openOffer.getOffer(); Offer offer = openOffer.getOffer();
if (openOffer.isAvailable()) { if (openOffer.isAvailable()) {
offerBookService.deactivateOffer(offer.getOfferPayload(), offerBookService.deactivateOffer(offer.getOfferPayload(),
() -> { () -> {
openOffer.setState(OpenOffer.State.DEACTIVATED); openOffer.deactivate(deactivatedByTrigger);
requestPersistence(); requestPersistence();
log.debug("deactivateOpenOffer, offerId={}", offer.getId()); log.debug("deactivateOpenOffer, offerId={}", offer.getId());
resultHandler.handleResult(); resultHandler.handleResult();
@ -661,6 +662,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
if (openOffer.isAvailable()) { if (openOffer.isAvailable()) {
deactivateOpenOffer(openOffer, deactivateOpenOffer(openOffer,
false,
resultHandler, resultHandler,
errorMessage -> { errorMessage -> {
offersToBeEdited.remove(openOffer.getId()); offersToBeEdited.remove(openOffer.getId());

View File

@ -92,12 +92,11 @@ public class TriggerPriceService {
.filter(marketPrice -> openOffersByCurrency.containsKey(marketPrice.getCurrencyCode())) .filter(marketPrice -> openOffersByCurrency.containsKey(marketPrice.getCurrencyCode()))
.forEach(marketPrice -> { .forEach(marketPrice -> {
openOffersByCurrency.get(marketPrice.getCurrencyCode()).stream() openOffersByCurrency.get(marketPrice.getCurrencyCode()).stream()
.filter(openOffer -> !openOffer.isDeactivated())
.forEach(openOffer -> checkPriceThreshold(marketPrice, openOffer)); .forEach(openOffer -> checkPriceThreshold(marketPrice, openOffer));
}); });
} }
public static boolean wasTriggered(MarketPrice marketPrice, OpenOffer openOffer) { public static boolean isTriggered(MarketPrice marketPrice, OpenOffer openOffer) {
Price price = openOffer.getOffer().getPrice(); Price price = openOffer.getOffer().getPrice();
if (price == null || marketPrice == null) { if (price == null || marketPrice == null) {
return false; return false;
@ -125,13 +124,12 @@ public class TriggerPriceService {
} }
private void checkPriceThreshold(MarketPrice marketPrice, OpenOffer openOffer) { private void checkPriceThreshold(MarketPrice marketPrice, OpenOffer openOffer) {
if (wasTriggered(marketPrice, openOffer)) { String currencyCode = openOffer.getOffer().getCurrencyCode();
String currencyCode = openOffer.getOffer().getCurrencyCode(); int smallestUnitExponent = CurrencyUtil.isTraditionalCurrency(currencyCode) ?
int smallestUnitExponent = CurrencyUtil.isTraditionalCurrency(currencyCode) ? TraditionalMoney.SMALLEST_UNIT_EXPONENT :
TraditionalMoney.SMALLEST_UNIT_EXPONENT : CryptoMoney.SMALLEST_UNIT_EXPONENT;
CryptoMoney.SMALLEST_UNIT_EXPONENT;
long triggerPrice = openOffer.getTriggerPrice();
if (openOffer.getState() == OpenOffer.State.AVAILABLE && isTriggered(marketPrice, openOffer)) {
log.info("Market price exceeded the trigger price of the open offer.\n" + log.info("Market price exceeded the trigger price of the open offer.\n" +
"We deactivate the open offer with ID {}.\nCurrency: {};\nOffer direction: {};\n" + "We deactivate the open offer with ID {}.\nCurrency: {};\nOffer direction: {};\n" +
"Market price: {};\nTrigger price: {}", "Market price: {};\nTrigger price: {}",
@ -139,14 +137,26 @@ public class TriggerPriceService {
currencyCode, currencyCode,
openOffer.getOffer().getDirection(), openOffer.getOffer().getDirection(),
marketPrice.getPrice(), marketPrice.getPrice(),
MathUtils.scaleDownByPowerOf10(triggerPrice, smallestUnitExponent) MathUtils.scaleDownByPowerOf10(openOffer.getTriggerPrice(), smallestUnitExponent)
); );
openOfferManager.deactivateOpenOffer(openOffer, () -> { openOfferManager.deactivateOpenOffer(openOffer, true, () -> {
}, errorMessage -> {
});
} else if (openOffer.getState() == OpenOffer.State.DEACTIVATED && openOffer.isDeactivatedByTrigger() && !isTriggered(marketPrice, openOffer)) {
log.info("Market price is back within the trigger price of the open offer.\n" +
"We reactivate the open offer with ID {}.\nCurrency: {};\nOffer direction: {};\n" +
"Market price: {};\nTrigger price: {}",
openOffer.getOffer().getShortId(),
currencyCode,
openOffer.getOffer().getDirection(),
marketPrice.getPrice(),
MathUtils.scaleDownByPowerOf10(openOffer.getTriggerPrice(), smallestUnitExponent)
);
openOfferManager.activateOpenOffer(openOffer, () -> {
}, errorMessage -> { }, errorMessage -> {
}); });
} else if (openOffer.getState() == OpenOffer.State.AVAILABLE) {
// TODO: check if open offer's reserve tx is failed or double spend seen
} }
} }

View File

@ -69,7 +69,7 @@ class OpenOffersDataModel extends ActivatableDataModel {
} }
void onDeactivateOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { void onDeactivateOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
openOfferManager.deactivateOpenOffer(openOffer, resultHandler, errorMessageHandler); openOfferManager.deactivateOpenOffer(openOffer, false, resultHandler, errorMessageHandler);
} }
void onRemoveOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { void onRemoveOpenOffer(OpenOffer openOffer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
@ -94,7 +94,7 @@ class OpenOffersDataModel extends ActivatableDataModel {
list.sort((o1, o2) -> o2.getOffer().getDate().compareTo(o1.getOffer().getDate())); list.sort((o1, o2) -> o2.getOffer().getDate().compareTo(o1.getOffer().getDate()));
} }
boolean wasTriggered(OpenOffer openOffer) { boolean isTriggered(OpenOffer openOffer) {
return TriggerPriceService.wasTriggered(priceFeedService.getMarketPrice(openOffer.getOffer().getCurrencyCode()), openOffer); return TriggerPriceService.isTriggered(priceFeedService.getMarketPrice(openOffer.getOffer().getCurrencyCode()), openOffer);
} }
} }

View File

@ -368,7 +368,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
} }
private void onActivateOpenOffer(OpenOffer openOffer) { private void onActivateOpenOffer(OpenOffer openOffer) {
if (model.isBootstrappedOrShowPopup() && !model.dataModel.wasTriggered(openOffer)) { if (model.isBootstrappedOrShowPopup() && !model.dataModel.isTriggered(openOffer)) {
model.onActivateOpenOffer(openOffer, model.onActivateOpenOffer(openOffer,
() -> log.debug("Activate offer was successful"), () -> log.debug("Activate offer was successful"),
(message) -> { (message) -> {
@ -720,7 +720,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
checkBox.setPadding(new Insets(-7, 0, -7, 0)); checkBox.setPadding(new Insets(-7, 0, -7, 0));
checkBox.setGraphic(iconView); checkBox.setGraphic(iconView);
} }
checkBox.setDisable(model.dataModel.wasTriggered(openOffer)); checkBox.setDisable(model.dataModel.isTriggered(openOffer));
checkBox.setOnAction(event -> { checkBox.setOnAction(event -> {
if (openOffer.isDeactivated()) { if (openOffer.isDeactivated()) {
onActivateOpenOffer(openOffer); onActivateOpenOffer(openOffer);
@ -798,7 +798,7 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
boolean triggerPriceSet = item.getOpenOffer().getTriggerPrice() > 0; boolean triggerPriceSet = item.getOpenOffer().getTriggerPrice() > 0;
button.setVisible(triggerPriceSet); button.setVisible(triggerPriceSet);
if (model.dataModel.wasTriggered(item.getOpenOffer())) { if (model.dataModel.isTriggered(item.getOpenOffer())) {
button.getGraphic().getStyleClass().add("warning"); button.getGraphic().getStyleClass().add("warning");
button.setTooltip(new Tooltip(Res.get("openOffer.triggered"))); button.setTooltip(new Tooltip(Res.get("openOffer.triggered")));
} else { } else {

View File

@ -1421,6 +1421,7 @@ message OpenOffer {
string reserve_tx_hex = 10; string reserve_tx_hex = 10;
string reserve_tx_key = 11; string reserve_tx_key = 11;
string challenge = 12; string challenge = 12;
bool deactivated_by_trigger = 13;
} }
message Tradable { message Tradable {