Add TradeStatsisticsManager, add CoreOptionKeys, rename method in Storage

This commit is contained in:
Manfred Karrer 2016-07-25 10:16:06 +02:00
parent 242efeecb9
commit a6660d04c9
23 changed files with 202 additions and 177 deletions

View File

@ -2,5 +2,5 @@ package io.bitsquare.common;
public class CommonOptionKeys {
public static final String LOG_LEVEL_KEY = "logLevel";
public static final String IGNORE_DEV_MSG_KEY = "ignoreDevMsg";
}

View File

@ -77,7 +77,7 @@ public class Storage<T extends Serializable> {
}
@Nullable
public T initAndGetPersisted(String fileName) {
public T initAndGetPersistedWithFileName(String fileName) {
this.fileName = fileName;
storageFile = new File(dir, fileName);
fileManager = new FileManager<>(dir, storageFile, 300);

View File

@ -19,7 +19,7 @@ package io.bitsquare.alert;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import io.bitsquare.common.CommonOptionKeys;
import io.bitsquare.app.CoreOptionKeys;
import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.storage.HashMapChangedListener;
@ -56,7 +56,7 @@ public class AlertManager {
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public AlertManager(P2PService p2PService, KeyRing keyRing, User user, @Named(CommonOptionKeys.IGNORE_DEV_MSG_KEY) boolean ignoreDevMsg) {
public AlertManager(P2PService p2PService, KeyRing keyRing, User user, @Named(CoreOptionKeys.IGNORE_DEV_MSG_KEY) boolean ignoreDevMsg) {
this.p2PService = p2PService;
this.keyRing = keyRing;
this.user = user;

View File

@ -19,10 +19,13 @@ package io.bitsquare.alert;
import com.google.inject.Singleton;
import io.bitsquare.app.AppModule;
import io.bitsquare.app.CoreOptionKeys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import static com.google.inject.name.Names.named;
public class AlertModule extends AppModule {
private static final Logger log = LoggerFactory.getLogger(AlertModule.class);
@ -34,5 +37,6 @@ public class AlertModule extends AppModule {
protected final void configure() {
bind(AlertManager.class).in(Singleton.class);
bind(PrivateNotificationManager.class).in(Singleton.class);
bindConstant().annotatedWith(named(CoreOptionKeys.IGNORE_DEV_MSG_KEY)).to(env.getRequiredProperty(CoreOptionKeys.IGNORE_DEV_MSG_KEY));
}
}

View File

@ -19,7 +19,7 @@ package io.bitsquare.alert;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import io.bitsquare.common.CommonOptionKeys;
import io.bitsquare.app.CoreOptionKeys;
import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.crypto.DecryptedMsgWithPubKey;
import io.bitsquare.p2p.Message;
@ -58,7 +58,7 @@ public class PrivateNotificationManager {
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public PrivateNotificationManager(P2PService p2PService, KeyRing keyRing, @Named(CommonOptionKeys.IGNORE_DEV_MSG_KEY) boolean ignoreDevMsg) {
public PrivateNotificationManager(P2PService p2PService, KeyRing keyRing, @Named(CoreOptionKeys.IGNORE_DEV_MSG_KEY) boolean ignoreDevMsg) {
this.p2PService = p2PService;
this.keyRing = keyRing;

View File

@ -85,7 +85,7 @@ public class BitsquareEnvironment extends StandardEnvironment {
private final String btcNetworkDir;
private final String logLevel;
private BitcoinNetwork bitcoinNetwork;
private final String btcSeedNodes, seedNodes, ignoreDevMsg, useTorForBtc, myAddress, banList;
private final String btcSeedNodes, seedNodes, ignoreDevMsg, useTorForBtc, myAddress, banList, dumpStatistics;
public BitsquareEnvironment(OptionSet options) {
this(new JOptCommandLinePropertySource(BITSQUARE_COMMANDLINE_PROPERTY_SOURCE_NAME, checkNotNull(
@ -153,8 +153,8 @@ public class BitsquareEnvironment extends StandardEnvironment {
(String) commandLineProperties.getProperty(NetworkOptionKeys.BAN_LIST) :
"";
ignoreDevMsg = commandLineProperties.containsProperty(CommonOptionKeys.IGNORE_DEV_MSG_KEY) ?
(String) commandLineProperties.getProperty(CommonOptionKeys.IGNORE_DEV_MSG_KEY) :
ignoreDevMsg = commandLineProperties.containsProperty(CoreOptionKeys.IGNORE_DEV_MSG_KEY) ?
(String) commandLineProperties.getProperty(CoreOptionKeys.IGNORE_DEV_MSG_KEY) :
"";
btcSeedNodes = commandLineProperties.containsProperty(BtcOptionKeys.BTC_SEED_NODES) ?
@ -165,6 +165,10 @@ public class BitsquareEnvironment extends StandardEnvironment {
(String) commandLineProperties.getProperty(BtcOptionKeys.USE_TOR_FOR_BTC) :
"";
dumpStatistics = commandLineProperties.containsProperty(CoreOptionKeys.DUMP_STATISTICS) ?
(String) commandLineProperties.getProperty(CoreOptionKeys.DUMP_STATISTICS) :
"";
MutablePropertySources propertySources = this.getPropertySources();
propertySources.addFirst(commandLineProperties);
@ -226,7 +230,8 @@ public class BitsquareEnvironment extends StandardEnvironment {
setProperty(NetworkOptionKeys.SEED_NODES_KEY, seedNodes);
setProperty(NetworkOptionKeys.MY_ADDRESS, myAddress);
setProperty(NetworkOptionKeys.BAN_LIST, banList);
setProperty(CommonOptionKeys.IGNORE_DEV_MSG_KEY, ignoreDevMsg);
setProperty(CoreOptionKeys.IGNORE_DEV_MSG_KEY, ignoreDevMsg);
setProperty(CoreOptionKeys.DUMP_STATISTICS, dumpStatistics);
setProperty(BtcOptionKeys.BTC_SEED_NODES, btcSeedNodes);
setProperty(BtcOptionKeys.USE_TOR_FOR_BTC, useTorForBtc);

View File

@ -86,11 +86,15 @@ public abstract class BitsquareExecutable {
parser.accepts(NetworkOptionKeys.BAN_LIST, description("Nodes to exclude from network connections.", ""))
.withRequiredArg();
parser.accepts(CommonOptionKeys.IGNORE_DEV_MSG_KEY, description("If set to true all signed messages from Bitsquare developers are ignored " +
parser.accepts(CoreOptionKeys.IGNORE_DEV_MSG_KEY, description("If set to true all signed messages from Bitsquare developers are ignored " +
"(Global alert, Version update alert, Filters for offers, nodes or payment account data)", false))
.withRequiredArg()
.ofType(boolean.class);
parser.accepts(CoreOptionKeys.DUMP_STATISTICS, description("If set to true the trade statistics are stored as json file in the data dir.", false))
.withRequiredArg()
.ofType(boolean.class);
parser.accepts(BtcOptionKeys.BTC_SEED_NODES, description("Custom seed nodes used for BitcoinJ.", ""))
.withRequiredArg();
parser.accepts(BtcOptionKeys.USE_TOR_FOR_BTC, description("If set to true BitcoinJ is routed over our native Tor instance.", ""))

View File

@ -0,0 +1,7 @@
package io.bitsquare.app;
public class CoreOptionKeys {
public static final String IGNORE_DEV_MSG_KEY = "ignoreDevMsg";
public static final String DUMP_STATISTICS = "dumpStatistics";
}

View File

@ -135,7 +135,7 @@ public class WalletService {
useTor = preferences.getUseTorForBitcoinJ();
storage = new Storage<>(walletDir);
Long persisted = storage.initAndGetPersisted("BloomFilterNonce");
Long persisted = storage.initAndGetPersistedWithFileName("BloomFilterNonce");
if (persisted != null) {
bloomFilterTweak = persisted;
} else {

View File

@ -19,7 +19,7 @@ package io.bitsquare.filter;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import io.bitsquare.common.CommonOptionKeys;
import io.bitsquare.app.CoreOptionKeys;
import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.common.util.Tuple3;
import io.bitsquare.common.util.Utilities;
@ -59,7 +59,7 @@ public class FilterManager {
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public FilterManager(P2PService p2PService, KeyRing keyRing, User user, @Named(CommonOptionKeys.IGNORE_DEV_MSG_KEY) boolean ignoreDevMsg) {
public FilterManager(P2PService p2PService, KeyRing keyRing, User user, @Named(CoreOptionKeys.IGNORE_DEV_MSG_KEY) boolean ignoreDevMsg) {
this.p2PService = p2PService;
this.keyRing = keyRing;
this.user = user;

View File

@ -19,7 +19,7 @@ package io.bitsquare.filter;
import com.google.inject.Singleton;
import io.bitsquare.app.AppModule;
import io.bitsquare.common.CommonOptionKeys;
import io.bitsquare.app.CoreOptionKeys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
@ -36,6 +36,6 @@ public class FilterModule extends AppModule {
@Override
protected final void configure() {
bind(FilterManager.class).in(Singleton.class);
bindConstant().annotatedWith(named(CommonOptionKeys.IGNORE_DEV_MSG_KEY)).to(env.getRequiredProperty(CommonOptionKeys.IGNORE_DEV_MSG_KEY));
bindConstant().annotatedWith(named(CoreOptionKeys.IGNORE_DEV_MSG_KEY)).to(env.getRequiredProperty(CoreOptionKeys.IGNORE_DEV_MSG_KEY));
}
}

View File

@ -84,7 +84,8 @@ public class TradeManager {
private final FailedTradesManager failedTradesManager;
private final ArbitratorManager arbitratorManager;
private final P2PService p2PService;
private FilterManager filterManager;
private final FilterManager filterManager;
private final TradeStatisticsManager tradeStatisticsManager;
private final Storage<TradableList<Trade>> tradableListStorage;
private final TradableList<Trade> trades;
@ -107,6 +108,7 @@ public class TradeManager {
P2PService p2PService,
PriceFeed priceFeed,
FilterManager filterManager,
TradeStatisticsManager tradeStatisticsManager,
@Named(Storage.DIR_KEY) File storageDir) {
this.user = user;
this.keyRing = keyRing;
@ -118,6 +120,7 @@ public class TradeManager {
this.arbitratorManager = arbitratorManager;
this.p2PService = p2PService;
this.filterManager = filterManager;
this.tradeStatisticsManager = tradeStatisticsManager;
tradableListStorage = new Storage<>(storageDir);
trades = new TradableList<>(tradableListStorage, "PendingTrades");
@ -197,17 +200,7 @@ public class TradeManager {
toRemove.add(trade);
}
// Only offerer publishes statistic data of trades
if (isMyOffer(trade.getOffer()) && isTradeDateValidForStatistics(trade)) {
TradeStatistics tradeStatistics = new TradeStatistics(trade.getOffer(),
trade.getTradePrice(),
trade.getTradeAmount(),
trade.getDate(),
(trade.getDepositTx() != null ? trade.getDepositTx().getHashAsString() : ""),
trade.getContractHash(),
keyRing.getPubKeyRing());
p2PService.addData(tradeStatistics, true);
}
addTradeStatistics(trade);
}
for (Trade trade : toAdd)
addTradeToFailedTrades(trade);
@ -218,25 +211,24 @@ public class TradeManager {
for (Tradable tradable : closedTradableManager.getClosedTrades()) {
if (tradable instanceof Trade) {
Trade trade = (Trade) tradable;
// Only offerer publishes statistic data of trades
if (isMyOffer(trade.getOffer()) && isTradeDateValidForStatistics(trade)) {
TradeStatistics tradeStatistics = new TradeStatistics(trade.getOffer(),
trade.getTradePrice(),
trade.getTradeAmount(),
trade.getDate(),
(trade.getDepositTx() != null ? trade.getDepositTx().getHashAsString() : ""),
trade.getContractHash(),
keyRing.getPubKeyRing());
p2PService.addData(tradeStatistics, true);
}
addTradeStatistics(trade);
}
}
pendingTradesInitialized.set(true);
}
private boolean isTradeDateValidForStatistics(Trade trade) {
return (new Date().getTime() - trade.getDate().getTime()) < TimeUnit.DAYS.toMillis(20);
private void addTradeStatistics(Trade trade) {
TradeStatistics tradeStatistics = new TradeStatistics(trade.getOffer(),
trade.getTradePrice(),
trade.getTradeAmount(),
trade.getDate(),
(trade.getDepositTx() != null ? trade.getDepositTx().getHashAsString() : ""),
keyRing.getPubKeyRing());
tradeStatisticsManager.add(tradeStatistics);
// Only offerer publishes statistic data of trades, only trades from last 20 days
if (isMyOffer(trade.getOffer()) && (new Date().getTime() - trade.getDate().getTime()) < TimeUnit.DAYS.toMillis(20))
p2PService.addData(tradeStatistics, true);
}
private void handleInitialTakeOfferRequest(TradeMessage message, NodeAddress peerNodeAddress) {

View File

@ -19,12 +19,15 @@ package io.bitsquare.trade;
import com.google.inject.Singleton;
import io.bitsquare.app.AppModule;
import io.bitsquare.app.CoreOptionKeys;
import io.bitsquare.trade.closed.ClosedTradableManager;
import io.bitsquare.trade.failed.FailedTradesManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import static com.google.inject.name.Names.named;
public class TradeModule extends AppModule {
private static final Logger log = LoggerFactory.getLogger(TradeModule.class);
@ -35,7 +38,9 @@ public class TradeModule extends AppModule {
@Override
protected void configure() {
bind(TradeManager.class).in(Singleton.class);
bind(TradeStatisticsManager.class).in(Singleton.class);
bind(ClosedTradableManager.class).in(Singleton.class);
bind(FailedTradesManager.class).in(Singleton.class);
bindConstant().annotatedWith(named(CoreOptionKeys.DUMP_STATISTICS)).to(env.getRequiredProperty(CoreOptionKeys.DUMP_STATISTICS));
}
}

View File

@ -2,6 +2,7 @@ package io.bitsquare.trade;
import io.bitsquare.app.Version;
import io.bitsquare.common.crypto.PubKeyRing;
import io.bitsquare.common.util.JsonExclude;
import io.bitsquare.p2p.storage.payload.CapabilityRequiringPayload;
import io.bitsquare.p2p.storage.payload.StoragePayload;
import io.bitsquare.trade.offer.Offer;
@ -16,29 +17,40 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
public final class TradeStatistics implements StoragePayload, CapabilityRequiringPayload {
@JsonExclude
private static final long serialVersionUID = Version.P2P_NETWORK_VERSION;
@JsonExclude
public static final long TTL = TimeUnit.DAYS.toMillis(10);
public final Offer offer;
public final long tradePriceAsLong;
public final long tradeAmountAsLong;
public final long tradeDateAsTime;
public final String currency;
public final Offer.Direction direction;
public final long tradePrice;
public final long tradeAmount;
public final long tradeDate;
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 depositTxId;
public final byte[] contractHash;
@JsonExclude
public final PubKeyRing pubKeyRing;
public final int protocolVersion;
public TradeStatistics(Offer offer, Fiat tradePrice, Coin tradeAmount, Date tradeDate, String depositTxId, byte[] contractHash, PubKeyRing pubKeyRing) {
this.offer = offer;
public TradeStatistics(Offer offer, Fiat tradePrice, Coin tradeAmount, Date tradeDate, String depositTxId, PubKeyRing pubKeyRing) {
this.direction = offer.getDirection();
this.currency = offer.getCurrencyCode();
this.paymentMethod = offer.getPaymentMethod().getId();
this.offerDate = offer.getDate().getTime();
this.useMarketBasedPrice = offer.getUseMarketBasedPrice();
this.marketPriceMargin = offer.getMarketPriceMargin();
this.offerAmount = offer.getAmount().value;
this.offerMinAmount = offer.getMinAmount().value;
this.tradePrice = tradePrice.longValue();
this.tradeAmount = tradeAmount.value;
this.tradeDate = tradeDate.getTime();
this.depositTxId = depositTxId;
this.tradePriceAsLong = tradePrice.longValue();
tradeAmountAsLong = tradeAmount.value;
this.tradeDateAsTime = tradeDate.getTime();
this.contractHash = contractHash;
this.pubKeyRing = pubKeyRing;
protocolVersion = Version.TRADE_PROTOCOL_VERSION;
}
@Override
@ -59,15 +71,15 @@ public final class TradeStatistics implements StoragePayload, CapabilityRequirin
}
public Date getTradeDate() {
return new Date(tradeDateAsTime);
return new Date(tradeDate);
}
public Fiat getTradePrice() {
return Fiat.valueOf(offer.getCurrencyCode(), tradePriceAsLong);
return Fiat.valueOf(currency, tradePrice);
}
public Coin getTradeAmount() {
return Coin.valueOf(tradeAmountAsLong);
return Coin.valueOf(tradeAmount);
}
public Fiat getTradeVolume() {
@ -76,68 +88,4 @@ public final class TradeStatistics implements StoragePayload, CapabilityRequirin
else
return null;
}
// We compare the objects of both traders to match.
// pubKeyRing is not matching so we excluded it
public boolean isSameTrade(TradeStatistics o) {
if (this == o) return true;
if (!(o instanceof TradeStatistics)) return false;
TradeStatistics that = (TradeStatistics) o;
if (tradePriceAsLong != that.tradePriceAsLong) return false;
if (tradeAmountAsLong != that.tradeAmountAsLong) return false;
if (tradeDateAsTime != that.tradeDateAsTime) return false;
if (protocolVersion != that.protocolVersion) return false;
if (offer != null ? !offer.equals(that.offer) : that.offer != null) return false;
if (depositTxId != null ? !depositTxId.equals(that.depositTxId) : that.depositTxId != null) return false;
return Arrays.equals(contractHash, that.contractHash);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof TradeStatistics)) return false;
TradeStatistics that = (TradeStatistics) o;
if (tradePriceAsLong != that.tradePriceAsLong) return false;
if (tradeAmountAsLong != that.tradeAmountAsLong) return false;
if (tradeDateAsTime != that.tradeDateAsTime) return false;
if (protocolVersion != that.protocolVersion) return false;
if (offer != null ? !offer.equals(that.offer) : that.offer != null) return false;
if (depositTxId != null ? !depositTxId.equals(that.depositTxId) : that.depositTxId != null) return false;
if (!Arrays.equals(contractHash, that.contractHash)) return false;
return !(pubKeyRing != null ? !pubKeyRing.equals(that.pubKeyRing) : that.pubKeyRing != null);
}
@Override
public int hashCode() {
int result = offer != null ? offer.hashCode() : 0;
result = 31 * result + (int) (tradePriceAsLong ^ (tradePriceAsLong >>> 32));
result = 31 * result + (int) (tradeAmountAsLong ^ (tradeAmountAsLong >>> 32));
result = 31 * result + (int) (tradeDateAsTime ^ (tradeDateAsTime >>> 32));
result = 31 * result + (depositTxId != null ? depositTxId.hashCode() : 0);
result = 31 * result + (contractHash != null ? Arrays.hashCode(contractHash) : 0);
result = 31 * result + (pubKeyRing != null ? pubKeyRing.hashCode() : 0);
result = 31 * result + protocolVersion;
return result;
}
@Override
public String toString() {
return "TradeStatistics{" +
"offer=" + offer +
", tradePriceAsLong=" + tradePriceAsLong +
", tradeAmountAsLong=" + tradeAmountAsLong +
", tradeDateAsTime=" + tradeDateAsTime +
", depositTxId='" + depositTxId + '\'' +
", contractHash=" + Arrays.toString(contractHash) +
", pubKeyRing=" + pubKeyRing +
", protocolVersion=" + protocolVersion +
'}';
}
}

View File

@ -0,0 +1,84 @@
package io.bitsquare.trade;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import io.bitsquare.app.CoreOptionKeys;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.storage.HashMapChangedListener;
import io.bitsquare.p2p.storage.payload.StoragePayload;
import io.bitsquare.p2p.storage.storageentry.ProtectedStorageEntry;
import io.bitsquare.storage.Storage;
import javafx.collections.FXCollections;
import javafx.collections.ObservableSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
public class TradeStatisticsManager {
private static final Logger log = LoggerFactory.getLogger(TradeStatisticsManager.class);
private final Storage<HashSet<TradeStatistics>> storage;
private Storage<String> jsonStorage;
private boolean dumpStatistics;
private ObservableSet<TradeStatistics> observableTradeStatisticsSet = FXCollections.observableSet();
private HashSet<TradeStatistics> tradeStatisticsSet = new HashSet<>();
@Inject
public TradeStatisticsManager(Storage<HashSet<TradeStatistics>> storage, Storage<String> jsonStorage, P2PService p2PService, @Named(CoreOptionKeys.DUMP_STATISTICS) boolean dumpStatistics) {
this.storage = storage;
this.jsonStorage = jsonStorage;
this.dumpStatistics = dumpStatistics;
if (dumpStatistics)
this.jsonStorage.initAndGetPersistedWithFileName("trade_statistics.json");
HashSet<TradeStatistics> persisted = storage.initAndGetPersistedWithFileName("TradeStatistics");
if (persisted != null)
observableTradeStatisticsSet = FXCollections.observableSet(persisted);
p2PService.addHashSetChangedListener(new HashMapChangedListener() {
@Override
public void onAdded(ProtectedStorageEntry data) {
final StoragePayload storagePayload = data.getStoragePayload();
if (storagePayload instanceof TradeStatistics) {
add((TradeStatistics) storagePayload);
}
}
@Override
public void onRemoved(ProtectedStorageEntry data) {
// We don't remove items
}
});
}
public void add(TradeStatistics tradeStatistics) {
if (!observableTradeStatisticsSet.contains(tradeStatistics)) {
observableTradeStatisticsSet.add(tradeStatistics);
tradeStatisticsSet.add(tradeStatistics);
storage.queueUpForSave(tradeStatisticsSet, 2000);
if (dumpStatistics) {
// We store the statistics as json so it is easy for further processing (e.g. for web based services)
// TODO This is just a quick solution for storing to one file.
// 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<TradeStatistics> list = tradeStatisticsSet.stream().collect(Collectors.toList());
list.sort((o1, o2) -> (o1.tradeDate < o2.tradeDate ? 1 : (o1.tradeDate == o2.tradeDate ? 0 : -1)));
TradeStatistics[] array = new TradeStatistics[tradeStatisticsSet.size()];
list.toArray(array);
jsonStorage.queueUpForSave(Utilities.objectToJson(array), 5_000);
}
}
}
public ObservableSet<TradeStatistics> getObservableTradeStatisticsSet() {
return observableTradeStatisticsSet;
}
}

View File

@ -144,6 +144,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
transient private OfferAvailabilityProtocol availabilityProtocol;
@JsonExclude
transient private StringProperty errorMessageProperty = new SimpleStringProperty();
@JsonExclude
transient private PriceFeed priceFeed;

View File

@ -40,7 +40,6 @@ public class PublishTradeStatistics extends TradeTask {
trade.getTradeAmount(),
trade.getDate(),
(trade.getDepositTx() != null ? trade.getDepositTx().getHashAsString() : ""),
trade.getContractHash(),
processModel.getPubKeyRing());
processModel.getP2PService().addData(tradeStatistics, true);
complete();

View File

@ -485,14 +485,14 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
public void updateItem(final TradeStatistics item, boolean empty) {
super.updateItem(item, empty);
if (item != null)
setText(formatter.getDirection(item.offer.getDirection()));
setText(formatter.getDirection(item.direction));
else
setText("");
}
};
}
});
directionColumn.setComparator((o1, o2) -> o1.offer.getDirection().compareTo(o2.offer.getDirection()));
directionColumn.setComparator((o1, o2) -> o1.direction.compareTo(o2.direction));
tableView.getColumns().add(directionColumn);
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);

View File

@ -23,16 +23,14 @@ import io.bitsquare.gui.common.model.ActivatableViewModel;
import io.bitsquare.gui.main.markets.trades.charts.CandleData;
import io.bitsquare.locale.CurrencyUtil;
import io.bitsquare.locale.TradeCurrency;
import io.bitsquare.p2p.P2PService;
import io.bitsquare.p2p.storage.HashMapChangedListener;
import io.bitsquare.p2p.storage.payload.StoragePayload;
import io.bitsquare.p2p.storage.storageentry.ProtectedStorageEntry;
import io.bitsquare.trade.TradeStatistics;
import io.bitsquare.trade.TradeStatisticsManager;
import io.bitsquare.user.Preferences;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.SetChangeListener;
import javafx.scene.chart.XYChart;
import org.bitcoinj.core.Coin;
import org.slf4j.Logger;
@ -58,13 +56,12 @@ class TradesChartsViewModel extends ActivatableViewModel {
MINUTE
}
private final TradeStatisticsManager tradeStatisticsManager;
final Preferences preferences;
private P2PService p2PService;
private final HashMapChangedListener mapChangedListener;
private final SetChangeListener<TradeStatistics> setChangeListener;
final ObjectProperty<TradeCurrency> tradeCurrencyProperty = new SimpleObjectProperty<>();
private final Set<TradeStatistics> allTradeStatistics = new HashSet<>();
final ObservableList<TradeStatistics> tradeStatisticsByCurrency = FXCollections.observableArrayList();
ObservableList<XYChart.Data<Number, Number>> priceItems = FXCollections.observableArrayList();
ObservableList<XYChart.Data<Number, Number>> volumeItems = FXCollections.observableArrayList();
@ -78,25 +75,11 @@ class TradesChartsViewModel extends ActivatableViewModel {
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public TradesChartsViewModel(P2PService p2PService, Preferences preferences) {
this.p2PService = p2PService;
public TradesChartsViewModel(TradeStatisticsManager tradeStatisticsManager, Preferences preferences) {
this.tradeStatisticsManager = tradeStatisticsManager;
this.preferences = preferences;
mapChangedListener = new HashMapChangedListener() {
@Override
public void onAdded(ProtectedStorageEntry data) {
addItem(data.getStoragePayload(), true);
}
@Override
public void onRemoved(ProtectedStorageEntry data) {
final StoragePayload storagePayload = data.getStoragePayload();
if (storagePayload instanceof TradeStatistics && allTradeStatistics.contains(storagePayload)) {
allTradeStatistics.remove(storagePayload);
updateChartData();
}
}
};
setChangeListener = change -> updateChartData();
Optional<TradeCurrency> tradeCurrencyOptional = CurrencyUtil.getTradeCurrency(preferences.getTradeStatisticsScreenCurrencyCode());
if (tradeCurrencyOptional.isPresent())
@ -110,21 +93,21 @@ class TradesChartsViewModel extends ActivatableViewModel {
@VisibleForTesting
TradesChartsViewModel() {
mapChangedListener = null;
setChangeListener = null;
preferences = null;
tradeStatisticsManager = null;
}
@Override
protected void activate() {
p2PService.getDataMap().entrySet().stream().forEach(e -> addItem(e.getValue().getStoragePayload(), false));
p2PService.addHashSetChangedListener(mapChangedListener);
tradeStatisticsManager.getObservableTradeStatisticsSet().addListener(setChangeListener);
updateChartData();
}
@Override
protected void deactivate() {
p2PService.removeHashMapChangedListener(mapChangedListener);
tradeStatisticsManager.getObservableTradeStatisticsSet().removeListener(setChangeListener);
}
@ -166,24 +149,17 @@ class TradesChartsViewModel extends ActivatableViewModel {
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void addItem(StoragePayload storagePayload, boolean doUpdate) {
if (storagePayload instanceof TradeStatistics && !allTradeStatistics.contains(storagePayload)) {
allTradeStatistics.add((TradeStatistics) storagePayload);
if (doUpdate)
updateChartData();
}
}
private void updateChartData() {
tradeStatisticsByCurrency.setAll(allTradeStatistics.stream()
.filter(e -> e.offer.getCurrencyCode().equals(getCurrencyCode()))
tradeStatisticsByCurrency.setAll(tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
.filter(e -> e.currency.equals(getCurrencyCode()))
.collect(Collectors.toList()));
// Get all entries for the defined time interval
Map<Long, Set<TradeStatistics>> itemsPerInterval = new HashMap<>();
tradeStatisticsByCurrency.stream().forEach(e -> {
Set<TradeStatistics> set;
final long time = getTickFromTime(e.tradeDateAsTime, tickUnit);
final long time = getTickFromTime(e.tradeDate, tickUnit);
final long now = getTickFromTime(new Date().getTime(), tickUnit);
long index = maxTicks - (now - time);
if (itemsPerInterval.containsKey(index)) {
@ -220,19 +196,19 @@ class TradesChartsViewModel extends ActivatableViewModel {
long accumulatedAmount = 0;
for (TradeStatistics item : set) {
final long tradePriceAsLong = item.tradePriceAsLong;
final long tradePriceAsLong = item.tradePrice;
low = (low != 0) ? Math.min(low, tradePriceAsLong) : tradePriceAsLong;
high = (high != 0) ? Math.max(high, tradePriceAsLong) : tradePriceAsLong;
accumulatedVolume += item.getTradeVolume().value;
accumulatedAmount += item.tradeAmountAsLong;
accumulatedVolume += (item.getTradeVolume() != null) ? item.getTradeVolume().value : 0;
accumulatedAmount += item.tradeAmount;
}
long averagePrice = Math.round(accumulatedVolume * Coin.COIN.value / accumulatedAmount);
List<TradeStatistics> list = new ArrayList<>(set);
list.sort((o1, o2) -> (o1.tradeDateAsTime < o2.tradeDateAsTime ? -1 : (o1.tradeDateAsTime == o2.tradeDateAsTime ? 0 : 1)));
list.sort((o1, o2) -> (o1.tradeDate < o2.tradeDate ? -1 : (o1.tradeDate == o2.tradeDate ? 0 : 1)));
if (list.size() > 0) {
open = list.get(0).tradePriceAsLong;
close = list.get(list.size() - 1).tradePriceAsLong;
open = list.get(0).tradePrice;
close = list.get(list.size() - 1).tradePrice;
}
boolean isBullish = close > open;
return new CandleData(tick, open, close, high, low, averagePrice, accumulatedAmount, accumulatedVolume, isBullish);

View File

@ -99,7 +99,7 @@ public class GUIUtil {
String directory = Paths.get(path).getParent().toString();
preferences.setDefaultPath(directory);
Storage<ArrayList<PaymentAccount>> paymentAccountsStorage = new Storage<>(new File(directory));
ArrayList<PaymentAccount> persisted = paymentAccountsStorage.initAndGetPersisted(fileName);
ArrayList<PaymentAccount> persisted = paymentAccountsStorage.initAndGetPersistedWithFileName(fileName);
if (persisted != null) {
final StringBuilder msg = new StringBuilder();
persisted.stream().forEach(paymentAccount -> {

View File

@ -51,10 +51,10 @@ public class TradesChartsViewModelTest {
null,
null,
null);
set.add(new TradeStatistics(offer, Fiat.parseFiat("EUR", "520"), Coin.parseCoin("1"), new Date(now.getTime()), null, null, null));
set.add(new TradeStatistics(offer, Fiat.parseFiat("EUR", "500"), Coin.parseCoin("1"), new Date(now.getTime() + 100), null, null, null));
set.add(new TradeStatistics(offer, Fiat.parseFiat("EUR", "600"), Coin.parseCoin("1"), new Date(now.getTime() + 200), null, null, null));
set.add(new TradeStatistics(offer, Fiat.parseFiat("EUR", "580"), Coin.parseCoin("1"), new Date(now.getTime() + 300), null, null, null));
set.add(new TradeStatistics(offer, Fiat.parseFiat("EUR", "520"), Coin.parseCoin("1"), new Date(now.getTime()), null, null));
set.add(new TradeStatistics(offer, Fiat.parseFiat("EUR", "500"), Coin.parseCoin("1"), new Date(now.getTime() + 100), null, null));
set.add(new TradeStatistics(offer, Fiat.parseFiat("EUR", "600"), Coin.parseCoin("1"), new Date(now.getTime() + 200), null, null));
set.add(new TradeStatistics(offer, Fiat.parseFiat("EUR", "580"), Coin.parseCoin("1"), new Date(now.getTime() + 300), null, null));
CandleData candleData = model.getCandleData(model.getTickFromTime(now.getTime(), TradesChartsViewModel.TickUnit.DAY), set);
assertEquals(open, candleData.open);

View File

@ -95,7 +95,7 @@ public class PeerManager implements ConnectionListener {
this.seedNodeAddresses = new HashSet<>(seedNodeAddresses);
networkNode.addConnectionListener(this);
dbStorage = new Storage<>(storageDir);
HashSet<Peer> persistedPeers = dbStorage.initAndGetPersisted("PersistedPeers");
HashSet<Peer> persistedPeers = dbStorage.initAndGetPersistedWithFileName("PersistedPeers");
if (persistedPeers != null) {
log.info("We have persisted reported peers. persistedPeers.size()=" + persistedPeers.size());
this.persistedPeers.addAll(persistedPeers);

View File

@ -72,7 +72,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
storage = new Storage<>(storageDir);
HashMap<ByteArray, MapValue> persisted = storage.initAndGetPersisted("SequenceNumberMap");
HashMap<ByteArray, MapValue> persisted = storage.initAndGetPersistedWithFileName("SequenceNumberMap");
if (persisted != null)
sequenceNumberMap = getPurgedSequenceNumberMap(persisted);
}