// Called once for each transaction for which a match will be searched. void IStockMatch.BeginMatch(SingleTransaction s) { int ret = asofDate.CompareTo(s.TransactionDate); if (ret < 0) { //if (debug) // System.Console.WriteLine("Transaction happened on {0,15:d} after date {1,15:d}. Ignoring...", s.TransactionDate, asofDate); return; } TimeSpan ts = new TimeSpan(); ts = asofDate - s.TransactionDate; if (debug) { System.Console.WriteLine("--------------------------------------------------------------------------------------------------------"); s.Dump(); } if (s.IsAcquisition()) { thisstockqty += s.TransactionQty; if (ts.Days > longtermdays) thisstockqtyLT += s.TransactionQty; } else { thisstockqty -= s.TransactionQty; if (ts.Days > longtermdays) thisstockqtyLT -= s.TransactionQty; } if (debug) System.Console.WriteLine("BEGIN MATCH Stock balance for {0,6} as of {1,15:d} is {2,15}. Long term quantities {3,15}", s.StockCode, asofDate, thisstockqty, thisstockqtyLT); }
void IStockMatch.NoMatchFound(SingleTransaction s) { if (asofDate.CompareTo(s.TransactionDate) < 0) { if (debug) System.Console.WriteLine("Transaction happened on {0,15:d} after date {1,15:d}. Ignoring...", s.TransactionDate, asofDate); return; } TimeSpan ts = new TimeSpan(); ts = asofDate - s.TransactionDate; //Prefix LT or ST to the transaction if (ts.Days > longtermdays) System.Console.Write("LONG TERM "); else System.Console.Write("SHORT TERM "); // Output the transaction details s.Dump(); // Keep track of stock balance for unsold stocks if (s.IsAcquisition()) thisstockqty += s.TransactionQty; else thisstockqty -= s.TransactionQty; }
public void MatchFound(SingleTransaction first, SingleTransaction matched, bool firstPartialMatch, bool secondPartialMatch) { double spunit = 0; double cpunit = 0; double gainlossunit = 0; double thistransgainorloss = 0.0; double returnOnInvestment = 0.0; double roIAnnualized = 0.0; if (first.TransactionDate.CompareTo(matched.TransactionDate) < 0) { spunit = Convert.ToDouble(matched.TransactionPrice - matched.UnitCharges); cpunit = Convert.ToDouble(first.TransactionPrice + first.UnitCharges); } else { spunit = Convert.ToDouble(first.TransactionPrice - first.UnitCharges); cpunit = Convert.ToDouble(matched.TransactionPrice + matched.UnitCharges); } gainlossunit = spunit - cpunit; SingleTransaction selltrans = null; if (first.IsDisposal()) selltrans = first; else selltrans = matched; TimeSpan ts = new TimeSpan(); ts = matched.TransactionDate - first.TransactionDate; int days = ts.Days; if (days < 0) days *= -1; double yearsheld = Convert.ToDouble(days) / 365; double baseamount = 0; if (!selltrans.IsRemoval()) // if this is a removal there is no gain or loss. { thistransgainorloss = gainlossunit * first.TransactionQty; gainorloss = gainorloss + thistransgainorloss; totalshortterm += thistransgainorloss; baseamount = Convert.ToDouble(first.TransactionPrice * first.TransactionQty) + Convert.ToDouble(first.transactionCharges) + Convert.ToDouble(matched.transactionCharges); returnOnInvestment = (thistransgainorloss / baseamount); if (returnOnInvestment < 0) { roIAnnualized = -1 * (Math.Pow((-1*returnOnInvestment)+1, (1 / yearsheld))-1); } else { roIAnnualized = Math.Pow(returnOnInvestment+1, (1 / yearsheld)) -1; } } OutputHelper.PrintRoI(first, matched, thistransgainorloss, days, (returnOnInvestment*100), (roIAnnualized*100), header); header = false; }
void IStockMatch.MatchFound(SingleTransaction first, SingleTransaction matched, bool firstPartialMatch, bool secondPartialMatch) { decimal spunit = 0.0M; decimal cpunit = 0.0M; decimal gainlossunit = 0.0M; decimal tshortterm = 0.0M; decimal tlongterm = 0.0M; if (first.TransactionDate.CompareTo(matched.TransactionDate) < 0) { spunit = matched.TransactionPrice - matched.UnitCharges; cpunit = first.TransactionPrice + first.UnitCharges; } else { spunit = first.TransactionPrice - first.UnitCharges; cpunit = matched.TransactionPrice + matched.UnitCharges; } gainlossunit = spunit - cpunit; SingleTransaction selltrans = null; if (first.IsDisposal()) selltrans = first; else selltrans = matched; if (selltrans.TransactionDate.CompareTo(fromdate) >= 0 && selltrans.TransactionDate.CompareTo(todate) <= 0) { atLeastOneTrans = true; TimeSpan ts = new TimeSpan(); ts = matched.TransactionDate - first.TransactionDate; int days = ts.Days; if (days < 0) days *= -1; if (!selltrans.IsRemoval()) // if this is a removal there is no gain or loss. { if (days < longtermdays || gainlossunit < 0) { tshortterm = gainlossunit * first.TransactionQty; shortterm += tshortterm; totalshortterm += tshortterm; tlongterm = 0.0M; } else { tlongterm = gainlossunit * first.TransactionQty; longterm += tlongterm; totallongterm += tlongterm; tshortterm = 0.0M; } } Helpers.OutputHelper.PrintGains(first, matched, tshortterm, tlongterm, header); header = false; } }
void IStockMatch.NoMatchFound(SingleTransaction s) { if (asofDate.CompareTo(s.TransactionDate) < 0) { if (debug) System.Console.WriteLine("Transaction happened on {0,15:d} after date {1,15:d}. Ignoring...", s.TransactionDate, asofDate); return; } // Keep track of stock balance for unsold stocks if (!s.IsAcquisition()) { System.Console.Write("Unmatched sell transaction!!!"); s.Dump(); return; } thisstockqty += s.TransactionQty; totalcost += s.TransactionQty * (s.TransactionPrice + s.UnitCharges); }
public int AddTransaction(SingleTransaction.eTransactionType ttype, DateTime tDate, string tStockCode, long tQty, decimal tPrice, decimal tCharges, string tlotId) { // Validations // Create a new transaction SingleTransaction thisTrans = new SingleTransaction(ttype,tDate,tStockCode,tQty, tPrice, tCharges, tlotId); acTransactions[lastTransactionUsed++] = thisTrans; if (lastTransactionUsed >= acTransactions.GetLength(0)) return -1; return 0; } // AddTransaction
public SingleTransaction(SingleTransaction.eTransactionType tType, DateTime tDate, string tStockCode, long tQty, decimal tPrice, decimal tCharges,string tlotId) { transactionType = tType; transactionStockCode = tStockCode; transactionQty = tQty; transactionPrice = tPrice; transactionCharges = tCharges; transactionDate = tDate; transactionReconciledFlag = false; transactionLotIdentifier = tlotId; Debug.WriteLine (this, "Constructor of SingleTransaction called"); }
public void NoMatchFound(SingleTransaction s) { }
public void BeginMatch(SingleTransaction s) { }
// Called each time a matching transaction is found void IStockMatch.MatchFound(SingleTransaction s, SingleTransaction matched, bool firstPartialMatch, bool secondPartialMatch) { }
public static void PrintGains(SingleTransaction first, SingleTransaction second, decimal shortgain, decimal longgain, bool header) { if (header) { Console.WriteLine("{0,6} {1,10} {2,4} {3,5:d} {4,8} {5,18} || {6,10} {7,4} {8,5:d} {9,8} {10,18} {11,12} {12,12}", "CODE", "DATE", "OTYP", "OQTY", "PRICE", "ORDER REF", "DATE", "OTYP", "OQTY", "PRICE", "ORDER REF", "GAIN ST", "GAIN LT"); } if (first == null || second == null) return; SingleTransaction one = null; SingleTransaction two = null; if (first.transactionStockCode != second.transactionStockCode) { Console.WriteLine("Order {0} stockcode {1} not matching order {2} stockcode {3}", first.transactionLotIdentifier, first.transactionStockCode, second.transactionLotIdentifier, second.transactionStockCode); return; } if (first.transactionDate.CompareTo(second.transactionDate) == 0) { if (first.transactionType == SingleTransaction.eTransactionType.Buy) { one = first; two = second; } else { one = second; two = first; } } else if (first.transactionDate.CompareTo(second.transactionDate) < 0) { one = first; two = second; } else { one = second; two = first; } Console.WriteLine("{0,6} {1,10:d} {2,4} {3,5:d} {4,8:F2} {5,18} || {6,10:d} {7,4} {8,5:d} {9,8:F2} {10,18} {11,12:F2} {12,12:F2}", one.transactionStockCode, one.transactionDate, one.transactionType.ToString(), one.transactionQty, one.transactionPrice, one.transactionLotIdentifier, two.transactionDate, two.transactionType.ToString(), two.transactionQty, two.transactionPrice, two.transactionLotIdentifier, shortgain, longgain); }
// Called once for each transaction for which a match will be searched. void IStockMatch.BeginMatch(SingleTransaction s) { if (debug) System.Console.WriteLine("BEGIN MATCH Stock balance for {0,6} as of {1,15:d} is {2,15}.", s.StockCode, asofDate, thisstockqty); }
// Called each time a matching transaction is found void IStockMatch.MatchFound(SingleTransaction s, SingleTransaction matched, bool firstPartialMatch, bool secondPartialMatch) { // This stock was sold. So remove it from our counting int ret = asofDate.CompareTo(s.TransactionDate); if (ret < 0) { //if (debug) //System.Console.WriteLine("Transaction happened on {0,15:d} after date {1,15:d}. Ignoring...", s.TransactionDate, asofDate); return; } TimeSpan ts = new TimeSpan(); ts = asofDate - s.TransactionDate; if (debug) { s.Dump(); matched.Dump(); } if (matched.IsDisposal()) { thisstockqty -= matched.TransactionQty; if (ts.Days > longtermdays) thisstockqtyLT -= matched.TransactionQty; } else { thisstockqty += matched.TransactionQty; if (ts.Days > longtermdays) thisstockqtyLT += matched.TransactionQty; } if (debug) { System.Console.WriteLine("END MATCH Stock balance for {0,6} as of {1,15:d} is {2,15}. Long term quantities {3,15}", s.StockCode, asofDate, thisstockqty, thisstockqtyLT); System.Console.WriteLine("-----------------------------------------------------------------------------------"); } }
void IStockMatch.NoMatchFound(SingleTransaction s) { }
void IStockMatch.BeginMatch(SingleTransaction s) { }
}// StockTaking public void CapitalGains(System.DateTime fromdate, System.DateTime todate, int longtermdays) { // Sort transactions by stock code. // Data structure is a Hashtable of StockCode (Key) to ArrayList of SingleTransaction (value) Hashtable mystocktable = new Hashtable(); if (!SortTransactionsByStockCode(out mystocktable)) return; decimal totallongterm = 0.0M; decimal totalshortterm = 0.0M; // Now process each stock bool header = true; bool bAtLeastOneTransForThisStock = false; foreach (String stock in mystocktable.Keys) { ArrayList thisStockTransactions = (ArrayList)mystocktable[stock]; header = true; bAtLeastOneTransForThisStock = false; thisStockTransactions.Sort(); bool bfirst = true; long nStockQty = 0; decimal longterm = 0.0M; decimal shortterm = 0.0M; decimal sUnitBrokerage = 0.0M; for (int i=0;i< thisStockTransactions.Count;i++) { SingleTransaction s = (SingleTransaction)thisStockTransactions[i]; if (s.transactionReconciledFlag) continue; sUnitBrokerage = s.transactionCharges/s.transactionQty; if (bfirst) { bfirst = false; if (!s.IsAcquisition()) Console.WriteLine("First transaction for {0} is not a BUY order. ref {1}", s.transactionStockCode, s.transactionLotIdentifier); if (s.IsAcquisition()) nStockQty += s.transactionQty; else nStockQty -= s.transactionQty; } if (s.transactionReconciledFlag) continue; if (nStockQty != 0) { // find matching transaction SingleTransaction.eTransactionType thisttype = s.transactionType; bool restartloop = false; do { restartloop = false; decimal tlongterm = 0.0M; decimal tshortterm = 0.0M; for (int j=i+1; j<thisStockTransactions.Count;j++) { SingleTransaction snext = (SingleTransaction)thisStockTransactions[j]; if (snext.transactionReconciledFlag) continue; if (!snext.IsOppositeType(thisttype) ) continue; if (SingleTransaction.ReferenceEquals(s,snext)) continue; decimal spunit = 0.0M; decimal cpunit = 0.0M;decimal gainlossunit = 0.0M; if (s.transactionDate.CompareTo(snext.transactionDate) < 0) { spunit = snext.transactionPrice - snext.transactionCharges/snext.transactionQty; cpunit = s.transactionPrice + sUnitBrokerage; } else { spunit = s.transactionPrice - sUnitBrokerage; cpunit = snext.transactionPrice + snext.transactionCharges/snext.transactionQty; } gainlossunit = spunit - cpunit; if (nStockQty == snext.transactionQty) { SingleTransaction first = null; if (nStockQty == s.transactionQty) first = s; else first = new SingleTransaction(s.transactionType,s.transactionDate,s.transactionStockCode,nStockQty,s.transactionPrice,s.transactionCharges/s.transactionQty*nStockQty,s.transactionLotIdentifier); SingleTransaction selltrans = null; if (first.IsDisposal()) selltrans = first; else selltrans = snext; if (selltrans.transactionDate.CompareTo(fromdate) >= 0 && selltrans.transactionDate.CompareTo(todate) <= 0) { TimeSpan ts = new TimeSpan(); ts = snext.transactionDate-first.transactionDate; if (ts.Days < longtermdays) { tshortterm = gainlossunit*nStockQty; shortterm += tshortterm; totalshortterm += tshortterm; tlongterm = 0.0M; } else { tlongterm = gainlossunit*nStockQty; longterm += tlongterm; totallongterm += tlongterm; tshortterm = 0.0M; } Helpers.OutputHelper.PrintGains(first,snext,tshortterm,tlongterm,header); header = false; bAtLeastOneTransForThisStock = true; } s.transactionReconciledFlag = true; snext.transactionReconciledFlag = true; nStockQty = 0; bfirst = true; break; } else if (nStockQty > snext.transactionQty) { long qty = snext.transactionQty; SingleTransaction first = new SingleTransaction(s.transactionType,s.transactionDate,s.transactionStockCode,qty,s.transactionPrice,s.transactionCharges/s.transactionQty*qty,s.transactionLotIdentifier); SingleTransaction selltrans = null; if (first.IsDisposal()) selltrans = first; else selltrans = snext; if (selltrans.transactionDate.CompareTo(fromdate) >= 0 && selltrans.transactionDate.CompareTo(todate) <= 0) { TimeSpan ts = new TimeSpan(); ts = snext.transactionDate-first.transactionDate; if (ts.Days < longtermdays) { tshortterm = gainlossunit*qty; shortterm += tshortterm; totalshortterm += tshortterm; tlongterm = 0.0M; } else { tlongterm = gainlossunit*qty; longterm += tlongterm; totallongterm += tlongterm; tshortterm = 0.0M; } Helpers.OutputHelper.PrintGains(first,snext,tshortterm,tlongterm,header); header = false; bAtLeastOneTransForThisStock = true; } nStockQty -= qty; snext.transactionReconciledFlag = true; } else if (nStockQty < snext.transactionQty) { SingleTransaction first = new SingleTransaction(s.transactionType,s.transactionDate,s.transactionStockCode,nStockQty,s.transactionPrice,s.transactionCharges/s.transactionQty*nStockQty,s.transactionLotIdentifier); SingleTransaction second = new SingleTransaction(snext.transactionType,snext.transactionDate,snext.transactionStockCode,nStockQty,snext.transactionPrice,snext.transactionCharges/snext.transactionQty*nStockQty,snext.transactionLotIdentifier); SingleTransaction selltrans = null; if (first.IsDisposal()) selltrans = first; else selltrans = second; int nFrom = selltrans.transactionDate.CompareTo(fromdate); int nTo = selltrans.transactionDate.CompareTo(todate); if ( nFrom >= 0 && nTo <= 0) { TimeSpan ts = new TimeSpan(); ts = second.transactionDate-first.transactionDate; if (ts.Days < longtermdays) { tshortterm = gainlossunit*nStockQty; shortterm += tshortterm; totalshortterm += tshortterm; tlongterm = 0.0M; } else { tlongterm = gainlossunit*nStockQty; longterm += tlongterm; totallongterm += tlongterm; tshortterm = 0.0M; } Helpers.OutputHelper.PrintGains(first,second,tshortterm,tlongterm,header); header = false; bAtLeastOneTransForThisStock = true; } nStockQty = snext.transactionQty - nStockQty; s.transactionReconciledFlag = true; s = snext; sUnitBrokerage = s.transactionCharges/s.transactionQty; thisttype = s.transactionType; restartloop = true; break; } } }while (restartloop); } else bfirst = true; } if (bAtLeastOneTransForThisStock) { Console.WriteLine("{0,110} {1,12:F2} {2,12:F2}", "", shortterm, longterm); Console.WriteLine("---------------------------------------------------------------------------------------------------------------------------------------------"); } } Console.WriteLine("{0,110} {1,12:F2} {2,12:F2}", "", totalshortterm, totallongterm); Console.WriteLine("---------------------------------------------------------------------------------------------------------------------------------------------"); return; }// CapitalGains
public void MatchTransactions(string matchthis, IStockMatch eventSink) { ResetMatchedTransactions(); eventSink.BeginOperation(); Hashtable mystocktable; if (!SortTransactionsByStockCode(out mystocktable)) return; long nStockQty = 0; foreach (String stock in mystocktable.Keys) { if (matchthis != "" && matchthis != stock) continue; ArrayList thisStockTransactions = (ArrayList)mystocktable[stock]; bool bfirst = true; eventSink.BeginStock(stock); for (int i = 0; i < thisStockTransactions.Count; i++) { SingleTransaction s = (SingleTransaction)thisStockTransactions[i]; if (s.transactionReconciledFlag) continue; if (bfirst) { bfirst = false; nStockQty = s.transactionQty; eventSink.BeginMatch(s); } if (s.transactionReconciledFlag) continue; if (nStockQty == 0) continue; // find matching transaction SingleTransaction.eTransactionType thisttype = s.transactionType; bool restartloop = false; do { restartloop = false; bool matchfound = false; for (int j = i + 1; j < thisStockTransactions.Count; j++, matchfound = false) { SingleTransaction snext = (SingleTransaction)thisStockTransactions[j]; if (snext.transactionReconciledFlag) continue; if (!snext.IsOppositeType(thisttype)) continue; if (SingleTransaction.ReferenceEquals(s, snext)) continue; matchfound = true; if (nStockQty == snext.transactionQty) { SingleTransaction first = null; if (nStockQty == s.transactionQty) first = s; else first = new SingleTransaction(s.transactionType, s.transactionDate, s.transactionStockCode, nStockQty, s.transactionPrice, s.transactionCharges / s.transactionQty * nStockQty, s.transactionLotIdentifier); eventSink.MatchFound(first, snext, false, false); s.transactionReconciledFlag = true; snext.transactionReconciledFlag = true; nStockQty = 0; bfirst = true; // A BUY transaction has been completely reconciled with one or more SELL transactions. // Now we need to start with the next BUY transaction. Hence break out! break; } else if (nStockQty > snext.transactionQty) { long qty = snext.transactionQty; SingleTransaction first = new SingleTransaction(s.transactionType, s.transactionDate, s.transactionStockCode, qty, s.transactionPrice, s.transactionCharges / s.transactionQty * qty, s.transactionLotIdentifier); eventSink.MatchFound(first, snext, true, false); nStockQty -= qty; snext.transactionReconciledFlag = true; } else if (nStockQty < snext.transactionQty) { SingleTransaction first = new SingleTransaction(s.transactionType, s.transactionDate, s.transactionStockCode, nStockQty, s.transactionPrice, s.transactionCharges / s.transactionQty * nStockQty, s.transactionLotIdentifier); SingleTransaction second = new SingleTransaction(snext.transactionType, snext.transactionDate, snext.transactionStockCode, nStockQty, snext.transactionPrice, snext.transactionCharges / snext.transactionQty * nStockQty, snext.transactionLotIdentifier); eventSink.MatchFound(first, second, false, true); nStockQty = snext.transactionQty - nStockQty; s.transactionReconciledFlag = true; s = snext; // Now the SELL transaction which is at a later date is left with some qty that is not matched. We need to find // a matching BUY transaction from an earlier date to completely reconcile this SELL transaction. Hence start // from the beginning of the transaction list. SingleTransaction begin = new SingleTransaction(s.transactionType, s.transactionDate, s.transactionStockCode, nStockQty, s.transactionPrice, s.transactionCharges / s.transactionQty * nStockQty, s.transactionLotIdentifier); eventSink.BeginMatch(begin); thisttype = s.transactionType; restartloop = true; break; } } // loop on all transactions following the current one we are looking for a match for. // if there is no matching transaction, the next transaction should still be considered a new one. if (!matchfound) { SingleTransaction sUnmatched = new SingleTransaction(s.transactionType, s.transactionDate, s.transactionStockCode, nStockQty, s.transactionPrice, s.transactionCharges / s.transactionQty * nStockQty, s.transactionLotIdentifier); s.transactionReconciledFlag = true; eventSink.NoMatchFound(sUnmatched); bfirst = true; } } while (restartloop); } // loop on all transactions for current stock eventSink.EndStock(stock); } // loop on all stocks eventSink.EndOperation(); }
public static void PrintRoI(SingleTransaction first, SingleTransaction second, double gain, int days, double returnOnInvestment, double annualizedRoI, bool header) { if (header) { Console.WriteLine("{0,6} {1,10} {2,4} {3,5} {4,8} {5,18} || {6,10} {7,4} {8,5} {9,8} {10,18} {11,12} {12,4} {13,8} {14,8}", "CODE", // 0 "DATE", // 1 "OTYP", // 2 "OQTY", // 3 "PRICE", // 4 "ORDER REF", // 5 "DATE", // 6 "OTYP", // 7 "OQTY", // 8 "PRICE", // 9 "ORDER REF", //10 "GAIN LOSS", //11 "DAYS", //12 "GAIN%", //13 "RoI(Ann.)%"); //14 } if (first == null || second == null) return; SingleTransaction one = null; SingleTransaction two = null; if (first.transactionStockCode != second.transactionStockCode) { Console.WriteLine("Order {0} stockcode {1} not matching order {2} stockcode {3}", first.transactionLotIdentifier, first.transactionStockCode, second.transactionLotIdentifier, second.transactionStockCode); return; } if (first.transactionDate.CompareTo(second.transactionDate) == 0) { if (first.transactionType == SingleTransaction.eTransactionType.Buy) { one = first; two = second; } else { one = second; two = first; } } else if (first.transactionDate.CompareTo(second.transactionDate) < 0) { one = first; two = second; } else { one = second; two = first; } Console.WriteLine("{0,6} {1,10:d} {2,4} {3,5} {4,8} {5,18} || {6,10:d} {7,4} {8,5} {9,8} {10,18} {11,12:F2} {12,4} {13,8:F2}% {14,8:F2}%", one.transactionStockCode, // 0 one.transactionDate, // 1 one.transactionType.ToString(), // 2 one.transactionQty, // 3 one.transactionPrice, // 4 one.transactionLotIdentifier, // 5 two.transactionDate, // 6 two.transactionType.ToString(), // 7 two.transactionQty, // 8 two.transactionPrice, // 9 two.transactionLotIdentifier, //10 gain, //11 days, //12 returnOnInvestment, //13 annualizedRoI //14 ); }