mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-25 07:27:18 +01:00
Merge pull request #4611 from chimp1984/new-trade-statistics
New trade statistics
This commit is contained in:
commit
3687a03695
64 changed files with 1170 additions and 975 deletions
|
@ -41,5 +41,6 @@ public enum Capability {
|
||||||
MEDIATION, // Supports mediation feature
|
MEDIATION, // Supports mediation feature
|
||||||
REFUND_AGENT, // Supports refund agents
|
REFUND_AGENT, // Supports refund agents
|
||||||
TRADE_STATISTICS_HASH_UPDATE, // We changed the hash method in 1.2.0 and that requires update to 1.2.2 for handling it correctly, otherwise the seed nodes have to process too much data.
|
TRADE_STATISTICS_HASH_UPDATE, // We changed the hash method in 1.2.0 and that requires update to 1.2.2 for handling it correctly, otherwise the seed nodes have to process too much data.
|
||||||
NO_ADDRESS_PRE_FIX // At 1.4.0 we removed the prefix filter for mailbox messages. If a peer has that capability we do not sent the prefix.
|
NO_ADDRESS_PRE_FIX, // At 1.4.0 we removed the prefix filter for mailbox messages. If a peer has that capability we do not sent the prefix.
|
||||||
|
TRADE_STATISTICS_3 // We used a new reduced trade statistics model from v1.4.0 on
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,7 +128,6 @@ public class Version {
|
||||||
'}');
|
'}');
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO move to consensus area
|
|
||||||
public static final byte COMPENSATION_REQUEST = (byte) 0x01;
|
public static final byte COMPENSATION_REQUEST = (byte) 0x01;
|
||||||
public static final byte REIMBURSEMENT_REQUEST = (byte) 0x01;
|
public static final byte REIMBURSEMENT_REQUEST = (byte) 0x01;
|
||||||
public static final byte PROPOSAL = (byte) 0x01;
|
public static final byte PROPOSAL = (byte) 0x01;
|
||||||
|
|
|
@ -24,8 +24,5 @@ package bisq.common.consensus;
|
||||||
* Better to use the excludeFromJsonDataMap (annotated with @JsonExclude; used in PaymentAccountPayload) to
|
* Better to use the excludeFromJsonDataMap (annotated with @JsonExclude; used in PaymentAccountPayload) to
|
||||||
* add a key/value pair.
|
* add a key/value pair.
|
||||||
*/
|
*/
|
||||||
// TODO PubKeyRing and NodeAddress (network) are using UsedForTradeContractJson that is why it is in common module,
|
|
||||||
// which is a bit weird... Maybe we need either rename common or split it to util and common where common is common code
|
|
||||||
// used in network and core?
|
|
||||||
public interface UsedForTradeContractJson {
|
public interface UsedForTradeContractJson {
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,6 @@ import java.util.Arrays;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
// TODO is Hmac needed/make sense?
|
|
||||||
public class Encryption {
|
public class Encryption {
|
||||||
private static final Logger log = LoggerFactory.getLogger(Encryption.class);
|
private static final Logger log = LoggerFactory.getLogger(Encryption.class);
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,6 @@ import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import static bisq.common.util.Preconditions.checkDir;
|
import static bisq.common.util.Preconditions.checkDir;
|
||||||
|
|
||||||
// TODO: use a password protection for key storage
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class KeyStorage {
|
public class KeyStorage {
|
||||||
private static final Logger log = LoggerFactory.getLogger(KeyStorage.class);
|
private static final Logger log = LoggerFactory.getLogger(KeyStorage.class);
|
||||||
|
|
|
@ -30,7 +30,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class JsonFileManager {
|
public class JsonFileManager {
|
||||||
private final ThreadPoolExecutor executor = Utilities.getThreadPoolExecutor("JsonFileManagerExecutor", 5, 50, 60);
|
private final ThreadPoolExecutor executor;
|
||||||
private final File dir;
|
private final File dir;
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,6 +41,8 @@ public class JsonFileManager {
|
||||||
public JsonFileManager(File dir) {
|
public JsonFileManager(File dir) {
|
||||||
this.dir = dir;
|
this.dir = dir;
|
||||||
|
|
||||||
|
this.executor = Utilities.getThreadPoolExecutor("JsonFileManagerExecutor", 5, 50, 60);
|
||||||
|
|
||||||
if (!dir.exists())
|
if (!dir.exists())
|
||||||
if (!dir.mkdir())
|
if (!dir.mkdir())
|
||||||
log.warn("make dir failed");
|
log.warn("make dir failed");
|
||||||
|
|
|
@ -48,7 +48,6 @@ public abstract class NetworkEnvelope implements Envelope {
|
||||||
return getNetworkEnvelopeBuilder().build();
|
return getNetworkEnvelopeBuilder().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo remove
|
|
||||||
public protobuf.NetworkEnvelope toProtoNetworkEnvelope() {
|
public protobuf.NetworkEnvelope toProtoNetworkEnvelope() {
|
||||||
return getNetworkEnvelopeBuilder().build();
|
return getNetworkEnvelopeBuilder().build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ import bisq.core.monetary.Price;
|
||||||
import bisq.core.offer.Offer;
|
import bisq.core.offer.Offer;
|
||||||
import bisq.core.offer.OfferPayload;
|
import bisq.core.offer.OfferPayload;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.trade.statistics.TradeStatistics2;
|
import bisq.core.trade.statistics.TradeStatistics3;
|
||||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||||
|
|
||||||
import bisq.common.app.Version;
|
import bisq.common.app.Version;
|
||||||
|
@ -196,7 +196,7 @@ public class CoreApi {
|
||||||
walletsService.removeWalletPassword(password);
|
walletsService.removeWalletPassword(password);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<TradeStatistics2> getTradeStatistics() {
|
public List<TradeStatistics3> getTradeStatistics() {
|
||||||
return new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsSet());
|
return new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import bisq.core.offer.OpenOfferManager;
|
||||||
import bisq.core.setup.CorePersistedDataHost;
|
import bisq.core.setup.CorePersistedDataHost;
|
||||||
import bisq.core.setup.CoreSetup;
|
import bisq.core.setup.CoreSetup;
|
||||||
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
||||||
|
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||||
import bisq.core.trade.txproof.xmr.XmrTxProofService;
|
import bisq.core.trade.txproof.xmr.XmrTxProofService;
|
||||||
|
|
||||||
import bisq.network.p2p.P2PService;
|
import bisq.network.p2p.P2PService;
|
||||||
|
@ -220,6 +221,7 @@ public abstract class BisqExecutable implements GracefulShutDownHandler, BisqSet
|
||||||
|
|
||||||
try {
|
try {
|
||||||
injector.getInstance(ArbitratorManager.class).shutDown();
|
injector.getInstance(ArbitratorManager.class).shutDown();
|
||||||
|
injector.getInstance(TradeStatisticsManager.class).shutDown();
|
||||||
injector.getInstance(XmrTxProofService.class).shutDown();
|
injector.getInstance(XmrTxProofService.class).shutDown();
|
||||||
injector.getInstance(DaoSetup.class).shutDown();
|
injector.getInstance(DaoSetup.class).shutDown();
|
||||||
injector.getInstance(AvoidStandbyModeService.class).shutDown();
|
injector.getInstance(AvoidStandbyModeService.class).shutDown();
|
||||||
|
|
|
@ -154,10 +154,10 @@ public class AssetService implements DaoSetupService, DaoStateListener {
|
||||||
// TradeAmountDateTuple object holding only the data we need.
|
// TradeAmountDateTuple object holding only the data we need.
|
||||||
Map<String, List<TradeAmountDateTuple>> lookupMap = new HashMap<>();
|
Map<String, List<TradeAmountDateTuple>> lookupMap = new HashMap<>();
|
||||||
tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
|
tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
|
||||||
.filter(e -> CurrencyUtil.isCryptoCurrency(e.getBaseCurrency()))
|
.filter(e -> CurrencyUtil.isCryptoCurrency(e.getCurrency()))
|
||||||
.forEach(e -> {
|
.forEach(e -> {
|
||||||
lookupMap.putIfAbsent(e.getBaseCurrency(), new ArrayList<>());
|
lookupMap.putIfAbsent(e.getCurrency(), new ArrayList<>());
|
||||||
lookupMap.get(e.getBaseCurrency()).add(new TradeAmountDateTuple(e.getTradeAmount().getValue(), e.getTradeDate().getTime()));
|
lookupMap.get(e.getCurrency()).add(new TradeAmountDateTuple(e.getAmount(), e.getDate()));
|
||||||
});
|
});
|
||||||
|
|
||||||
getStatefulAssets().stream()
|
getStatefulAssets().stream()
|
||||||
|
|
|
@ -19,7 +19,7 @@ package bisq.core.offer.availability;
|
||||||
|
|
||||||
import bisq.core.support.dispute.agent.DisputeAgent;
|
import bisq.core.support.dispute.agent.DisputeAgent;
|
||||||
import bisq.core.support.dispute.agent.DisputeAgentManager;
|
import bisq.core.support.dispute.agent.DisputeAgentManager;
|
||||||
import bisq.core.trade.statistics.TradeStatistics2;
|
import bisq.core.trade.statistics.TradeStatistics3;
|
||||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||||
|
|
||||||
import bisq.common.util.Tuple2;
|
import bisq.common.util.Tuple2;
|
||||||
|
@ -42,36 +42,37 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class DisputeAgentSelection {
|
public class DisputeAgentSelection {
|
||||||
|
public static final int LOOK_BACK_RANGE = 100;
|
||||||
|
|
||||||
public static <T extends DisputeAgent> T getLeastUsedMediator(TradeStatisticsManager tradeStatisticsManager,
|
public static <T extends DisputeAgent> T getLeastUsedMediator(TradeStatisticsManager tradeStatisticsManager,
|
||||||
DisputeAgentManager<T> disputeAgentManager) {
|
DisputeAgentManager<T> disputeAgentManager) {
|
||||||
return getLeastUsedDisputeAgent(tradeStatisticsManager,
|
return getLeastUsedDisputeAgent(tradeStatisticsManager,
|
||||||
disputeAgentManager,
|
disputeAgentManager,
|
||||||
TradeStatistics2.MEDIATOR_ADDRESS);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends DisputeAgent> T getLeastUsedRefundAgent(TradeStatisticsManager tradeStatisticsManager,
|
public static <T extends DisputeAgent> T getLeastUsedRefundAgent(TradeStatisticsManager tradeStatisticsManager,
|
||||||
DisputeAgentManager<T> disputeAgentManager) {
|
DisputeAgentManager<T> disputeAgentManager) {
|
||||||
return getLeastUsedDisputeAgent(tradeStatisticsManager,
|
return getLeastUsedDisputeAgent(tradeStatisticsManager,
|
||||||
disputeAgentManager,
|
disputeAgentManager,
|
||||||
TradeStatistics2.REFUND_AGENT_ADDRESS);
|
false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T extends DisputeAgent> T getLeastUsedDisputeAgent(TradeStatisticsManager tradeStatisticsManager,
|
private static <T extends DisputeAgent> T getLeastUsedDisputeAgent(TradeStatisticsManager tradeStatisticsManager,
|
||||||
DisputeAgentManager<T> disputeAgentManager,
|
DisputeAgentManager<T> disputeAgentManager,
|
||||||
String extraMapKey) {
|
boolean isMediator) {
|
||||||
// We take last 100 entries from trade statistics
|
// We take last 100 entries from trade statistics
|
||||||
List<TradeStatistics2> list = new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsSet());
|
List<TradeStatistics3> list = new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsSet());
|
||||||
list.sort(Comparator.comparing(TradeStatistics2::getTradeDate));
|
list.sort(Comparator.comparing(TradeStatistics3::getDate));
|
||||||
Collections.reverse(list);
|
Collections.reverse(list);
|
||||||
if (!list.isEmpty()) {
|
if (!list.isEmpty()) {
|
||||||
int max = Math.min(list.size(), 100);
|
int max = Math.min(list.size(), LOOK_BACK_RANGE);
|
||||||
list = list.subList(0, max);
|
list = list.subList(0, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We stored only first 4 chars of disputeAgents onion address
|
// We stored only first 4 chars of disputeAgents onion address
|
||||||
List<String> lastAddressesUsedInTrades = list.stream()
|
List<String> lastAddressesUsedInTrades = list.stream()
|
||||||
.filter(tradeStatistics2 -> tradeStatistics2.getExtraDataMap() != null)
|
.map(tradeStatistics3 -> isMediator ? tradeStatistics3.getMediator() : tradeStatistics3.getRefundAgent())
|
||||||
.map(tradeStatistics2 -> tradeStatistics2.getExtraDataMap().get(extraMapKey))
|
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,7 @@ import bisq.core.payment.payload.VenmoAccountPayload;
|
||||||
import bisq.core.payment.payload.WeChatPayAccountPayload;
|
import bisq.core.payment.payload.WeChatPayAccountPayload;
|
||||||
import bisq.core.payment.payload.WesternUnionAccountPayload;
|
import bisq.core.payment.payload.WesternUnionAccountPayload;
|
||||||
import bisq.core.trade.statistics.TradeStatistics2;
|
import bisq.core.trade.statistics.TradeStatistics2;
|
||||||
|
import bisq.core.trade.statistics.TradeStatistics3;
|
||||||
|
|
||||||
import bisq.common.proto.ProtoResolver;
|
import bisq.common.proto.ProtoResolver;
|
||||||
import bisq.common.proto.ProtobufferRuntimeException;
|
import bisq.common.proto.ProtobufferRuntimeException;
|
||||||
|
@ -178,6 +179,8 @@ public class CoreProtoResolver implements ProtoResolver {
|
||||||
return BlindVotePayload.fromProto(proto.getBlindVotePayload());
|
return BlindVotePayload.fromProto(proto.getBlindVotePayload());
|
||||||
case SIGNED_WITNESS:
|
case SIGNED_WITNESS:
|
||||||
return SignedWitness.fromProto(proto.getSignedWitness());
|
return SignedWitness.fromProto(proto.getSignedWitness());
|
||||||
|
case TRADE_STATISTICS3:
|
||||||
|
return TradeStatistics3.fromProto(proto.getTradeStatistics3());
|
||||||
default:
|
default:
|
||||||
throw new ProtobufferRuntimeException("Unknown proto message case (PB.PersistableNetworkPayload). messageCase=" + proto.getMessageCase());
|
throw new ProtobufferRuntimeException("Unknown proto message case (PB.PersistableNetworkPayload). messageCase=" + proto.getMessageCase());
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,6 @@ import bisq.core.trade.messages.PayoutTxPublishedMessage;
|
||||||
import bisq.core.trade.messages.PeerPublishedDelayedPayoutTxMessage;
|
import bisq.core.trade.messages.PeerPublishedDelayedPayoutTxMessage;
|
||||||
import bisq.core.trade.messages.RefreshTradeStateRequest;
|
import bisq.core.trade.messages.RefreshTradeStateRequest;
|
||||||
import bisq.core.trade.messages.TraderSignedWitnessMessage;
|
import bisq.core.trade.messages.TraderSignedWitnessMessage;
|
||||||
import bisq.core.trade.statistics.TradeStatistics;
|
|
||||||
|
|
||||||
import bisq.network.p2p.AckMessage;
|
import bisq.network.p2p.AckMessage;
|
||||||
import bisq.network.p2p.BundleOfEnvelopes;
|
import bisq.network.p2p.BundleOfEnvelopes;
|
||||||
|
@ -265,9 +264,6 @@ public class CoreNetworkProtoResolver extends CoreProtoResolver implements Netwo
|
||||||
return RefundAgent.fromProto(proto.getRefundAgent());
|
return RefundAgent.fromProto(proto.getRefundAgent());
|
||||||
case FILTER:
|
case FILTER:
|
||||||
return Filter.fromProto(proto.getFilter());
|
return Filter.fromProto(proto.getFilter());
|
||||||
case TRADE_STATISTICS:
|
|
||||||
// Still used to convert TradeStatistics data from pre v0.6 versions
|
|
||||||
return TradeStatistics.fromProto(proto.getTradeStatistics());
|
|
||||||
case MAILBOX_STORAGE_PAYLOAD:
|
case MAILBOX_STORAGE_PAYLOAD:
|
||||||
return MailboxStoragePayload.fromProto(proto.getMailboxStoragePayload());
|
return MailboxStoragePayload.fromProto(proto.getMailboxStoragePayload());
|
||||||
case OFFER_PAYLOAD:
|
case OFFER_PAYLOAD:
|
||||||
|
|
|
@ -39,6 +39,7 @@ import bisq.core.support.dispute.mediation.MediationDisputeList;
|
||||||
import bisq.core.support.dispute.refund.RefundDisputeList;
|
import bisq.core.support.dispute.refund.RefundDisputeList;
|
||||||
import bisq.core.trade.TradableList;
|
import bisq.core.trade.TradableList;
|
||||||
import bisq.core.trade.statistics.TradeStatistics2Store;
|
import bisq.core.trade.statistics.TradeStatistics2Store;
|
||||||
|
import bisq.core.trade.statistics.TradeStatistics3Store;
|
||||||
import bisq.core.user.PreferencesPayload;
|
import bisq.core.user.PreferencesPayload;
|
||||||
import bisq.core.user.UserPayload;
|
import bisq.core.user.UserPayload;
|
||||||
|
|
||||||
|
@ -126,6 +127,8 @@ public class CorePersistenceProtoResolver extends CoreProtoResolver implements P
|
||||||
return UnconfirmedBsqChangeOutputList.fromProto(proto.getUnconfirmedBsqChangeOutputList());
|
return UnconfirmedBsqChangeOutputList.fromProto(proto.getUnconfirmedBsqChangeOutputList());
|
||||||
case SIGNED_WITNESS_STORE:
|
case SIGNED_WITNESS_STORE:
|
||||||
return SignedWitnessStore.fromProto(proto.getSignedWitnessStore());
|
return SignedWitnessStore.fromProto(proto.getSignedWitnessStore());
|
||||||
|
case TRADE_STATISTICS3_STORE:
|
||||||
|
return TradeStatistics3Store.fromProto(proto.getTradeStatistics3Store());
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new ProtobufferRuntimeException("Unknown proto message case(PB.PersistableEnvelope). " +
|
throw new ProtobufferRuntimeException("Unknown proto message case(PB.PersistableEnvelope). " +
|
||||||
|
|
|
@ -52,7 +52,6 @@ import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
// TODO use dao parameters for fee
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class FeeService {
|
public class FeeService {
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ import bisq.core.monetary.Altcoin;
|
||||||
import bisq.core.monetary.Price;
|
import bisq.core.monetary.Price;
|
||||||
import bisq.core.provider.PriceNodeHttpClient;
|
import bisq.core.provider.PriceNodeHttpClient;
|
||||||
import bisq.core.provider.ProvidersRepository;
|
import bisq.core.provider.ProvidersRepository;
|
||||||
import bisq.core.trade.statistics.TradeStatistics2;
|
import bisq.core.trade.statistics.TradeStatistics3;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
|
|
||||||
import bisq.network.http.HttpClient;
|
import bisq.network.http.HttpClient;
|
||||||
|
@ -50,6 +50,7 @@ import javafx.beans.property.StringProperty;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -288,12 +289,12 @@ public class PriceFeedService {
|
||||||
return new Date(epochInMillisAtLastRequest);
|
return new Date(epochInMillisAtLastRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyLatestBisqMarketPrice(Set<TradeStatistics2> tradeStatisticsSet) {
|
public void applyLatestBisqMarketPrice(Set<TradeStatistics3> tradeStatisticsSet) {
|
||||||
// takes about 10 ms for 5000 items
|
// takes about 10 ms for 5000 items
|
||||||
Map<String, List<TradeStatistics2>> mapByCurrencyCode = new HashMap<>();
|
Map<String, List<TradeStatistics3>> mapByCurrencyCode = new HashMap<>();
|
||||||
tradeStatisticsSet.forEach(e -> {
|
tradeStatisticsSet.forEach(e -> {
|
||||||
final List<TradeStatistics2> list;
|
List<TradeStatistics3> list;
|
||||||
final String currencyCode = e.getCurrencyCode();
|
String currencyCode = e.getCurrency();
|
||||||
if (mapByCurrencyCode.containsKey(currencyCode)) {
|
if (mapByCurrencyCode.containsKey(currencyCode)) {
|
||||||
list = mapByCurrencyCode.get(currencyCode);
|
list = mapByCurrencyCode.get(currencyCode);
|
||||||
} else {
|
} else {
|
||||||
|
@ -306,9 +307,9 @@ public class PriceFeedService {
|
||||||
mapByCurrencyCode.values().stream()
|
mapByCurrencyCode.values().stream()
|
||||||
.filter(list -> !list.isEmpty())
|
.filter(list -> !list.isEmpty())
|
||||||
.forEach(list -> {
|
.forEach(list -> {
|
||||||
list.sort((o1, o2) -> o1.getTradeDate().compareTo(o2.getTradeDate()));
|
list.sort(Comparator.comparing(TradeStatistics3::getTradeDate));
|
||||||
TradeStatistics2 tradeStatistics = list.get(list.size() - 1);
|
TradeStatistics3 tradeStatistics = list.get(list.size() - 1);
|
||||||
setBisqMarketPrice(tradeStatistics.getCurrencyCode(), tradeStatistics.getTradePrice());
|
setBisqMarketPrice(tradeStatistics.getCurrency(), tradeStatistics.getTradePrice());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,8 @@ public class CoreNetworkCapabilities {
|
||||||
Capability.SIGNED_ACCOUNT_AGE_WITNESS,
|
Capability.SIGNED_ACCOUNT_AGE_WITNESS,
|
||||||
Capability.REFUND_AGENT,
|
Capability.REFUND_AGENT,
|
||||||
Capability.TRADE_STATISTICS_HASH_UPDATE,
|
Capability.TRADE_STATISTICS_HASH_UPDATE,
|
||||||
Capability.NO_ADDRESS_PRE_FIX
|
Capability.NO_ADDRESS_PRE_FIX,
|
||||||
|
Capability.TRADE_STATISTICS_3
|
||||||
);
|
);
|
||||||
|
|
||||||
if (config.daoActivated) {
|
if (config.daoActivated) {
|
||||||
|
|
|
@ -157,7 +157,7 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
|
||||||
@Override
|
@Override
|
||||||
protected String getDisputeInfo(Dispute dispute) {
|
protected String getDisputeInfo(Dispute dispute) {
|
||||||
String role = Res.get("shared.arbitrator").toLowerCase();
|
String role = Res.get("shared.arbitrator").toLowerCase();
|
||||||
String link = "https://docs.bisq.network/trading-rules.html#legacy-arbitration"; //TODO needs to be created
|
String link = "https://docs.bisq.network/trading-rules.html#legacy-arbitration";
|
||||||
return Res.get("support.initialInfo", role, role, link);
|
return Res.get("support.initialInfo", role, role, link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,6 @@ import bisq.core.account.witness.AccountAgeWitnessStorageService;
|
||||||
import bisq.core.trade.closed.ClosedTradableManager;
|
import bisq.core.trade.closed.ClosedTradableManager;
|
||||||
import bisq.core.trade.failed.FailedTradesManager;
|
import bisq.core.trade.failed.FailedTradesManager;
|
||||||
import bisq.core.trade.statistics.ReferralIdService;
|
import bisq.core.trade.statistics.ReferralIdService;
|
||||||
import bisq.core.trade.statistics.TradeStatistics2StorageService;
|
|
||||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
|
||||||
|
|
||||||
import bisq.common.app.AppModule;
|
import bisq.common.app.AppModule;
|
||||||
import bisq.common.config.Config;
|
import bisq.common.config.Config;
|
||||||
|
@ -46,8 +44,6 @@ public class TradeModule extends AppModule {
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
bind(TradeManager.class).in(Singleton.class);
|
bind(TradeManager.class).in(Singleton.class);
|
||||||
bind(TradeStatisticsManager.class).in(Singleton.class);
|
|
||||||
bind(TradeStatistics2StorageService.class).in(Singleton.class);
|
|
||||||
bind(ClosedTradableManager.class).in(Singleton.class);
|
bind(ClosedTradableManager.class).in(Singleton.class);
|
||||||
bind(FailedTradesManager.class).in(Singleton.class);
|
bind(FailedTradesManager.class).in(Singleton.class);
|
||||||
bind(AccountAgeWitnessService.class).in(Singleton.class);
|
bind(AccountAgeWitnessService.class).in(Singleton.class);
|
||||||
|
|
|
@ -24,7 +24,6 @@ import bisq.core.trade.messages.DepositTxAndDelayedPayoutTxMessage;
|
||||||
import bisq.core.trade.messages.PayoutTxPublishedMessage;
|
import bisq.core.trade.messages.PayoutTxPublishedMessage;
|
||||||
import bisq.core.trade.messages.TradeMessage;
|
import bisq.core.trade.messages.TradeMessage;
|
||||||
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
||||||
import bisq.core.trade.protocol.tasks.PublishTradeStatistics;
|
|
||||||
import bisq.core.trade.protocol.tasks.TradeTask;
|
import bisq.core.trade.protocol.tasks.TradeTask;
|
||||||
import bisq.core.trade.protocol.tasks.buyer.BuyerProcessDepositTxAndDelayedPayoutTxMessage;
|
import bisq.core.trade.protocol.tasks.buyer.BuyerProcessDepositTxAndDelayedPayoutTxMessage;
|
||||||
import bisq.core.trade.protocol.tasks.buyer.BuyerProcessPayoutTxPublishedMessage;
|
import bisq.core.trade.protocol.tasks.buyer.BuyerProcessPayoutTxPublishedMessage;
|
||||||
|
@ -114,8 +113,7 @@ public abstract class BuyerProtocol extends DisputeProtocol {
|
||||||
removeMailboxMessageAfterProcessing(message);
|
removeMailboxMessageAfterProcessing(message);
|
||||||
}))
|
}))
|
||||||
.setup(tasks(BuyerProcessDepositTxAndDelayedPayoutTxMessage.class,
|
.setup(tasks(BuyerProcessDepositTxAndDelayedPayoutTxMessage.class,
|
||||||
BuyerVerifiesFinalDelayedPayoutTx.class,
|
BuyerVerifiesFinalDelayedPayoutTx.class)
|
||||||
PublishTradeStatistics.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
.using(new TradeTaskRunner(trade,
|
||||||
() -> {
|
() -> {
|
||||||
stopTimeout();
|
stopTimeout();
|
||||||
|
|
|
@ -23,13 +23,13 @@ import bisq.core.trade.messages.CounterCurrencyTransferStartedMessage;
|
||||||
import bisq.core.trade.messages.DelayedPayoutTxSignatureResponse;
|
import bisq.core.trade.messages.DelayedPayoutTxSignatureResponse;
|
||||||
import bisq.core.trade.messages.TradeMessage;
|
import bisq.core.trade.messages.TradeMessage;
|
||||||
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
||||||
import bisq.core.trade.protocol.tasks.PublishTradeStatistics;
|
|
||||||
import bisq.core.trade.protocol.tasks.TradeTask;
|
import bisq.core.trade.protocol.tasks.TradeTask;
|
||||||
import bisq.core.trade.protocol.tasks.seller.SellerBroadcastPayoutTx;
|
import bisq.core.trade.protocol.tasks.seller.SellerBroadcastPayoutTx;
|
||||||
import bisq.core.trade.protocol.tasks.seller.SellerFinalizesDelayedPayoutTx;
|
import bisq.core.trade.protocol.tasks.seller.SellerFinalizesDelayedPayoutTx;
|
||||||
import bisq.core.trade.protocol.tasks.seller.SellerProcessCounterCurrencyTransferStartedMessage;
|
import bisq.core.trade.protocol.tasks.seller.SellerProcessCounterCurrencyTransferStartedMessage;
|
||||||
import bisq.core.trade.protocol.tasks.seller.SellerProcessDelayedPayoutTxSignatureResponse;
|
import bisq.core.trade.protocol.tasks.seller.SellerProcessDelayedPayoutTxSignatureResponse;
|
||||||
import bisq.core.trade.protocol.tasks.seller.SellerPublishesDepositTx;
|
import bisq.core.trade.protocol.tasks.seller.SellerPublishesDepositTx;
|
||||||
|
import bisq.core.trade.protocol.tasks.seller.SellerPublishesTradeStatistics;
|
||||||
import bisq.core.trade.protocol.tasks.seller.SellerSendPayoutTxPublishedMessage;
|
import bisq.core.trade.protocol.tasks.seller.SellerSendPayoutTxPublishedMessage;
|
||||||
import bisq.core.trade.protocol.tasks.seller.SellerSendsDepositTxAndDelayedPayoutTxMessage;
|
import bisq.core.trade.protocol.tasks.seller.SellerSendsDepositTxAndDelayedPayoutTxMessage;
|
||||||
import bisq.core.trade.protocol.tasks.seller.SellerSignAndFinalizePayoutTx;
|
import bisq.core.trade.protocol.tasks.seller.SellerSignAndFinalizePayoutTx;
|
||||||
|
@ -39,12 +39,17 @@ import bisq.network.p2p.NodeAddress;
|
||||||
|
|
||||||
import bisq.common.handlers.ErrorMessageHandler;
|
import bisq.common.handlers.ErrorMessageHandler;
|
||||||
import bisq.common.handlers.ResultHandler;
|
import bisq.common.handlers.ResultHandler;
|
||||||
|
import bisq.common.util.Utilities;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public abstract class SellerProtocol extends DisputeProtocol {
|
public abstract class SellerProtocol extends DisputeProtocol {
|
||||||
enum SellerEvent implements FluentProtocol.Event {
|
enum SellerEvent implements FluentProtocol.Event {
|
||||||
|
STARTUP,
|
||||||
PAYMENT_RECEIVED
|
PAYMENT_RECEIVED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +57,27 @@ public abstract class SellerProtocol extends DisputeProtocol {
|
||||||
super(trade);
|
super(trade);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onInitialized() {
|
||||||
|
super.onInitialized();
|
||||||
|
|
||||||
|
// We get called the constructor with any possible state and phase. As we don't want to log an error for such
|
||||||
|
// cases we use the alternative 'given' method instead of 'expect'.
|
||||||
|
|
||||||
|
// We only re-publish for about 2 weeks after 1.4.0 release until most nodes have updated to
|
||||||
|
// achieve sufficient resilience.
|
||||||
|
boolean currentDateBeforeCutOffDate = new Date().before(Utilities.getUTCDate(2020, GregorianCalendar.NOVEMBER, 1));
|
||||||
|
given(anyPhase(Trade.Phase.DEPOSIT_PUBLISHED,
|
||||||
|
Trade.Phase.DEPOSIT_CONFIRMED,
|
||||||
|
Trade.Phase.FIAT_SENT,
|
||||||
|
Trade.Phase.FIAT_RECEIVED,
|
||||||
|
Trade.Phase.PAYOUT_PUBLISHED)
|
||||||
|
.with(SellerEvent.STARTUP)
|
||||||
|
.preCondition(currentDateBeforeCutOffDate))
|
||||||
|
.setup(tasks(SellerPublishesTradeStatistics.class))
|
||||||
|
.executeTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Mailbox
|
// Mailbox
|
||||||
|
@ -79,7 +105,7 @@ public abstract class SellerProtocol extends DisputeProtocol {
|
||||||
SellerFinalizesDelayedPayoutTx.class,
|
SellerFinalizesDelayedPayoutTx.class,
|
||||||
SellerSendsDepositTxAndDelayedPayoutTxMessage.class,
|
SellerSendsDepositTxAndDelayedPayoutTxMessage.class,
|
||||||
SellerPublishesDepositTx.class,
|
SellerPublishesDepositTx.class,
|
||||||
PublishTradeStatistics.class))
|
SellerPublishesTradeStatistics.class))
|
||||||
.run(() -> {
|
.run(() -> {
|
||||||
// We stop timeout here and don't start a new one as the
|
// We stop timeout here and don't start a new one as the
|
||||||
// SellerSendsDepositTxAndDelayedPayoutTxMessage repeats the send the message and has it's own
|
// SellerSendsDepositTxAndDelayedPayoutTxMessage repeats the send the message and has it's own
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of Bisq.
|
|
||||||
*
|
|
||||||
* Bisq is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package bisq.core.trade.protocol.tasks;
|
|
||||||
|
|
||||||
import bisq.core.offer.Offer;
|
|
||||||
import bisq.core.offer.OfferPayload;
|
|
||||||
import bisq.core.trade.Trade;
|
|
||||||
import bisq.core.trade.statistics.TradeStatistics2;
|
|
||||||
|
|
||||||
import bisq.network.p2p.NodeAddress;
|
|
||||||
import bisq.network.p2p.network.NetworkNode;
|
|
||||||
import bisq.network.p2p.network.TorNetworkNode;
|
|
||||||
|
|
||||||
import bisq.common.taskrunner.TaskRunner;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
public class PublishTradeStatistics extends TradeTask {
|
|
||||||
public PublishTradeStatistics(TaskRunner<Trade> taskHandler, Trade trade) {
|
|
||||||
super(taskHandler, trade);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void run() {
|
|
||||||
try {
|
|
||||||
runInterceptHook();
|
|
||||||
|
|
||||||
checkNotNull(trade.getDepositTx());
|
|
||||||
|
|
||||||
Map<String, String> extraDataMap = new HashMap<>();
|
|
||||||
if (processModel.getReferralIdService().getOptionalReferralId().isPresent()) {
|
|
||||||
extraDataMap.put(OfferPayload.REFERRAL_ID, processModel.getReferralIdService().getOptionalReferralId().get());
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeAddress mediatorNodeAddress = checkNotNull(trade.getMediatorNodeAddress());
|
|
||||||
// The first 4 chars are sufficient to identify a mediator.
|
|
||||||
// For testing with regtest/localhost we use the full address as its localhost and would result in
|
|
||||||
// same values for multiple mediators.
|
|
||||||
NetworkNode networkNode = model.getProcessModel().getP2PService().getNetworkNode();
|
|
||||||
String address = networkNode instanceof TorNetworkNode ?
|
|
||||||
mediatorNodeAddress.getFullAddress().substring(0, 4) :
|
|
||||||
mediatorNodeAddress.getFullAddress();
|
|
||||||
extraDataMap.put(TradeStatistics2.MEDIATOR_ADDRESS, address);
|
|
||||||
|
|
||||||
Offer offer = checkNotNull(trade.getOffer());
|
|
||||||
TradeStatistics2 tradeStatistics = new TradeStatistics2(offer.getOfferPayload(),
|
|
||||||
trade.getTradePrice(),
|
|
||||||
checkNotNull(trade.getTradeAmount()),
|
|
||||||
trade.getDate(),
|
|
||||||
trade.getDepositTxId(),
|
|
||||||
extraDataMap);
|
|
||||||
processModel.getP2PService().addPersistableNetworkPayload(tradeStatistics, true);
|
|
||||||
|
|
||||||
complete();
|
|
||||||
} catch (Throwable t) {
|
|
||||||
failed(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.core.trade.protocol.tasks.seller;
|
||||||
|
|
||||||
|
import bisq.core.offer.Offer;
|
||||||
|
import bisq.core.offer.OfferPayload;
|
||||||
|
import bisq.core.trade.Trade;
|
||||||
|
import bisq.core.trade.protocol.tasks.TradeTask;
|
||||||
|
import bisq.core.trade.statistics.TradeStatistics3;
|
||||||
|
|
||||||
|
import bisq.network.p2p.NodeAddress;
|
||||||
|
import bisq.network.p2p.network.NetworkNode;
|
||||||
|
import bisq.network.p2p.network.TorNetworkNode;
|
||||||
|
|
||||||
|
import bisq.common.app.Capability;
|
||||||
|
import bisq.common.taskrunner.TaskRunner;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class SellerPublishesTradeStatistics extends TradeTask {
|
||||||
|
public SellerPublishesTradeStatistics(TaskRunner<Trade> taskHandler, Trade trade) {
|
||||||
|
super(taskHandler, trade);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run() {
|
||||||
|
try {
|
||||||
|
runInterceptHook();
|
||||||
|
|
||||||
|
checkNotNull(trade.getDepositTx());
|
||||||
|
|
||||||
|
processModel.getP2PService().findPeersCapabilities(trade.getTradingPeerNodeAddress())
|
||||||
|
.filter(capabilities -> capabilities.containsAll(Capability.TRADE_STATISTICS_3))
|
||||||
|
.ifPresentOrElse(capabilities -> {
|
||||||
|
// Our peer has updated, so as we are the seller we will publish the trade statistics.
|
||||||
|
// The peer as buyer does not publish anymore with v.1.4.0 (where Capability.TRADE_STATISTICS_3 was added)
|
||||||
|
|
||||||
|
Map<String, String> extraDataMap = new HashMap<>();
|
||||||
|
if (processModel.getReferralIdService().getOptionalReferralId().isPresent()) {
|
||||||
|
extraDataMap.put(OfferPayload.REFERRAL_ID, processModel.getReferralIdService().getOptionalReferralId().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeAddress mediatorNodeAddress = checkNotNull(trade.getMediatorNodeAddress());
|
||||||
|
// The first 4 chars are sufficient to identify a mediator.
|
||||||
|
// For testing with regtest/localhost we use the full address as its localhost and would result in
|
||||||
|
// same values for multiple mediators.
|
||||||
|
NetworkNode networkNode = model.getProcessModel().getP2PService().getNetworkNode();
|
||||||
|
String truncatedMediatorNodeAddress = networkNode instanceof TorNetworkNode ?
|
||||||
|
mediatorNodeAddress.getFullAddress().substring(0, 4) :
|
||||||
|
mediatorNodeAddress.getFullAddress();
|
||||||
|
|
||||||
|
NodeAddress refundAgentNodeAddress = checkNotNull(trade.getRefundAgentNodeAddress());
|
||||||
|
String truncatedRefundAgentNodeAddress = networkNode instanceof TorNetworkNode ?
|
||||||
|
refundAgentNodeAddress.getFullAddress().substring(0, 4) :
|
||||||
|
refundAgentNodeAddress.getFullAddress();
|
||||||
|
|
||||||
|
Offer offer = checkNotNull(trade.getOffer());
|
||||||
|
TradeStatistics3 tradeStatistics = new TradeStatistics3(offer.getCurrencyCode(),
|
||||||
|
trade.getTradePrice().getValue(),
|
||||||
|
trade.getTradeAmountAsLong(),
|
||||||
|
offer.getPaymentMethod().getId(),
|
||||||
|
trade.getTakeOfferDate().getTime(),
|
||||||
|
truncatedMediatorNodeAddress,
|
||||||
|
truncatedRefundAgentNodeAddress,
|
||||||
|
extraDataMap);
|
||||||
|
if (tradeStatistics.isValid()) {
|
||||||
|
log.info("Publishing trade statistics");
|
||||||
|
processModel.getP2PService().addPersistableNetworkPayload(tradeStatistics, true);
|
||||||
|
} else {
|
||||||
|
log.warn("Trade statistics are invalid. We do not publish. {}", tradeStatistics);
|
||||||
|
}
|
||||||
|
|
||||||
|
complete();
|
||||||
|
},
|
||||||
|
() -> {
|
||||||
|
log.info("Our peer does not has updated yet, so they will publish the trade statistics. " +
|
||||||
|
"To avoid duplicates we do not publish from our side.");
|
||||||
|
complete();
|
||||||
|
});
|
||||||
|
} catch (Throwable t) {
|
||||||
|
failed(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,236 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of Bisq.
|
|
||||||
*
|
|
||||||
* Bisq is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package bisq.core.trade.statistics;
|
|
||||||
|
|
||||||
import bisq.core.monetary.Altcoin;
|
|
||||||
import bisq.core.monetary.AltcoinExchangeRate;
|
|
||||||
import bisq.core.monetary.Price;
|
|
||||||
import bisq.core.monetary.Volume;
|
|
||||||
import bisq.core.offer.OfferPayload;
|
|
||||||
|
|
||||||
import bisq.network.p2p.storage.payload.ExpirablePayload;
|
|
||||||
import bisq.network.p2p.storage.payload.ProcessOncePersistableNetworkPayload;
|
|
||||||
import bisq.network.p2p.storage.payload.ProtectedStoragePayload;
|
|
||||||
|
|
||||||
import bisq.common.crypto.Sig;
|
|
||||||
import bisq.common.proto.persistable.PersistablePayload;
|
|
||||||
import bisq.common.util.CollectionUtils;
|
|
||||||
import bisq.common.util.ExtraDataMapValidator;
|
|
||||||
import bisq.common.util.JsonExclude;
|
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
|
||||||
import org.bitcoinj.utils.ExchangeRate;
|
|
||||||
import org.bitcoinj.utils.Fiat;
|
|
||||||
|
|
||||||
import java.security.PublicKey;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import lombok.Value;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Was used in pre v0.6.0 version
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
@Slf4j
|
|
||||||
@EqualsAndHashCode(exclude = {"signaturePubKeyBytes"})
|
|
||||||
@Value
|
|
||||||
public final class TradeStatistics implements ProcessOncePersistableNetworkPayload, ProtectedStoragePayload, ExpirablePayload, PersistablePayload {
|
|
||||||
private final OfferPayload.Direction direction;
|
|
||||||
private final String baseCurrency;
|
|
||||||
private final String counterCurrency;
|
|
||||||
private final String offerPaymentMethod;
|
|
||||||
private final long offerDate;
|
|
||||||
private final boolean offerUseMarketBasedPrice;
|
|
||||||
private final double offerMarketPriceMargin;
|
|
||||||
private final long offerAmount;
|
|
||||||
private final long offerMinAmount;
|
|
||||||
private final String offerId;
|
|
||||||
private final long tradePrice;
|
|
||||||
private final long tradeAmount;
|
|
||||||
private final long tradeDate;
|
|
||||||
private final String depositTxId;
|
|
||||||
@JsonExclude
|
|
||||||
private final byte[] signaturePubKeyBytes;
|
|
||||||
@JsonExclude
|
|
||||||
transient private final PublicKey signaturePubKey;
|
|
||||||
|
|
||||||
// Should be only used in emergency case if we need to add data but do not want to break backward compatibility
|
|
||||||
// at the P2P network storage checks. The hash of the object will be used to verify if the data is valid. Any new
|
|
||||||
// field in a class would break that hash and therefore break the storage mechanism.
|
|
||||||
@Nullable
|
|
||||||
private Map<String, String> extraDataMap;
|
|
||||||
|
|
||||||
public TradeStatistics(OfferPayload offerPayload,
|
|
||||||
Price tradePrice,
|
|
||||||
Coin tradeAmount,
|
|
||||||
Date tradeDate,
|
|
||||||
String depositTxId,
|
|
||||||
byte[] signaturePubKeyBytes) {
|
|
||||||
this(offerPayload.getDirection(),
|
|
||||||
offerPayload.getBaseCurrencyCode(),
|
|
||||||
offerPayload.getCounterCurrencyCode(),
|
|
||||||
offerPayload.getPaymentMethodId(),
|
|
||||||
offerPayload.getDate(),
|
|
||||||
offerPayload.isUseMarketBasedPrice(),
|
|
||||||
offerPayload.getMarketPriceMargin(),
|
|
||||||
offerPayload.getAmount(),
|
|
||||||
offerPayload.getMinAmount(),
|
|
||||||
offerPayload.getId(),
|
|
||||||
tradePrice.getValue(),
|
|
||||||
tradeAmount.value,
|
|
||||||
tradeDate.getTime(),
|
|
||||||
depositTxId,
|
|
||||||
signaturePubKeyBytes,
|
|
||||||
null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// PROTO BUFFER
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
TradeStatistics(OfferPayload.Direction direction,
|
|
||||||
String baseCurrency,
|
|
||||||
String counterCurrency,
|
|
||||||
String offerPaymentMethod,
|
|
||||||
long offerDate,
|
|
||||||
boolean offerUseMarketBasedPrice,
|
|
||||||
double offerMarketPriceMargin,
|
|
||||||
long offerAmount,
|
|
||||||
long offerMinAmount,
|
|
||||||
String offerId,
|
|
||||||
long tradePrice,
|
|
||||||
long tradeAmount,
|
|
||||||
long tradeDate,
|
|
||||||
String depositTxId,
|
|
||||||
byte[] signaturePubKeyBytes,
|
|
||||||
@Nullable Map<String, String> extraDataMap) {
|
|
||||||
this.direction = direction;
|
|
||||||
this.baseCurrency = baseCurrency;
|
|
||||||
this.counterCurrency = counterCurrency;
|
|
||||||
this.offerPaymentMethod = offerPaymentMethod;
|
|
||||||
this.offerDate = offerDate;
|
|
||||||
this.offerUseMarketBasedPrice = offerUseMarketBasedPrice;
|
|
||||||
this.offerMarketPriceMargin = offerMarketPriceMargin;
|
|
||||||
this.offerAmount = offerAmount;
|
|
||||||
this.offerMinAmount = offerMinAmount;
|
|
||||||
this.offerId = offerId;
|
|
||||||
this.tradePrice = tradePrice;
|
|
||||||
this.tradeAmount = tradeAmount;
|
|
||||||
this.tradeDate = tradeDate;
|
|
||||||
this.depositTxId = depositTxId;
|
|
||||||
this.signaturePubKeyBytes = signaturePubKeyBytes;
|
|
||||||
this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap);
|
|
||||||
|
|
||||||
signaturePubKey = Sig.getPublicKeyFromBytes(signaturePubKeyBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public protobuf.StoragePayload toProtoMessage() {
|
|
||||||
final protobuf.TradeStatistics.Builder builder = protobuf.TradeStatistics.newBuilder()
|
|
||||||
.setDirection(OfferPayload.Direction.toProtoMessage(direction))
|
|
||||||
.setBaseCurrency(baseCurrency)
|
|
||||||
.setCounterCurrency(counterCurrency)
|
|
||||||
.setPaymentMethodId(offerPaymentMethod)
|
|
||||||
.setOfferDate(offerDate)
|
|
||||||
.setOfferUseMarketBasedPrice(offerUseMarketBasedPrice)
|
|
||||||
.setOfferMarketPriceMargin(offerMarketPriceMargin)
|
|
||||||
.setOfferAmount(offerAmount)
|
|
||||||
.setOfferMinAmount(offerMinAmount)
|
|
||||||
.setOfferId(offerId)
|
|
||||||
.setTradePrice(tradePrice)
|
|
||||||
.setTradeAmount(tradeAmount)
|
|
||||||
.setTradeDate(tradeDate)
|
|
||||||
.setDepositTxId(depositTxId)
|
|
||||||
.setSignaturePubKeyBytes(ByteString.copyFrom(signaturePubKeyBytes));
|
|
||||||
Optional.ofNullable(extraDataMap).ifPresent(builder::putAllExtraData);
|
|
||||||
return protobuf.StoragePayload.newBuilder().setTradeStatistics(builder).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public protobuf.TradeStatistics toProtoTradeStatistics() {
|
|
||||||
return toProtoMessage().getTradeStatistics();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TradeStatistics fromProto(protobuf.TradeStatistics proto) {
|
|
||||||
return new TradeStatistics(
|
|
||||||
OfferPayload.Direction.fromProto(proto.getDirection()),
|
|
||||||
proto.getBaseCurrency(),
|
|
||||||
proto.getCounterCurrency(),
|
|
||||||
proto.getPaymentMethodId(),
|
|
||||||
proto.getOfferDate(),
|
|
||||||
proto.getOfferUseMarketBasedPrice(),
|
|
||||||
proto.getOfferMarketPriceMargin(),
|
|
||||||
proto.getOfferAmount(),
|
|
||||||
proto.getOfferMinAmount(),
|
|
||||||
proto.getOfferId(),
|
|
||||||
proto.getTradePrice(),
|
|
||||||
proto.getTradeAmount(),
|
|
||||||
proto.getTradeDate(),
|
|
||||||
proto.getDepositTxId(),
|
|
||||||
proto.getSignaturePubKeyBytes().toByteArray(),
|
|
||||||
CollectionUtils.isEmpty(proto.getExtraDataMap()) ? null : proto.getExtraDataMap());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Getters
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getTTL() {
|
|
||||||
return TimeUnit.DAYS.toMillis(30);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PublicKey getOwnerPubKey() {
|
|
||||||
return signaturePubKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Date getTradeDate() {
|
|
||||||
return new Date(tradeDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Price getTradePrice() {
|
|
||||||
return Price.valueOf(getCurrencyCode(), tradePrice);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCurrencyCode() {
|
|
||||||
return baseCurrency.equals("BTC") ? counterCurrency : baseCurrency;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Coin getTradeAmount() {
|
|
||||||
return Coin.valueOf(tradeAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Volume getTradeVolume() {
|
|
||||||
if (getTradePrice().getMonetary() instanceof Altcoin)
|
|
||||||
return new Volume(new AltcoinExchangeRate((Altcoin) getTradePrice().getMonetary()).coinToAltcoin(getTradeAmount()));
|
|
||||||
else
|
|
||||||
return new Volume(new ExchangeRate((Fiat) getTradePrice().getMonetary()).coinToFiat(getTradeAmount()));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -61,7 +61,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
/**
|
/**
|
||||||
* Serialized size is about 180-210 byte. Nov 2017 we have 5500 objects
|
* Serialized size is about 180-210 byte. Nov 2017 we have 5500 objects
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Value
|
@Value
|
||||||
public final class TradeStatistics2 implements ProcessOncePersistableNetworkPayload, PersistableNetworkPayload,
|
public final class TradeStatistics2 implements ProcessOncePersistableNetworkPayload, PersistableNetworkPayload,
|
||||||
|
|
|
@ -29,6 +29,7 @@ import javax.inject.Named;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
@ -65,11 +66,29 @@ public class TradeStatistics2StorageService extends MapStoreService<TradeStatist
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<P2PDataStorage.ByteArray, PersistableNetworkPayload> getMap() {
|
public Map<P2PDataStorage.ByteArray, PersistableNetworkPayload> getMap() {
|
||||||
return store.getMap();
|
// As it is used for data request and response and we do not want to send any old trade stat data anymore.
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We overwrite that method to receive old trade stats from the network. As we deactivated getMap to not deliver
|
||||||
|
// hashes we needed to use the getMapOfAllData method to actually store the data.
|
||||||
|
// That's a bit of a hack but it's just for transition and can be removed after a few months anyway.
|
||||||
|
// Alternatively we could create a new interface to handle it differently on the other client classes but that
|
||||||
|
// seems to be not justified as it is needed only temporarily.
|
||||||
|
@Override
|
||||||
|
protected PersistableNetworkPayload putIfAbsent(P2PDataStorage.ByteArray hash, PersistableNetworkPayload payload) {
|
||||||
|
PersistableNetworkPayload previous = getMapOfAllData().putIfAbsent(hash, payload);
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void readFromResources(String postFix) {
|
||||||
|
// We do not attempt to read from resources as that file is not provided anymore
|
||||||
|
readStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<P2PDataStorage.ByteArray, PersistableNetworkPayload> getMapOfAllData() {
|
public Map<P2PDataStorage.ByteArray, PersistableNetworkPayload> getMapOfAllData() {
|
||||||
return getMap();
|
return store.getMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,355 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.core.trade.statistics;
|
||||||
|
|
||||||
|
import bisq.core.monetary.Altcoin;
|
||||||
|
import bisq.core.monetary.AltcoinExchangeRate;
|
||||||
|
import bisq.core.monetary.Price;
|
||||||
|
import bisq.core.monetary.Volume;
|
||||||
|
import bisq.core.offer.OfferUtil;
|
||||||
|
|
||||||
|
import bisq.network.p2p.storage.payload.CapabilityRequiringPayload;
|
||||||
|
import bisq.network.p2p.storage.payload.PersistableNetworkPayload;
|
||||||
|
import bisq.network.p2p.storage.payload.ProcessOncePersistableNetworkPayload;
|
||||||
|
|
||||||
|
import bisq.common.app.Capabilities;
|
||||||
|
import bisq.common.app.Capability;
|
||||||
|
import bisq.common.crypto.Hash;
|
||||||
|
import bisq.common.proto.ProtoUtil;
|
||||||
|
import bisq.common.util.CollectionUtils;
|
||||||
|
import bisq.common.util.ExtraDataMapValidator;
|
||||||
|
import bisq.common.util.JsonExclude;
|
||||||
|
import bisq.common.util.Utilities;
|
||||||
|
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
|
|
||||||
|
import org.bitcoinj.core.Coin;
|
||||||
|
import org.bitcoinj.utils.ExchangeRate;
|
||||||
|
import org.bitcoinj.utils.Fiat;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This new trade statistics class uses only the bare minimum of data.
|
||||||
|
* Data size is about 50 bytes in average
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Getter
|
||||||
|
public final class TradeStatistics3 implements ProcessOncePersistableNetworkPayload, PersistableNetworkPayload,
|
||||||
|
CapabilityRequiringPayload {
|
||||||
|
|
||||||
|
// This enum must not change the order as we use the ordinal for storage to reduce data size.
|
||||||
|
// The payment method string can be quite long and would consume 15% more space.
|
||||||
|
// When we get a new payment method we can add it to the enum at the end. Old users would add it as string if not
|
||||||
|
// recognized.
|
||||||
|
private enum PaymentMethodMapper {
|
||||||
|
OK_PAY,
|
||||||
|
CASH_APP,
|
||||||
|
VENMO,
|
||||||
|
AUSTRALIA_PAYID, // seems there is a dev trade
|
||||||
|
UPHOLD,
|
||||||
|
MONEY_BEAM,
|
||||||
|
POPMONEY,
|
||||||
|
REVOLUT,
|
||||||
|
PERFECT_MONEY,
|
||||||
|
SEPA,
|
||||||
|
SEPA_INSTANT,
|
||||||
|
FASTER_PAYMENTS,
|
||||||
|
NATIONAL_BANK,
|
||||||
|
JAPAN_BANK,
|
||||||
|
SAME_BANK,
|
||||||
|
SPECIFIC_BANKS,
|
||||||
|
SWISH,
|
||||||
|
ALI_PAY,
|
||||||
|
WECHAT_PAY,
|
||||||
|
CLEAR_X_CHANGE,
|
||||||
|
CHASE_QUICK_PAY,
|
||||||
|
INTERAC_E_TRANSFER,
|
||||||
|
US_POSTAL_MONEY_ORDER,
|
||||||
|
CASH_DEPOSIT,
|
||||||
|
MONEY_GRAM,
|
||||||
|
WESTERN_UNION,
|
||||||
|
HAL_CASH,
|
||||||
|
F2F,
|
||||||
|
BLOCK_CHAINS,
|
||||||
|
PROMPT_PAY,
|
||||||
|
ADVANCED_CASH,
|
||||||
|
BLOCK_CHAINS_INSTANT
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String currency;
|
||||||
|
private final long price;
|
||||||
|
private final long amount;
|
||||||
|
private final String paymentMethod;
|
||||||
|
// As only seller is publishing it is the sellers trade date
|
||||||
|
private final long date;
|
||||||
|
|
||||||
|
// Old converted trade stat objects might not have it set
|
||||||
|
@Nullable
|
||||||
|
@JsonExclude
|
||||||
|
private String mediator;
|
||||||
|
@Nullable
|
||||||
|
@JsonExclude
|
||||||
|
private String refundAgent;
|
||||||
|
|
||||||
|
// todo should we add referrerId as well? get added to extra map atm but not used so far
|
||||||
|
|
||||||
|
// Hash get set in constructor from json of all the other data fields (with hash = null).
|
||||||
|
@JsonExclude
|
||||||
|
private final byte[] hash;
|
||||||
|
// Should be only used in emergency case if we need to add data but do not want to break backward compatibility
|
||||||
|
// at the P2P network storage checks. The hash of the object will be used to verify if the data is valid. Any new
|
||||||
|
// field in a class would break that hash and therefore break the storage mechanism.
|
||||||
|
@Nullable
|
||||||
|
@JsonExclude
|
||||||
|
private final Map<String, String> extraDataMap;
|
||||||
|
|
||||||
|
public TradeStatistics3(String currency,
|
||||||
|
long price,
|
||||||
|
long amount,
|
||||||
|
String paymentMethod,
|
||||||
|
long date,
|
||||||
|
String mediator,
|
||||||
|
String refundAgent,
|
||||||
|
@Nullable Map<String, String> extraDataMap) {
|
||||||
|
this(currency,
|
||||||
|
price,
|
||||||
|
amount,
|
||||||
|
paymentMethod,
|
||||||
|
date,
|
||||||
|
mediator,
|
||||||
|
refundAgent,
|
||||||
|
extraDataMap,
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used from conversion method where we use the hash of the TradeStatistics2 objects to avoid duplicate entries
|
||||||
|
public TradeStatistics3(String currency,
|
||||||
|
long price,
|
||||||
|
long amount,
|
||||||
|
String paymentMethod,
|
||||||
|
long date,
|
||||||
|
String mediator,
|
||||||
|
String refundAgent,
|
||||||
|
@Nullable byte[] hash) {
|
||||||
|
this(currency,
|
||||||
|
price,
|
||||||
|
amount,
|
||||||
|
paymentMethod,
|
||||||
|
date,
|
||||||
|
mediator,
|
||||||
|
refundAgent,
|
||||||
|
null,
|
||||||
|
hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// PROTO BUFFER
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public TradeStatistics3(String currency,
|
||||||
|
long price,
|
||||||
|
long amount,
|
||||||
|
String paymentMethod,
|
||||||
|
long date,
|
||||||
|
@Nullable String mediator,
|
||||||
|
@Nullable String refundAgent,
|
||||||
|
@Nullable Map<String, String> extraDataMap,
|
||||||
|
@Nullable byte[] hash) {
|
||||||
|
this.currency = currency;
|
||||||
|
this.price = price;
|
||||||
|
this.amount = amount;
|
||||||
|
String tempPaymentMethod;
|
||||||
|
try {
|
||||||
|
tempPaymentMethod = String.valueOf(PaymentMethodMapper.valueOf(paymentMethod).ordinal());
|
||||||
|
} catch (Throwable t) {
|
||||||
|
tempPaymentMethod = paymentMethod;
|
||||||
|
}
|
||||||
|
this.paymentMethod = tempPaymentMethod;
|
||||||
|
this.date = date;
|
||||||
|
this.mediator = mediator;
|
||||||
|
this.refundAgent = refundAgent;
|
||||||
|
this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap);
|
||||||
|
|
||||||
|
this.hash = hash == null ? createHash() : hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] createHash() {
|
||||||
|
// We create hash from all fields excluding hash itself. We use json as simple data serialisation.
|
||||||
|
// TradeDate is different for both peers so we ignore it for hash. ExtraDataMap is ignored as well as at
|
||||||
|
// software updates we might have different entries which would cause a different hash.
|
||||||
|
return Hash.getSha256Ripemd160hash(Utilities.objectToJson(this).getBytes(Charsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
private protobuf.TradeStatistics3.Builder getBuilder() {
|
||||||
|
protobuf.TradeStatistics3.Builder builder = protobuf.TradeStatistics3.newBuilder()
|
||||||
|
.setCurrency(currency)
|
||||||
|
.setPrice(price)
|
||||||
|
.setAmount(amount)
|
||||||
|
.setPaymentMethod(paymentMethod)
|
||||||
|
.setDate(date)
|
||||||
|
.setHash(ByteString.copyFrom(hash));
|
||||||
|
Optional.ofNullable(mediator).ifPresent(builder::setMediator);
|
||||||
|
Optional.ofNullable(refundAgent).ifPresent(builder::setRefundAgent);
|
||||||
|
Optional.ofNullable(extraDataMap).ifPresent(builder::putAllExtraData);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public protobuf.TradeStatistics3 toProtoTradeStatistics3() {
|
||||||
|
return getBuilder().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public protobuf.PersistableNetworkPayload toProtoMessage() {
|
||||||
|
return protobuf.PersistableNetworkPayload.newBuilder().setTradeStatistics3(getBuilder()).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TradeStatistics3 fromProto(protobuf.TradeStatistics3 proto) {
|
||||||
|
return new TradeStatistics3(
|
||||||
|
proto.getCurrency(),
|
||||||
|
proto.getPrice(),
|
||||||
|
proto.getAmount(),
|
||||||
|
proto.getPaymentMethod(),
|
||||||
|
proto.getDate(),
|
||||||
|
ProtoUtil.stringOrNullFromProto(proto.getMediator()),
|
||||||
|
ProtoUtil.stringOrNullFromProto(proto.getRefundAgent()),
|
||||||
|
CollectionUtils.isEmpty(proto.getExtraDataMap()) ? null : proto.getExtraDataMap(),
|
||||||
|
proto.getHash().toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// API
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getHash() {
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean verifyHashSize() {
|
||||||
|
checkNotNull(hash, "hash must not be null");
|
||||||
|
return hash.length == 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Capabilities getRequiredCapabilities() {
|
||||||
|
return new Capabilities(Capability.TRADE_STATISTICS_3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pruneOptionalData() {
|
||||||
|
mediator = null;
|
||||||
|
refundAgent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPaymentMethod() {
|
||||||
|
try {
|
||||||
|
return PaymentMethodMapper.values()[Integer.parseInt(paymentMethod)].name();
|
||||||
|
} catch (Throwable ignore) {
|
||||||
|
return paymentMethod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getTradeDate() {
|
||||||
|
return new Date(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Price getTradePrice() {
|
||||||
|
return Price.valueOf(currency, price);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Coin getTradeAmount() {
|
||||||
|
return Coin.valueOf(amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Volume getTradeVolume() {
|
||||||
|
if (getTradePrice().getMonetary() instanceof Altcoin) {
|
||||||
|
return new Volume(new AltcoinExchangeRate((Altcoin) getTradePrice().getMonetary()).coinToAltcoin(getTradeAmount()));
|
||||||
|
} else {
|
||||||
|
Volume volume = new Volume(new ExchangeRate((Fiat) getTradePrice().getMonetary()).coinToFiat(getTradeAmount()));
|
||||||
|
return OfferUtil.getRoundedFiatVolume(volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValid() {
|
||||||
|
return amount > 0 &&
|
||||||
|
price > 0 &&
|
||||||
|
date > 0 &&
|
||||||
|
paymentMethod != null &&
|
||||||
|
!paymentMethod.isEmpty() &&
|
||||||
|
currency != null &&
|
||||||
|
!currency.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof TradeStatistics3)) return false;
|
||||||
|
|
||||||
|
TradeStatistics3 that = (TradeStatistics3) o;
|
||||||
|
|
||||||
|
if (price != that.price) return false;
|
||||||
|
if (amount != that.amount) return false;
|
||||||
|
if (date != that.date) return false;
|
||||||
|
if (currency != null ? !currency.equals(that.currency) : that.currency != null) return false;
|
||||||
|
if (paymentMethod != null ? !paymentMethod.equals(that.paymentMethod) : that.paymentMethod != null)
|
||||||
|
return false;
|
||||||
|
return Arrays.equals(hash, that.hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = currency != null ? currency.hashCode() : 0;
|
||||||
|
result = 31 * result + (int) (price ^ (price >>> 32));
|
||||||
|
result = 31 * result + (int) (amount ^ (amount >>> 32));
|
||||||
|
result = 31 * result + (paymentMethod != null ? paymentMethod.hashCode() : 0);
|
||||||
|
result = 31 * result + (int) (date ^ (date >>> 32));
|
||||||
|
result = 31 * result + Arrays.hashCode(hash);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TradeStatistics3{" +
|
||||||
|
"\n currency='" + currency + '\'' +
|
||||||
|
",\n price=" + price +
|
||||||
|
",\n amount=" + amount +
|
||||||
|
",\n paymentMethod='" + paymentMethod + '\'' +
|
||||||
|
",\n date=" + date +
|
||||||
|
",\n mediator='" + mediator + '\'' +
|
||||||
|
",\n refundAgent='" + refundAgent + '\'' +
|
||||||
|
",\n hash=" + Utilities.bytesAsHexString(hash) +
|
||||||
|
",\n extraDataMap=" + extraDataMap +
|
||||||
|
"\n}";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.core.trade.statistics;
|
||||||
|
|
||||||
|
import bisq.network.p2p.storage.payload.PersistableNetworkPayload;
|
||||||
|
import bisq.network.p2p.storage.persistence.HistoricalDataStoreService;
|
||||||
|
|
||||||
|
import bisq.common.config.Config;
|
||||||
|
import bisq.common.persistence.PersistenceManager;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Slf4j
|
||||||
|
public class TradeStatistics3StorageService extends HistoricalDataStoreService<TradeStatistics3Store> {
|
||||||
|
private static final String FILE_NAME = "TradeStatistics3Store";
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public TradeStatistics3StorageService(@Named(Config.STORAGE_DIR) File storageDir,
|
||||||
|
PersistenceManager<TradeStatistics3Store> persistenceManager) {
|
||||||
|
super(storageDir, persistenceManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// API
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFileName() {
|
||||||
|
return FILE_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initializePersistenceManager() {
|
||||||
|
persistenceManager.initialize(store, PersistenceManager.Source.NETWORK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canHandle(PersistableNetworkPayload payload) {
|
||||||
|
return payload instanceof TradeStatistics3;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Protected
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TradeStatistics3Store createStore() {
|
||||||
|
return new TradeStatistics3Store();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void persistNow() {
|
||||||
|
persistenceManager.persistNow(() -> {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.core.trade.statistics;
|
||||||
|
|
||||||
|
import bisq.network.p2p.storage.P2PDataStorage;
|
||||||
|
import bisq.network.p2p.storage.persistence.PersistableNetworkPayloadStore;
|
||||||
|
|
||||||
|
import com.google.protobuf.Message;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We store only the payload in the PB file to save disc space. The hash of the payload can be created anyway and
|
||||||
|
* is only used as key in the map. So we have a hybrid data structure which is represented as list in the protobuffer
|
||||||
|
* definition and provide a hashMap for the domain access.
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class TradeStatistics3Store extends PersistableNetworkPayloadStore<TradeStatistics3> {
|
||||||
|
|
||||||
|
TradeStatistics3Store() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// PROTO BUFFER
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private TradeStatistics3Store(List<TradeStatistics3> list) {
|
||||||
|
list.forEach(item -> map.put(new P2PDataStorage.ByteArray(item.getHash()), item));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Message toProtoMessage() {
|
||||||
|
return protobuf.PersistableEnvelope.newBuilder()
|
||||||
|
.setTradeStatistics3Store(getBuilder())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private protobuf.TradeStatistics3Store.Builder getBuilder() {
|
||||||
|
List<protobuf.TradeStatistics3> protoList = map.values().stream()
|
||||||
|
.map(payload -> (TradeStatistics3) payload)
|
||||||
|
.map(TradeStatistics3::toProtoTradeStatistics3)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
return protobuf.TradeStatistics3Store.newBuilder().addAllItems(protoList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TradeStatistics3Store fromProto(protobuf.TradeStatistics3Store proto) {
|
||||||
|
List<TradeStatistics3> list = proto.getItemsList().stream()
|
||||||
|
.map(TradeStatistics3::fromProto).collect(Collectors.toList());
|
||||||
|
return new TradeStatistics3Store(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsKey(P2PDataStorage.ByteArray hash) {
|
||||||
|
return map.containsKey(hash);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.core.trade.statistics;
|
||||||
|
|
||||||
|
import bisq.core.offer.availability.DisputeAgentSelection;
|
||||||
|
|
||||||
|
import bisq.network.p2p.BootstrapListener;
|
||||||
|
import bisq.network.p2p.P2PService;
|
||||||
|
import bisq.network.p2p.storage.P2PDataStorage;
|
||||||
|
import bisq.network.p2p.storage.payload.PersistableNetworkPayload;
|
||||||
|
import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreService;
|
||||||
|
|
||||||
|
import bisq.common.UserThread;
|
||||||
|
import bisq.common.config.Config;
|
||||||
|
import bisq.common.file.FileUtil;
|
||||||
|
import bisq.common.util.Utilities;
|
||||||
|
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Slf4j
|
||||||
|
public class TradeStatisticsConverter {
|
||||||
|
|
||||||
|
private ExecutorService executor;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public TradeStatisticsConverter(P2PService p2PService,
|
||||||
|
P2PDataStorage p2PDataStorage,
|
||||||
|
TradeStatistics2StorageService tradeStatistics2StorageService,
|
||||||
|
TradeStatistics3StorageService tradeStatistics3StorageService,
|
||||||
|
AppendOnlyDataStoreService appendOnlyDataStoreService,
|
||||||
|
@Named(Config.STORAGE_DIR) File storageDir) {
|
||||||
|
File tradeStatistics2Store = new File(storageDir, "TradeStatistics2Store");
|
||||||
|
appendOnlyDataStoreService.addService(tradeStatistics2StorageService);
|
||||||
|
|
||||||
|
p2PService.addP2PServiceListener(new BootstrapListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTorNodeReady() {
|
||||||
|
if (!tradeStatistics2Store.exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
executor = Utilities.getSingleThreadExecutor("TradeStatisticsConverter");
|
||||||
|
executor.submit(() -> {
|
||||||
|
// We convert early once tor is initialized but still not ready to receive data
|
||||||
|
Map<P2PDataStorage.ByteArray, PersistableNetworkPayload> tempMap = new HashMap<>();
|
||||||
|
convertToTradeStatistics3(tradeStatistics2StorageService.getMapOfAllData().values())
|
||||||
|
.forEach(e -> tempMap.put(new P2PDataStorage.ByteArray(e.getHash()), e));
|
||||||
|
|
||||||
|
// We map to user thread to avoid potential threading issues
|
||||||
|
UserThread.execute(() -> {
|
||||||
|
tradeStatistics3StorageService.getMapOfLiveData().putAll(tempMap);
|
||||||
|
tradeStatistics3StorageService.persistNow();
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
log.info("We delete now the old trade statistics file as it was converted to the new format.");
|
||||||
|
FileUtil.deleteFileIfExists(tradeStatistics2Store);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
log.error(e.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpdatedDataReceived() {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// We listen to old TradeStatistics2 objects, convert and store them and rebroadcast.
|
||||||
|
p2PDataStorage.addAppendOnlyDataStoreListener(payload -> {
|
||||||
|
if (payload instanceof TradeStatistics2) {
|
||||||
|
TradeStatistics3 tradeStatistics3 = convertToTradeStatistics3((TradeStatistics2) payload, true);
|
||||||
|
// We add it to the p2PDataStorage, which handles to get the data stored in the maps and maybe
|
||||||
|
// re-broadcast as tradeStatistics3 object if not already received.
|
||||||
|
p2PDataStorage.addPersistableNetworkPayload(tradeStatistics3, null, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutDown() {
|
||||||
|
if (executor != null)
|
||||||
|
executor.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<TradeStatistics3> convertToTradeStatistics3(Collection<PersistableNetworkPayload> persistableNetworkPayloads) {
|
||||||
|
List<TradeStatistics3> list = new ArrayList<>();
|
||||||
|
long ts = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// We might have duplicate entries from both traders as the trade date was different from old clients.
|
||||||
|
// This should not be the case with converting old persisted data as we did filter those out but it is the case
|
||||||
|
// when we receive old trade stat objects from the network of 2 not updated traders.
|
||||||
|
// The hash was ignoring the trade date so we use that to get a unique list
|
||||||
|
Map<P2PDataStorage.ByteArray, TradeStatistics2> mapWithoutDuplicates = new HashMap<>();
|
||||||
|
persistableNetworkPayloads.stream()
|
||||||
|
.filter(e -> e instanceof TradeStatistics2)
|
||||||
|
.map(e -> (TradeStatistics2) e)
|
||||||
|
.filter(TradeStatistics2::isValid)
|
||||||
|
.forEach(e -> mapWithoutDuplicates.putIfAbsent(new P2PDataStorage.ByteArray(e.getHash()), e));
|
||||||
|
|
||||||
|
log.info("We convert the existing {} trade statistics objects to the new format.", mapWithoutDuplicates.size());
|
||||||
|
|
||||||
|
mapWithoutDuplicates.values().stream()
|
||||||
|
.map(e -> convertToTradeStatistics3(e, false))
|
||||||
|
.filter(TradeStatistics3::isValid)
|
||||||
|
.forEach(list::add);
|
||||||
|
|
||||||
|
int size = list.size();
|
||||||
|
log.info("Conversion to {} new trade statistic objects has been completed after {} ms",
|
||||||
|
size, System.currentTimeMillis() - ts);
|
||||||
|
|
||||||
|
// We prune mediator and refundAgent data from all objects but the last 100 as we only use the
|
||||||
|
// last 100 entries (DisputeAgentSelection.LOOK_BACK_RANGE).
|
||||||
|
list.sort(Comparator.comparing(TradeStatistics3::getDate));
|
||||||
|
if (size > DisputeAgentSelection.LOOK_BACK_RANGE) {
|
||||||
|
int start = size - DisputeAgentSelection.LOOK_BACK_RANGE;
|
||||||
|
for (int i = start; i < size; i++) {
|
||||||
|
TradeStatistics3 tradeStatistics3 = list.get(i);
|
||||||
|
tradeStatistics3.pruneOptionalData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TradeStatistics3 convertToTradeStatistics3(TradeStatistics2 tradeStatistics2, boolean fromNetwork) {
|
||||||
|
Map<String, String> extraDataMap = tradeStatistics2.getExtraDataMap();
|
||||||
|
String mediator = extraDataMap != null ? extraDataMap.get(TradeStatistics2.MEDIATOR_ADDRESS) : null;
|
||||||
|
String refundAgent = extraDataMap != null ? extraDataMap.get(TradeStatistics2.REFUND_AGENT_ADDRESS) : null;
|
||||||
|
long time = tradeStatistics2.getTradeDate().getTime();
|
||||||
|
byte[] hash = null;
|
||||||
|
if (fromNetwork) {
|
||||||
|
// We need to avoid that we duplicate tradeStatistics2 objects in case both traders have not udpated yet.
|
||||||
|
// Before v1.4.0 both traders published the trade statistics. If one trader has updated he will check
|
||||||
|
// the capabilities of the peer and if the peer has not updated he will leave publishing to the peer, so we
|
||||||
|
// do not have the problem of duplicated objects.
|
||||||
|
// To ensure we add only one object we will use the hash of the tradeStatistics2 object which is the same
|
||||||
|
// for both traders as it excluded the trade date which is different for both.
|
||||||
|
hash = tradeStatistics2.getHash();
|
||||||
|
}
|
||||||
|
return new TradeStatistics3(tradeStatistics2.getCurrencyCode(),
|
||||||
|
tradeStatistics2.getTradePrice().getValue(),
|
||||||
|
tradeStatistics2.getTradeAmount().getValue(),
|
||||||
|
tradeStatistics2.getOfferPaymentMethod(),
|
||||||
|
time,
|
||||||
|
mediator,
|
||||||
|
refundAgent,
|
||||||
|
hash);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,6 @@ import bisq.core.locale.CurrencyUtil;
|
||||||
import bisq.core.locale.Res;
|
import bisq.core.locale.Res;
|
||||||
import bisq.core.monetary.Price;
|
import bisq.core.monetary.Price;
|
||||||
import bisq.core.monetary.Volume;
|
import bisq.core.monetary.Volume;
|
||||||
import bisq.core.offer.OfferPayload;
|
|
||||||
|
|
||||||
import bisq.common.util.MathUtils;
|
import bisq.common.util.MathUtils;
|
||||||
|
|
||||||
|
@ -38,65 +37,44 @@ import javax.annotation.concurrent.Immutable;
|
||||||
@ToString
|
@ToString
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public final class TradeStatisticsForJson {
|
public final class TradeStatisticsForJson {
|
||||||
|
|
||||||
public final String currency;
|
public final String currency;
|
||||||
public final OfferPayload.Direction direction;
|
|
||||||
public final long tradePrice;
|
public final long tradePrice;
|
||||||
public final long tradeAmount;
|
public final long tradeAmount;
|
||||||
public final long tradeDate;
|
public final long tradeDate;
|
||||||
public final String paymentMethod;
|
public final String paymentMethod;
|
||||||
public final long offerDate;
|
|
||||||
public final boolean useMarketBasedPrice;
|
|
||||||
public final double marketPriceMargin;
|
|
||||||
public final long offerAmount;
|
|
||||||
public final long offerMinAmount;
|
|
||||||
public final String offerId;
|
|
||||||
public final String depositTxId;
|
|
||||||
|
|
||||||
// primaryMarket fields are based on industry standard where primaryMarket is always in the focus (in the app BTC is always in the focus - will be changed in a larger refactoring once)
|
// primaryMarket fields are based on industry standard where primaryMarket is always in the focus (in the app BTC is always in the focus - will be changed in a larger refactoring once)
|
||||||
public String currencyPair;
|
public String currencyPair;
|
||||||
public OfferPayload.Direction primaryMarketDirection;
|
|
||||||
|
|
||||||
public long primaryMarketTradePrice;
|
public long primaryMarketTradePrice;
|
||||||
public long primaryMarketTradeAmount;
|
public long primaryMarketTradeAmount;
|
||||||
public long primaryMarketTradeVolume;
|
public long primaryMarketTradeVolume;
|
||||||
|
|
||||||
public TradeStatisticsForJson(TradeStatistics2 tradeStatistics) {
|
public TradeStatisticsForJson(TradeStatistics3 tradeStatistics) {
|
||||||
this.direction = OfferPayload.Direction.valueOf(tradeStatistics.getDirection().name());
|
this.currency = tradeStatistics.getCurrency();
|
||||||
this.currency = tradeStatistics.getCurrencyCode();
|
this.paymentMethod = tradeStatistics.getPaymentMethod();
|
||||||
this.paymentMethod = tradeStatistics.getOfferPaymentMethod();
|
this.tradePrice = tradeStatistics.getPrice();
|
||||||
this.offerDate = tradeStatistics.getOfferDate();
|
this.tradeAmount = tradeStatistics.getAmount();
|
||||||
this.useMarketBasedPrice = tradeStatistics.isOfferUseMarketBasedPrice();
|
this.tradeDate = tradeStatistics.getDate();
|
||||||
this.marketPriceMargin = tradeStatistics.getOfferMarketPriceMargin();
|
|
||||||
this.offerAmount = tradeStatistics.getOfferAmount();
|
|
||||||
this.offerMinAmount = tradeStatistics.getOfferMinAmount();
|
|
||||||
this.offerId = tradeStatistics.getOfferId();
|
|
||||||
this.tradePrice = tradeStatistics.getTradePrice().getValue();
|
|
||||||
this.tradeAmount = tradeStatistics.getTradeAmount().getValue();
|
|
||||||
this.tradeDate = tradeStatistics.getTradeDate().getTime();
|
|
||||||
this.depositTxId = tradeStatistics.getDepositTxId();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final Price tradePrice = getTradePrice();
|
Price tradePrice = getTradePrice();
|
||||||
if (CurrencyUtil.isCryptoCurrency(currency)) {
|
if (CurrencyUtil.isCryptoCurrency(currency)) {
|
||||||
primaryMarketDirection = direction == OfferPayload.Direction.BUY ? OfferPayload.Direction.SELL : OfferPayload.Direction.BUY;
|
|
||||||
currencyPair = currency + "/" + Res.getBaseCurrencyCode();
|
currencyPair = currency + "/" + Res.getBaseCurrencyCode();
|
||||||
|
|
||||||
primaryMarketTradePrice = tradePrice.getValue();
|
primaryMarketTradePrice = tradePrice.getValue();
|
||||||
|
primaryMarketTradeAmount = getTradeVolume() != null ?
|
||||||
primaryMarketTradeAmount = getTradeVolume() != null ? getTradeVolume().getValue() : 0;
|
getTradeVolume().getValue() :
|
||||||
|
0;
|
||||||
primaryMarketTradeVolume = getTradeAmount().getValue();
|
primaryMarketTradeVolume = getTradeAmount().getValue();
|
||||||
} else {
|
} else {
|
||||||
primaryMarketDirection = direction;
|
|
||||||
currencyPair = Res.getBaseCurrencyCode() + "/" + currency;
|
currencyPair = Res.getBaseCurrencyCode() + "/" + currency;
|
||||||
|
|
||||||
// we use precision 4 for fiat based price but on the markets api we use precision 8 so we scale up by 10000
|
// we use precision 4 for fiat based price but on the markets api we use precision 8 so we scale up by 10000
|
||||||
primaryMarketTradePrice = (long) MathUtils.scaleUpByPowerOf10(tradePrice.getValue(), 4);
|
primaryMarketTradePrice = (long) MathUtils.scaleUpByPowerOf10(tradePrice.getValue(), 4);
|
||||||
|
|
||||||
primaryMarketTradeAmount = getTradeAmount().getValue();
|
primaryMarketTradeAmount = getTradeAmount().getValue();
|
||||||
// we use precision 4 for fiat but on the markets api we use precision 8 so we scale up by 10000
|
// we use precision 4 for fiat but on the markets api we use precision 8 so we scale up by 10000
|
||||||
primaryMarketTradeVolume = getTradeVolume() != null ?
|
primaryMarketTradeVolume = getTradeVolume() != null ?
|
||||||
(long) MathUtils.scaleUpByPowerOf10(getTradeVolume().getValue(), 4) : 0;
|
(long) MathUtils.scaleUpByPowerOf10(getTradeVolume().getValue(), 4) :
|
||||||
|
0;
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
log.error(t.getMessage());
|
log.error(t.getMessage());
|
||||||
|
@ -113,6 +91,10 @@ public final class TradeStatisticsForJson {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Volume getTradeVolume() {
|
public Volume getTradeVolume() {
|
||||||
|
try {
|
||||||
return getTradePrice().getVolumeByAmount(getTradeAmount());
|
return getTradePrice().getVolumeByAmount(getTradeAmount());
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return Volume.parse("0", currency);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import bisq.common.util.Utilities;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableSet;
|
import javafx.collections.ObservableSet;
|
||||||
|
@ -40,94 +41,85 @@ import java.io.File;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class TradeStatisticsManager {
|
public class TradeStatisticsManager {
|
||||||
|
|
||||||
private final JsonFileManager jsonFileManager;
|
|
||||||
private final P2PService p2PService;
|
private final P2PService p2PService;
|
||||||
private final PriceFeedService priceFeedService;
|
private final PriceFeedService priceFeedService;
|
||||||
private final TradeStatistics2StorageService tradeStatistics2StorageService;
|
private final TradeStatistics3StorageService tradeStatistics3StorageService;
|
||||||
|
private final TradeStatisticsConverter tradeStatisticsConverter;
|
||||||
|
private final File storageDir;
|
||||||
private final boolean dumpStatistics;
|
private final boolean dumpStatistics;
|
||||||
private final ObservableSet<TradeStatistics2> observableTradeStatisticsSet = FXCollections.observableSet();
|
private final ObservableSet<TradeStatistics3> observableTradeStatisticsSet = FXCollections.observableSet();
|
||||||
|
private JsonFileManager jsonFileManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public TradeStatisticsManager(P2PService p2PService,
|
public TradeStatisticsManager(P2PService p2PService,
|
||||||
PriceFeedService priceFeedService,
|
PriceFeedService priceFeedService,
|
||||||
TradeStatistics2StorageService tradeStatistics2StorageService,
|
TradeStatistics3StorageService tradeStatistics3StorageService,
|
||||||
AppendOnlyDataStoreService appendOnlyDataStoreService,
|
AppendOnlyDataStoreService appendOnlyDataStoreService,
|
||||||
|
TradeStatisticsConverter tradeStatisticsConverter,
|
||||||
@Named(Config.STORAGE_DIR) File storageDir,
|
@Named(Config.STORAGE_DIR) File storageDir,
|
||||||
@Named(Config.DUMP_STATISTICS) boolean dumpStatistics) {
|
@Named(Config.DUMP_STATISTICS) boolean dumpStatistics) {
|
||||||
this.p2PService = p2PService;
|
this.p2PService = p2PService;
|
||||||
this.priceFeedService = priceFeedService;
|
this.priceFeedService = priceFeedService;
|
||||||
this.tradeStatistics2StorageService = tradeStatistics2StorageService;
|
this.tradeStatistics3StorageService = tradeStatistics3StorageService;
|
||||||
|
this.tradeStatisticsConverter = tradeStatisticsConverter;
|
||||||
|
this.storageDir = storageDir;
|
||||||
this.dumpStatistics = dumpStatistics;
|
this.dumpStatistics = dumpStatistics;
|
||||||
jsonFileManager = new JsonFileManager(storageDir);
|
|
||||||
|
|
||||||
appendOnlyDataStoreService.addService(tradeStatistics2StorageService);
|
|
||||||
|
appendOnlyDataStoreService.addService(tradeStatistics3StorageService);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutDown() {
|
||||||
|
tradeStatisticsConverter.shutDown();
|
||||||
|
if (jsonFileManager != null) {
|
||||||
|
jsonFileManager.shutDown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onAllServicesInitialized() {
|
public void onAllServicesInitialized() {
|
||||||
p2PService.getP2PDataStorage().addAppendOnlyDataStoreListener(payload -> {
|
p2PService.getP2PDataStorage().addAppendOnlyDataStoreListener(payload -> {
|
||||||
if (payload instanceof TradeStatistics2)
|
if (payload instanceof TradeStatistics3) {
|
||||||
addToSet((TradeStatistics2) payload);
|
TradeStatistics3 tradeStatistics = (TradeStatistics3) payload;
|
||||||
});
|
|
||||||
|
|
||||||
Set<TradeStatistics2> set = tradeStatistics2StorageService.getMapOfAllData().values().stream()
|
|
||||||
.filter(e -> e instanceof TradeStatistics2)
|
|
||||||
.map(e -> (TradeStatistics2) e)
|
|
||||||
.map(WrapperTradeStatistics2::new)
|
|
||||||
.distinct()
|
|
||||||
.map(WrapperTradeStatistics2::unwrap)
|
|
||||||
.filter(TradeStatistics2::isValid)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
observableTradeStatisticsSet.addAll(set);
|
|
||||||
|
|
||||||
priceFeedService.applyLatestBisqMarketPrice(observableTradeStatisticsSet);
|
|
||||||
|
|
||||||
dump();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObservableSet<TradeStatistics2> getObservableTradeStatisticsSet() {
|
|
||||||
return observableTradeStatisticsSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addToSet(TradeStatistics2 tradeStatistics) {
|
|
||||||
if (!observableTradeStatisticsSet.contains(tradeStatistics)) {
|
|
||||||
Optional<TradeStatistics2> duplicate = observableTradeStatisticsSet.stream().filter(
|
|
||||||
e -> e.getOfferId().equals(tradeStatistics.getOfferId())).findAny();
|
|
||||||
|
|
||||||
if (duplicate.isPresent()) {
|
|
||||||
// TODO: Can be removed as soon as everyone uses v1.2.6+
|
|
||||||
// Removes an existing object with a trade id if the new one matches the existing except
|
|
||||||
// for the deposit tx id
|
|
||||||
if (tradeStatistics.getDepositTxId() == null &&
|
|
||||||
tradeStatistics.isValid() &&
|
|
||||||
duplicate.get().compareTo(tradeStatistics) == 0) {
|
|
||||||
observableTradeStatisticsSet.remove(duplicate.get());
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tradeStatistics.isValid()) {
|
if (!tradeStatistics.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
observableTradeStatisticsSet.add(tradeStatistics);
|
observableTradeStatisticsSet.add(tradeStatistics);
|
||||||
priceFeedService.applyLatestBisqMarketPrice(observableTradeStatisticsSet);
|
priceFeedService.applyLatestBisqMarketPrice(observableTradeStatisticsSet);
|
||||||
dump();
|
maybeDumpStatistics();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Set<TradeStatistics3> set = tradeStatistics3StorageService.getMapOfAllData().values().stream()
|
||||||
|
.filter(e -> e instanceof TradeStatistics3)
|
||||||
|
.map(e -> (TradeStatistics3) e)
|
||||||
|
.filter(TradeStatistics3::isValid)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
observableTradeStatisticsSet.addAll(set);
|
||||||
|
priceFeedService.applyLatestBisqMarketPrice(observableTradeStatisticsSet);
|
||||||
|
maybeDumpStatistics();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dump() {
|
public ObservableSet<TradeStatistics3> getObservableTradeStatisticsSet() {
|
||||||
if (dumpStatistics) {
|
return observableTradeStatisticsSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeDumpStatistics() {
|
||||||
|
if (!dumpStatistics) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jsonFileManager == null) {
|
||||||
|
jsonFileManager = new JsonFileManager(storageDir);
|
||||||
|
|
||||||
|
// We only dump once the currencies as they do not change during runtime
|
||||||
ArrayList<CurrencyTuple> fiatCurrencyList = CurrencyUtil.getAllSortedFiatCurrencies().stream()
|
ArrayList<CurrencyTuple> fiatCurrencyList = CurrencyUtil.getAllSortedFiatCurrencies().stream()
|
||||||
.map(e -> new CurrencyTuple(e.getCode(), e.getName(), 8))
|
.map(e -> new CurrencyTuple(e.getCode(), e.getName(), 8))
|
||||||
.collect(Collectors.toCollection(ArrayList::new));
|
.collect(Collectors.toCollection(ArrayList::new));
|
||||||
|
@ -138,44 +130,14 @@ public class TradeStatisticsManager {
|
||||||
.collect(Collectors.toCollection(ArrayList::new));
|
.collect(Collectors.toCollection(ArrayList::new));
|
||||||
cryptoCurrencyList.add(0, new CurrencyTuple(Res.getBaseCurrencyCode(), Res.getBaseCurrencyName(), 8));
|
cryptoCurrencyList.add(0, new CurrencyTuple(Res.getBaseCurrencyCode(), Res.getBaseCurrencyName(), 8));
|
||||||
jsonFileManager.writeToDisc(Utilities.objectToJson(cryptoCurrencyList), "crypto_currency_list");
|
jsonFileManager.writeToDisc(Utilities.objectToJson(cryptoCurrencyList), "crypto_currency_list");
|
||||||
|
}
|
||||||
|
|
||||||
// We store the statistics as json so it is easy for further processing (e.g. for web based services)
|
List<TradeStatisticsForJson> list = observableTradeStatisticsSet.stream()
|
||||||
// TODO This is just a quick solution for storing to one file.
|
.map(TradeStatisticsForJson::new)
|
||||||
// 1 statistic entry has 500 bytes as json.
|
|
||||||
// Need a more scalable solution later when we get more volume.
|
|
||||||
// The flag will only be activated by dedicated nodes, so it should not be too critical for the moment, but needs to
|
|
||||||
// get improved. Maybe a LevelDB like DB...? Could be impl. in a headless version only.
|
|
||||||
List<TradeStatisticsForJson> list = observableTradeStatisticsSet.stream().map(TradeStatisticsForJson::new)
|
|
||||||
.sorted((o1, o2) -> (Long.compare(o2.tradeDate, o1.tradeDate)))
|
.sorted((o1, o2) -> (Long.compare(o2.tradeDate, o1.tradeDate)))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
TradeStatisticsForJson[] array = new TradeStatisticsForJson[list.size()];
|
TradeStatisticsForJson[] array = new TradeStatisticsForJson[list.size()];
|
||||||
list.toArray(array);
|
list.toArray(array);
|
||||||
jsonFileManager.writeToDisc(Utilities.objectToJson(array), "trade_statistics");
|
jsonFileManager.writeToDisc(Utilities.objectToJson(array), "trade_statistics");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static class WrapperTradeStatistics2 {
|
|
||||||
private final TradeStatistics2 tradeStatistics;
|
|
||||||
|
|
||||||
public WrapperTradeStatistics2(TradeStatistics2 tradeStatistics) {
|
|
||||||
this.tradeStatistics = tradeStatistics;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TradeStatistics2 unwrap() {
|
|
||||||
return this.tradeStatistics;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (this == obj) return true;
|
|
||||||
if (obj == null || getClass() != obj.getClass()) return false;
|
|
||||||
var wrapper = (WrapperTradeStatistics2) obj;
|
|
||||||
return Objects.equals(tradeStatistics.getOfferId(), wrapper.tradeStatistics.getOfferId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(tradeStatistics.getOfferId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of Bisq.
|
|
||||||
*
|
|
||||||
* Bisq is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package bisq.core.trade.statistics;
|
|
||||||
|
|
||||||
import bisq.core.monetary.Price;
|
|
||||||
import bisq.core.offer.OfferPayload;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
|
||||||
|
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
import com.natpryce.makeiteasy.Instantiator;
|
|
||||||
import com.natpryce.makeiteasy.Maker;
|
|
||||||
import com.natpryce.makeiteasy.Property;
|
|
||||||
|
|
||||||
import static com.natpryce.makeiteasy.MakeItEasy.a;
|
|
||||||
|
|
||||||
public class TradeStatistics2Maker {
|
|
||||||
|
|
||||||
public static final Property<TradeStatistics2, Date> date = new Property<>();
|
|
||||||
public static final Property<TradeStatistics2, String> depositTxId = new Property<>();
|
|
||||||
public static final Property<TradeStatistics2, Coin> tradeAmount = new Property<>();
|
|
||||||
|
|
||||||
public static final Instantiator<TradeStatistics2> TradeStatistic2 = lookup -> {
|
|
||||||
Calendar calendar = Calendar.getInstance();
|
|
||||||
calendar.set(2016, 3, 19);
|
|
||||||
|
|
||||||
return new TradeStatistics2(
|
|
||||||
new OfferPayload("1234",
|
|
||||||
0L,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
OfferPayload.Direction.BUY,
|
|
||||||
100000L,
|
|
||||||
0.0,
|
|
||||||
false,
|
|
||||||
100000L,
|
|
||||||
100000L,
|
|
||||||
"BTC",
|
|
||||||
"USD",
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"SEPA",
|
|
||||||
"",
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"",
|
|
||||||
0L,
|
|
||||||
0L,
|
|
||||||
0L,
|
|
||||||
false,
|
|
||||||
0L,
|
|
||||||
0L,
|
|
||||||
0L,
|
|
||||||
0L,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
0L,
|
|
||||||
0L,
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
0),
|
|
||||||
Price.valueOf("BTC", 100000L),
|
|
||||||
lookup.valueOf(tradeAmount, Coin.SATOSHI),
|
|
||||||
lookup.valueOf(date, new Date(calendar.getTimeInMillis())),
|
|
||||||
lookup.valueOf(depositTxId, "123456"),
|
|
||||||
Collections.emptyMap());
|
|
||||||
};
|
|
||||||
public static final Maker<TradeStatistics2> dayZeroTrade = a(TradeStatistic2);
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of Bisq.
|
|
||||||
*
|
|
||||||
* Bisq is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package bisq.core.trade.statistics;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import static bisq.core.trade.statistics.TradeStatistics2Maker.dayZeroTrade;
|
|
||||||
import static bisq.core.trade.statistics.TradeStatistics2Maker.depositTxId;
|
|
||||||
import static com.natpryce.makeiteasy.MakeItEasy.make;
|
|
||||||
import static com.natpryce.makeiteasy.MakeItEasy.withNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
|
|
||||||
public class TradeStatistics2Test {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isValid_WithDepositTxId() {
|
|
||||||
|
|
||||||
TradeStatistics2 tradeStatistic = make(dayZeroTrade);
|
|
||||||
|
|
||||||
assertTrue(tradeStatistic.isValid());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isValid_WithEmptyDepositTxId() {
|
|
||||||
TradeStatistics2 tradeStatistic = make(dayZeroTrade.but(withNull(depositTxId)));
|
|
||||||
|
|
||||||
assertTrue(tradeStatistic.isValid());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,114 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is part of Bisq.
|
|
||||||
*
|
|
||||||
* Bisq is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package bisq.core.trade.statistics;
|
|
||||||
|
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
|
||||||
|
|
||||||
import bisq.network.p2p.P2PService;
|
|
||||||
import bisq.network.p2p.storage.P2PDataStorage;
|
|
||||||
import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreListener;
|
|
||||||
import bisq.network.p2p.storage.persistence.AppendOnlyDataStoreService;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import org.mockito.ArgumentCaptor;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import static bisq.core.trade.statistics.TradeStatistics2Maker.dayZeroTrade;
|
|
||||||
import static bisq.core.trade.statistics.TradeStatistics2Maker.depositTxId;
|
|
||||||
import static bisq.core.trade.statistics.TradeStatistics2Maker.tradeAmount;
|
|
||||||
import static com.natpryce.makeiteasy.MakeItEasy.make;
|
|
||||||
import static com.natpryce.makeiteasy.MakeItEasy.with;
|
|
||||||
import static com.natpryce.makeiteasy.MakeItEasy.withNull;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
public class TradeStatisticsManagerTest {
|
|
||||||
|
|
||||||
private TradeStatisticsManager manager;
|
|
||||||
private TradeStatistics2 tradeWithNullDepositTxId;
|
|
||||||
private ArgumentCaptor<AppendOnlyDataStoreListener> listenerArgumentCaptor;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void prepareMocksAndObjects() {
|
|
||||||
P2PService p2PService = mock(P2PService.class);
|
|
||||||
P2PDataStorage p2PDataStorage = mock(P2PDataStorage.class);
|
|
||||||
File storageDir = mock(File.class);
|
|
||||||
TradeStatistics2StorageService tradeStatistics2StorageService = mock(TradeStatistics2StorageService.class);
|
|
||||||
PriceFeedService priceFeedService = mock(PriceFeedService.class);
|
|
||||||
|
|
||||||
AppendOnlyDataStoreService appendOnlyDataStoreService = mock(AppendOnlyDataStoreService.class);
|
|
||||||
when(p2PService.getP2PDataStorage()).thenReturn(p2PDataStorage);
|
|
||||||
|
|
||||||
manager = new TradeStatisticsManager(p2PService, priceFeedService,
|
|
||||||
tradeStatistics2StorageService, appendOnlyDataStoreService, storageDir, false);
|
|
||||||
|
|
||||||
tradeWithNullDepositTxId = make(dayZeroTrade.but(withNull(depositTxId)));
|
|
||||||
|
|
||||||
manager.onAllServicesInitialized();
|
|
||||||
listenerArgumentCaptor = ArgumentCaptor.forClass(AppendOnlyDataStoreListener.class);
|
|
||||||
verify(p2PDataStorage).addAppendOnlyDataStoreListener(listenerArgumentCaptor.capture());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addToSet_ObjectWithNullDepositTxId() {
|
|
||||||
listenerArgumentCaptor.getValue().onAdded(tradeWithNullDepositTxId);
|
|
||||||
assertTrue(manager.getObservableTradeStatisticsSet().contains(tradeWithNullDepositTxId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addToSet_RemoveExistingObjectIfObjectWithNullDepositTxIdIsAdded() {
|
|
||||||
TradeStatistics2 tradeWithDepositTxId = make(dayZeroTrade);
|
|
||||||
|
|
||||||
listenerArgumentCaptor.getValue().onAdded(tradeWithDepositTxId);
|
|
||||||
listenerArgumentCaptor.getValue().onAdded(tradeWithNullDepositTxId);
|
|
||||||
|
|
||||||
assertFalse(manager.getObservableTradeStatisticsSet().contains(tradeWithDepositTxId));
|
|
||||||
assertTrue(manager.getObservableTradeStatisticsSet().contains(tradeWithNullDepositTxId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addToSet_NotRemoveExistingObjectIfObjectsNotEqual() {
|
|
||||||
TradeStatistics2 tradeWithDepositTxId = make(dayZeroTrade.but(with(tradeAmount, Coin.FIFTY_COINS)));
|
|
||||||
|
|
||||||
listenerArgumentCaptor.getValue().onAdded(tradeWithDepositTxId);
|
|
||||||
listenerArgumentCaptor.getValue().onAdded(tradeWithNullDepositTxId);
|
|
||||||
|
|
||||||
assertTrue(manager.getObservableTradeStatisticsSet().contains(tradeWithDepositTxId));
|
|
||||||
assertFalse(manager.getObservableTradeStatisticsSet().contains(tradeWithNullDepositTxId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addToSet_IgnoreObjectIfObjectWithNullDepositTxIdAlreadyExists() {
|
|
||||||
TradeStatistics2 tradeWithDepositTxId = make(dayZeroTrade);
|
|
||||||
|
|
||||||
listenerArgumentCaptor.getValue().onAdded(tradeWithNullDepositTxId);
|
|
||||||
listenerArgumentCaptor.getValue().onAdded(tradeWithDepositTxId);
|
|
||||||
|
|
||||||
assertTrue(manager.getObservableTradeStatisticsSet().contains(tradeWithNullDepositTxId));
|
|
||||||
assertFalse(manager.getObservableTradeStatisticsSet().contains(tradeWithDepositTxId));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
package bisq.daemon.grpc;
|
package bisq.daemon.grpc;
|
||||||
|
|
||||||
import bisq.core.api.CoreApi;
|
import bisq.core.api.CoreApi;
|
||||||
import bisq.core.trade.statistics.TradeStatistics2;
|
import bisq.core.trade.statistics.TradeStatistics3;
|
||||||
|
|
||||||
import bisq.proto.grpc.GetTradeStatisticsGrpc;
|
import bisq.proto.grpc.GetTradeStatisticsGrpc;
|
||||||
import bisq.proto.grpc.GetTradeStatisticsReply;
|
import bisq.proto.grpc.GetTradeStatisticsReply;
|
||||||
|
@ -27,7 +27,7 @@ class GrpcGetTradeStatisticsService extends GetTradeStatisticsGrpc.GetTradeStati
|
||||||
StreamObserver<GetTradeStatisticsReply> responseObserver) {
|
StreamObserver<GetTradeStatisticsReply> responseObserver) {
|
||||||
|
|
||||||
var tradeStatistics = coreApi.getTradeStatistics().stream()
|
var tradeStatistics = coreApi.getTradeStatistics().stream()
|
||||||
.map(TradeStatistics2::toProtoTradeStatistics2)
|
.map(TradeStatistics3::toProtoTradeStatistics3)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
var reply = GetTradeStatisticsReply.newBuilder().addAllTradeStatistics(tradeStatistics).build();
|
var reply = GetTradeStatisticsReply.newBuilder().addAllTradeStatistics(tradeStatistics).build();
|
||||||
|
|
|
@ -78,9 +78,6 @@ public class AddressTextField extends AnchorPane {
|
||||||
});
|
});
|
||||||
|
|
||||||
textField.focusTraversableProperty().set(focusTraversableProperty().get());
|
textField.focusTraversableProperty().set(focusTraversableProperty().get());
|
||||||
//TODO app wide focus
|
|
||||||
//focusedProperty().addListener((ov, oldValue, newValue) -> textField.requestFocus());
|
|
||||||
|
|
||||||
Label extWalletIcon = new Label();
|
Label extWalletIcon = new Label();
|
||||||
extWalletIcon.setLayoutY(3);
|
extWalletIcon.setLayoutY(3);
|
||||||
extWalletIcon.getStyleClass().addAll("icon", "highlight");
|
extWalletIcon.getStyleClass().addAll("icon", "highlight");
|
||||||
|
|
|
@ -74,9 +74,6 @@ public class BsqAddressTextField extends AnchorPane {
|
||||||
});
|
});
|
||||||
|
|
||||||
textField.focusTraversableProperty().set(focusTraversableProperty().get());
|
textField.focusTraversableProperty().set(focusTraversableProperty().get());
|
||||||
//TODO app wide focus
|
|
||||||
//focusedProperty().addListener((ov, oldValue, newValue) -> textField.requestFocus());
|
|
||||||
|
|
||||||
|
|
||||||
Label copyIcon = new Label();
|
Label copyIcon = new Label();
|
||||||
copyIcon.setLayoutY(3);
|
copyIcon.setLayoutY(3);
|
||||||
|
|
|
@ -79,9 +79,6 @@ public class TextFieldWithCopyIcon extends AnchorPane {
|
||||||
AnchorPane.setRightAnchor(textField, 30.0);
|
AnchorPane.setRightAnchor(textField, 30.0);
|
||||||
AnchorPane.setLeftAnchor(textField, 0.0);
|
AnchorPane.setLeftAnchor(textField, 0.0);
|
||||||
textField.focusTraversableProperty().set(focusTraversableProperty().get());
|
textField.focusTraversableProperty().set(focusTraversableProperty().get());
|
||||||
//TODO app wide focus
|
|
||||||
//focusedProperty().addListener((ov, oldValue, newValue) -> textField.requestFocus());
|
|
||||||
|
|
||||||
getChildren().addAll(textField, copyIcon);
|
getChildren().addAll(textField, copyIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,8 +64,6 @@ import static bisq.desktop.util.FormBuilder.addMultilineLabel;
|
||||||
import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
|
import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
|
||||||
import static bisq.desktop.util.FormBuilder.addTopLabelTextField;
|
import static bisq.desktop.util.FormBuilder.addTopLabelTextField;
|
||||||
|
|
||||||
// TODO translation string keys should renamed to be more generic.
|
|
||||||
// Lets do it for 1.1.7 the translator have time to add new string.
|
|
||||||
public abstract class AgentRegistrationView<R extends DisputeAgent, T extends AgentRegistrationViewModel<R, ?>>
|
public abstract class AgentRegistrationView<R extends DisputeAgent, T extends AgentRegistrationViewModel<R, ?>>
|
||||||
extends ActivatableViewAndModel<VBox, T> {
|
extends ActivatableViewAndModel<VBox, T> {
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ import bisq.core.locale.Res;
|
||||||
import bisq.core.monetary.Altcoin;
|
import bisq.core.monetary.Altcoin;
|
||||||
import bisq.core.monetary.Price;
|
import bisq.core.monetary.Price;
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
import bisq.core.trade.statistics.TradeStatistics2;
|
import bisq.core.trade.statistics.TradeStatistics3;
|
||||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
import bisq.core.util.FormattingUtils;
|
import bisq.core.util.FormattingUtils;
|
||||||
|
@ -121,7 +121,7 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private BsqDashboardView(DaoFacade daoFacade,
|
public BsqDashboardView(DaoFacade daoFacade,
|
||||||
TradeStatisticsManager tradeStatisticsManager,
|
TradeStatisticsManager tradeStatisticsManager,
|
||||||
PriceFeedService priceFeedService,
|
PriceFeedService priceFeedService,
|
||||||
Preferences preferences,
|
Preferences preferences,
|
||||||
|
@ -173,7 +173,7 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
|
||||||
marketCapTextField = addTopLabelReadOnlyTextField(root, ++gridRow,
|
marketCapTextField = addTopLabelReadOnlyTextField(root, ++gridRow,
|
||||||
Res.get("dao.factsAndFigures.dashboard.marketCap")).second;
|
Res.get("dao.factsAndFigures.dashboard.marketCap")).second;
|
||||||
|
|
||||||
availableAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, 1,
|
availableAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, 1,
|
||||||
Res.get("dao.factsAndFigures.dashboard.availableAmount")).second;
|
Res.get("dao.factsAndFigures.dashboard.availableAmount")).second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,17 +289,17 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
|
||||||
private void updateBsqPriceData() {
|
private void updateBsqPriceData() {
|
||||||
seriesBSQPrice.getData().clear();
|
seriesBSQPrice.getData().clear();
|
||||||
|
|
||||||
Map<LocalDate, List<TradeStatistics2>> bsqPriceByDate = tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
|
Map<LocalDate, List<TradeStatistics3>> bsqPriceByDate = tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
|
||||||
.filter(e -> e.getCurrencyCode().equals("BSQ"))
|
.filter(e -> e.getCurrency().equals("BSQ"))
|
||||||
.sorted(Comparator.comparing(TradeStatistics2::getTradeDate))
|
.sorted(Comparator.comparing(TradeStatistics3::getDate))
|
||||||
.collect(Collectors.groupingBy(item -> new java.sql.Date(item.getTradeDate().getTime()).toLocalDate()
|
.collect(Collectors.groupingBy(item -> new java.sql.Date(item.getDate()).toLocalDate()
|
||||||
.with(ADJUSTERS.get(DAY))));
|
.with(ADJUSTERS.get(DAY))));
|
||||||
|
|
||||||
List<XYChart.Data<Number, Number>> updatedBSQPrice = bsqPriceByDate.keySet().stream()
|
List<XYChart.Data<Number, Number>> updatedBSQPrice = bsqPriceByDate.keySet().stream()
|
||||||
.map(e -> {
|
.map(e -> {
|
||||||
ZonedDateTime zonedDateTime = e.atStartOfDay(ZoneId.systemDefault());
|
ZonedDateTime zonedDateTime = e.atStartOfDay(ZoneId.systemDefault());
|
||||||
return new XYChart.Data<Number, Number>(zonedDateTime.toInstant().getEpochSecond(), bsqPriceByDate.get(e).stream()
|
return new XYChart.Data<Number, Number>(zonedDateTime.toInstant().getEpochSecond(), bsqPriceByDate.get(e).stream()
|
||||||
.map(TradeStatistics2::getTradePrice)
|
.map(TradeStatistics3::getTradePrice)
|
||||||
.mapToDouble(Price::getValue)
|
.mapToDouble(Price::getValue)
|
||||||
.average()
|
.average()
|
||||||
.orElse(Double.NaN)
|
.orElse(Double.NaN)
|
||||||
|
@ -370,12 +370,12 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
|
||||||
|
|
||||||
private long updateAveragePriceField(TextField textField, int days, boolean isUSDField) {
|
private long updateAveragePriceField(TextField textField, int days, boolean isUSDField) {
|
||||||
Date pastXDays = getPastDate(days);
|
Date pastXDays = getPastDate(days);
|
||||||
List<TradeStatistics2> bsqTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
|
List<TradeStatistics3> bsqTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
|
||||||
.filter(e -> e.getCurrencyCode().equals("BSQ"))
|
.filter(e -> e.getCurrency().equals("BSQ"))
|
||||||
.filter(e -> e.getTradeDate().after(pastXDays))
|
.filter(e -> e.getTradeDate().after(pastXDays))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
List<TradeStatistics2> usdTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
|
List<TradeStatistics3> usdTradePastXDays = tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
|
||||||
.filter(e -> e.getCurrencyCode().equals("USD"))
|
.filter(e -> e.getCurrency().equals("USD"))
|
||||||
.filter(e -> e.getTradeDate().after(pastXDays))
|
.filter(e -> e.getTradeDate().after(pastXDays))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
long average = isUSDField ? getUSDAverage(bsqTradePastXDays, usdTradePastXDays) :
|
long average = isUSDField ? getUSDAverage(bsqTradePastXDays, usdTradePastXDays) :
|
||||||
|
@ -391,11 +391,11 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
|
||||||
return average;
|
return average;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getBTCAverage(List<TradeStatistics2> bsqList) {
|
private long getBTCAverage(List<TradeStatistics3> bsqList) {
|
||||||
long accumulatedVolume = 0;
|
long accumulatedVolume = 0;
|
||||||
long accumulatedAmount = 0;
|
long accumulatedAmount = 0;
|
||||||
|
|
||||||
for (TradeStatistics2 item : bsqList) {
|
for (TradeStatistics3 item : bsqList) {
|
||||||
accumulatedVolume += item.getTradeVolume().getValue();
|
accumulatedVolume += item.getTradeVolume().getValue();
|
||||||
accumulatedAmount += item.getTradeAmount().getValue(); // Amount of BTC traded
|
accumulatedAmount += item.getTradeAmount().getValue(); // Amount of BTC traded
|
||||||
}
|
}
|
||||||
|
@ -406,17 +406,17 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
|
||||||
return averagePrice;
|
return averagePrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getUSDAverage(List<TradeStatistics2> bsqList, List<TradeStatistics2> usdList) {
|
private long getUSDAverage(List<TradeStatistics3> bsqList, List<TradeStatistics3> usdList) {
|
||||||
// Use next USD/BTC print as price to calculate BSQ/USD rate
|
// Use next USD/BTC print as price to calculate BSQ/USD rate
|
||||||
// Store each trade as amount of USD and amount of BSQ traded
|
// Store each trade as amount of USD and amount of BSQ traded
|
||||||
List<Tuple2<Double, Double>> usdBsqList = new ArrayList<>(bsqList.size());
|
List<Tuple2<Double, Double>> usdBsqList = new ArrayList<>(bsqList.size());
|
||||||
usdList.sort(Comparator.comparing(o -> o.getTradeDate().getTime()));
|
usdList.sort(Comparator.comparing(TradeStatistics3::getDate));
|
||||||
var usdBTCPrice = 10000d; // Default to 10000 USD per BTC if there is no USD feed at all
|
var usdBTCPrice = 10000d; // Default to 10000 USD per BTC if there is no USD feed at all
|
||||||
|
|
||||||
for (TradeStatistics2 item : bsqList) {
|
for (TradeStatistics3 item : bsqList) {
|
||||||
// Find usdprice for trade item
|
// Find usdprice for trade item
|
||||||
usdBTCPrice = usdList.stream()
|
usdBTCPrice = usdList.stream()
|
||||||
.filter(usd -> usd.getTradeDate().getTime() > item.getTradeDate().getTime())
|
.filter(usd -> usd.getDate() > item.getDate())
|
||||||
.map(usd -> MathUtils.scaleDownByPowerOf10((double) usd.getTradePrice().getValue(),
|
.map(usd -> MathUtils.scaleDownByPowerOf10((double) usd.getTradePrice().getValue(),
|
||||||
Fiat.SMALLEST_UNIT_EXPONENT))
|
Fiat.SMALLEST_UNIT_EXPONENT))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
|
|
|
@ -27,7 +27,6 @@ import bisq.core.offer.placeoffer.tasks.AddToOfferBook;
|
||||||
import bisq.core.offer.placeoffer.tasks.CreateMakerFeeTx;
|
import bisq.core.offer.placeoffer.tasks.CreateMakerFeeTx;
|
||||||
import bisq.core.offer.placeoffer.tasks.ValidateOffer;
|
import bisq.core.offer.placeoffer.tasks.ValidateOffer;
|
||||||
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
||||||
import bisq.core.trade.protocol.tasks.PublishTradeStatistics;
|
|
||||||
import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness;
|
import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness;
|
||||||
import bisq.core.trade.protocol.tasks.buyer.BuyerProcessDelayedPayoutTxSignatureRequest;
|
import bisq.core.trade.protocol.tasks.buyer.BuyerProcessDelayedPayoutTxSignatureRequest;
|
||||||
import bisq.core.trade.protocol.tasks.buyer.BuyerProcessDepositTxAndDelayedPayoutTxMessage;
|
import bisq.core.trade.protocol.tasks.buyer.BuyerProcessDepositTxAndDelayedPayoutTxMessage;
|
||||||
|
@ -56,6 +55,7 @@ import bisq.core.trade.protocol.tasks.seller.SellerFinalizesDelayedPayoutTx;
|
||||||
import bisq.core.trade.protocol.tasks.seller.SellerProcessCounterCurrencyTransferStartedMessage;
|
import bisq.core.trade.protocol.tasks.seller.SellerProcessCounterCurrencyTransferStartedMessage;
|
||||||
import bisq.core.trade.protocol.tasks.seller.SellerProcessDelayedPayoutTxSignatureResponse;
|
import bisq.core.trade.protocol.tasks.seller.SellerProcessDelayedPayoutTxSignatureResponse;
|
||||||
import bisq.core.trade.protocol.tasks.seller.SellerPublishesDepositTx;
|
import bisq.core.trade.protocol.tasks.seller.SellerPublishesDepositTx;
|
||||||
|
import bisq.core.trade.protocol.tasks.seller.SellerPublishesTradeStatistics;
|
||||||
import bisq.core.trade.protocol.tasks.seller.SellerSendDelayedPayoutTxSignatureRequest;
|
import bisq.core.trade.protocol.tasks.seller.SellerSendDelayedPayoutTxSignatureRequest;
|
||||||
import bisq.core.trade.protocol.tasks.seller.SellerSendPayoutTxPublishedMessage;
|
import bisq.core.trade.protocol.tasks.seller.SellerSendPayoutTxPublishedMessage;
|
||||||
import bisq.core.trade.protocol.tasks.seller.SellerSendsDepositTxAndDelayedPayoutTxMessage;
|
import bisq.core.trade.protocol.tasks.seller.SellerSendsDepositTxAndDelayedPayoutTxMessage;
|
||||||
|
@ -145,7 +145,7 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
||||||
SellerFinalizesDelayedPayoutTx.class,
|
SellerFinalizesDelayedPayoutTx.class,
|
||||||
SellerSendsDepositTxAndDelayedPayoutTxMessage.class,
|
SellerSendsDepositTxAndDelayedPayoutTxMessage.class,
|
||||||
SellerPublishesDepositTx.class,
|
SellerPublishesDepositTx.class,
|
||||||
PublishTradeStatistics.class,
|
SellerPublishesTradeStatistics.class,
|
||||||
|
|
||||||
SellerProcessCounterCurrencyTransferStartedMessage.class,
|
SellerProcessCounterCurrencyTransferStartedMessage.class,
|
||||||
ApplyFilter.class,
|
ApplyFilter.class,
|
||||||
|
@ -179,7 +179,6 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
||||||
|
|
||||||
BuyerProcessDepositTxAndDelayedPayoutTxMessage.class,
|
BuyerProcessDepositTxAndDelayedPayoutTxMessage.class,
|
||||||
BuyerVerifiesFinalDelayedPayoutTx.class,
|
BuyerVerifiesFinalDelayedPayoutTx.class,
|
||||||
PublishTradeStatistics.class,
|
|
||||||
|
|
||||||
ApplyFilter.class,
|
ApplyFilter.class,
|
||||||
MakerVerifyTakerFeePayment.class,
|
MakerVerifyTakerFeePayment.class,
|
||||||
|
@ -216,7 +215,6 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
||||||
|
|
||||||
BuyerProcessDepositTxAndDelayedPayoutTxMessage.class,
|
BuyerProcessDepositTxAndDelayedPayoutTxMessage.class,
|
||||||
BuyerVerifiesFinalDelayedPayoutTx.class,
|
BuyerVerifiesFinalDelayedPayoutTx.class,
|
||||||
PublishTradeStatistics.class,
|
|
||||||
|
|
||||||
ApplyFilter.class,
|
ApplyFilter.class,
|
||||||
TakerVerifyMakerFeePayment.class,
|
TakerVerifyMakerFeePayment.class,
|
||||||
|
@ -248,7 +246,7 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
||||||
SellerFinalizesDelayedPayoutTx.class,
|
SellerFinalizesDelayedPayoutTx.class,
|
||||||
SellerSendsDepositTxAndDelayedPayoutTxMessage.class,
|
SellerSendsDepositTxAndDelayedPayoutTxMessage.class,
|
||||||
SellerPublishesDepositTx.class,
|
SellerPublishesDepositTx.class,
|
||||||
PublishTradeStatistics.class,
|
SellerPublishesTradeStatistics.class,
|
||||||
|
|
||||||
SellerProcessCounterCurrencyTransferStartedMessage.class,
|
SellerProcessCounterCurrencyTransferStartedMessage.class,
|
||||||
ApplyFilter.class,
|
ApplyFilter.class,
|
||||||
|
|
|
@ -35,7 +35,7 @@ import bisq.desktop.util.DisplayUtils;
|
||||||
import bisq.core.locale.CurrencyUtil;
|
import bisq.core.locale.CurrencyUtil;
|
||||||
import bisq.core.locale.Res;
|
import bisq.core.locale.Res;
|
||||||
import bisq.core.offer.OfferPayload;
|
import bisq.core.offer.OfferPayload;
|
||||||
import bisq.core.trade.statistics.TradeStatistics2;
|
import bisq.core.trade.statistics.TradeStatistics3;
|
||||||
import bisq.core.util.FormattingUtils;
|
import bisq.core.util.FormattingUtils;
|
||||||
import bisq.core.util.coin.CoinFormatter;
|
import bisq.core.util.coin.CoinFormatter;
|
||||||
|
|
||||||
|
@ -82,7 +82,10 @@ public class MarketView extends ActivatableView<TabPane, Void> {
|
||||||
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public MarketView(CachingViewLoader viewLoader, P2PService p2PService, OfferBook offerBook, @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
|
public MarketView(CachingViewLoader viewLoader,
|
||||||
|
P2PService p2PService,
|
||||||
|
OfferBook offerBook,
|
||||||
|
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
|
||||||
Navigation navigation) {
|
Navigation navigation) {
|
||||||
this.viewLoader = viewLoader;
|
this.viewLoader = viewLoader;
|
||||||
this.p2PService = p2PService;
|
this.p2PService = p2PService;
|
||||||
|
@ -179,20 +182,19 @@ public class MarketView extends ActivatableView<TabPane, Void> {
|
||||||
// If both traders had set it the tradeStatistics is only delivered once.
|
// If both traders had set it the tradeStatistics is only delivered once.
|
||||||
// If both traders used a different referral ID then we would get 2 objects.
|
// If both traders used a different referral ID then we would get 2 objects.
|
||||||
List<String> list = p2PService.getP2PDataStorage().getAppendOnlyDataStoreMap().values().stream()
|
List<String> list = p2PService.getP2PDataStorage().getAppendOnlyDataStoreMap().values().stream()
|
||||||
.filter(e -> e instanceof TradeStatistics2)
|
.filter(e -> e instanceof TradeStatistics3)
|
||||||
.map(e -> (TradeStatistics2) e)
|
.map(e -> (TradeStatistics3) e)
|
||||||
.filter(tradeStatistics2 -> tradeStatistics2.getExtraDataMap() != null)
|
.filter(tradeStatistics3 -> tradeStatistics3.getExtraDataMap() != null)
|
||||||
.filter(tradeStatistics2 -> tradeStatistics2.getExtraDataMap().get(OfferPayload.REFERRAL_ID) != null)
|
.filter(tradeStatistics3 -> tradeStatistics3.getExtraDataMap().get(OfferPayload.REFERRAL_ID) != null)
|
||||||
.map(trade -> {
|
.map(tradeStatistics3 -> {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("Trade ID: ").append(trade.getOfferId()).append("\n")
|
sb.append("Date: ").append(DisplayUtils.formatDateTime(tradeStatistics3.getTradeDate())).append("\n")
|
||||||
.append("Date: ").append(DisplayUtils.formatDateTime(trade.getTradeDate())).append("\n")
|
.append("Market: ").append(CurrencyUtil.getCurrencyPair(tradeStatistics3.getCurrency())).append("\n")
|
||||||
.append("Market: ").append(CurrencyUtil.getCurrencyPair(trade.getCurrencyCode())).append("\n")
|
.append("Price: ").append(FormattingUtils.formatPrice(tradeStatistics3.getTradePrice())).append("\n")
|
||||||
.append("Price: ").append(FormattingUtils.formatPrice(trade.getTradePrice())).append("\n")
|
.append("Amount: ").append(formatter.formatCoin(tradeStatistics3.getTradeAmount())).append("\n")
|
||||||
.append("Amount: ").append(formatter.formatCoin(trade.getTradeAmount())).append("\n")
|
.append("Volume: ").append(DisplayUtils.formatVolume(tradeStatistics3.getTradeVolume())).append("\n")
|
||||||
.append("Volume: ").append(DisplayUtils.formatVolume(trade.getTradeVolume())).append("\n")
|
.append("Payment method: ").append(Res.get(tradeStatistics3.getPaymentMethod())).append("\n")
|
||||||
.append("Payment method: ").append(Res.get(trade.getOfferPaymentMethod())).append("\n")
|
.append("ReferralID: ").append(tradeStatistics3.getExtraDataMap().get(OfferPayload.REFERRAL_ID));
|
||||||
.append("ReferralID: ").append(trade.getExtraDataMap().get(OfferPayload.REFERRAL_ID));
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
})
|
})
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
|
@ -298,7 +298,7 @@ public class SpreadView extends ActivatableViewAndModel<GridPane, SpreadViewMode
|
||||||
public void updateItem(final SpreadItem item, boolean empty) {
|
public void updateItem(final SpreadItem item, boolean empty) {
|
||||||
super.updateItem(item, empty);
|
super.updateItem(item, empty);
|
||||||
if (item != null && !empty) {
|
if (item != null && !empty) {
|
||||||
// TODO maybe show exra colums with item.priceSpread and use real amount diff
|
// TODO maybe show extra columns with item.priceSpread and use real amount diff
|
||||||
// not % based
|
// not % based
|
||||||
if (item.priceSpread != null)
|
if (item.priceSpread != null)
|
||||||
setText(item.percentage);
|
setText(item.percentage);
|
||||||
|
|
|
@ -172,7 +172,7 @@ class SpreadViewModel extends ActivatableViewModel {
|
||||||
else
|
else
|
||||||
spread = bestBuyOfferPrice.subtract(bestSellOfferPrice);
|
spread = bestBuyOfferPrice.subtract(bestSellOfferPrice);
|
||||||
|
|
||||||
// TODO maybe show extra colums with spread and use real amount diff
|
// TODO maybe show extra columns with spread and use real amount diff
|
||||||
// not % based. e.g. diff between best buy and sell offer (of small amounts its a smaller gain)
|
// not % based. e.g. diff between best buy and sell offer (of small amounts its a smaller gain)
|
||||||
|
|
||||||
if (spread != null && marketPrice != null && marketPrice.isPriceAvailable()) {
|
if (spread != null && marketPrice != null && marketPrice.isPriceAvailable()) {
|
||||||
|
|
|
@ -34,8 +34,7 @@ import bisq.core.locale.CurrencyUtil;
|
||||||
import bisq.core.locale.Res;
|
import bisq.core.locale.Res;
|
||||||
import bisq.core.monetary.Price;
|
import bisq.core.monetary.Price;
|
||||||
import bisq.core.monetary.Volume;
|
import bisq.core.monetary.Volume;
|
||||||
import bisq.core.offer.OfferPayload;
|
import bisq.core.trade.statistics.TradeStatistics3;
|
||||||
import bisq.core.trade.statistics.TradeStatistics2;
|
|
||||||
import bisq.core.util.FormattingUtils;
|
import bisq.core.util.FormattingUtils;
|
||||||
import bisq.core.util.coin.CoinFormatter;
|
import bisq.core.util.coin.CoinFormatter;
|
||||||
|
|
||||||
|
@ -103,7 +102,7 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
||||||
|
|
||||||
private final CoinFormatter formatter;
|
private final CoinFormatter formatter;
|
||||||
|
|
||||||
private TableView<TradeStatistics2> tableView;
|
private TableView<TradeStatistics3> tableView;
|
||||||
private AutocompleteComboBox<CurrencyListItem> currencyComboBox;
|
private AutocompleteComboBox<CurrencyListItem> currencyComboBox;
|
||||||
private VolumeChart volumeChart;
|
private VolumeChart volumeChart;
|
||||||
private CandleStickChart priceChart;
|
private CandleStickChart priceChart;
|
||||||
|
@ -118,12 +117,12 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
||||||
private ChangeListener<Toggle> timeUnitChangeListener;
|
private ChangeListener<Toggle> timeUnitChangeListener;
|
||||||
private ToggleGroup toggleGroup;
|
private ToggleGroup toggleGroup;
|
||||||
private final ListChangeListener<XYChart.Data<Number, Number>> itemsChangeListener;
|
private final ListChangeListener<XYChart.Data<Number, Number>> itemsChangeListener;
|
||||||
private SortedList<TradeStatistics2> sortedList;
|
private SortedList<TradeStatistics3> sortedList;
|
||||||
private Label nrOfTradeStatisticsLabel;
|
private Label nrOfTradeStatisticsLabel;
|
||||||
private ListChangeListener<TradeStatistics2> tradeStatisticsByCurrencyListener;
|
private ListChangeListener<TradeStatistics3> tradeStatisticsByCurrencyListener;
|
||||||
private ChangeListener<Number> selectedTabIndexListener;
|
private ChangeListener<Number> selectedTabIndexListener;
|
||||||
private SingleSelectionModel<Tab> tabPaneSelectionModel;
|
private SingleSelectionModel<Tab> tabPaneSelectionModel;
|
||||||
private TableColumn<TradeStatistics2, TradeStatistics2> priceColumn, volumeColumn, marketColumn;
|
private TableColumn<TradeStatistics3, TradeStatistics3> priceColumn, volumeColumn, marketColumn;
|
||||||
@SuppressWarnings("FieldCanBeLocal")
|
@SuppressWarnings("FieldCanBeLocal")
|
||||||
private MonadicBinding<Void> currencySelectionBinding;
|
private MonadicBinding<Void> currencySelectionBinding;
|
||||||
private Subscription currencySelectionSubscriber;
|
private Subscription currencySelectionSubscriber;
|
||||||
|
@ -550,7 +549,7 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
||||||
VBox.setVgrow(tableView, Priority.ALWAYS);
|
VBox.setVgrow(tableView, Priority.ALWAYS);
|
||||||
|
|
||||||
// date
|
// date
|
||||||
TableColumn<TradeStatistics2, TradeStatistics2> dateColumn = new AutoTooltipTableColumn<>(Res.get("shared.dateTime")) {
|
TableColumn<TradeStatistics3, TradeStatistics3> dateColumn = new AutoTooltipTableColumn<>(Res.get("shared.dateTime")) {
|
||||||
{
|
{
|
||||||
setMinWidth(240);
|
setMinWidth(240);
|
||||||
setMaxWidth(240);
|
setMaxWidth(240);
|
||||||
|
@ -561,11 +560,11 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
||||||
dateColumn.setCellFactory(
|
dateColumn.setCellFactory(
|
||||||
new Callback<>() {
|
new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public TableCell<TradeStatistics2, TradeStatistics2> call(
|
public TableCell<TradeStatistics3, TradeStatistics3> call(
|
||||||
TableColumn<TradeStatistics2, TradeStatistics2> column) {
|
TableColumn<TradeStatistics3, TradeStatistics3> column) {
|
||||||
return new TableCell<>() {
|
return new TableCell<>() {
|
||||||
@Override
|
@Override
|
||||||
public void updateItem(final TradeStatistics2 item, boolean empty) {
|
public void updateItem(final TradeStatistics3 item, boolean empty) {
|
||||||
super.updateItem(item, empty);
|
super.updateItem(item, empty);
|
||||||
if (item != null)
|
if (item != null)
|
||||||
setText(DisplayUtils.formatDateTime(item.getTradeDate()));
|
setText(DisplayUtils.formatDateTime(item.getTradeDate()));
|
||||||
|
@ -575,7 +574,7 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
dateColumn.setComparator(Comparator.comparing(TradeStatistics2::getTradeDate));
|
dateColumn.setComparator(Comparator.comparing(TradeStatistics3::getTradeDate));
|
||||||
tableView.getColumns().add(dateColumn);
|
tableView.getColumns().add(dateColumn);
|
||||||
|
|
||||||
// market
|
// market
|
||||||
|
@ -590,21 +589,21 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
||||||
marketColumn.setCellFactory(
|
marketColumn.setCellFactory(
|
||||||
new Callback<>() {
|
new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public TableCell<TradeStatistics2, TradeStatistics2> call(
|
public TableCell<TradeStatistics3, TradeStatistics3> call(
|
||||||
TableColumn<TradeStatistics2, TradeStatistics2> column) {
|
TableColumn<TradeStatistics3, TradeStatistics3> column) {
|
||||||
return new TableCell<>() {
|
return new TableCell<>() {
|
||||||
@Override
|
@Override
|
||||||
public void updateItem(final TradeStatistics2 item, boolean empty) {
|
public void updateItem(final TradeStatistics3 item, boolean empty) {
|
||||||
super.updateItem(item, empty);
|
super.updateItem(item, empty);
|
||||||
if (item != null)
|
if (item != null)
|
||||||
setText(CurrencyUtil.getCurrencyPair(item.getCurrencyCode()));
|
setText(CurrencyUtil.getCurrencyPair(item.getCurrency()));
|
||||||
else
|
else
|
||||||
setText("");
|
setText("");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
marketColumn.setComparator(Comparator.comparing(TradeStatistics2::getTradeDate));
|
marketColumn.setComparator(Comparator.comparing(TradeStatistics3::getTradeDate));
|
||||||
tableView.getColumns().add(marketColumn);
|
tableView.getColumns().add(marketColumn);
|
||||||
|
|
||||||
// price
|
// price
|
||||||
|
@ -614,11 +613,11 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
||||||
priceColumn.setCellFactory(
|
priceColumn.setCellFactory(
|
||||||
new Callback<>() {
|
new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public TableCell<TradeStatistics2, TradeStatistics2> call(
|
public TableCell<TradeStatistics3, TradeStatistics3> call(
|
||||||
TableColumn<TradeStatistics2, TradeStatistics2> column) {
|
TableColumn<TradeStatistics3, TradeStatistics3> column) {
|
||||||
return new TableCell<>() {
|
return new TableCell<>() {
|
||||||
@Override
|
@Override
|
||||||
public void updateItem(final TradeStatistics2 item, boolean empty) {
|
public void updateItem(final TradeStatistics3 item, boolean empty) {
|
||||||
super.updateItem(item, empty);
|
super.updateItem(item, empty);
|
||||||
if (item != null)
|
if (item != null)
|
||||||
setText(FormattingUtils.formatPrice(item.getTradePrice()));
|
setText(FormattingUtils.formatPrice(item.getTradePrice()));
|
||||||
|
@ -628,21 +627,21 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
priceColumn.setComparator(Comparator.comparing(TradeStatistics2::getTradePrice));
|
priceColumn.setComparator(Comparator.comparing(TradeStatistics3::getTradePrice));
|
||||||
tableView.getColumns().add(priceColumn);
|
tableView.getColumns().add(priceColumn);
|
||||||
|
|
||||||
// amount
|
// amount
|
||||||
TableColumn<TradeStatistics2, TradeStatistics2> amountColumn = new AutoTooltipTableColumn<>(Res.get("shared.amountWithCur", Res.getBaseCurrencyCode()));
|
TableColumn<TradeStatistics3, TradeStatistics3> amountColumn = new AutoTooltipTableColumn<>(Res.get("shared.amountWithCur", Res.getBaseCurrencyCode()));
|
||||||
amountColumn.getStyleClass().add("number-column");
|
amountColumn.getStyleClass().add("number-column");
|
||||||
amountColumn.setCellValueFactory((tradeStatistics) -> new ReadOnlyObjectWrapper<>(tradeStatistics.getValue()));
|
amountColumn.setCellValueFactory((tradeStatistics) -> new ReadOnlyObjectWrapper<>(tradeStatistics.getValue()));
|
||||||
amountColumn.setCellFactory(
|
amountColumn.setCellFactory(
|
||||||
new Callback<>() {
|
new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public TableCell<TradeStatistics2, TradeStatistics2> call(
|
public TableCell<TradeStatistics3, TradeStatistics3> call(
|
||||||
TableColumn<TradeStatistics2, TradeStatistics2> column) {
|
TableColumn<TradeStatistics3, TradeStatistics3> column) {
|
||||||
return new TableCell<>() {
|
return new TableCell<>() {
|
||||||
@Override
|
@Override
|
||||||
public void updateItem(final TradeStatistics2 item, boolean empty) {
|
public void updateItem(final TradeStatistics3 item, boolean empty) {
|
||||||
super.updateItem(item, empty);
|
super.updateItem(item, empty);
|
||||||
if (item != null)
|
if (item != null)
|
||||||
setGraphic(new ColoredDecimalPlacesWithZerosText(formatter.formatCoin(item.getTradeAmount(),
|
setGraphic(new ColoredDecimalPlacesWithZerosText(formatter.formatCoin(item.getTradeAmount(),
|
||||||
|
@ -653,7 +652,7 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
amountColumn.setComparator(Comparator.comparing(TradeStatistics2::getTradeAmount));
|
amountColumn.setComparator(Comparator.comparing(TradeStatistics3::getTradeAmount));
|
||||||
tableView.getColumns().add(amountColumn);
|
tableView.getColumns().add(amountColumn);
|
||||||
|
|
||||||
// volume
|
// volume
|
||||||
|
@ -663,11 +662,11 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
||||||
volumeColumn.setCellFactory(
|
volumeColumn.setCellFactory(
|
||||||
new Callback<>() {
|
new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public TableCell<TradeStatistics2, TradeStatistics2> call(
|
public TableCell<TradeStatistics3, TradeStatistics3> call(
|
||||||
TableColumn<TradeStatistics2, TradeStatistics2> column) {
|
TableColumn<TradeStatistics3, TradeStatistics3> column) {
|
||||||
return new TableCell<>() {
|
return new TableCell<>() {
|
||||||
@Override
|
@Override
|
||||||
public void updateItem(final TradeStatistics2 item, boolean empty) {
|
public void updateItem(final TradeStatistics3 item, boolean empty) {
|
||||||
super.updateItem(item, empty);
|
super.updateItem(item, empty);
|
||||||
if (item != null)
|
if (item != null)
|
||||||
setText(model.showAllTradeCurrenciesProperty.get() ?
|
setText(model.showAllTradeCurrenciesProperty.get() ?
|
||||||
|
@ -687,17 +686,17 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
||||||
tableView.getColumns().add(volumeColumn);
|
tableView.getColumns().add(volumeColumn);
|
||||||
|
|
||||||
// paymentMethod
|
// paymentMethod
|
||||||
TableColumn<TradeStatistics2, TradeStatistics2> paymentMethodColumn = new AutoTooltipTableColumn<>(Res.get("shared.paymentMethod"));
|
TableColumn<TradeStatistics3, TradeStatistics3> paymentMethodColumn = new AutoTooltipTableColumn<>(Res.get("shared.paymentMethod"));
|
||||||
paymentMethodColumn.getStyleClass().add("number-column");
|
paymentMethodColumn.getStyleClass().add("number-column");
|
||||||
paymentMethodColumn.setCellValueFactory((tradeStatistics) -> new ReadOnlyObjectWrapper<>(tradeStatistics.getValue()));
|
paymentMethodColumn.setCellValueFactory((tradeStatistics) -> new ReadOnlyObjectWrapper<>(tradeStatistics.getValue()));
|
||||||
paymentMethodColumn.setCellFactory(
|
paymentMethodColumn.setCellFactory(
|
||||||
new Callback<>() {
|
new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public TableCell<TradeStatistics2, TradeStatistics2> call(
|
public TableCell<TradeStatistics3, TradeStatistics3> call(
|
||||||
TableColumn<TradeStatistics2, TradeStatistics2> column) {
|
TableColumn<TradeStatistics3, TradeStatistics3> column) {
|
||||||
return new TableCell<>() {
|
return new TableCell<>() {
|
||||||
@Override
|
@Override
|
||||||
public void updateItem(final TradeStatistics2 item, boolean empty) {
|
public void updateItem(final TradeStatistics3 item, boolean empty) {
|
||||||
super.updateItem(item, empty);
|
super.updateItem(item, empty);
|
||||||
if (item != null)
|
if (item != null)
|
||||||
setText(getPaymentMethodLabel(item));
|
setText(getPaymentMethodLabel(item));
|
||||||
|
@ -710,30 +709,6 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
||||||
paymentMethodColumn.setComparator(Comparator.comparing(this::getPaymentMethodLabel));
|
paymentMethodColumn.setComparator(Comparator.comparing(this::getPaymentMethodLabel));
|
||||||
tableView.getColumns().add(paymentMethodColumn);
|
tableView.getColumns().add(paymentMethodColumn);
|
||||||
|
|
||||||
// direction
|
|
||||||
TableColumn<TradeStatistics2, TradeStatistics2> directionColumn = new AutoTooltipTableColumn<>(Res.get("shared.offerType"));
|
|
||||||
directionColumn.getStyleClass().addAll("number-column", "last-column");
|
|
||||||
directionColumn.setCellValueFactory((tradeStatistics) -> new ReadOnlyObjectWrapper<>(tradeStatistics.getValue()));
|
|
||||||
directionColumn.setCellFactory(
|
|
||||||
new Callback<>() {
|
|
||||||
@Override
|
|
||||||
public TableCell<TradeStatistics2, TradeStatistics2> call(
|
|
||||||
TableColumn<TradeStatistics2, TradeStatistics2> column) {
|
|
||||||
return new TableCell<>() {
|
|
||||||
@Override
|
|
||||||
public void updateItem(final TradeStatistics2 item, boolean empty) {
|
|
||||||
super.updateItem(item, empty);
|
|
||||||
if (item != null)
|
|
||||||
setText(getDirectionLabel(item));
|
|
||||||
else
|
|
||||||
setText("");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
directionColumn.setComparator(Comparator.comparing(this::getDirectionLabel));
|
|
||||||
tableView.getColumns().add(directionColumn);
|
|
||||||
|
|
||||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||||
Label placeholder = new AutoTooltipLabel(Res.get("table.placeholder.noData"));
|
Label placeholder = new AutoTooltipLabel(Res.get("table.placeholder.noData"));
|
||||||
placeholder.setWrapText(true);
|
placeholder.setWrapText(true);
|
||||||
|
@ -743,13 +718,8 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private String getDirectionLabel(TradeStatistics2 item) {
|
private String getPaymentMethodLabel(TradeStatistics3 item) {
|
||||||
return DisplayUtils.getDirectionWithCode(OfferPayload.Direction.valueOf(item.getDirection().name()), item.getCurrencyCode());
|
return Res.get(item.getPaymentMethod());
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private String getPaymentMethodLabel(TradeStatistics2 item) {
|
|
||||||
return Res.get(item.getOfferPaymentMethod());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void layout() {
|
private void layout() {
|
||||||
|
|
|
@ -34,7 +34,7 @@ import bisq.core.locale.GlobalSettings;
|
||||||
import bisq.core.locale.TradeCurrency;
|
import bisq.core.locale.TradeCurrency;
|
||||||
import bisq.core.monetary.Altcoin;
|
import bisq.core.monetary.Altcoin;
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
import bisq.core.trade.statistics.TradeStatistics2;
|
import bisq.core.trade.statistics.TradeStatistics3;
|
||||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
|
|
||||||
|
@ -97,18 +97,18 @@ class TradesChartsViewModel extends ActivatableViewModel {
|
||||||
|
|
||||||
private final TradeStatisticsManager tradeStatisticsManager;
|
private final TradeStatisticsManager tradeStatisticsManager;
|
||||||
final Preferences preferences;
|
final Preferences preferences;
|
||||||
private PriceFeedService priceFeedService;
|
private final PriceFeedService priceFeedService;
|
||||||
private Navigation navigation;
|
private final Navigation navigation;
|
||||||
|
|
||||||
private final SetChangeListener<TradeStatistics2> setChangeListener;
|
private final SetChangeListener<TradeStatistics3> setChangeListener;
|
||||||
final ObjectProperty<TradeCurrency> selectedTradeCurrencyProperty = new SimpleObjectProperty<>();
|
final ObjectProperty<TradeCurrency> selectedTradeCurrencyProperty = new SimpleObjectProperty<>();
|
||||||
final BooleanProperty showAllTradeCurrenciesProperty = new SimpleBooleanProperty(false);
|
final BooleanProperty showAllTradeCurrenciesProperty = new SimpleBooleanProperty(false);
|
||||||
private final CurrencyList currencyListItems;
|
private final CurrencyList currencyListItems;
|
||||||
private final CurrencyListItem showAllCurrencyListItem = new CurrencyListItem(new CryptoCurrency(GUIUtil.SHOW_ALL_FLAG, ""), -1);
|
private final CurrencyListItem showAllCurrencyListItem = new CurrencyListItem(new CryptoCurrency(GUIUtil.SHOW_ALL_FLAG, ""), -1);
|
||||||
final ObservableList<TradeStatistics2> tradeStatisticsByCurrency = FXCollections.observableArrayList();
|
final ObservableList<TradeStatistics3> tradeStatisticsByCurrency = FXCollections.observableArrayList();
|
||||||
final ObservableList<XYChart.Data<Number, Number>> priceItems = FXCollections.observableArrayList();
|
final ObservableList<XYChart.Data<Number, Number>> priceItems = FXCollections.observableArrayList();
|
||||||
final ObservableList<XYChart.Data<Number, Number>> volumeItems = FXCollections.observableArrayList();
|
final ObservableList<XYChart.Data<Number, Number>> volumeItems = FXCollections.observableArrayList();
|
||||||
private Map<Long, Pair<Date, Set<TradeStatistics2>>> itemsPerInterval;
|
private Map<Long, Pair<Date, Set<TradeStatistics3>>> itemsPerInterval;
|
||||||
|
|
||||||
TickUnit tickUnit;
|
TickUnit tickUnit;
|
||||||
final int maxTicks = 90;
|
final int maxTicks = 90;
|
||||||
|
@ -119,7 +119,8 @@ class TradesChartsViewModel extends ActivatableViewModel {
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
TradesChartsViewModel(TradeStatisticsManager tradeStatisticsManager, Preferences preferences, PriceFeedService priceFeedService, Navigation navigation) {
|
TradesChartsViewModel(TradeStatisticsManager tradeStatisticsManager, Preferences preferences,
|
||||||
|
PriceFeedService priceFeedService, Navigation navigation) {
|
||||||
this.tradeStatisticsManager = tradeStatisticsManager;
|
this.tradeStatisticsManager = tradeStatisticsManager;
|
||||||
this.preferences = preferences;
|
this.preferences = preferences;
|
||||||
this.priceFeedService = priceFeedService;
|
this.priceFeedService = priceFeedService;
|
||||||
|
@ -145,7 +146,7 @@ class TradesChartsViewModel extends ActivatableViewModel {
|
||||||
private void fillTradeCurrencies() {
|
private void fillTradeCurrencies() {
|
||||||
// Don't use a set as we need all entries
|
// Don't use a set as we need all entries
|
||||||
List<TradeCurrency> tradeCurrencyList = tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
|
List<TradeCurrency> tradeCurrencyList = tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
|
||||||
.flatMap(e -> CurrencyUtil.getTradeCurrency(e.getCurrencyCode()).stream())
|
.flatMap(e -> CurrencyUtil.getTradeCurrency(e.getCurrency()).stream())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
currencyListItems.updateWithCurrencies(tradeCurrencyList, showAllCurrencyListItem);
|
currencyListItems.updateWithCurrencies(tradeCurrencyList, showAllCurrencyListItem);
|
||||||
|
@ -241,15 +242,15 @@ class TradesChartsViewModel extends ActivatableViewModel {
|
||||||
|
|
||||||
private void updateChartData() {
|
private void updateChartData() {
|
||||||
tradeStatisticsByCurrency.setAll(tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
|
tradeStatisticsByCurrency.setAll(tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
|
||||||
.filter(e -> showAllTradeCurrenciesProperty.get() || e.getCurrencyCode().equals(getCurrencyCode()))
|
.filter(e -> showAllTradeCurrenciesProperty.get() || e.getCurrency().equals(getCurrencyCode()))
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
|
|
||||||
// Generate date range and create sets for all ticks
|
// Generate date range and create sets for all ticks
|
||||||
itemsPerInterval = new HashMap<>();
|
itemsPerInterval = new HashMap<>();
|
||||||
Date time = new Date();
|
Date time = new Date();
|
||||||
for (long i = maxTicks + 1; i >= 0; --i) {
|
for (long i = maxTicks + 1; i >= 0; --i) {
|
||||||
Set<TradeStatistics2> set = new HashSet<>();
|
Set<TradeStatistics3> set = new HashSet<>();
|
||||||
Pair<Date, Set<TradeStatistics2>> pair = new Pair<>((Date) time.clone(), set);
|
Pair<Date, Set<TradeStatistics3>> pair = new Pair<>((Date) time.clone(), set);
|
||||||
itemsPerInterval.put(i, pair);
|
itemsPerInterval.put(i, pair);
|
||||||
time.setTime(time.getTime() - 1);
|
time.setTime(time.getTime() - 1);
|
||||||
time = roundToTick(time, tickUnit);
|
time = roundToTick(time, tickUnit);
|
||||||
|
@ -258,7 +259,7 @@ class TradesChartsViewModel extends ActivatableViewModel {
|
||||||
// Get all entries for the defined time interval
|
// Get all entries for the defined time interval
|
||||||
tradeStatisticsByCurrency.forEach(e -> {
|
tradeStatisticsByCurrency.forEach(e -> {
|
||||||
for (long i = maxTicks; i > 0; --i) {
|
for (long i = maxTicks; i > 0; --i) {
|
||||||
Pair<Date, Set<TradeStatistics2>> p = itemsPerInterval.get(i);
|
Pair<Date, Set<TradeStatistics3>> p = itemsPerInterval.get(i);
|
||||||
if (e.getTradeDate().after(p.getKey())) {
|
if (e.getTradeDate().after(p.getKey())) {
|
||||||
p.getValue().add(e);
|
p.getValue().add(e);
|
||||||
break;
|
break;
|
||||||
|
@ -283,7 +284,7 @@ class TradesChartsViewModel extends ActivatableViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
CandleData getCandleData(long tick, Set<TradeStatistics2> set) {
|
CandleData getCandleData(long tick, Set<TradeStatistics3> set) {
|
||||||
long open = 0;
|
long open = 0;
|
||||||
long close = 0;
|
long close = 0;
|
||||||
long high = 0;
|
long high = 0;
|
||||||
|
@ -293,7 +294,7 @@ class TradesChartsViewModel extends ActivatableViewModel {
|
||||||
long numTrades = set.size();
|
long numTrades = set.size();
|
||||||
List<Long> tradePrices = new ArrayList<>(set.size());
|
List<Long> tradePrices = new ArrayList<>(set.size());
|
||||||
|
|
||||||
for (TradeStatistics2 item : set) {
|
for (TradeStatistics3 item : set) {
|
||||||
long tradePriceAsLong = item.getTradePrice().getValue();
|
long tradePriceAsLong = item.getTradePrice().getValue();
|
||||||
// Previously a check was done which inverted the low and high for cryptocurrencies.
|
// Previously a check was done which inverted the low and high for cryptocurrencies.
|
||||||
low = (low != 0) ? Math.min(low, tradePriceAsLong) : tradePriceAsLong;
|
low = (low != 0) ? Math.min(low, tradePriceAsLong) : tradePriceAsLong;
|
||||||
|
@ -305,8 +306,8 @@ class TradesChartsViewModel extends ActivatableViewModel {
|
||||||
}
|
}
|
||||||
Collections.sort(tradePrices);
|
Collections.sort(tradePrices);
|
||||||
|
|
||||||
List<TradeStatistics2> list = new ArrayList<>(set);
|
List<TradeStatistics3> list = new ArrayList<>(set);
|
||||||
list.sort(Comparator.comparingLong(o -> o.getTradeDate().getTime()));
|
list.sort(Comparator.comparingLong(TradeStatistics3::getDate));
|
||||||
if (list.size() > 0) {
|
if (list.size() > 0) {
|
||||||
open = list.get(0).getTradePrice().getValue();
|
open = list.get(0).getTradePrice().getValue();
|
||||||
close = list.get(list.size() - 1).getTradePrice().getValue();
|
close = list.get(list.size() - 1).getTradePrice().getValue();
|
||||||
|
|
|
@ -43,7 +43,7 @@ import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.provider.fee.FeeService;
|
import bisq.core.provider.fee.FeeService;
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
import bisq.core.trade.handlers.TransactionResultHandler;
|
import bisq.core.trade.handlers.TransactionResultHandler;
|
||||||
import bisq.core.trade.statistics.TradeStatistics2;
|
import bisq.core.trade.statistics.TradeStatistics3;
|
||||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
import bisq.core.user.User;
|
import bisq.core.user.User;
|
||||||
|
@ -344,9 +344,9 @@ public abstract class MutableOfferDataModel extends OfferDataModel implements Bs
|
||||||
var blocksRange = Restrictions.getLockTime(paymentAccount.getPaymentMethod().isAsset());
|
var blocksRange = Restrictions.getLockTime(paymentAccount.getPaymentMethod().isAsset());
|
||||||
var startDate = new Date(System.currentTimeMillis() - blocksRange * 10 * 60000);
|
var startDate = new Date(System.currentTimeMillis() - blocksRange * 10 * 60000);
|
||||||
var sortedRangeData = tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
|
var sortedRangeData = tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
|
||||||
.filter(e -> e.getCurrencyCode().equals(getTradeCurrency().getCode()))
|
.filter(e -> e.getCurrency().equals(getTradeCurrency().getCode()))
|
||||||
.filter(e -> e.getTradeDate().compareTo(startDate) >= 0)
|
.filter(e -> e.getTradeDate().compareTo(startDate) >= 0)
|
||||||
.sorted(Comparator.comparing(TradeStatistics2::getTradeDate))
|
.sorted(Comparator.comparing(TradeStatistics3::getTradeDate))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
var movingAverage = new MathUtils.MovingAverage(10, 0.2);
|
var movingAverage = new MathUtils.MovingAverage(10, 0.2);
|
||||||
double[] extremes = {Double.MAX_VALUE, Double.MIN_VALUE};
|
double[] extremes = {Double.MAX_VALUE, Double.MIN_VALUE};
|
||||||
|
|
|
@ -332,7 +332,7 @@ public class FormBuilder {
|
||||||
|
|
||||||
final Tuple2<Label, VBox> topLabelWithVBox = addTopLabelWithVBox(gridPane, rowIndex, title, textField, top);
|
final Tuple2<Label, VBox> topLabelWithVBox = addTopLabelWithVBox(gridPane, rowIndex, title, textField, top);
|
||||||
|
|
||||||
// TOD not 100% sure if that is a good idea....
|
// TODO not 100% sure if that is a good idea....
|
||||||
//topLabelWithVBox.first.getStyleClass().add("jfx-text-field-top-label");
|
//topLabelWithVBox.first.getStyleClass().add("jfx-text-field-top-label");
|
||||||
|
|
||||||
return new Tuple3<>(topLabelWithVBox.first, textField, topLabelWithVBox.second);
|
return new Tuple3<>(topLabelWithVBox.first, textField, topLabelWithVBox.second);
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
</root>
|
</root>
|
||||||
|
|
||||||
<logger name="com.neemre.btcdcli4j" level="WARN"/>
|
<logger name="com.neemre.btcdcli4j" level="WARN"/>
|
||||||
<!-- <logger name="bisq.core.provider" level="WARN"/>
|
<logger name="com.neemre.btcdcli4j.core.client.ClientConfigurator" level="ERROR"/>
|
||||||
<logger name="bisq.network" level="WARN"/>-->
|
|
||||||
|
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|
|
@ -23,14 +23,12 @@ import bisq.desktop.main.market.trades.charts.CandleData;
|
||||||
import bisq.core.locale.FiatCurrency;
|
import bisq.core.locale.FiatCurrency;
|
||||||
import bisq.core.monetary.Price;
|
import bisq.core.monetary.Price;
|
||||||
import bisq.core.offer.OfferPayload;
|
import bisq.core.offer.OfferPayload;
|
||||||
|
import bisq.core.payment.payload.PaymentMethod;
|
||||||
import bisq.core.provider.price.PriceFeedService;
|
import bisq.core.provider.price.PriceFeedService;
|
||||||
import bisq.core.trade.statistics.TradeStatistics2;
|
import bisq.core.trade.statistics.TradeStatistics3;
|
||||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
|
|
||||||
import bisq.common.crypto.KeyRing;
|
|
||||||
import bisq.common.crypto.KeyStorage;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
import org.bitcoinj.utils.Fiat;
|
import org.bitcoinj.utils.Fiat;
|
||||||
|
|
||||||
|
@ -49,9 +47,6 @@ import java.util.Date;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -63,9 +58,7 @@ public class TradesChartsViewModelTest {
|
||||||
TradesChartsViewModel model;
|
TradesChartsViewModel model;
|
||||||
TradeStatisticsManager tradeStatisticsManager;
|
TradeStatisticsManager tradeStatisticsManager;
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(TradesChartsViewModelTest.class);
|
|
||||||
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||||
private KeyRing keyRing;
|
|
||||||
private File dir;
|
private File dir;
|
||||||
OfferPayload offer = new OfferPayload(null,
|
OfferPayload offer = new OfferPayload(null,
|
||||||
0,
|
0,
|
||||||
|
@ -106,6 +99,7 @@ public class TradesChartsViewModelTest {
|
||||||
null,
|
null,
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() throws IOException {
|
public void setup() throws IOException {
|
||||||
tradeStatisticsManager = mock(TradeStatisticsManager.class);
|
tradeStatisticsManager = mock(TradeStatisticsManager.class);
|
||||||
|
@ -116,7 +110,6 @@ public class TradesChartsViewModelTest {
|
||||||
dir.delete();
|
dir.delete();
|
||||||
//noinspection ResultOfMethodCallIgnored
|
//noinspection ResultOfMethodCallIgnored
|
||||||
dir.mkdir();
|
dir.mkdir();
|
||||||
keyRing = new KeyRing(new KeyStorage(dir));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
|
@ -134,13 +127,45 @@ public class TradesChartsViewModelTest {
|
||||||
long volume = Fiat.parseFiat("EUR", "2200").value;
|
long volume = Fiat.parseFiat("EUR", "2200").value;
|
||||||
boolean isBullish = true;
|
boolean isBullish = true;
|
||||||
|
|
||||||
Set<TradeStatistics2> set = new HashSet<>();
|
Set<TradeStatistics3> set = new HashSet<>();
|
||||||
final Date now = new Date();
|
final Date now = new Date();
|
||||||
|
|
||||||
set.add(new TradeStatistics2(offer, Price.parse("EUR", "520"), Coin.parseCoin("1"), new Date(now.getTime()), null, null));
|
set.add(new TradeStatistics3(offer.getCurrencyCode(),
|
||||||
set.add(new TradeStatistics2(offer, Price.parse("EUR", "500"), Coin.parseCoin("1"), new Date(now.getTime() + 100), null, null));
|
Price.parse("EUR", "520").getValue(),
|
||||||
set.add(new TradeStatistics2(offer, Price.parse("EUR", "600"), Coin.parseCoin("1"), new Date(now.getTime() + 200), null, null));
|
Coin.parseCoin("1").getValue(),
|
||||||
set.add(new TradeStatistics2(offer, Price.parse("EUR", "580"), Coin.parseCoin("1"), new Date(now.getTime() + 300), null, null));
|
PaymentMethod.BLOCK_CHAINS_ID,
|
||||||
|
now.getTime(),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null));
|
||||||
|
set.add(new TradeStatistics3(offer.getCurrencyCode(),
|
||||||
|
Price.parse("EUR", "500").getValue(),
|
||||||
|
Coin.parseCoin("1").getValue(),
|
||||||
|
PaymentMethod.BLOCK_CHAINS_ID,
|
||||||
|
now.getTime() + 100,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null));
|
||||||
|
set.add(new TradeStatistics3(offer.getCurrencyCode(),
|
||||||
|
Price.parse("EUR", "600").getValue(),
|
||||||
|
Coin.parseCoin("1").getValue(),
|
||||||
|
PaymentMethod.BLOCK_CHAINS_ID,
|
||||||
|
now.getTime() + 200,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null));
|
||||||
|
set.add(new TradeStatistics3(offer.getCurrencyCode(),
|
||||||
|
Price.parse("EUR", "580").getValue(),
|
||||||
|
Coin.parseCoin("1").getValue(),
|
||||||
|
PaymentMethod.BLOCK_CHAINS_ID,
|
||||||
|
now.getTime() + 300,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null));
|
||||||
|
|
||||||
CandleData candleData = model.getCandleData(model.roundToTick(now, TradesChartsViewModel.TickUnit.DAY).getTime(), set);
|
CandleData candleData = model.getCandleData(model.roundToTick(now, TradesChartsViewModel.TickUnit.DAY).getTime(), set);
|
||||||
assertEquals(open, candleData.open);
|
assertEquals(open, candleData.open);
|
||||||
|
@ -183,7 +208,6 @@ public class TradesChartsViewModelTest {
|
||||||
ArrayList<Trade> trades = new ArrayList<>();
|
ArrayList<Trade> trades = new ArrayList<>();
|
||||||
|
|
||||||
// Set predetermined time to use as "now" during test
|
// Set predetermined time to use as "now" during test
|
||||||
Date test_time = dateFormat.parse("2018-01-01T00:00:05"); // Monday
|
|
||||||
/* new MockUp<System>() {
|
/* new MockUp<System>() {
|
||||||
@Mock
|
@Mock
|
||||||
long currentTimeMillis() {
|
long currentTimeMillis() {
|
||||||
|
@ -194,11 +218,19 @@ public class TradesChartsViewModelTest {
|
||||||
// Two trades 10 seconds apart, different YEAR, MONTH, WEEK, DAY, HOUR, MINUTE_10
|
// Two trades 10 seconds apart, different YEAR, MONTH, WEEK, DAY, HOUR, MINUTE_10
|
||||||
trades.add(new Trade("2017-12-31T23:59:52", "1", "100", "EUR"));
|
trades.add(new Trade("2017-12-31T23:59:52", "1", "100", "EUR"));
|
||||||
trades.add(new Trade("2018-01-01T00:00:02", "1", "110", "EUR"));
|
trades.add(new Trade("2018-01-01T00:00:02", "1", "110", "EUR"));
|
||||||
Set<TradeStatistics2> set = new HashSet<>();
|
Set<TradeStatistics3> set = new HashSet<>();
|
||||||
trades.forEach(t ->
|
trades.forEach(t ->
|
||||||
set.add(new TradeStatistics2(offer, Price.parse(t.cc, t.price), Coin.parseCoin(t.size), t.date, null, null))
|
set.add(new TradeStatistics3(offer.getCurrencyCode(),
|
||||||
|
Price.parse(t.cc, t.price).getValue(),
|
||||||
|
Coin.parseCoin(t.size).getValue(),
|
||||||
|
PaymentMethod.BLOCK_CHAINS_ID,
|
||||||
|
t.date.getTime(),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null))
|
||||||
);
|
);
|
||||||
ObservableSet<TradeStatistics2> tradeStats = FXCollections.observableSet(set);
|
ObservableSet<TradeStatistics3> tradeStats = FXCollections.observableSet(set);
|
||||||
|
|
||||||
// Run test for each tick type
|
// Run test for each tick type
|
||||||
for (TradesChartsViewModel.TickUnit tick : TradesChartsViewModel.TickUnit.values()) {
|
for (TradesChartsViewModel.TickUnit tick : TradesChartsViewModel.TickUnit.values()) {
|
||||||
|
@ -209,7 +241,7 @@ public class TradesChartsViewModelTest {
|
||||||
|
|
||||||
// Trigger chart update
|
// Trigger chart update
|
||||||
model.setTickUnit(tick);
|
model.setTickUnit(tick);
|
||||||
assertEquals(model.selectedTradeCurrencyProperty.get().getCode(), tradeStats.iterator().next().getCurrencyCode());
|
assertEquals(model.selectedTradeCurrencyProperty.get().getCode(), tradeStats.iterator().next().getCurrency());
|
||||||
assertEquals(2, model.priceItems.size());
|
assertEquals(2, model.priceItems.size());
|
||||||
assertEquals(2, model.volumeItems.size());
|
assertEquals(2, model.volumeItems.size());
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,7 +90,8 @@ public class Monitor {
|
||||||
Capability.DAO_STATE,
|
Capability.DAO_STATE,
|
||||||
Capability.BUNDLE_OF_ENVELOPES,
|
Capability.BUNDLE_OF_ENVELOPES,
|
||||||
Capability.REFUND_AGENT,
|
Capability.REFUND_AGENT,
|
||||||
Capability.MEDIATION);
|
Capability.MEDIATION,
|
||||||
|
Capability.TRADE_STATISTICS_3);
|
||||||
|
|
||||||
// assemble Metrics
|
// assemble Metrics
|
||||||
// - create reporters
|
// - create reporters
|
||||||
|
|
|
@ -20,30 +20,15 @@ package bisq.monitor.metric;
|
||||||
import bisq.monitor.Metric;
|
import bisq.monitor.Metric;
|
||||||
import bisq.monitor.Reporter;
|
import bisq.monitor.Reporter;
|
||||||
|
|
||||||
import bisq.asset.Asset;
|
|
||||||
import bisq.asset.AssetRegistry;
|
|
||||||
|
|
||||||
import bisq.network.p2p.storage.payload.ProtectedStoragePayload;
|
|
||||||
|
|
||||||
import org.berndpruenster.netlayer.tor.TorCtlException;
|
|
||||||
|
|
||||||
import com.runjva.sourceforge.jsocks.protocol.SocksSocket;
|
|
||||||
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.BufferedWriter;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
|
@ -110,7 +95,6 @@ public class MarketStats extends Metric {
|
||||||
} catch (IllegalStateException ignore) {
|
} catch (IllegalStateException ignore) {
|
||||||
// no match found
|
// no match found
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ import bisq.monitor.Reporter;
|
||||||
import bisq.core.account.witness.AccountAgeWitnessStore;
|
import bisq.core.account.witness.AccountAgeWitnessStore;
|
||||||
import bisq.core.offer.OfferPayload;
|
import bisq.core.offer.OfferPayload;
|
||||||
import bisq.core.proto.persistable.CorePersistenceProtoResolver;
|
import bisq.core.proto.persistable.CorePersistenceProtoResolver;
|
||||||
import bisq.core.trade.statistics.TradeStatistics2Store;
|
import bisq.core.trade.statistics.TradeStatistics3Store;
|
||||||
|
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
import bisq.network.p2p.network.Connection;
|
import bisq.network.p2p.network.Connection;
|
||||||
|
@ -163,8 +163,8 @@ public class P2PMarketStats extends P2PSeedNodeSnapshotBase {
|
||||||
String networkPostfix = "_" + BaseCurrencyNetwork.values()[Version.getBaseCurrencyNetwork()].toString();
|
String networkPostfix = "_" + BaseCurrencyNetwork.values()[Version.getBaseCurrencyNetwork()].toString();
|
||||||
try {
|
try {
|
||||||
PersistenceManager<PersistableEnvelope> persistenceManager = new PersistenceManager<>(dir, new CorePersistenceProtoResolver(null, null), null);
|
PersistenceManager<PersistableEnvelope> persistenceManager = new PersistenceManager<>(dir, new CorePersistenceProtoResolver(null, null), null);
|
||||||
TradeStatistics2Store tradeStatistics2Store = (TradeStatistics2Store) persistenceManager.getPersisted(TradeStatistics2Store.class.getSimpleName() + networkPostfix);
|
TradeStatistics3Store tradeStatistics3Store = (TradeStatistics3Store) persistenceManager.getPersisted(TradeStatistics3Store.class.getSimpleName() + networkPostfix);
|
||||||
hashes.addAll(tradeStatistics2Store.getMap().keySet().stream().map(byteArray -> byteArray.bytes).collect(Collectors.toList()));
|
hashes.addAll(tradeStatistics3Store.getMap().keySet().stream().map(byteArray -> byteArray.bytes).collect(Collectors.toList()));
|
||||||
|
|
||||||
AccountAgeWitnessStore accountAgeWitnessStore = (AccountAgeWitnessStore) persistenceManager.getPersisted(AccountAgeWitnessStore.class.getSimpleName() + networkPostfix);
|
AccountAgeWitnessStore accountAgeWitnessStore = (AccountAgeWitnessStore) persistenceManager.getPersisted(AccountAgeWitnessStore.class.getSimpleName() + networkPostfix);
|
||||||
hashes.addAll(accountAgeWitnessStore.getMap().keySet().stream().map(byteArray -> byteArray.bytes).collect(Collectors.toList()));
|
hashes.addAll(accountAgeWitnessStore.getMap().keySet().stream().map(byteArray -> byteArray.bytes).collect(Collectors.toList()));
|
||||||
|
|
|
@ -27,7 +27,7 @@ import bisq.core.dao.monitoring.network.messages.GetDaoStateHashesRequest;
|
||||||
import bisq.core.dao.monitoring.network.messages.GetProposalStateHashesRequest;
|
import bisq.core.dao.monitoring.network.messages.GetProposalStateHashesRequest;
|
||||||
import bisq.core.dao.monitoring.network.messages.GetStateHashesResponse;
|
import bisq.core.dao.monitoring.network.messages.GetStateHashesResponse;
|
||||||
import bisq.core.proto.persistable.CorePersistenceProtoResolver;
|
import bisq.core.proto.persistable.CorePersistenceProtoResolver;
|
||||||
import bisq.core.trade.statistics.TradeStatistics2Store;
|
import bisq.core.trade.statistics.TradeStatistics3Store;
|
||||||
|
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
import bisq.network.p2p.network.Connection;
|
import bisq.network.p2p.network.Connection;
|
||||||
|
@ -137,8 +137,8 @@ public class P2PSeedNodeSnapshot extends P2PSeedNodeSnapshotBase {
|
||||||
String networkPostfix = "_" + BaseCurrencyNetwork.values()[Version.getBaseCurrencyNetwork()].toString();
|
String networkPostfix = "_" + BaseCurrencyNetwork.values()[Version.getBaseCurrencyNetwork()].toString();
|
||||||
try {
|
try {
|
||||||
PersistenceManager<PersistableEnvelope> persistenceManager = new PersistenceManager<>(dir, new CorePersistenceProtoResolver(null, null), null);
|
PersistenceManager<PersistableEnvelope> persistenceManager = new PersistenceManager<>(dir, new CorePersistenceProtoResolver(null, null), null);
|
||||||
TradeStatistics2Store tradeStatistics2Store = (TradeStatistics2Store) persistenceManager.getPersisted(TradeStatistics2Store.class.getSimpleName() + networkPostfix);
|
TradeStatistics3Store tradeStatistics3Store = (TradeStatistics3Store) persistenceManager.getPersisted(TradeStatistics3Store.class.getSimpleName() + networkPostfix);
|
||||||
hashes.addAll(tradeStatistics2Store.getMap().keySet().stream().map(byteArray -> byteArray.bytes).collect(Collectors.toList()));
|
hashes.addAll(tradeStatistics3Store.getMap().keySet().stream().map(byteArray -> byteArray.bytes).collect(Collectors.toList()));
|
||||||
|
|
||||||
AccountAgeWitnessStore accountAgeWitnessStore = (AccountAgeWitnessStore) persistenceManager.getPersisted(AccountAgeWitnessStore.class.getSimpleName() + networkPostfix);
|
AccountAgeWitnessStore accountAgeWitnessStore = (AccountAgeWitnessStore) persistenceManager.getPersisted(AccountAgeWitnessStore.class.getSimpleName() + networkPostfix);
|
||||||
hashes.addAll(accountAgeWitnessStore.getMap().keySet().stream().map(byteArray -> byteArray.bytes).collect(Collectors.toList()));
|
hashes.addAll(accountAgeWitnessStore.getMap().keySet().stream().map(byteArray -> byteArray.bytes).collect(Collectors.toList()));
|
||||||
|
@ -233,9 +233,9 @@ public class P2PSeedNodeSnapshot extends P2PSeedNodeSnapshotBase {
|
||||||
int oldest = (int) nodeAddressTupleMap.values().stream().min(Comparator.comparingLong(Tuple::getHeight)).get().height;
|
int oldest = (int) nodeAddressTupleMap.values().stream().min(Comparator.comparingLong(Tuple::getHeight)).get().height;
|
||||||
|
|
||||||
// - update queried height
|
// - update queried height
|
||||||
if(type.contains("DaoState"))
|
if (type.contains("DaoState"))
|
||||||
daostateheight = oldest - 20;
|
daostateheight = oldest - 20;
|
||||||
else if(type.contains("Proposal"))
|
else if (type.contains("Proposal"))
|
||||||
proposalheight = oldest - 20;
|
proposalheight = oldest - 20;
|
||||||
else
|
else
|
||||||
blindvoteheight = oldest - 20;
|
blindvoteheight = oldest - 20;
|
||||||
|
@ -255,7 +255,6 @@ public class P2PSeedNodeSnapshot extends P2PSeedNodeSnapshotBase {
|
||||||
|
|
||||||
List<ByteBuffer> states = hitcount.entrySet().stream().sorted((o1, o2) -> o2.getValue().compareTo(o1.getValue())).map(byteBufferIntegerEntry -> byteBufferIntegerEntry.getKey()).collect(Collectors.toList());
|
List<ByteBuffer> states = hitcount.entrySet().stream().sorted((o1, o2) -> o2.getValue().compareTo(o1.getValue())).map(byteBufferIntegerEntry -> byteBufferIntegerEntry.getKey()).collect(Collectors.toList());
|
||||||
hitcount.clear();
|
hitcount.clear();
|
||||||
|
|
||||||
nodeAddressTupleMap.forEach((nodeAddress, tuple) -> daoreport.put(type + "." + OnionParser.prettyPrint(nodeAddress) + ".hash", Integer.toString(Arrays.asList(states.toArray()).indexOf(ByteBuffer.wrap(tuple.hash)))));
|
nodeAddressTupleMap.forEach((nodeAddress, tuple) -> daoreport.put(type + "." + OnionParser.prettyPrint(nodeAddress) + ".hash", Integer.toString(Arrays.asList(states.toArray()).indexOf(ByteBuffer.wrap(tuple.hash)))));
|
||||||
|
|
||||||
// - report reference head
|
// - report reference head
|
||||||
|
|
|
@ -159,7 +159,6 @@ public class PriceNodeStats extends Metric {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (TorCtlException | IOException e) {
|
} catch (TorCtlException | IOException e) {
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import bisq.monitor.Metric;
|
||||||
import bisq.monitor.OnionParser;
|
import bisq.monitor.OnionParser;
|
||||||
import bisq.monitor.Reporter;
|
import bisq.monitor.Reporter;
|
||||||
import bisq.monitor.StatisticsHelper;
|
import bisq.monitor.StatisticsHelper;
|
||||||
|
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
|
|
||||||
import org.berndpruenster.netlayer.tor.Tor;
|
import org.berndpruenster.netlayer.tor.Tor;
|
||||||
|
@ -33,6 +34,7 @@ import java.io.IOException;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -84,7 +86,6 @@ public class TorRoundTripTime extends Metric {
|
||||||
reporter.report(StatisticsHelper.process(samples), getName());
|
reporter.report(StatisticsHelper.process(samples), getName());
|
||||||
}
|
}
|
||||||
} catch (TorCtlException | IOException e) {
|
} catch (TorCtlException | IOException e) {
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,6 @@ public class TorStartupTime extends Metric {
|
||||||
try {
|
try {
|
||||||
torOverrides = new Torrc(overrides);
|
torOverrides = new Torrc(overrides);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +78,6 @@ public class TorStartupTime extends Metric {
|
||||||
// stop the timer and set its timestamp
|
// stop the timer and set its timestamp
|
||||||
reporter.report(System.currentTimeMillis() - start, getName());
|
reporter.report(System.currentTimeMillis() - start, getName());
|
||||||
} catch (TorCtlException e) {
|
} catch (TorCtlException e) {
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} finally {
|
} finally {
|
||||||
// cleanup
|
// cleanup
|
||||||
|
|
|
@ -920,6 +920,13 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
return keyRing;
|
return keyRing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<Capabilities> findPeersCapabilities(NodeAddress peer) {
|
||||||
|
return networkNode.getConfirmedConnections().stream()
|
||||||
|
.filter(e -> e.getPeersNodeAddressOptional().isPresent())
|
||||||
|
.filter(e -> e.getPeersNodeAddressOptional().get().equals(peer))
|
||||||
|
.map(Connection::getCapabilities)
|
||||||
|
.findAny();
|
||||||
|
}
|
||||||
|
|
||||||
@Value
|
@Value
|
||||||
public class MailboxItem {
|
public class MailboxItem {
|
||||||
|
|
|
@ -61,7 +61,7 @@ public abstract class MapStoreService<T extends PersistableEnvelope, R extends P
|
||||||
requestPersistence();
|
requestPersistence();
|
||||||
}
|
}
|
||||||
|
|
||||||
R putIfAbsent(P2PDataStorage.ByteArray hash, R payload) {
|
protected R putIfAbsent(P2PDataStorage.ByteArray hash, R payload) {
|
||||||
R previous = getMap().putIfAbsent(hash, payload);
|
R previous = getMap().putIfAbsent(hash, payload);
|
||||||
requestPersistence();
|
requestPersistence();
|
||||||
return previous;
|
return previous;
|
||||||
|
|
BIN
p2p/src/main/resources/TradeStatistics2Store_BTC_MAINNET
(Stored with Git LFS)
BIN
p2p/src/main/resources/TradeStatistics2Store_BTC_MAINNET
(Stored with Git LFS)
Binary file not shown.
BIN
p2p/src/main/resources/TradeStatistics3Store_1.4.0_BTC_MAINNET
(Stored with Git LFS)
Normal file
BIN
p2p/src/main/resources/TradeStatistics3Store_1.4.0_BTC_MAINNET
(Stored with Git LFS)
Normal file
Binary file not shown.
|
@ -154,7 +154,7 @@ message GetTradeStatisticsRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetTradeStatisticsReply {
|
message GetTradeStatisticsReply {
|
||||||
repeated TradeStatistics2 TradeStatistics = 1;
|
repeated TradeStatistics3 TradeStatistics = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -510,9 +510,7 @@ message StoragePayload {
|
||||||
Mediator mediator = 3;
|
Mediator mediator = 3;
|
||||||
Filter filter = 4;
|
Filter filter = 4;
|
||||||
|
|
||||||
// not used anymore from v0.6 on. But leave it for receiving TradeStatistics objects from older
|
// TradeStatistics trade_statistics = 5 [deprecated = true]; Removed in v.1.4.0
|
||||||
// versions and convert it to TradeStatistics2 objects.
|
|
||||||
TradeStatistics trade_statistics = 5 [deprecated = true];
|
|
||||||
|
|
||||||
MailboxStoragePayload mailbox_storage_payload = 6;
|
MailboxStoragePayload mailbox_storage_payload = 6;
|
||||||
OfferPayload offer_payload = 7;
|
OfferPayload offer_payload = 7;
|
||||||
|
@ -524,10 +522,11 @@ message StoragePayload {
|
||||||
message PersistableNetworkPayload {
|
message PersistableNetworkPayload {
|
||||||
oneof message {
|
oneof message {
|
||||||
AccountAgeWitness account_age_witness = 1;
|
AccountAgeWitness account_age_witness = 1;
|
||||||
TradeStatistics2 trade_statistics2 = 2;
|
TradeStatistics2 trade_statistics2 = 2 [deprecated = true];
|
||||||
ProposalPayload proposal_payload = 3;
|
ProposalPayload proposal_payload = 3;
|
||||||
BlindVotePayload blind_vote_payload = 4;
|
BlindVotePayload blind_vote_payload = 4;
|
||||||
SignedWitness signed_witness = 5;
|
SignedWitness signed_witness = 5;
|
||||||
|
TradeStatistics3 trade_statistics3 = 6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -650,44 +649,36 @@ message Filter {
|
||||||
bool disable_auto_conf = 24;
|
bool disable_auto_conf = 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
// not used anymore from v0.6 on. But leave it for receiving TradeStatistics objects from older
|
// Deprecated
|
||||||
// versions and convert it to TradeStatistics2 objects.
|
message TradeStatistics2 {
|
||||||
message TradeStatistics {
|
string base_currency = 1 [deprecated = true];
|
||||||
string base_currency = 1;
|
string counter_currency = 2 [deprecated = true];
|
||||||
string counter_currency = 2;
|
OfferPayload.Direction direction = 3 [deprecated = true];
|
||||||
OfferPayload.Direction direction = 3;
|
int64 trade_price = 4 [deprecated = true];
|
||||||
int64 trade_price = 4;
|
int64 trade_amount = 5 [deprecated = true];
|
||||||
int64 trade_amount = 5;
|
int64 trade_date = 6 [deprecated = true];
|
||||||
int64 trade_date = 6;
|
string payment_method_id = 7 [deprecated = true];
|
||||||
string payment_method_id = 7;
|
int64 offer_date = 8 [deprecated = true];
|
||||||
int64 offer_date = 8;
|
bool offer_use_market_based_price = 9 [deprecated = true];
|
||||||
bool offer_use_market_based_price = 9;
|
double offer_market_price_margin = 10 [deprecated = true];
|
||||||
double offer_market_price_margin = 10;
|
int64 offer_amount = 11 [deprecated = true];
|
||||||
int64 offer_amount = 11;
|
int64 offer_min_amount = 12 [deprecated = true];
|
||||||
int64 offer_min_amount = 12;
|
string offer_id = 13 [deprecated = true];
|
||||||
string offer_id = 13;
|
string deposit_tx_id = 14 [deprecated = true];
|
||||||
string deposit_tx_id = 14;
|
bytes hash = 15 [deprecated = true];
|
||||||
bytes signature_pub_key_bytes = 15;
|
map<string, string> extra_data = 16 [deprecated = true];
|
||||||
map<string, string> extra_data = 16;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message TradeStatistics2 {
|
message TradeStatistics3 {
|
||||||
string base_currency = 1;
|
string currency = 1;
|
||||||
string counter_currency = 2;
|
int64 price = 2;
|
||||||
OfferPayload.Direction direction = 3;
|
int64 amount = 3;
|
||||||
int64 trade_price = 4;
|
string payment_method = 4;
|
||||||
int64 trade_amount = 5;
|
int64 date = 5;
|
||||||
int64 trade_date = 6;
|
string mediator = 6;
|
||||||
string payment_method_id = 7;
|
string refund_agent = 7;
|
||||||
int64 offer_date = 8;
|
bytes hash = 8;
|
||||||
bool offer_use_market_based_price = 9;
|
map<string, string> extra_data = 9;
|
||||||
double offer_market_price_margin = 10;
|
|
||||||
int64 offer_amount = 11;
|
|
||||||
int64 offer_min_amount = 12;
|
|
||||||
string offer_id = 13;
|
|
||||||
string deposit_tx_id = 14;
|
|
||||||
bytes hash = 15;
|
|
||||||
map<string, string> extra_data = 16;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message MailboxStoragePayload {
|
message MailboxStoragePayload {
|
||||||
|
@ -1152,7 +1143,7 @@ message PersistableEnvelope {
|
||||||
// BsqState bsq_state = 12; // not used but as other non-dao data have a higher index number we leave it to make clear that we cannot change following indexes
|
// BsqState bsq_state = 12; // not used but as other non-dao data have a higher index number we leave it to make clear that we cannot change following indexes
|
||||||
|
|
||||||
AccountAgeWitnessStore account_age_witness_store = 13;
|
AccountAgeWitnessStore account_age_witness_store = 13;
|
||||||
TradeStatistics2Store trade_statistics2_store = 14;
|
TradeStatistics2Store trade_statistics2_store = 14 [deprecated = true];
|
||||||
|
|
||||||
// PersistableNetworkPayloadList persistable_network_payload_list = 15; // long deprecated & migration away from it is already done
|
// PersistableNetworkPayloadList persistable_network_payload_list = 15; // long deprecated & migration away from it is already done
|
||||||
|
|
||||||
|
@ -1171,6 +1162,7 @@ message PersistableEnvelope {
|
||||||
SignedWitnessStore signed_witness_store = 28;
|
SignedWitnessStore signed_witness_store = 28;
|
||||||
MediationDisputeList mediation_dispute_list = 29;
|
MediationDisputeList mediation_dispute_list = 29;
|
||||||
RefundDisputeList refund_dispute_list = 30;
|
RefundDisputeList refund_dispute_list = 30;
|
||||||
|
TradeStatistics3Store trade_statistics3_store = 31;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1211,8 +1203,13 @@ message SignedWitnessStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use a list not a hash map to save disc space. The hash can be calculated from the payload anyway
|
// We use a list not a hash map to save disc space. The hash can be calculated from the payload anyway
|
||||||
|
// Deprecated
|
||||||
message TradeStatistics2Store {
|
message TradeStatistics2Store {
|
||||||
repeated TradeStatistics2 items = 1;
|
repeated TradeStatistics2 items = 1 [deprecated = true];
|
||||||
|
}
|
||||||
|
|
||||||
|
message TradeStatistics3Store {
|
||||||
|
repeated TradeStatistics3 items = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message PeerList {
|
message PeerList {
|
||||||
|
|
Loading…
Add table
Reference in a new issue