package cc.glsn.v15.fincalc; import java.io.FileInputStream; import java.io.File; import java.io.PrintStream; import java.io.FileOutputStream; import java.util.Scanner; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.LinkedList; import java.util.Collection; import java.util.TreeMap; import java.util.TreeSet; public class PerfTrack { public static String ListPath="/home/clash/projects/invest"; public static void main(String Args[]) throws Exception { new PerfTrack(Args); } private ValueList investList; private ValueList valueList; private SimpleDateFormat sdf; public PerfTrack(String Args[]) throws Exception { if (Args.length < 2) { System.out.println("Options:"); System.out.println("addinv "); System.out.println("addval "); System.out.println("report "); System.out.println(); System.out.println("All dates in format: YYYYMMDD"); return; } String acct=Args[1]; String InvestListPath=ListPath +"/" + acct + "/invest-list.txt"; String ValueListPath=ListPath +"/" + acct + "/value-list.txt"; investList=new ValueList(new Scanner(new FileInputStream(InvestListPath))); valueList=new ValueList(new Scanner(new FileInputStream(ValueListPath))); sdf=new SimpleDateFormat("yyyyMMdd"); if (Args[0].equals("addinv")) { double amt=new Double(Args[2]); Date d=sdf.parse(Args[3]); System.out.println("Adding investment: $" + amt + " " + sdf.format(d)); investList.addValue(new ValuePoint(d,amt)); safeSave(InvestListPath,investList); } if (Args[0].equals("addval")) { double amt=new Double(Args[2]); Date d=sdf.parse(Args[3]); System.out.println("Adding value: $" + amt + " " + sdf.format(d)); valueList.addValue(new ValuePoint(d,amt)); safeSave(ValueListPath,valueList); } if (Args[0].equals("report")) { ValuePoint start=new ValuePoint(new Date(0),0.0); ValuePoint end=null; for(ValuePoint p : valueList.getAllPoints()) { if ((end==null) || (p.getTime().compareTo(end.getTime()) > 0)) { end=p; } } System.out.println("Total effective return per year: " + printRate(getContInterestRate(start,end,investList.getAllPoints()))); TreeMap rates=getAllRates(investList,valueList); for(Date d : rates.keySet()) { //System.out.println("" + d + ": " + rates.get(d)); } Date first=valueList.getOrderedPoints().first().getTime(); Date last=valueList.getOrderedPoints().last().getTime(); //TreeMap month=getTaggedDatesForMonthly(first,last); printReport(getTaggedDatesForMonthly(first,last),rates); //System.out.println(month); System.out.println("Note: all numbers are in CCI yearly"); } } private void safeSave(String path, ValueList list) throws Exception { String tmp_path=path +".tmp"; File real_file=new File(path); File tmp_file=new File(tmp_path); tmp_file.delete(); FileOutputStream fos=new FileOutputStream(tmp_path); PrintStream out=new PrintStream(fos); list.write(out); out.close(); real_file.delete(); tmp_file.renameTo(real_file); } private void printReport(TreeMap tags, TreeMap rates) { ArrayList dates=new ArrayList(); dates.addAll(tags.keySet()); for(int i=0; i getAllRates(ValueList invest_list, ValueList value_list) { TreeMap out=new TreeMap(); ValuePoint start=null; ValuePoint end=new ValuePoint(new Date(0l),0.0); for(ValuePoint p : value_list.getOrderedPoints()) { start=end; end=p; Collection invest_group=invest_list.getPointsInRange(start.getTime(),end.getTime()); double rate=getContInterestRate(start,end,invest_group); out.put(start.getTime(), rate); } return out; } private double getRateBetween(TreeMap rates, Date start, Date end) { Date highest_before=null; for(Date d : rates.keySet()) { if (d.compareTo(start) <= 0) { if ((highest_before==null) || (d.compareTo(highest_before) > 0)) { highest_before=d; } } } TreeMap changes=new TreeMap(rates.subMap(start, end)); ValuePoint p_a=null; ValuePoint p_b=new ValuePoint(start,100); double rate=rates.get(highest_before); for(Date d : changes.keySet()) { p_a=p_b; LinkedList fake_list=new LinkedList(); fake_list.add(p_a); double amt=getValueForRate(fake_list,rate,d); p_b=new ValuePoint(d,amt); rate=changes.get(d); } p_a=p_b; LinkedList fake_list=new LinkedList(); fake_list.add(p_a); double amt=getValueForRate(fake_list,rate,end); double r=getContInterestRate(new ValuePoint(start,100),new ValuePoint(end,amt),new LinkedList()); //System.out.println("From: " + start + " To: " + end + " 100->" + amt); return r; } /** * Get the continiously componded interest rate for the given start, end and investment list. * * rate is per year. * * @param start * @param end * @param invest_list * @return */ private double getContInterestRate(ValuePoint start, ValuePoint end, Collection invest_list) { LinkedList inputs=new LinkedList(); inputs.add(start); inputs.addAll(invest_list); return getContInterestRate(inputs,end,-10.0,10.0); } /** * binary search for correct rate * @param lst * @param end * @param min * @param max * @return */ private double getContInterestRate(LinkedList lst, ValuePoint end,double min, double max) { double target=end.getValue(); double mid=(min+max)/2.0; if (Math.abs(min-max) < 0.00001) return mid; double sum=getValueForRate(lst,mid,end.getTime()); if (sum < target) { return getContInterestRate(lst,end,mid,max); } else { return getContInterestRate(lst,end,min,mid); } } /* * returns the final sum for the given list of investments (or starting values), * interest rate and end date. * * Calculated using A=pe^(rt) * */ private double getValueForRate(LinkedList lst, double rate, Date end_date) { double sum=0.0; for(ValuePoint vp : lst) { Date start_date=vp.getTime(); double time_delta=end_date.getTime() - start_date.getTime(); time_delta/=1000.0; //to seconds time_delta/=86400.0; //to days sum += vp.getValue() * Math.exp(rate * time_delta / 356.0); } return sum; } private TreeMap getTaggedDatesForMonthly(Date first, Date last) { TreeMap out=new TreeMap(); Calendar c=new GregorianCalendar(); c.setTime(first); c.set(Calendar.DAY_OF_MONTH, 1); c.set(Calendar.HOUR_OF_DAY, 0); c.set(Calendar.MINUTE,0); c.set(Calendar.SECOND,0); c.set(Calendar.MILLISECOND,0); TreeSet Tags=new TreeSet(); { Calendar c2=new GregorianCalendar(); c2.setTime(last); c2.add(Calendar.MONTH, 1); last=c2.getTime(); } while(c.getTime().before(last)) { int m=c.get(Calendar.MONTH)+1; int y=c.get(Calendar.YEAR); String z=""; if (m<10) z="0"; String tag="" + y + "/" + z + m; if (!Tags.contains(tag)) { Tags.add(tag); out.put(c.getTime(), tag); //System.out.println(tag); } c.add(Calendar.MONTH, 1); //System.out.println(c.getTime()); } return out; } private String printRate(double rate) { DecimalFormat df=new DecimalFormat("0.####"); return df.format(rate); } }