package org.cryptocoinpartners.schema;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.persistence.Cacheable;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ManyToOne;
import javax.persistence.NamedAttributeNode;
import javax.persistence.NamedEntityGraph;
import javax.persistence.NamedEntityGraphs;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.NamedSubgraph;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.OrderColumn;
import javax.persistence.Transient;
import org.apache.commons.lang.NotImplementedException;
import org.cryptocoinpartners.enumeration.PositionEffect;
import org.cryptocoinpartners.enumeration.PositionType;
import org.cryptocoinpartners.enumeration.TransactionType;
import org.cryptocoinpartners.module.Context;
import org.cryptocoinpartners.schema.dao.Dao;
import org.cryptocoinpartners.schema.dao.PortfolioDao;
import org.cryptocoinpartners.service.OrderService;
import org.cryptocoinpartners.service.PortfolioService;
import org.cryptocoinpartners.util.EM;
import org.cryptocoinpartners.util.Remainder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Many Owners may have Stakes in the Portfolio, but there is only one PortfolioManager, who is not necessarily an Owner. The Portfolio has muFltiple Positions.
*
* @author Tim Olson
*/
@Entity
@Cacheable
//@NamedQueries({ @NamedQuery(name = "Portfolio.findOpenPositions", query = "select po from Portfolio po where po.positions.fills.fill.openVolumeCount<>0", hints = { @QueryHint(name = "javax.persistence.fetchgraph", value = "graph.Portfolio.positions") }) })
//@NamedEntityGraph(name = "graph.Position.fills", attributeNodes = @NamedAttributeNode(value = "fills", subgraph = "fills"), subgraphs = @NamedSubgraph(name = "fills", attributeNodes = @NamedAttributeNode("order")))
//@NamedEntityGraph(name = "graph.Portfolio.positions", attributeNodes = @NamedAttributeNode(value = "positions", subgraph = "positions"), subgraphs = @NamedSubgraph(name = "positions", attributeNodes = @NamedAttributeNode("portfolio")))
//@NamedEntityGraph(name = "graph.Portfolio.positions", attributeNodes = @NamedAttributeNode("positions"))
//select po from portfolio po where p.fill.openVolumeCount<>0
@NamedQueries({ @NamedQuery(name = "Portfolio.findOpenPositions", query = "select p from Portfolio p where name=?1") })
//
@NamedEntityGraphs({
@NamedEntityGraph(name = "portfolioWithPositions", attributeNodes = { @NamedAttributeNode(value = "positions", subgraph = "positionsWithFills") }, subgraphs = { @NamedSubgraph(name = "positionsWithFills", attributeNodes = { @NamedAttributeNode("fills") })
// @NamedSubgraph(name = "fillsWithChildOrders", attributeNodes = { @NamedAttributeNode("fillChildOrders") })
})
// @NamedSubgraph(name = "fills", attributeNodes = @NamedAttributeNode(value = "fills", subgraph = "order"))
//,@NamedSubgraph(name = "order", attributeNodes = @NamedAttributeNode("order"))
})
// @NamedQueries({ @NamedQuery(name = "Portfolio.findOpenPositions", query = "select po from Portfolio po where po.position.fill.openVolumeCount<>0", hints = { @QueryHint(name = "javax.persistence.fetchgraph", value = "graph.Portfolio.positions") }) })
// @NamedEntityGraph(name = "graph.Position.fills", attributeNodes = @NamedAttributeNode(value = "fills", subgraph = "fills"), subgraphs = { @NamedSubgraph(name = "fills", attributeNodes = @NamedAttributeNode("order")) }),
// @NamedEntityGraph(name = "graph.Portfolio.positions", attributeNodes = @NamedAttributeNode(value = "positions", subgraph = "positions"), subgraphs = { @NamedSubgraph(name = "positions" attributeNodes = @NamedAttributeNode(value = "positions", subgraph = "fills")
// ),
// @NamedSubgraph(
// name = "Book.authors.name",
// attributeNodes = @NamedAttributeNode("name")
// )
//
//
//
//
//
// , attributeNodes = @NamedAttributeNode("fills")) }),
// @NamedEntityGraph(name = "graph.Position.portfolio", attributeNodes = @NamedAttributeNode("portfolio"))
//
//
public class Portfolio extends EntityBase {
private static Object lock = new Object();
private static ExecutorService service = Executors.newFixedThreadPool(10);
/** returns all Positions, whether they are tied to an open Order or not. Use getTradeablePositions() */
public @Transient
List<Fill> getDetailedPositions() {
List<Fill> allPositions = new CopyOnWriteArrayList<Fill>();
for (Asset asset : positionsMap.keySet()) {
// Asset asset = it.next();
for (Exchange exchange : positionsMap.get(asset).keySet()) {
for (Listing listing : positionsMap.get(asset).get(exchange).keySet()) {
for (TransactionType transactionType : positionsMap.get(asset).get(exchange).get(listing).keySet()) {
for (Iterator<Position> itp = positionsMap.get(asset).get(exchange).get(listing).get(transactionType).iterator(); itp.hasNext();) {
Position pos = itp.next();
for (Fill fill : pos.getFills()) {
allPositions.add(fill);
}
}
}
}
}
}
return allPositions;
}
protected @Transient
void persistPositions(Asset asset, Exchange exchange, Listing listing) {
if (positionsMap != null && positionsMap.get(asset) != null && positionsMap.get(asset).get(exchange) != null
&& positionsMap.get(asset).get(exchange).get(listing) != null)
for (TransactionType transactionType : positionsMap.get(asset).get(exchange).get(listing).keySet()) {
for (Position position : positionsMap.get(asset).get(exchange).get(listing).get(transactionType)) {
position.persit();
}
}
}
// fetch = FetchType.EAGER,
@Nullable
@OneToMany(mappedBy = "portfolio")
@OrderColumn(name = "version")
//, cascade = { CascadeType.MERGE, CascadeType.REFRESH })
public List<Position> getPositions() {
if (positions == null)
positions = new CopyOnWriteArrayList<Position>();
return positions;
}
protected void setPositions(List<Position> positions) {
this.positions = positions;
}
public @Transient
Collection<Position> getNetPositions() {
ConcurrentLinkedQueue<Position> allPositions = new ConcurrentLinkedQueue<Position>();
for (Asset asset : positionsMap.keySet()) {
for (Exchange exchange : positionsMap.get(asset).keySet()) {
for (Listing listing : positionsMap.get(asset).get(exchange).keySet()) {
for (TransactionType transactionType : positionsMap.get(asset).get(exchange).get(listing).keySet()) {
Amount longVolume = DecimalAmount.ZERO;
Amount longAvgPrice = DecimalAmount.ZERO;
Amount longAvgStopPrice = DecimalAmount.ZERO;
Amount shortVolume = DecimalAmount.ZERO;
Amount shortAvgPrice = DecimalAmount.ZERO;
Amount shortAvgStopPrice = DecimalAmount.ZERO;
for (Position position : positionsMap.get(asset).get(exchange).get(listing).get(transactionType)) {
allPositions.add(position);
// for (Fill pos : position.getFills()) {
//
// if (pos.isLong()) {
// longAvgPrice = ((longAvgPrice.times(longVolume, Remainder.ROUND_EVEN)).plus(pos.getOpenVolume().times(pos.getPrice(),
// Remainder.ROUND_EVEN))).dividedBy(longVolume.plus(pos.getOpenVolume()), Remainder.ROUND_EVEN);
// if (pos.getStopPrice() != null)
// longAvgStopPrice = ((longAvgStopPrice.times(longVolume, Remainder.ROUND_EVEN)).plus(pos.getOpenVolume().times(
// pos.getStopPrice(), Remainder.ROUND_EVEN))).dividedBy(longVolume.plus(pos.getOpenVolume()),
// Remainder.ROUND_EVEN);
//
// longVolume = longVolume.plus(pos.getOpenVolume());
// } else if (pos.isShort()) {
// shortAvgPrice = ((shortAvgPrice.times(shortVolume, Remainder.ROUND_EVEN)).plus(pos.getOpenVolume().times(pos.getPrice(),
// Remainder.ROUND_EVEN))).dividedBy(shortVolume.plus(pos.getOpenVolume()), Remainder.ROUND_EVEN);
// if (pos.getStopPrice() != null)
// shortAvgStopPrice = ((shortAvgStopPrice.times(longVolume, Remainder.ROUND_EVEN)).plus(pos.getOpenVolume().times(
// pos.getStopPrice(), Remainder.ROUND_EVEN))).dividedBy(longVolume.plus(pos.getOpenVolume()),
// Remainder.ROUND_EVEN);
//
// shortVolume = shortVolume.plus(pos.getOpenVolume());
// }
// }
}
// need to change this to just return one position that is the total, not one long and one short.
// if (!shortVolume.isZero() || !longVolume.isZero()) {
// Market market = Market.findOrCreate(exchange, listing);
// Fill pos = new Fill();
// pos.setPortfolio(this);
// pos.setMarket(market);
//
// pos.setPriceCount(longAvgPrice.toBasis(market.getPriceBasis(), Remainder.ROUND_EVEN).getCount());
// pos.setVolumeCount(longVolume.toBasis(market.getPriceBasis(), Remainder.ROUND_EVEN).getCount());
// Position position = new Position(pos);
// allPositions.add(position);
// }
}
}
}
}
return allPositions;
}
private class handleUpdateWorkingExitRunnable implements Runnable {
private final Fill fill;
// protected Logger log;
public handleUpdateWorkingExitRunnable(Fill fill) {
this.fill = fill;
}
@Override
public void run() {
// get all pending orders for this fill
// cancel the specfic orders that are maker
// we know this is a closing trade
// need to update any stop orders with new quanity
ArrayList<Order> allChildOrders = new ArrayList<Order>();
allChildOrders = new ArrayList<Order>();
fill.getAllOrdersByParentFill(allChildOrders);
TransactionType oppositeSide = (fill.getVolumeCount() > 0) ? TransactionType.SELL : TransactionType.BUY;
for (Order childOrder : allChildOrders) {
if ((childOrder.getPositionEffect() == null && childOrder.getTransactionType().equals(oppositeSide))
|| childOrder.getPositionEffect() == (PositionEffect.CLOSE)) {
log.info("updating quanity to : " + fill.getOpenVolume().negate() + " for order: " + childOrder);
orderService.updateWorkingOrderQuantity(childOrder, fill.getOpenVolume().negate());
}
}
/*
ArrayList<Order> allChildOrders = new ArrayList<Order>();
orderService.getAllOrdersByParentFill(fill, allChildOrders);
SpecificOrder specficOrder = null;
for (Order childOrder : allChildOrders)
if (childOrder instanceof SpecificOrder) {
specficOrder = (SpecificOrder) childOrder;
if (specficOrder.getParentFill() != null && specficOrder.getLimitPrice() != null)
if (fill.getOpenVolumeCount() == 0)
orderService.cancelOrder(specficOrder);
else
orderService.updateOrderQuantity(specficOrder, fill.getOpenVolume());
}*/
// cancelStopOrders(fill);
// fill.persit();
// portfolio.modifyPosition(fill, new Authorization("Fill for " + fill.toString()));
}
}
private class handleCancelRunnable implements Runnable {
private final Fill fill;
// protected Logger log;
public handleCancelRunnable(Fill fill) {
this.fill = fill;
}
@Override
public void run() {
//
orderService.handleCancelSpecificOrderByParentFill(fill);
// cancelStopOrders(fill);
// fill.persit();
// portfolio.modifyPosition(fill, new Authorization("Fill for " + fill.toString()));
}
}
public @Transient
Position getNetPosition(Asset asset, Market market) {
//ArrayList<Position> allPositions = new ArrayList<Position>();
Position position = null;
//TODO need to add these per portfoio, portoflio should not be null
// Position position = new Position(null, market.getExchange(), market, asset, DecimalAmount.ZERO, DecimalAmount.ZERO);
// new ConcurrentLinkedQueue<Transaction>();
Collection<Fill> fills = new ArrayList<Fill>();
if (positionsMap.get(asset) != null && positionsMap.get(asset).get(market.getExchange()) != null
&& positionsMap.get(asset).get(market.getExchange()).get(market.getListing()) != null)
for (TransactionType transactionType : positionsMap.get(asset).get(market.getExchange()).get(market.getListing()).keySet()) {
// Amount longVolume = DecimalAmount.ZERO;
// Amount longAvgPrice = DecimalAmount.ZERO;
// Amount longAvgStopPrice = DecimalAmount.ZERO;
// Amount shortVolume = DecimalAmount.ZERO;
// Amount shortAvgPrice = DecimalAmount.ZERO;
// Amount shortAvgStopPrice = DecimalAmount.ZERO;
for (Position detailedPosition : positionsMap.get(asset).get(market.getExchange()).get(market.getListing()).get(transactionType)) {
// fills.addAll(detailedPosition.getFills());
for (Fill pos : detailedPosition.getFills())
fills.add(pos);
//
// // if (pos.isLong()) {
// // longAvgPrice = ((longAvgPrice.times(longVolume, Remainder.ROUND_EVEN)).plus(pos.getOpenVolume().times(pos.getPrice(),
// // Remainder.ROUND_EVEN))).dividedBy(longVolume.plus(pos.getOpenVolume()), Remainder.ROUND_EVEN);
// // if (pos.getStopPrice() != null)
// // longAvgStopPrice = ((longAvgStopPrice.times(longVolume, Remainder.ROUND_EVEN)).plus(pos.getOpenVolume().times(pos.getStopPrice(),
// // Remainder.ROUND_EVEN))).dividedBy(longVolume.plus(pos.getOpenVolume()), Remainder.ROUND_EVEN);
// // //
// // longVolume = longVolume.plus(pos.getOpenVolume());
// // } else if (pos.isShort()) {
// // shortAvgPrice = ((shortAvgPrice.times(shortVolume, Remainder.ROUND_EVEN)).plus(pos.getOpenVolume().times(pos.getPrice(),
// // Remainder.ROUND_EVEN))).dividedBy(shortVolume.plus(pos.getOpenVolume()), Remainder.ROUND_EVEN);
// // if (pos.getStopPrice() != null)
// // shortAvgStopPrice = ((shortAvgStopPrice.times(longVolume, Remainder.ROUND_EVEN)).plus(pos.getOpenVolume().times(pos.getStopPrice(),
// // Remainder.ROUND_EVEN))).dividedBy(longVolume.plus(pos.getOpenVolume()), Remainder.ROUND_EVEN);
// // //
// // shortVolume = shortVolume.plus(pos.getOpenVolume());
// // }
// }
}
}
// need to change this to just return one position that is the total, not one long and one short.
//Position netPosition=new Position();
//
//netPosition.setExchange(market.getExchange());
//netPosition.setExchange(market.getExchange());
//
// if (!shortVolume.isZero() || !longVolume.isZero()) {
// Fill pos = new Fill();
// pos.setPortfolio(this);
// pos.setMarket(market);
//
// pos.setPriceCount(longAvgPrice.toBasis(market.getPriceBasis(), Remainder.ROUND_EVEN).getCount());
// pos.setVolumeCount(longVolume.toBasis(market.getVolumeBasis(), Remainder.ROUND_EVEN).getCount());
// position = new Position(pos);
// //allPositions.add(position);
// }
// log.debug("creating net position with fills" + fills);
return positionFactory.create(fills);
// return position;
}
public @Transient
Position getLongPosition(Asset asset, Market market) {
List<Fill> fills = new CopyOnWriteArrayList<Fill>();
if (positionsMap.get(asset) != null && positionsMap.get(asset).get(market.getExchange()) != null
&& positionsMap.get(asset).get(market.getExchange()).get(market.getListing()) != null
&& positionsMap.get(asset).get(market.getExchange()).get(market.getListing()).get(TransactionType.BUY) != null)
for (Position detailedPosition : positionsMap.get(asset).get(market.getExchange()).get(market.getListing()).get(TransactionType.BUY)) {
for (Fill pos : detailedPosition.getFills()) {
if (pos.getPositionEffect() == null || pos.getPositionEffect() == PositionEffect.OPEN)
fills.add(pos);
}
}
return positionFactory.create(fills);
}
public @Transient
Position getShortPosition(Asset asset, Market market) {
List<Fill> fills = new CopyOnWriteArrayList<Fill>();
if (positionsMap.get(asset) != null && positionsMap.get(asset).get(market.getExchange()) != null
&& positionsMap.get(asset).get(market.getExchange()).get(market.getListing()) != null
&& positionsMap.get(asset).get(market.getExchange()).get(market.getListing()).get(TransactionType.SELL) != null)
for (Position detailedPosition : positionsMap.get(asset).get(market.getExchange()).get(market.getListing()).get(TransactionType.SELL)) {
for (Fill pos : detailedPosition.getFills()) {
if (pos.getPositionEffect() == null || pos.getPositionEffect() == PositionEffect.OPEN)
fills.add(pos);
}
}
return positionFactory.create(fills);
}
public @Transient
Collection<Position> getPositions(Asset asset, Exchange exchange) {
Collection<Position> allPositions = new ConcurrentLinkedQueue<Position>();
if (positionsMap.get(asset) != null && positionsMap.get(asset).get(exchange) != null) {
//synchronized (lock) {
for (Iterator<Listing> itl = positionsMap.get(asset).get(exchange).keySet().iterator(); itl.hasNext();) {
Listing listing = itl.next();
for (Iterator<TransactionType> itt = positionsMap.get(asset).get(exchange).get(listing).keySet().iterator(); itt.hasNext();) {
TransactionType transactionType = itt.next();
for (Iterator<Position> itp = positionsMap.get(asset).get(exchange).get(listing).get(transactionType).iterator(); itp.hasNext();) {
Position pos = itp.next();
allPositions.add(pos);
}
}
}
// }
}
return allPositions;
}
public @Transient
ConcurrentHashMap<Asset, Amount> getRealisedPnLs() {
ConcurrentHashMap<Asset, Amount> allPnLs = new ConcurrentHashMap<Asset, Amount>();
// synchronized (lock) {
for (Iterator<Asset> it = getRealisedPnL().keySet().iterator(); it.hasNext();) {
Asset asset = it.next();
for (Iterator<Exchange> ite = getRealisedPnL().get(asset).keySet().iterator(); ite.hasNext();) {
Exchange exchange = ite.next();
for (Iterator<Listing> itl = getRealisedPnL().get(asset).get(exchange).keySet().iterator(); itl.hasNext();) {
Listing listing = itl.next();
Amount realisedPnL = getRealisedPnL().get(asset).get(exchange).get(listing);
if (allPnLs.get(asset) == null) {
allPnLs.put(asset, realisedPnL);
} else {
allPnLs.put(asset, allPnLs.get(asset).plus(realisedPnL));
}
}
}
}
// }
return allPnLs;
}
public @Transient
Amount getRealisedPnL(Asset asset) {
Amount realisedPnL = DecimalAmount.ZERO;
for (Iterator<Exchange> ite = getRealisedPnL().get(asset).keySet().iterator(); ite.hasNext();) {
Exchange exchange = ite.next();
for (Iterator<Listing> itl = getRealisedPnL().get(asset).get(exchange).keySet().iterator(); itl.hasNext();) {
Listing listing = itl.next();
realisedPnL = realisedPnL.plus(getRealisedPnL().get(asset).get(exchange).get(listing));
}
}
return realisedPnL;
}
public @Transient
ConcurrentHashMap<Asset, ConcurrentHashMap<Exchange, ConcurrentHashMap<Listing, Amount>>> getRealisedPnL() {
return realisedProfits;
}
/* public @Transient
DiscreteAmount getLongPositionAmount(Asset asset, Exchange exchange) {
long longVolumeCount = 0;
// synchronized (lock) {
if (positionsMap.get(asset) != null && positionsMap.get(asset).get(exchange) != null) {
for (Iterator<Listing> itl = positionsMap.get(asset).get(exchange).keySet().iterator(); itl.hasNext();) {
Listing listing = itl.next();
for (Position itpos : positionsMap.get(asset).get(exchange).get(listing).get(TransactionType.BUY)) {
for (Iterator<Fill> itp = itpos.getFills().iterator(); itp.hasNext();) {
Fill pos = itp.next();
longVolumeCount += pos.getOpenVolumeCount();
}
}
}
}
// }
return new DiscreteAmount(longVolumeCount, asset.getBasis());
}
*/
public @Transient
DiscreteAmount getNetPosition(Asset asset, Exchange exchange) {
long netVolumeCount = 0;
Fill pos = null;
// synchronized (lock) {
if (positionsMap.get(asset) != null && positionsMap.get(asset).get(exchange) != null) {
for (Iterator<Listing> itl = positionsMap.get(asset).get(exchange).keySet().iterator(); itl.hasNext();) {
Listing listing = itl.next();
for (Iterator<TransactionType> itt = positionsMap.get(asset).get(exchange).get(listing).keySet().iterator(); itt.hasNext();) {
TransactionType transactionType = itt.next();
for (Position itpos : positionsMap.get(asset).get(exchange).get(listing).get(transactionType)) {
for (Iterator<Fill> itp = itpos.getFills().iterator(); itp.hasNext();) {
pos = itp.next();
netVolumeCount += pos.getOpenVolumeCount();
}
}
}
}
}
// }
return new DiscreteAmount(netVolumeCount, asset.getBasis());
}
/* public @Transient
DiscreteAmount getShortPositionAmount(Asset asset, Exchange exchange) {
long shortVolumeCount = 0;
// synchronized (lock) {
if (positionsMap.get(asset) != null && positionsMap.get(asset).get(exchange) != null) {
for (Iterator<Listing> itl = positionsMap.get(asset).get(exchange).keySet().iterator(); itl.hasNext();) {
Listing listing = itl.next();
for (Position itpos : positionsMap.get(asset).get(exchange).get(listing).get(TransactionType.SELL)) {
for (Iterator<Fill> itp = itpos.getFills().iterator(); itp.hasNext();) {
Fill pos = itp.next();
shortVolumeCount += pos.getOpenVolumeCount();
}
}
}
}
// }
return new DiscreteAmount(shortVolumeCount, asset.getBasis());
}*/
// public @OneToMany ConcurrentHashMap<BalanceType, List<Wallet>> getBalances() { return balances; }
/**
* Returns all Positions in the Portfolio which are not reserved as payment for an open Order
*/
@Transient
public Collection<Position> getTradeableBalance(Exchange exchange) {
throw new NotImplementedException();
}
@Transient
public Collection<Transaction> getTransactions() {
ConcurrentLinkedQueue<Transaction> allTransactions = new ConcurrentLinkedQueue<Transaction>();
for (Iterator<Asset> it = transactions.keySet().iterator(); it.hasNext();) {
Asset asset = it.next();
for (Iterator<Exchange> ite = transactions.get(asset).keySet().iterator(); ite.hasNext();) {
Exchange exchange = ite.next();
for (Iterator<TransactionType> itt = transactions.get(asset).get(exchange).keySet().iterator(); itt.hasNext();) {
TransactionType type = itt.next();
for (Iterator<Transaction> ittr = transactions.get(asset).get(exchange).get(type).iterator(); ittr.hasNext();) {
Transaction tran = ittr.next();
allTransactions.add(tran);
}
}
}
}
return allTransactions;
}
private void logCloseOut(long updatedVolumeCount, Fill openFill, Fill closingFill, Boolean closed) {
// if (log.isDebugEnabled())
if (updatedVolumeCount == 0)
log.info("why closoue is zero");
if (closed)
log.info("Closed out " + updatedVolumeCount + " of open fill: " + openFill + " with closing fill: " + closingFill);
else
log.info("Closing out " + updatedVolumeCount + " of open fill: " + openFill + " with closing fill: " + closingFill);
}
@Transient
public void removeTransaction(Transaction reservation) {
if (transactions == null || transactions.isEmpty())
return;
if (reservation == null || reservation.getCurrency() == null)
return;
if (transactions.get(reservation.getCurrency()) == null)
return;
if (transactions.get(reservation.getCurrency()).get(reservation.getExchange()) == null)
return;
if (transactions.get(reservation.getCurrency()).get(reservation.getExchange()).get(reservation.getType()) == null)
return;
// synchronized (lock) {
transactions.get(reservation.getCurrency()).get(reservation.getExchange()).get(reservation.getType()).remove(reservation);
}
// Iterator<Transaction> it = transactions.get(reservation.getCurrency()).get(reservation.getExchange()).get(reservation.getType()).iterator();
// while (it.hasNext()) {
// Transaction transaction = it.next();
// if (transaction != null && reservation != null && transaction.equals(reservation))
// it.remove();
// }
// }
/**
* This is the main way for a Strategy to determine what assets it has available for trading
*/
@Transient
public Collection<Position> getReservedBalances(Exchange exchange) {
throw new NotImplementedException();
}
/**
* This is the main way for a Strategy to determine how much of a given asset it has available for trading
* @param f
* @return
*/
@Transient
public Collection<Position> getTradeableBalanceOf(Exchange exchange, Asset asset) {
throw new NotImplementedException();
}
/**
* Finds a Position in the Portfolio which has the same Asset as p, then breaks it into the amount p requires
* plus an unreserved amount. The resevered Position is then associated with the given order, while
* the unreserved remainder of the Position has getOrder()==null. To un-reserve the Position, call release(order)
*
* @param order the order which will be placed
* @param p the cost of the order. could be a different fungible than the order's quote fungible
* @throws IllegalArgumentException
*/
@Transient
public void reserve(SpecificOrder order, Position p) throws IllegalArgumentException {
throw new NotImplementedException();
}
@Transient
public void release(SpecificOrder order) {
throw new NotImplementedException();
}
@Transient
public void addRealisedPnL(Transaction transaction) {
Amount TotalRealisedPnL = transaction.getAmount().plus(
getRealisedPnL().get(transaction.getCurrency()).get(transaction.getExchange()).get(transaction.getFill().getMarket().getListing()));
getRealisedPnL().get(transaction.getCurrency()).get(transaction.getExchange()).put(transaction.getFill().getMarket().getListing(), TotalRealisedPnL);
}
@Transient
public boolean addTransaction(Transaction transaction) {
ConcurrentHashMap<Exchange, ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Transaction>>> assetTransactions = transactions.get(transaction
.getCurrency());
if (assetTransactions == null) {
ConcurrentLinkedQueue<Transaction> transactionList = new ConcurrentLinkedQueue<Transaction>();
assetTransactions = new ConcurrentHashMap<Exchange, ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Transaction>>>();
transactionList.add(transaction);
ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Transaction>> transactionGroup = new ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Transaction>>();
transactionGroup.put(transaction.getType(), transactionList);
assetTransactions.put(transaction.getExchange(), transactionGroup);
transactions.put(transaction.getCurrency(), assetTransactions);
if (transaction.getType() == TransactionType.REALISED_PROFIT_LOSS)
addRealisedPnL(transaction);
portfolioService.resetBalances();
return true;
} else {
//asset is present, so check the market
ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Transaction>> exchangeTransactions = assetTransactions.get(transaction.getExchange());
if (exchangeTransactions == null) {
ConcurrentLinkedQueue<Transaction> transactionList = new ConcurrentLinkedQueue<Transaction>();
transactionList.add(transaction);
ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Transaction>> transactionGroup = new ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Transaction>>();
transactionGroup.put(transaction.getType(), transactionList);
assetTransactions.put(transaction.getExchange(), transactionGroup);
if (transaction.getType() == TransactionType.REALISED_PROFIT_LOSS)
addRealisedPnL(transaction);
portfolioService.resetBalances();
return true;
} else {
ConcurrentLinkedQueue<Transaction> transactionList = exchangeTransactions.get(transaction.getType());
if (transactionList == null) {
transactionList = new ConcurrentLinkedQueue<Transaction>();
transactionList.add(transaction);
Amount TotalRealisedPnL = null;
if (transaction.getType() == TransactionType.REALISED_PROFIT_LOSS)
addRealisedPnL(transaction);
exchangeTransactions.put(transaction.getType(), transactionList);
portfolioService.resetBalances();
return true;
} else {
transactionList.add(transaction);
exchangeTransactions.put(transaction.getType(), transactionList);
if (transaction.getType() == TransactionType.REALISED_PROFIT_LOSS)
addRealisedPnL(transaction);
portfolioService.resetBalances();
return true;
}
}
}
}
/**
* finds other Positions in this portfolio which have the same Exchange and Asset and merges this position's
* amount into the found position's amount, thus maintaining only one Position for each Exchange/Asset pair.
* this method does not remove the position from the positions list.
* @return true iff another position was found and merged
*/
protected void publishPositionUpdate(Position position, PositionType lastType, Market market) {
PositionType mergedType = (position.isShort()) ? PositionType.SHORT : (position.isLong()) ? PositionType.LONG : PositionType.FLAT;
context.publish(new PositionUpdate(position, market, lastType, mergedType));
}
public synchronized void addPosition(Position position) {
// synchronized (lock) {
getPositions().add(position);
// }
}
public synchronized void removePositions(Collection<Position> removedPositions) {
// synchronized (lock) {
if (this.positions.removeAll(removedPositions))
for (Position removedPosition : removedPositions)
removedPosition.setPortfolio(null);
}
public synchronized void removePosition(Position removePosition) {
// synchronized (lock) {
// System.out.println("removing fill: " + fill + " from position: " + this);
if (positions.remove(removePosition))
removePosition.setPortfolio(null);
//TODO We should do a check to make sure the fill is the samme attributes as position
//}
//this.exchange = fill.getMarket().getExchange();
//this.market = fill.getMarket();
//this.asset = fill.getMarket().getListing().getBase();
//this.portfolio = fill.getPortfolio();
// reset();
}
public void updateWorkingExitOrders(Fill fill) {
// service.submit(new handleUpdateWorkingExitRunnable(fill));
}
/* public void cancelStopOrders(Fill fill) {
service.submit(new handleCancelRunnable(fill));
// orderService.handleCancelSpecificOrderByParentFill(fill);
for (Order order : fill.getFillChildOrders()) {
if (order instanceof GeneralOrder && order.getFillType() == FillType.STOP_LIMIT) {
orderService.handleCancelGeneralOrder((GeneralOrder) order);
}
}
// synchronized (lock) {
// }
}
*/
@Transient
public void insert(Position position) {
//baseCCY->Exchange->Listing->TransactionType
TransactionType transactionType = (position.isLong()) ? TransactionType.BUY : TransactionType.SELL;
ConcurrentHashMap<Exchange, ConcurrentHashMap<Listing, ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>>>> assetPositions = positionsMap
.get(position.getMarket().getBase());
if (assetPositions == null) {
assetPositions = new ConcurrentHashMap<Exchange, ConcurrentHashMap<Listing, ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>>>>();
ConcurrentHashMap<Listing, ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>>> listingPosition = new ConcurrentHashMap<Listing, ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>>>();
ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>> positionType = new ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>>();
ConcurrentLinkedQueue<Position> detailPosition = new ConcurrentLinkedQueue<Position>();
detailPosition.add(position);
positionType.put(transactionType, detailPosition);
listingPosition.put(position.getMarket().getListing(), positionType);
assetPositions.put(position.getMarket().getExchange(), listingPosition);
positionsMap.put(position.getMarket().getBase(), assetPositions);
return;
} else {
ConcurrentHashMap<Listing, ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>>> listingPosition = assetPositions.get(position
.getMarket().getExchange());
if (listingPosition == null) {
listingPosition = new ConcurrentHashMap<Listing, ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>>>();
ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>> positionType = new ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>>();
ConcurrentLinkedQueue<Position> detailPosition = new ConcurrentLinkedQueue<Position>();
detailPosition.add(position);
positionType.put(transactionType, detailPosition);
listingPosition.put(position.getMarket().getListing(), positionType);
assetPositions.put(position.getMarket().getExchange(), listingPosition);
return;
} else {
ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>> positionType = listingPosition.get(position.getMarket().getListing());
if (positionType == null) {
positionType = new ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>>();
ConcurrentLinkedQueue<Position> detailPosition = new ConcurrentLinkedQueue<Position>();
detailPosition.add(position);
positionType.put(transactionType, detailPosition);
listingPosition.put(position.getMarket().getListing(), positionType);
return;
} else {
ConcurrentLinkedQueue<Position> positions = positionType.get(transactionType);
if (positions == null) {
ConcurrentLinkedQueue<Position> detailPosition = new ConcurrentLinkedQueue<Position>();
detailPosition.add(position);
positionType.put(transactionType, detailPosition);
return;
} else {
positions.add(position);
return;
}
}
}
}
}
@Transient
public synchronized boolean merge(Fill fill) {
boolean persit = true;
log.debug(this.getClass().getSimpleName() + ":merge. Determing position for fill" + fill);
//synchronized (lock) {
// We need to have a queue of buys and a queue of sells ( two array lists), ensure the itterator is descendingIterator for LIFO,
// when we get a new trade coem in we add it to the buy or sell queue
// 1) caluate price difference
// 2) times price diff by min(trade quantity or the position) and add to relasied PnL
// 3) update the quaitity of the postion and remove from queue if zero
// 4) move onto next postion until the qty =0
// https://github.com/webpat/jquant-core/blob/173d5ca79b318385a3754c8e1357de79ece47be4/src/main/java/org/jquant/portfolio/Portfolio.java
TransactionType transactionType = (fill.isLong()) ? TransactionType.BUY : TransactionType.SELL;
TransactionType openingTransactionType = (transactionType == (TransactionType.BUY)) ? TransactionType.SELL : TransactionType.BUY;
PositionEffect openingPositionEffect = null;
ConcurrentHashMap<Exchange, ConcurrentHashMap<Listing, ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>>>> assetPositions = positionsMap
.get(fill.getMarket().getBase());
ConcurrentHashMap<Listing, ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>>> listingPosition = new ConcurrentHashMap<Listing, ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>>>();
//ConcurrentHashMap<Listing, ArrayList<Position>> listingPosition = new ConcurrentHashMap<Listing, ArrayList<Position>>();
ConcurrentHashMap<Listing, Amount> marketRealisedProfits;
ConcurrentHashMap<Exchange, ConcurrentHashMap<Listing, Amount>> assetRealisedProfits = getRealisedPnL().get(fill.getMarket().getTradedCurrency());
if (assetRealisedProfits != null) {
marketRealisedProfits = assetRealisedProfits.get(fill.getMarket().getListing());
}
if (assetPositions == null) {
log.debug(this.getClass().getSimpleName() + ":merge. creating new asset positions for fill" + fill);
ConcurrentLinkedQueue<Position> detailPosition = new ConcurrentLinkedQueue<Position>();
// need to wrapper position!!
Position detPosition;
if (fill.getPosition() == null) {
detPosition = positionFactory.create(fill);
detPosition.persit();
} else {
detPosition = fill.getPosition();
detPosition.merge();
// persit = false;
}
log.info(fill + "added to new " + fill.getMarket().getBase() + " position");
// if (persit)
// new Position(fill);
// PersistUtil.persist(detPosition);
detailPosition.add(detPosition);
ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>> positionType = new ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>>();
positionType.put(transactionType, detailPosition);
listingPosition.put(fill.getMarket().getListing(), positionType);
assetPositions = new ConcurrentHashMap<Exchange, ConcurrentHashMap<Listing, ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>>>>();
assetPositions.put(fill.getMarket().getExchange(), listingPosition);
positionsMap.put(fill.getMarket().getBase(), assetPositions);
Amount profits = DecimalAmount.ZERO;
if (assetRealisedProfits == null) {
assetRealisedProfits = new ConcurrentHashMap<Exchange, ConcurrentHashMap<Listing, Amount>>();
marketRealisedProfits = new ConcurrentHashMap<Listing, Amount>();
marketRealisedProfits.put(fill.getMarket().getListing(), profits);
assetRealisedProfits.put(fill.getMarket().getExchange(), marketRealisedProfits);
getRealisedPnL().put(fill.getMarket().getTradedCurrency(), assetRealisedProfits);
}
publishPositionUpdate(getNetPosition(fill.getMarket().getBase(), fill.getMarket()), PositionType.FLAT, fill.getMarket());
// else
// detPosition.merge();
// fill.merge();
return true;
} else {
//asset is present, so check the market
ConcurrentHashMap<Listing, ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>>> exchangePositions = assetPositions.get(fill
.getMarket().getExchange());
// Amount exchangeRealisedProfits = realisedProfits.get(position.getMarket().getTradedCurrency()).get(position.getExchange())
// .get(position.getMarket().getListing());
if (exchangePositions == null) {
log.debug(this.getClass().getSimpleName() + ":merge. creating new exchangePositions for fill" + fill);
ConcurrentLinkedQueue<Position> detailPosition = new ConcurrentLinkedQueue<Position>();
ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>> positionType = new ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>>();
Position detPosition;
if (fill.getPosition() == null) {
detPosition = positionFactory.create(fill);
detPosition.persit();
} else {
detPosition = fill.getPosition();
detPosition.merge();
}
log.info(fill + "added to new " + fill.getMarket().getExchange() + " position");
// PersistUtil.persist(detPosition);
detailPosition.add(detPosition);
positionType.put(transactionType, detailPosition);
listingPosition.put(fill.getMarket().getListing(), positionType);
assetPositions.put(fill.getMarket().getExchange(), listingPosition);
Amount profits = DecimalAmount.ZERO;
if (getRealisedPnL().get(fill.getMarket().getTradedCurrency()).get(fill.getMarket().getExchange()).get(fill.getMarket().getListing()) == null) {
marketRealisedProfits = new ConcurrentHashMap<Listing, Amount>();
marketRealisedProfits.put(fill.getMarket().getListing(), profits);
getRealisedPnL().get(fill.getMarket().getTradedCurrency()).put(fill.getMarket().getExchange(), marketRealisedProfits);
}
publishPositionUpdate(getNetPosition(fill.getMarket().getBase(), fill.getMarket()), PositionType.FLAT, fill.getMarket());
//else
// detPosition.merge();
//fill.merge();
return true;
} else {
//ConcurrentHashMap<TransactionType, ArrayList<Position>> listingPositions = exchangePositions.get(position.getMarket().getListing());
//asset is present, so check the market
// need yo vhnage this to have tne cocnurrent hashmap on here
//ConcurrentHashMap<TransactionType, ArrayList<Position>> listingPositions = exchangePositions.get(position.getMarket().getListing());
ConcurrentLinkedQueue<Position> listingPositions = exchangePositions.get(fill.getMarket().getListing()).get(transactionType);
ConcurrentLinkedQueue<Position> openingListingPositions = exchangePositions.get(fill.getMarket().getListing()).get(openingTransactionType);
if (listingPositions == null) {
log.debug(this.getClass().getSimpleName() + ":merge. creating new lisiting for fill" + fill);
ConcurrentLinkedQueue<Position> listingsDetailPosition = new ConcurrentLinkedQueue<Position>();
Position detPosition;
if (fill.getPosition() == null) {
detPosition = positionFactory.create(fill);
detPosition.persit();
} else {
detPosition = fill.getPosition();
detPosition.merge();
}
log.info(fill + "added to new " + transactionType + " position");
// PersistUtil.persist(detPosition);
listingsDetailPosition.add(detPosition);
exchangePositions.get(fill.getMarket().getListing()).put(transactionType, listingsDetailPosition);
listingPositions = exchangePositions.get(fill.getMarket().getListing()).get(transactionType);
Amount listingProfits = DecimalAmount.ZERO;
if (getRealisedPnL().get(fill.getMarket().getTradedCurrency()) == null
|| getRealisedPnL().get(fill.getMarket().getTradedCurrency()).get(fill.getMarket().getExchange()) == null
|| getRealisedPnL().get(fill.getMarket().getTradedCurrency()).get(fill.getMarket().getExchange())
.get(fill.getMarket().getListing()) == null) {
marketRealisedProfits = new ConcurrentHashMap<Listing, Amount>();
marketRealisedProfits.put(fill.getMarket().getListing(), listingProfits);
getRealisedPnL().get(fill.getMarket().getTradedCurrency()).put(fill.getMarket().getExchange(), marketRealisedProfits);
}
// else
// detPosition.merge();
// fill.merge();
} else {
if (!listingPositions.isEmpty() || listingPositions.peek() != null) {
Position position = listingPositions.peek();
log.info(fill + " prepareing to add to existing " + transactionType + " position:" + position);
fill.setPosition(position);
if (position.addFill(fill)) {
log.info(fill + " added to existing " + transactionType + " position:" + position);
// position.merge();
} else
log.info(fill + " not added to existing " + transactionType + " position:" + position);
// position.merge();
//listingPositions.peek().persit();
// listingPositions.peek().Merge();
// TODO need to persit the updated postitions
//PersistUtil.merge(listingPositions.peek());
} else {
Position detPosition;
if (fill.getPosition() == null) {
log.debug(this.getClass().getSimpleName() + ":merge. creating new position for fill" + fill);
detPosition = positionFactory.create(fill);
detPosition.persit();
// fill.merge();
} else {
log.debug(this.getClass().getSimpleName() + ":merge. adding to exising fill position for fill" + fill);
detPosition = fill.getPosition();
detPosition.merge();
// fill.merge();
}
listingPositions.add(detPosition);
// if (persit)
// detPosition.persit();
// else
// detPosition.merge();
// PersistUtil.persist(detPosition);
// // detPosition.addFill(fill);
// PersistUtil.insert(detPosition);
}
// fill.merge();
}
log.debug(this.getClass().getSimpleName() + ":merge. Determing closeouts fill " + fill + " with open positions " + openingListingPositions);
if (openingListingPositions != null && !(openingListingPositions.isEmpty())) {
// ArrayList<Position> positions = listingPositions.get(transactionType);
//somethign is up with the poistions calcuation for partial closeouts
// example 454 lots, closed out 421 lots, then added another 411 lots, total of 444 lots, but the average prices are not correct.
// need to update this .
// loop over OpenPositions fill by fill, if fill postions effect is null or of opengpostions effect, we close out
Amount realisedPnL = DecimalAmount.ZERO;
long closingVolumeCount = 0;
//position.getVolumeCount()
/* Iterator<Position> itPos = listingPositions.iterator();
while (itPos.hasNext()) {
// closing position
Position pos = itPos.next();
closingFills: for (Iterator<Fill> itP = pos.getFills().iterator(); itP.hasNext();) {
Fill p = itP.next();
if (p.getPositionEffect() == PositionEffect.OPEN)
continue;
if (Math.abs(p.getOpenVolumeCount()) == 0) {
itP.remove();
continue closingFills;
}*/
Amount entryPrice = DecimalAmount.ZERO;
Amount exitPrice = DecimalAmount.ZERO;
List<Position> closedOutListingPositions = new ArrayList<Position>();
CLOSEPOSITIONSLOOP: for (Position closePos : listingPositions) {
if (!closePos.hasFills()) {
log.debug("removing position: " + closePos + " from: " + listingPositions);
closedOutListingPositions.add(closePos);
continue;
}
List<Fill> closedOutClosingPositionFills = new ArrayList<Fill>();
CLOSEDFILLSLOOP: for (Fill closePosition : closePos.getFills()) {
if (closePosition.getOpenVolumeCount() == 0) {
log.debug("removing fill: " + closePosition + " from position: " + closePos);
closedOutClosingPositionFills.add(closePosition);
// closePosition.merge();
continue;
}
List<Position> closedOutOpenListingPositions = new ArrayList<Position>();
if (closePosition.getPositionEffect() != null)
openingPositionEffect = (closePosition.getPositionEffect() == (PositionEffect.CLOSE)) ? PositionEffect.OPEN
: PositionEffect.CLOSE;
OPENPOSITIONSLOOP: for (Position openPos : openingListingPositions) {
if (!openPos.hasFills()) {
log.debug("removing position: " + openPos + " from: " + openingListingPositions);
closedOutOpenListingPositions.add(openPos);
continue;
}
// Iterator<Position> itOlp = openingListingPositions.iterator();
//while (itOlp.hasNext()) {
// openg postion
List<Fill> closedOutOpenPositionFills = new ArrayList<Fill>();
OPENFILLSLOOP: for (Fill openPosition : openPos.getFills()) {
if (openPosition.getOpenVolumeCount() == 0) {
log.debug("removing fill: " + openPosition + " from position: " + openPos);
// openPosition.merge();
closedOutOpenPositionFills.add(openPosition);
continue;
}
// Iterator<Fill> itOp = openPos.getFills().iterator();
//while (itOp.hasNext()) {
//open fill
realisedPnL = DecimalAmount.ZERO;
closingVolumeCount = 0;
// we only loop if it is
if (openPosition.getPositionEffect() == null || openPosition.getPositionEffect() == openingPositionEffect
&& openPosition.getOpenVolumeCount() != 0) {
// || (openPosition.getPositionEffect() == PositionEffect.OPEN && p.getPositionEffect() == PositionEffect.CLOSE)) {
// // if (p.getPositionEffect() == PositionEffect.OPEN)
// log.info("the last fille should be a closing fill. Trying to net open position: " + openPosition
// + " with closing postion: " + p);
exitPrice = closePosition.getPrice();
entryPrice = openPosition.getPrice();
if (closePosition.getMarket().getTradedCurrency() == closePosition.getMarket().getBase()) {
// need to invert and revrese the prices if the traded ccy is not the quote ccy
entryPrice = openPosition.getPrice().invert();
exitPrice = closePosition.getPrice().invert();
} else if (closePosition.getMarket().getTradedCurrency() != closePosition.getMarket().getQuote()) {
throw new NotImplementedException("Listings traded in neither base or quote currency are not supported");
}
closingVolumeCount = (openingTransactionType == (TransactionType.SELL)) ? (Math.min(
Math.abs(openPosition.getOpenVolumeCount()), Math.abs(closePosition.getOpenVolumeCount())))
* -1 : (Math.min(Math.abs(openPosition.getOpenVolumeCount()), Math.abs(closePosition.getOpenVolumeCount())));
long updatedVolumeCount = 0;
if ((Math.abs(closePosition.getOpenVolumeCount()) >= Math.abs(openPosition.getOpenVolumeCount()))
&& (closePosition.getOpenVolumeCount() != 0)) {
updatedVolumeCount = closePosition.getOpenVolumeCount() + closingVolumeCount;
logCloseOut(closingVolumeCount, openPosition, closePosition, false);
log.debug(this.getClass().getSimpleName() + ":merge. setting open position " + openPosition
+ " open volume count to 0");
openPosition.setOpenVolumeCount(0);
openPosition.setPosition(null);
closedOutOpenPositionFills.add(openPosition);
// openPosition.merge();
log.debug(this.getClass().getSimpleName() + ":merge. setting close position " + closePosition
+ " open volume count to " + (closePosition.getOpenVolumeCount() + closingVolumeCount));
closePosition.setOpenVolumeCount(closePosition.getOpenVolumeCount() + closingVolumeCount);
if (closePosition.getOpenVolumeCount() == 0) {
log.debug(this.getClass().getSimpleName() + ":merge. setting close position " + closePosition
+ " position to null");
closePosition.setPosition(null);
closedOutClosingPositionFills.add(closePosition);
}
//closePosition.merge();
// closePosition.merge();
logCloseOut(closingVolumeCount, openPosition, closePosition, true);
//updateWorkingExitOrders(closePosition);
//updateWorkingExitOrders(openPosition);
// if the fill is now fully closed out I need to cancel any closing specfic limit orders associated with it
// or I need to update the qunaity to the
// closed all closing limit orders
} else if (closePosition.getOpenVolumeCount() != 0) {
updatedVolumeCount = openPosition.getOpenVolumeCount() - closingVolumeCount;
logCloseOut(closingVolumeCount, openPosition, closePosition, false);
log.debug(this.getClass().getSimpleName() + ":merge. setting close position " + openPosition
+ " open volume count to 0");
closePosition.setOpenVolumeCount(0);
closePosition.setPosition(null);
closedOutClosingPositionFills.add(closePosition);
// closePosition.merge();
//closePos.removeFill(fill)
//closePosition.re
// closePosition.merge();
log.debug(this.getClass().getSimpleName() + ":merge. setting open position " + closePosition
+ " open volume count to " + updatedVolumeCount);
// closed all closing limit orders
openPosition.setOpenVolumeCount(openPosition.getOpenVolumeCount() - closingVolumeCount);
if (closePosition.getOpenVolumeCount() == 0) {
log.debug(this.getClass().getSimpleName() + ":merge. setting open position " + openPosition
+ " position to null");
openPosition.setPosition(null);
closedOutOpenPositionFills.add(openPosition);
}
// openPosition.merge();
logCloseOut(closingVolumeCount, openPosition, closePosition, true);
// updateWorkingExitOrders(closePosition);
// updateWorkingExitOrders(openPosition);
// */// if the fill is now fully closed out I need to cancel any closing specfic limit orders associated with it
// or I need to update the qunaity to the
// closed all closing limit orders
}
// openPosition.merge();
/* if (openPosition.getOpenVolumeCount() == 0) {
log.debug("removing fill: " + openPosition + " from oprning position: " + openPos);
closedOutOpenPositionFills.add(openPosition);
// openPos.merge();
// openPosition.setPosition(null);
//openPosition.merge();
// cancel all specifc maker orders related to the closeing fill fill
if (openPosition.getPositionEffect() != null && openPosition.getPositionEffect() == PositionEffect.OPEN)
cancelWorkingExitOrders(closePosition);
else
cancelWorkingExitOrders(openPosition);
// cancelStopOrders(openPosition);
if (!openPos.hasFills()) {
log.debug("removing opening position: " + openPos + " from: " + openingListingPositions);
closedOutOpenListingPositions.add(openPos);
}
//itOlp.remove();
}
if (closePosition.getOpenVolumeCount() == 0) {
log.debug("removing fill: " + closePosition + " from closing position: " + closePos);
closedOutClosingPositionFills.add(closePosition);
// cancelStopOrders(closePosition);
if (!closePos.hasFills()) {
log.debug("removing closing position: " + closePos + " from: " + listingPositions);
closedOutListingPositions.add(closePos);
}
openPosition.merge }*/
openPosition.merge();
DiscreteAmount volDiscrete = new DiscreteAmount(closingVolumeCount, closePosition.getMarket().getListing()
.getVolumeBasis());
realisedPnL = realisedPnL.plus(((entryPrice.minus(exitPrice)).times(volDiscrete, Remainder.ROUND_EVEN)).times(
closePosition.getMarket().getContractSize(), Remainder.ROUND_EVEN));
// need to confonvert to deiscreete amount
Amount RealisedPnL = realisedPnL
.toBasis(closePosition.getMarket().getTradedCurrency().getBasis(), Remainder.ROUND_EVEN);
if (RealisedPnL.isNegative() && closePosition.getPositionEffect() == PositionEffect.OPEN)
log.info("realsiedPnL is a loss. netted open position: " + openPosition + " with closing postion: " + closePosition);
if (!RealisedPnL.isZero()) {
Amount TotalRealisedPnL = RealisedPnL.plus(getRealisedPnL().get(closePosition.getMarket().getTradedCurrency())
.get(closePosition.getMarket().getExchange()).get(closePosition.getMarket().getListing()));
Transaction trans = transactionFactory.create(closePosition, this, closePosition.getMarket().getExchange(),
closePosition.getMarket().getTradedCurrency(), TransactionType.REALISED_PROFIT_LOSS, RealisedPnL,
new DiscreteAmount(0, closePosition.getMarket().getTradedCurrency().getBasis()));
log.info("Realised PnL:" + trans);
context.setPublishTime(trans);
trans.persit();
context.route(trans);
}
if (closePosition.getOpenVolumeCount() == 0)
break;
else
log.info(closePosition + " not closed fully out with " + openPosition);
}
}
if (!closedOutOpenPositionFills.isEmpty()) {
log.debug("removing all fills: " + closedOutOpenPositionFills + "from: " + openPos);
Boolean removeFills = true;
for (Fill closedFill : closedOutOpenPositionFills) {
if (closedFill.getOpenVolumeCount() != 0)
removeFills = false;
// else
// closedFill.merge();
}
// closePos.merge();
if (removeFills)
openPos.removeFills(closedOutOpenPositionFills);
else
log.debug("unable to remove fills as dome fills have zero open qunaity in position " + openPos);
if (!openPos.hasFills())
closedOutOpenListingPositions.add(openPos);
}
}
if (!closedOutOpenListingPositions.isEmpty()) {
log.debug("removing all opening positions: " + openingListingPositions + "from: " + openingListingPositions);
for (Position closedPos : closedOutOpenListingPositions) {
/*Boolean removePosition = true;
for (Fill fillToRemove : closedPos.getFills()) {
if (fillToRemove.getOpenVolumeCount() != 0) //{
// fillToRemove.setPosition(null);
// fillToRemove.merge();
// } else
removePosition = false;
}*/
if (!closedPos.hasFills())
closedPos.delete();
else
log.debug("unable to remove fills as dome fills have zero open qunaity in position " + closedPos);
}
openingListingPositions.removeAll(closedOutOpenListingPositions);
}
closePosition.merge();
}
if (!closedOutClosingPositionFills.isEmpty()) {
log.debug("removing all fills: " + closedOutClosingPositionFills + "from: " + closePos);
Boolean removeFills = true;
for (Fill closedFill : closedOutClosingPositionFills) {
if (closedFill.getOpenVolumeCount() != 0)
removeFills = false;
}
// closePos.merge();
if (removeFills)
closePos.removeFills(closedOutClosingPositionFills);
else
log.debug("unable to remove fills as dome fills have zero open qunaity in position " + closePos);
if (!closePos.hasFills())
closedOutListingPositions.add(closePos);
}
}
if (!closedOutListingPositions.isEmpty()) {
log.debug("removing all closing positions: " + closedOutListingPositions + "from: " + listingPositions);
for (Position closedPos : closedOutListingPositions) {
// Boolean removePosition = true;
if (!closedPos.hasFills())
closedPos.delete();
else
log.debug("unable to remove fills as dome fills have zero open qunaity in position " + closedPos);
/* for (Fill fillToRemove : closedPos.getFills()) {
if (fillToRemove.getOpenVolumeCount() != 0)
// fillToRemove.setPosition(null);
// fillToRemove.merge();
//} else
removePosition = false;
// }
if (removePosition && !closedPos.hasFills())
closedPos.delete();
else
log.debug("unable to remove fills as dome fills have zero open qunaity in position " + closedPos);
*/
}
listingPositions.removeAll(closedOutListingPositions);
}
//}
//}
if (getNetPosition(fill.getMarket().getBase(), fill.getMarket()) == null) {
Position detPosition;
if (fill.getPosition() == null) {
detPosition = positionFactory.create(fill);
detPosition.persit();
} else {
detPosition = fill.getPosition();
detPosition.merge();
}
publishPositionUpdate(detPosition, PositionType.FLAT, fill.getMarket());
// if (persit)
// detPosition.persit();
// else
// detPosition.merge();
} else {
PositionType lastType = (openingTransactionType == TransactionType.BUY) ? PositionType.LONG : PositionType.SHORT;
publishPositionUpdate(getNetPosition(fill.getMarket().getBase(), fill.getMarket()), lastType, fill.getMarket());
}
// fill.merge();
return true;
}
PositionType lastType = (fill.isLong()) ? PositionType.LONG : PositionType.SHORT;
publishPositionUpdate(getNetPosition(fill.getMarket().getBase(), fill.getMarket()), lastType, fill.getMarket());
// fill.merge();
return true;
}
}
}
public Portfolio(String name, PortfolioManager manager) {
this.name = name;
this.manager = manager;
}
private String name;
public String getName() {
return name;
}
@Transient
public Context getContext() {
return context;
}
@Transient
public PortfolioService getPortfolioService() {
return portfolioService;
}
@Transient
public OrderService getOrderService() {
return orderService;
}
@Transient
public Collection<Order> getAllOrders() {
Collection<Order> orders = new ArrayList<Order>();
for (Position position : getPositions())
for (Fill fill : position.getFills()) {
orders.add(fill.getOrder());
fill.getAllOrdersByParentFill(orders);
}
return orders;
}
@Transient
public Collection<Fill> getAllFills() {
Collection<Fill> fills = new ArrayList<Fill>();
for (Position position : getPositions())
for (Fill fill : position.getFills()) {
fills.add(fill);
// fill.getAllOrdersByParentFill(orders);
}
return fills;
}
@OneToMany(fetch = FetchType.LAZY)
@OrderBy
public List<Stake> getStakes() {
return stakes;
}
@ManyToOne(fetch = FetchType.LAZY)
public Asset getBaseAsset() {
return baseAsset;
}
@Transient
public PortfolioManager getManager() {
return manager;
}
@Transient
public Logger getLogger() {
return log;
}
/**
* Adds the given position to this Portfolio. Must be authorized.
* @param position
* @param authorization
*/
@Transient
public void modifyPosition(Fill fill, Authorization authorization) {
assert authorization != null;
assert fill != null;
merge(fill);
// fill.persit();
//persistPositions(fill.getMarket().getBase(), fill.getMarket().getExchange(), fill.getMarket().getListing());
// fill.persit();
// if
// for (Position curPosition : positions) {
// if (curPosition.merge(position)) {
// modifiedExistingPosition = true;
// break;
// }
// }
// if (!modifiedExistingPosition)
// positions.add(position);
}
@Override
public String toString() {
return getName();
}
// JPA
public Portfolio() {
this.positionsMap = new ConcurrentHashMap<Asset, ConcurrentHashMap<Exchange, ConcurrentHashMap<Listing, ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>>>>>();
this.realisedProfits = new ConcurrentHashMap<Asset, ConcurrentHashMap<Exchange, ConcurrentHashMap<Listing, Amount>>>();
//this.balances = new CopyOnWriteArrayList<>();
this.transactions = new ConcurrentHashMap<Asset, ConcurrentHashMap<Exchange, ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Transaction>>>>();
}
protected void setPositions(
ConcurrentHashMap<Asset, ConcurrentHashMap<Exchange, ConcurrentHashMap<Listing, ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>>>>> positions) {
this.positionsMap = positions;
}
protected void setBalances(List<Balance> balances) {
this.balances = balances;
}
public void setBaseAsset(Asset baseAsset) {
this.baseAsset = baseAsset;
}
protected void setTransactions(
ConcurrentHashMap<Asset, ConcurrentHashMap<Exchange, ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Transaction>>>> transactions) {
this.transactions = transactions;
}
public void setName(String name) {
this.name = name;
}
protected void setContext(Context context) {
this.context = context;
}
protected void setPortfolioService(PortfolioService portfolioService) {
this.portfolioService = portfolioService;
}
protected void setOrderService(OrderService orderService) {
this.orderService = orderService;
}
protected void setStakes(List<Stake> stakes) {
this.stakes = stakes;
}
@SuppressWarnings("unchecked")
public static Portfolio findOrCreate(String portfolioName, Context context) {
final String queryStr = "select p.id from Portfolio p where name=?1";
Portfolio myPort = null;
// final String queryStr = "select p from Portfolio p JOIN FETCH p.positions where p.name=?1";
// final String queryPositoin = "select p from Portfolio p JOIN FETCH p.fills where p.name=?1";
try {
// Map hints = new HashMap();
//UUID portfolioID = EM.queryOne(UUID.class, queryStr, portfolioName);
Map hints = new HashMap();
Map withFillsHints = new HashMap();
Map withTransHints = new HashMap();
Map withChildrenHints = new HashMap();
Map withChildOrderHints = new HashMap();
// Map orderHints = new HashMap();
// UUID portfolioID = EM.queryOne(UUID.class, queryStr, portfolioName);
hints.put("javax.persistence.fetchgraph", "portfolioWithPositions");
withFillsHints.put("javax.persistence.fetchgraph", "orderWithFills");
withTransHints.put("javax.persistence.fetchgraph", "orderWithTransactions");
withChildOrderHints.put("javax.persistence.fetchgraph", "fillWithChildOrders");
myPort = EM.namedQueryZeroOne(Portfolio.class, "Portfolio.findOpenPositions", hints, portfolioName);
if (myPort == null)
return myPort;
// lets srippp of any emmpty positions.
myPort.getPositions().removeAll(Collections.singleton(null));
Map<Order, Order> portfolioOrders = new HashMap<Order, Order>();
Map<Fill, Fill> portfolioFills = new HashMap<Fill, Fill>();
for (Position position : myPort.getPositions()) {
context.getInjector().injectMembers(position);
position.getFills().removeAll(Collections.singleton(null));
List<Fill> fillsToBeAdded = new ArrayList<Fill>();
List<Fill> fills = new ArrayList<Fill>();
int index = 0;
for (Fill fill : position.getFills()) {
portfolioFills.put(fill, fill);
}
for (Fill fill : position.getFills()) {
// Fill filltest;
// if (!portfolioFills.containsKey(fill)) {
context.getInjector().injectMembers(fill);
fill.loadAllChildOrdersByFill(fill, portfolioOrders, portfolioFills);
fill.getOrder().loadAllChildOrdersByParentOrder(fill.getOrder(), portfolioOrders, portfolioFills);
// } else {
// filltest = portfolioFills.get(fill);
//fill = portfolioFills.get(fill);
// position.getFills().set(index, portfolioFills.get(fill));
// filltest = portfolioFills.get(fill);
// fill = portfolioFills.get(fill);
//}
//index++;
}
// when we are loading posiitons we need to link the fill to that position
//loop over all fills in the position
// then we load all order by child fill, for each order we load the fills, if the fill belongs to the posiont we get a differnt reference but same id.
// so when we load any fills, we need to see if we have them loaded somwehere else in the tree,
// we need to set the loaded fill to the parent fill
/* UUID orderId;
Fill fillWithChildren = EM.namedQueryZeroOne(Fill.class, "Fill.findFill", withChildOrderHints, fill.getId());
if (fillWithChildren != null)
fill.setFillChildOrders(fillWithChildren.getFillChildOrders());
Order orderWithFills;
Order orderWithChildren;
Order orderWithTransactions;
// so for each fill in the open position we need to load the whole order tree
// getorder, then get all childe orders, then for each child, load child orders, so on and so forth.
// load all child orders, and theri child ordres
// load all parent orders and thier parent orders
// need to laod all parent fills, their child orders, and their children
// get a list of all orders in the tree then load
orderId = fill.getOrder().getId();
try {
orderWithFills = EM.namedQueryZeroOne(Order.class, "Order.findOrder", withFillsHints, orderId);
orderWithChildren = EM.namedQueryZeroOne(Order.class, "Order.findOrder", withChildrenHints, orderId);
orderWithTransactions = EM.namedQueryZeroOne(Order.class, "Order.findOrder", withTransHints, orderId);
} catch (Error | Exception ex) {
log.error("Portfolio:findOrCreate unable to get order for orderID: " + orderId);
continue;
}
if ((orderWithFills != null && orderWithFills instanceof SpecificOrder && orderWithFills.getId().equals(orderId))
&& (orderWithTransactions != null && orderWithTransactions instanceof SpecificOrder && orderWithTransactions.getId()
.equals(orderId))
&& (orderWithChildren != null && orderWithChildren instanceof SpecificOrder && orderWithChildren.getId().equals(orderId))) {
SpecificOrder order = (SpecificOrder) orderWithFills;
order.setTransactions(orderWithTransactions.getTransactions());
order.setOrderChildren(orderWithChildren.getOrderChildren());
fill.setOrder(order);
log.error("Portfolio:findOrCreate found order for orderID: " + orderId);
}
}*/
}
for (Order order : myPort.getAllOrders()) {
context.getInjector().injectMembers(order);
for (Transaction transaction : order.getTransactions()) {
context.getInjector().injectMembers(transaction);
}
}
return myPort;
} catch (Error | Exception ex) {
log.error("unabled to load porfolio" + ex);
throw ex;
}
// PersistUtil.queryOne(Portfolio.class, queryStr, portfolioName);
}
protected void setManager(PortfolioManager manager) {
this.manager = manager;
}
@Override
// @Transactional
public synchronized void persit() {
try {
portfolioDao.persist(this);
//if (duplicate == null || duplicate.isEmpty())
} catch (Exception | Error ex) {
System.out.println("Unable to resubmit insert request in org.cryptocoinpartners.util.persistUtil::insert, full stack trace follows:" + ex);
// ex.printStackTrace();
}
}
// @Transactional
@Override
public EntityBase refresh() {
return portfolioDao.refresh(this);
}
@Override
public synchronized void merge() {
try {
portfolioDao.merge(this);
//if (duplicate == null || duplicate.isEmpty())
} catch (Exception | Error ex) {
System.out.println("Unable to resubmit insert request in org.cryptocoinpartners.util.persistUtil::insert, full stack trace follows:" + ex);
// ex.printStackTrace();
}
}
@Override
public boolean equals(Object object) {
boolean result = false;
if (object == null || object.getClass() != getClass()) {
result = false;
} else {
Portfolio portfolio = (Portfolio) object;
if (this.getName() == portfolio.getName()) {
result = true;
}
}
return result;
}
@Override
public int hashCode() {
return getId().hashCode();
}
private PortfolioManager manager;
protected static Logger log = LoggerFactory.getLogger("org.cryptocoinpartners.portfolio");
@Inject
public transient Context context;
@Inject
protected transient PortfolioService portfolioService;
@Inject
protected transient OrderService orderService;
@Inject
protected transient PositionFactory positionFactory;
@Inject
protected transient TransactionFactory transactionFactory;
@Inject
protected transient PortfolioDao portfolioDao;
private Asset baseAsset;
private ConcurrentHashMap<Asset, ConcurrentHashMap<Exchange, ConcurrentHashMap<Listing, ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Position>>>>> positionsMap;
private ConcurrentHashMap<Asset, ConcurrentHashMap<Exchange, ConcurrentHashMap<Listing, Amount>>> realisedProfits;
private List<Balance> balances = new CopyOnWriteArrayList<>();
private ConcurrentHashMap<Asset, ConcurrentHashMap<Exchange, ConcurrentHashMap<TransactionType, ConcurrentLinkedQueue<Transaction>>>> transactions;
private List<Stake> stakes = new CopyOnWriteArrayList<>();
private List<Position> positions = new CopyOnWriteArrayList<>();
//private ConcurrentHashMap<Market, ConcurrentSkipListMap<Long,ArrayList<TaxLot>>> longTaxLots;
//private ConcurrentHashMap<Market, ConcurrentSkipListMap<Long,ArrayList<TaxLot>>> shortTaxLots;
private final Collection<Balance> trades = new CopyOnWriteArrayList<>();
public <T> T find() {
// synchronized (persistanceLock) {
try {
return (T) portfolioDao.find(Portfolio.class, this.getId());
//if (duplicate == null || duplicate.isEmpty())
} catch (Exception | Error ex) {
return null;
// System.out.println("Unable to perform request in " + this.getClass().getSimpleName() + ":find, full stack trace follows:" + ex);
// ex.printStackTrace();
}
}
@Override
@Transient
public Dao getDao() {
return portfolioDao;
}
@Override
public void detach() {
// TODO Auto-generated method stub
}
@Override
public void delete() {
// TODO Auto-generated method stub
}
}