// Copyright 2015 Ivan Popivanov // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package net.tradelib.core; import java.time.LocalDateTime; import java.util.HashMap; import java.util.List; import java.util.TreeMap; public class Account { public static final String DEFAULT_NAME = "DefaultAccount"; private String name; private class Summary { LocalDateTime ts; double addition = 0.0; double withdrawal = 0.0; double interest = 0.0; double realizedPnl = 0.0; double unrealizedPnl = 0.0; double grossPnl = 0.0; double txnFees = 0.0; double netPnl = 0.0; double advisoryFees = 0.0; double netPerformance = 0.0; double endEq = Double.NaN; public Summary(LocalDateTime ts, double endEq) { this.ts = ts; this.endEq = endEq; } public Summary(LocalDateTime ts) { this.ts = ts; } } private LocalDateTime endEquityTimestamp; private TreeMap<LocalDateTime, Summary> summaries; private class PortfolioData { Portfolio portfolio; TimeSeries<Double> summary; public PortfolioData(String name, LocalDateTime startDate) { portfolio = new Portfolio(name); summary = new TimeSeries<Double>( "LongValue", "ShortValue", "NetValue", "GrossValue", "RealizedPnL", "UnrealizedPnL", "GrossPnL", "TxnFees", "NetPnL"); summary.add(startDate, 0.0); } } private HashMap<String, PortfolioData> portfolios; public Account(String name, LocalDateTime startDate, double initialEquity, String ...portfolioNames) { this.name = name; portfolios = new HashMap<String, PortfolioData>(); if(portfolioNames.length > 0) { for(int ii = 0; ii < portfolioNames.length; ++ii) { portfolios.put(portfolioNames[ii], new PortfolioData(portfolioNames[ii], startDate)); } } else { portfolios.put(Portfolio.DEFAULT_NAME, new PortfolioData(Portfolio.DEFAULT_NAME, startDate)); } summaries = new TreeMap<LocalDateTime, Summary>(); summaries.put(startDate, new Summary(startDate, initialEquity)); endEquityTimestamp = startDate; } public Account(LocalDateTime startDate, double initialEquity, String ...portfolioNames) { this(DEFAULT_NAME, startDate, initialEquity, portfolioNames); } public Account() { this(DEFAULT_NAME, LocalDateTime.MIN, 0.0, Portfolio.DEFAULT_NAME); } public void add(LocalDateTime ts, double amount) { Summary ss = summaries.get(ts); if(ss == null) ss = new Summary(ts); ss.addition += amount; summaries.put(ts, ss); } public void withdraw(LocalDateTime ts, double amount) { Summary ss = summaries.get(ts); if(ss == null) ss = new Summary(ts); ss.withdrawal += amount; summaries.put(ts, ss); } public void addInterest(LocalDateTime ts, double amount) { Summary ss = summaries.get(ts); if(ss == null) ss = new Summary(ts); ss.interest += amount; ss.netPerformance += amount; summaries.put(ts, ss); } public void addAdvisoryFee(LocalDateTime ts, double amount) { Summary ss = summaries.get(ts); if(ss == null) ss = new Summary(ts); ss.advisoryFees += amount; ss.netPerformance += amount; summaries.put(ts, ss); } public void addTransaction(String portfolio, Instrument instrument, LocalDateTime timeStamp, long quantity, double price, double fees) { portfolios.get(portfolio).portfolio.addTransaction(instrument, timeStamp, quantity, price, fees); } public void addTransaction(Instrument instrument, LocalDateTime timeStamp, long quantity, double price, double fees) { PortfolioData pd = portfolios.get(Portfolio.DEFAULT_NAME); if(pd == null) { pd = new PortfolioData(Portfolio.DEFAULT_NAME, timeStamp.minusDays(1)); } pd.portfolio.addTransaction(instrument, timeStamp, quantity, price, fees); } public void addTransaction(Execution execution) { addTransaction(execution.getInstrument(), execution.getDateTime(), execution.getQuantity(), execution.getPrice(), execution.getFees()); } public void setInitialEquity(LocalDateTime ts, double amount) { summaries.clear(); summaries.put(ts, new Summary(ts, amount)); endEquityTimestamp = ts; } public void updatePortfolio(String portfolio, Instrument instrument, TimeSeries<Double> prices) { portfolios.get(portfolio).portfolio.updatePnl(instrument, prices); } public void updatePortfolio(Instrument instrument, TimeSeries<Double> prices) { PortfolioData pd = portfolios.get(Portfolio.DEFAULT_NAME); if(pd == null) { pd = new PortfolioData(Portfolio.DEFAULT_NAME, prices.getTimestamp(0).minusDays(1)); } pd.portfolio.updatePnl(instrument, prices); } public void mark(String portfolio, Instrument instrument, LocalDateTime ts, double price) { // Mark the portfolio List<PositionPnl> posPnls = portfolios.get(portfolio).portfolio.mark(instrument, ts, price); if(posPnls != null) { // Mark the account with the updates generated by the portfolio for(PositionPnl posPnl : posPnls) { Summary ss = summaries.get(posPnl.ts); if(ss == null) ss = new Summary(posPnl.ts); ss.realizedPnl += posPnl.realizedPnl; ss.unrealizedPnl += posPnl.unrealizedPnl; ss.grossPnl += posPnl.grossPnl; ss.netPnl += posPnl.netPnl; ss.netPerformance += posPnl.netPnl; ss.txnFees += posPnl.fees; summaries.put(ss.ts, ss); } } } public void mark(Instrument instrument, LocalDateTime ts, double price) { mark(Portfolio.DEFAULT_NAME, instrument, ts, price); } public void mark(Instrument instrument, Bar bar) { mark(instrument, bar.getDateTime(), bar.getClose()); } public void updateEndEquity(LocalDateTime to) { double prevEndEquity = Double.NaN; for(Summary ss : summaries.values()) { if(ss.ts.isAfter(to)) { break; } else if(ss.ts.isAfter(endEquityTimestamp)) { if(Double.isNaN(prevEndEquity)) { throw new IllegalStateException("prevEndEquity not set!"); } ss.endEq = prevEndEquity + ss.addition + ss.withdrawal + ss.netPerformance; endEquityTimestamp = ss.ts; } prevEndEquity = ss.endEq; } } public void updateEndEquity() { updateEndEquity(summaries.lastKey()); } public Series getEquity() { Series result = new Series(1); for(Summary ss : summaries.values()) { result.append(ss.ts, ss.endEq); } return result; } public double getEndEquity() { // return summaries.lastEntry().getValue().endEq; return summaries.get(endEquityTimestamp).endEq; } public LocalDateTime getEndEquityTimestamp() { return endEquityTimestamp; } public double getEndEquity(LocalDateTime upTo) { assert !upTo.isAfter(endEquityTimestamp) : "NaN end equity requested"; return summaries.floorEntry(upTo).getValue().endEq; } public PositionPnl getPositionPnl(String portfolio, Instrument instrument) { return portfolios.get(portfolio).portfolio.getPositionPnl(instrument); } public PositionPnl getPositionPnl(Instrument instrument) { return portfolios.get(Portfolio.DEFAULT_NAME).portfolio.getPositionPnl(instrument); } public Iterable<String> getPortfolioSymbols(String portfolio) { return portfolios.get(portfolio).portfolio.symbols(); } public Iterable<String> getPortfolioSymbols() { return getPortfolioSymbols(Portfolio.DEFAULT_NAME); } public Series getPnlSeries(String portfolio, Instrument instrument) { return portfolios.get(portfolio).portfolio.getPnlSeries(instrument); } public Series getPnlSeries(Instrument instrument) { return getPnlSeries(Portfolio.DEFAULT_NAME, instrument); } public Pnl getPnl(String portfolio, Instrument instrument) { return portfolios.get(portfolio).portfolio.getPnl(instrument); } public Pnl getPnl(Instrument instrument) { return getPnl(Portfolio.DEFAULT_NAME, instrument); } public TradingResults getPortfolioTradingResults(String portfolio, Instrument instrument) { return portfolios.get(portfolio).portfolio.getTradingResults(instrument); } public TradingResults getPortfolioTradingResults(Instrument instrument) { return getPortfolioTradingResults(Portfolio.DEFAULT_NAME, instrument); } public Series getSummary() { Series result = new Series(11); for(Summary ss : summaries.values()) { result.append(ss.ts, ss.addition, ss.withdrawal, ss.interest, ss.realizedPnl, ss.unrealizedPnl, ss.grossPnl, ss.txnFees, ss.netPnl, ss.advisoryFees, ss.netPerformance, ss.endEq); } result.setNames("addition", "withdrawal", "interest", "realized.pnl", "unrealized.pnl", "gross.pnl", "fees", "net.pnl", "advisory.fees", "net.performance", "end.equity"); return result; } public Series getPortfolioSummary(String portfolio) { return portfolios.get(portfolio).portfolio.getSummary(); } public Series getPortfolioSummary() { return getPortfolioSummary(Portfolio.DEFAULT_NAME); } public Series getPositionPnls(String portfolio, Instrument instrument) { return portfolios.get(portfolio).portfolio.getPositionPnls(instrument); } public Series getPositionPnls(Instrument instrument) { return getPositionPnls(Portfolio.DEFAULT_NAME, instrument); } }