public void TestBidAsk() { ptt = new PapertradeTracker(); pt = new PositionTracker(); fills = 0; const string SYM = "TST"; ptt.SendDebugEvent += new DebugDelegate(debug); ptt.GotFillEvent += new FillDelegate(ptt_GotFillEvent); ptt.GotOrderEvent += new OrderDelegate(ptt_GotOrderEvent); // enable bid ask fills ptt.UseBidAskFills = true; // send an order ptt.sendorder(new BuyMarket(SYM, 1000)); // verify it did not fill Assert.AreEqual(0, pt[SYM].Size); // send a tick ptt.newTick(TickImpl.NewTrade(SYM, 10, 100)); // verify it did not fill Assert.AreEqual(0, pt[SYM].Size); // send bid ptt.newTick(TickImpl.NewBid(SYM, 10, 100)); // verify it did not fill Assert.AreEqual(0, pt[SYM].Size); // send ask ptt.newTick(TickImpl.NewAsk(SYM, 12, 100)); // verify it fills Assert.AreEqual(100, pt[SYM].Size); Assert.AreEqual(1, fills); // fill rest of the order ptt.newTick(TickImpl.NewAsk(SYM, 12, 100)); ptt.newTick(TickImpl.NewAsk(SYM, 12, 100)); ptt.newTick(TickImpl.NewAsk(SYM, 12, 100)); ptt.newTick(TickImpl.NewAsk(SYM, 12, 100)); ptt.newTick(TickImpl.NewAsk(SYM, 12, 100)); ptt.newTick(TickImpl.NewAsk(SYM, 12, 100)); ptt.newTick(TickImpl.NewAsk(SYM, 12, 100)); ptt.newTick(TickImpl.NewAsk(SYM, 12, 100)); ptt.newTick(TickImpl.NewAsk(SYM, 12, 100)); Assert.AreEqual(10, fills); Assert.AreEqual(1000, pt[SYM].Size); // send a bunch more ticks and ensure nothing else is filled ptt.newTick(TickImpl.NewAsk(SYM, 12, 100)); ptt.newTick(TickImpl.NewAsk(SYM, 12, 100)); ptt.newTick(TickImpl.NewAsk(SYM, 12, 100)); ptt.newTick(TickImpl.NewAsk(SYM, 12, 100)); ptt.newTick(TickImpl.NewAsk(SYM, 12, 100)); ptt.newTick(TickImpl.NewAsk(SYM, 12, 100)); ptt.newTick(TickImpl.NewAsk(SYM, 12, 100)); Assert.AreEqual(0, ptt.QueuedOrders.Length); Assert.AreEqual(10, fills); Assert.AreEqual(1000, pt[SYM].Size); }
public void BlankPositionReq() { PositionTracker pt = new PositionTracker(); bool except = false; int s = 100; try { s = pt["IBM"].Size; } catch { except = true; } Assert.AreEqual(0, s); Assert.IsFalse(except); }
public void NewPosition() { const string s = "IBM"; Position p = new PositionImpl(s,80,500); PositionTracker pt = new PositionTracker(); Assert.IsTrue(pt[s].isFlat); pt.NewPosition(p); Assert.AreEqual(500, pt[s].Size); }
/// <summary> /// gets absolute return of portfolio of positions at closing or market prices, or both /// </summary> /// <param name="pt"></param> /// <param name="marketprices"></param> /// <param name="countClosedPL"></param> /// <param name="countOpenPL"></param> /// <returns></returns> public static decimal[] AbsoluteReturn(PositionTracker pt, decimal[] marketprices, bool countClosedPL, bool countOpenPL) { decimal [] aret = new decimal[pt.Count]; if (countOpenPL && (pt.Count != marketprices.Length)) throw new Exception("market prices must have 1:1 correspondence with positions in tracker."); for (int i = 0; i < pt.Count; i++) { if (countOpenPL) aret[i] += Calc.OpenPL(marketprices[i],pt[i]); if (countClosedPL) aret[i] += pt[i].ClosedPL; } return aret; }
/// <summary> /// flat a symbol and flag it to allow prevention of future trading with status and supplied reason /// </summary> /// <param name="sym"></param> /// <param name="activesym"></param> /// <param name="_pt"></param> /// <param name="sendorder"></param> /// <param name="D"></param> /// <param name="reason"></param> public static void shutdown(string sym, GenericTracker <bool> activesym, PositionTracker _pt, OrderDelegate sendorder, DebugDelegate D, string reason) { if (!activesym[sym]) { return; } Order o = new MarketOrderFlat(_pt[sym]); if (D != null) { string r = reason == string.Empty ? string.Empty : " (" + reason + ")"; D("symbol shutdown" + r + ", flat order: " + o.ToString()); } sendorder(o); activesym[sym] = false; }
public void InitAndAdjust() { const string sym = "IBM"; // startup position tracker PositionTracker pt = new PositionTracker(); PositionTracker pt2 = new PositionTracker(); // give pt our initial position Position init = new PositionImpl(sym, 0, 0); pt.Adjust(init); pt2.Adjust(init); // fill a trade in both places Trade fill = new TradeImpl(sym,100, 100); pt.Adjust(fill); pt2.Adjust(fill); // make sure it's only 100 in both places Assert.AreEqual(100, pt[sym].Size); Assert.AreEqual(100, pt2[sym].Size); }
/// <summary> /// returns absolute return of all positions in order they are listed in position tracker /// both closed and open pl may be included /// </summary> /// <param name="pt"></param> /// <param name="marketprices"></param> /// <param name="countClosedPL"></param> /// <returns></returns> public static decimal[] AbsoluteReturn(PositionTracker pt, GenericTracker<decimal> marketprices, bool countClosedPL) { decimal[] aret = new decimal[pt.Count]; bool countOpenPL = marketprices.Count >= pt.Count; for (int i = 0; i < pt.Count; i++) { // get position Position p = pt[i]; // get index int idx = marketprices.getindex(p.Symbol); // see if we're doing open if (countOpenPL && (idx >= 0)) aret[i] += Calc.OpenPL(marketprices[idx], p); if (countClosedPL) aret[i] += p.ClosedPL; } return aret; }
public void Adjust() { const string s = "IBM"; TradeImpl t1 = new TradeImpl(s, 100, 100); PositionTracker pt = new PositionTracker(); // make we have no position yet Assert.IsTrue(pt[t1.symbol].isFlat); // send some adjustments decimal cpl = 0; cpl += pt.Adjust(t1); cpl += pt.Adjust(t1); // verify that adjustments took hold Assert.AreEqual(0, cpl); Assert.AreEqual(200, pt[t1.symbol].Size); }
public void TestTradeFill() { ptt = new PapertradeTracker(); ptt.SendDebugEvent+=new DebugDelegate(debug); ptt.UseBidAskFills = false; fills = 0; pt = new PositionTracker(); const string SYM = "TST"; ptt.GotFillEvent += new FillDelegate(ptt_GotFillEvent); ptt.GotOrderEvent+=new OrderDelegate(ptt_GotOrderEvent); // send an order ptt.sendorder(new BuyMarket(SYM, 1000)); // verify it did not fill Assert.AreEqual(0, pt[SYM].Size); // send a tick ptt.newTick(TickImpl.NewTrade(SYM, 10, 100)); // verify it fills Assert.AreEqual(100, pt[SYM].Size); }
public void MaxDD() { PositionTracker pt = new PositionTracker(); const string sym = "TST"; System.Collections.Generic.List<TradeLink.API.Trade> fills = new System.Collections.Generic.List<TradeLink.API.Trade>(); TradeImpl t = new TradeImpl(sym,10,100); System.Collections.Generic.List<decimal> ret = new System.Collections.Generic.List<decimal>(); fills.Add(t); pt.Adjust(t); t = new TradeImpl(sym, 11, -100); fills.Add(t); ret.Add(pt.Adjust(t)); t = new TradeImpl(sym, 11, -100); pt.Adjust(t); fills.Add(t); t = new TradeImpl(sym, 13, 100); fills.Add(t); ret.Add(pt.Adjust(t)); decimal maxdd = Calc.MaxDDVal(ret.ToArray()); decimal maxddp = Calc.MaxDDPct(fills); Assert.AreEqual(-300,maxdd); Assert.AreEqual(-.18m,Math.Round(maxddp,2)); }
public void FillBidAskPartial() { PapertradeTracker ptt = new PapertradeTracker(); PositionTracker pt = new PositionTracker(); ptt.UseBidAskFills = true; ptt.GotFillEvent += new FillDelegate(pt.GotFill); foreach (bool side in new bool[] { true, false }) { pt.Clear(); Order o = new MarketOrder("IBM", side, 1000); int size = o.size; o.id = 1; ptt.sendorder(o); Tick k = TickImpl.NewQuote("IBM", 100, 101, 400, 400, "", ""); ptt.newTick(k); // partial fill ptt.newTick(k); // partial fill ptt.newTick(k); // partial fill, completed Expect(pt["IBM"].Size, Is.EqualTo(size)); } }
/// <summary> /// flat a symbol and flag it to allow prevention of future trading with status /// </summary> /// <param name="sym"></param> /// <param name="activesym"></param> /// <param name="_pt"></param> /// <param name="sendorder"></param> /// <param name="D"></param> public static void shutdown(string sym, GenericTracker<bool> activesym, PositionTracker _pt, OrderDelegate sendorder, DebugDelegate D) { shutdown(sym, activesym, _pt, sendorder, D, string.Empty); }
public OversellTracker(PositionTracker pt) { _pt = pt; }
public void MultipleAccount() { // setup defaults for 1st and 2nd accounts and positions string sym = "TST"; string a1 = "account1"; string a2 = "account2"; int s1 = 300; int s2 = 500; decimal p = 100m; // create position tracker PositionTracker pt = new PositionTracker(); // set initial position in 1st account pt.Adjust(new PositionImpl(sym, p, s1, 0, a1)); // set initial position in 2nd account pt.Adjust(new PositionImpl(sym, p, s2, 0, a2)); // verify I can query default account and it's correct Assert.AreEqual(s1, pt[sym].Size); // change default to 2nd account pt.DefaultAccount = a2; // verify I can query default and it's correct Assert.AreEqual(s2, pt[sym].Size); // verify I can query 1st account and correct Assert.AreEqual(s1, pt[sym,a1].Size); // verify I can query 2nd account and correct Assert.AreEqual(s2, pt[sym,a2].Size); // get fill in sym for 1st account TradeImpl f = new TradeImpl(sym, p, s1); f.Account = a1; pt.Adjust(f); // get fill in sym for 2nd account TradeImpl f2 = new TradeImpl(sym, p, s2); f2.Account = a2; pt.Adjust(f2); // verify that I can querry 1st account and correct Assert.AreEqual(s1*2, pt[sym, a1].Size); // verify I can query 2nd account and correct Assert.AreEqual(s2*2, pt[sym, a2].Size); // reset pt.Clear(); // ensure I can query first and second account and get flat symbols Assert.AreEqual(0, pt[sym].Size); Assert.AreEqual(0, pt[sym, a1].Size); Assert.AreEqual(0, pt[sym, a2].Size); Assert.IsTrue(pt[sym, a1].isFlat); Assert.IsTrue(pt[sym, a2].isFlat); Assert.IsTrue(pt[sym].isFlat); Assert.AreEqual(string.Empty, pt.DefaultAccount); }
// helper stuff /// <summary> /// shutdown a response entirely, flat all positions and notify user /// </summary> /// <param name="_pt"></param> /// <param name="gt"></param> public void shutdown(PositionTracker _pt, GenericTrackerI gt) { send_event(MimeType.shutdown, "foo", "boo".ToJson()); if (!isValid) return; D("ShutdownTime"); isValid = false; bool ShutdownFlat = _pt != null; bool usegt = gt != null; if (ShutdownFlat) { D("flatting positions at shutdown."); foreach (Position p in _pt) { if (usegt && (gt.getindex(p.symbol) < 0)) continue; Order o = new MarketOrderFlat(p); D("flat order: " + o.ToString()); sendorder(o); } } }
/// <summary> /// flat a symbol and flag it to allow prevention of future trading with status and supplied reason /// </summary> /// <param name="sym"></param> /// <param name="activesym"></param> /// <param name="_pt"></param> /// <param name="sendorder"></param> /// <param name="D"></param> /// <param name="reason"></param> public static void shutdown(string sym, GenericTracker<bool> activesym, PositionTracker _pt, OrderDelegate sendorder, DebugDelegate D, string reason) { //send_event(MimeType.shutdown, "foo", "boo".ToJson()); if (!activesym[sym]) return; Order o = new MarketOrderFlat(_pt[sym]); if (D != null) { string r = reason == string.Empty ? string.Empty : " (" + reason + ")"; D("symbol shutdown" + r + ", flat order: " + o.ToString()); } sendorder(o); activesym[sym] = false; }
/// <summary> /// computes money used to purchase a portfolio of positions. /// uses average price for position. /// </summary> /// <param name="pt"></param> /// <returns></returns> public static decimal[] MoneyInUse(PositionTracker pt) { decimal [] miu = new decimal[pt.Count]; for (int i = 0; i<pt.Count; i++) miu[i] += pt[i].AvgPrice * pt[i].UnsignedSize; return miu; }
public OversellTracker(PositionTracker pt, IdTracker idt) { _pt = pt; _idt = idt; }
public static Results FetchResults(List<TradeResult> results, decimal RiskFreeRate, decimal CommissionPerContractShare, bool persymbol, DebugDelegate d) { try { List<Trade> fills = new List<Trade>(); foreach (TradeResult tr in results) fills.Add(tr.Source); List<decimal> _MIU = new List<decimal>(); List<decimal> _return = new List<decimal>(); List<int> days = new List<int>(); //clear position tracker PositionTracker pt = new PositionTracker(results.Count); // setup new results Results r = new Results(); r.ResultsDateTime = Util.ToTLDate() * 1000000 + Util.ToTLTime(); r.ComPerShare = CommissionPerContractShare; r.RiskFreeRet = string.Format("{0:P2}", RiskFreeRate); int consecWinners = 0; int consecLosers = 0; List<long> exitscounted = new List<long>(); decimal winpl = 0; decimal losepl = 0; Dictionary<string, int> tradecount = new Dictionary<string, int>(); List<decimal> negret = new List<decimal>(results.Count); foreach (TradeResult tr in results) { if (tradecount.ContainsKey(tr.Source.symbol)) tradecount[tr.Source.symbol]++; else tradecount.Add(tr.Source.symbol, 1); if (!days.Contains(tr.Source.xdate)) days.Add(tr.Source.xdate); int usizebefore = pt[tr.Source.symbol].UnsignedSize; pt.Adjust(tr.Source); bool isclosing = pt[tr.Source.symbol].UnsignedSize<usizebefore; // calculate MIU and store on array decimal miu = Calc.Sum(Calc.MoneyInUse(pt)); if (miu!=0) _MIU.Add(miu); // if we closed something, update return if (isclosing) { // get p&l for portfolio decimal pl = Calc.Sum(Calc.AbsoluteReturn(pt)); // count return _return.Add(pl); // get pct return for portfolio decimal pctret = _MIU[_MIU.Count - 1] == 0 ? 0 : pl / _MIU[_MIU.Count - 1]; // if it is below our zero, count it as negative return if (pctret < 0) negret.Add(pl); } if (!r.Symbols.Contains(tr.Source.symbol)) r.Symbols += tr.Source.symbol + ","; r.Trades++; r.SharesTraded += Math.Abs(tr.Source.xsize); r.GrossPL += tr.ClosedPL; if ((tr.ClosedPL > 0) && !exitscounted.Contains(tr.Source.id)) { if (tr.Source.side) { r.SellWins++; r.SellPL += tr.ClosedPL; } else { r.BuyWins++; r.BuyPL += tr.ClosedPL; } if (tr.Source.id != 0) exitscounted.Add(tr.id); r.Winners++; consecWinners++; consecLosers = 0; } else if ((tr.ClosedPL < 0) && !exitscounted.Contains(tr.Source.id)) { if (tr.Source.side) { r.SellLosers++; r.SellPL += tr.ClosedPL; } else { r.BuyLosers++; r.BuyPL += tr.ClosedPL; } if (tr.Source.id != 0) exitscounted.Add(tr.id); r.Losers++; consecLosers++; consecWinners = 0; } if (tr.ClosedPL > 0) winpl += tr.ClosedPL; else if (tr.ClosedPL < 0) losepl += tr.ClosedPL; if (consecWinners > r.ConsecWin) r.ConsecWin = consecWinners; if (consecLosers > r.ConsecLose) r.ConsecLose = consecLosers; if ((tr.OpenSize == 0) && (tr.ClosedPL == 0)) r.Flats++; if (tr.ClosedPL > r.MaxWin) r.MaxWin = tr.ClosedPL; if (tr.ClosedPL < r.MaxLoss) r.MaxLoss = tr.ClosedPL; if (tr.OpenPL > r.MaxOpenWin) r.MaxOpenWin = tr.OpenPL; if (tr.OpenPL < r.MaxOpenLoss) r.MaxOpenLoss = tr.OpenPL; } if (r.Trades != 0) { r.AvgPerTrade = Math.Round((losepl + winpl) / r.Trades, 2); r.AvgLoser = r.Losers == 0 ? 0 : Math.Round(losepl / r.Losers, 2); r.AvgWin = r.Winners == 0 ? 0 : Math.Round(winpl / r.Winners, 2); r.MoneyInUse = Math.Round(Calc.Max(_MIU.ToArray()), 2); r.MaxPL = Math.Round(Calc.Max(_return.ToArray()), 2); r.MinPL = Math.Round(Calc.Min(_return.ToArray()), 2); r.MaxDD = string.Format("{0:P1}", Calc.MaxDDPct(fills)); r.SymbolCount = pt.Count; r.DaysTraded = days.Count; r.GrossPerDay = Math.Round(r.GrossPL / days.Count, 2); r.GrossPerSymbol = Math.Round(r.GrossPL / pt.Count, 2); if (persymbol) { for (int i = 0; i < pt.Count; i++) { r.PerSymbolStats.Add(pt[i].Symbol + ": " + tradecount[pt[i].Symbol] + " for " + pt[i].ClosedPL.ToString("C2")); } } } else { r.MoneyInUse = 0; r.MaxPL = 0; r.MinPL = 0; r.MaxDD = "0"; r.GrossPerDay = 0; r.GrossPerSymbol = 0; } try { r.SharpeRatio = _return.Count < 2 ? 0 : Math.Round(Calc.SharpeRatio(_return[_return.Count - 1], Calc.StdDev(_return.ToArray()), (RiskFreeRate*r.MoneyInUse)), 3); } catch (Exception ex) { if (d != null) d("sharp error: " + ex.Message); } try { if (_return.Count == 0) r.SortinoRatio = 0; else if (negret.Count == 1) r.SortinoRatio = 0; else if ((negret.Count == 0) && (_return[_return.Count - 1] == 0)) r.SortinoRatio = 0; else if ((negret.Count == 0) && (_return[_return.Count - 1] > 0)) r.SortinoRatio = decimal.MaxValue; else if ((negret.Count == 0) && (_return[_return.Count - 1] < 0)) r.SortinoRatio = decimal.MinValue; else r.SortinoRatio = Math.Round(Calc.SortinoRatio(_return[_return.Count - 1], Calc.StdDev(negret.ToArray()), (RiskFreeRate * r.MoneyInUse)), 3); } catch (Exception ex) { if (d != null) d("sortino error: " + ex.Message); } return r; } catch (Exception ex) { if (d != null) d("error generting report: " + ex.Message + ex.StackTrace); return new Results(); } }
public static decimal GetPortfolioPlot(string title,decimal start, int startdate, int starttime, int enddate, int endtime, List<Trade> trades, ref ChartControl c, decimal compershare) { var cureq = start; if (trades.Count == 0) return GetPortfolioPlot(title,start, startdate, starttime, enddate, endtime, ref c); c.NewBarList(new BarListImpl(title)); var tradessorted = SortTrades(trades); c.newPoint(title, cureq, 0, tradessorted[0].xdate, 100); // plot money made PositionTracker pt = new PositionTracker(); foreach (var t in tradessorted) { var grosspl = pt.Adjust(t); var netpl = grosspl - (compershare * Math.Abs(t.xsize)); cureq += netpl; c.newPoint(title, cureq, t.xtime, t.xdate, 100); } c.redraw(); // set final equity return cureq; }
public void ClosedPL() { const string sym = "RYN"; PositionTracker pt = new PositionTracker(); Position p = new PositionImpl(sym, 44.39m, 800, 0); pt.Adjust(p); System.IO.StreamReader sr = new System.IO.StreamReader("TestPositionClosedPL.txt"); string[] file = sr.ReadToEnd().Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries); foreach (string line in file) { Trade t = TradeImpl.FromString(line); pt.Adjust(t); } Assert.AreEqual(-66, pt[sym].ClosedPL); }
public static double[] MoneyInUse2Double(PositionTracker pt) { double[] miu = new double[pt.Count]; for (int i = 0; i < pt.Count; i++) miu[i] += (double)pt[i].AvgPrice * pt[i].UnsignedSize; return miu; }
public REGSHO_ShortTracker(PositionTracker PT) : base() { pt = PT; pt.DefaultAccount = DefaultAccount; }
/// <summary> /// calculate absolute return only for closed portions of positions /// </summary> /// <param name="pt"></param> /// <returns></returns> public static decimal[] AbsoluteReturn(PositionTracker pt) { return AbsoluteReturn(pt, new GenericTracker<decimal>(0), true); }
/// <summary> /// creates a trail tracker from an existing position tracker component /// </summary> /// <param name="pt"></param> public TrailTracker(PositionTracker pt) { _pt = pt; }
public static decimal[] AbsoluteReturn(PositionTracker pt, GenericTracker<decimal> marketprices) { return AbsoluteReturn(pt, marketprices, true); }
public void Stop() { v("stopping ServerMB connector."); pt = null; m_HistMgr = null; m_OpenOrders = null; m_Quotes.UnadviseAll(this); //pmh - 9/18/12 - MUST NOT FORGET THIS! m_Quotes.Disconnect(); m_OrderClient.Disconnect(); m_Quotes = null; m_OrderClient = null; m_HistMgr = null; }
/// <summary> /// maximum drawdown as a percentage /// </summary> /// <param name="fills"></param> /// <returns></returns> public static decimal MaxDDPct(List<Trade> fills) { PositionTracker pt = new PositionTracker(); List<decimal> ret = new List<decimal>(); decimal mmiu = 0; for (int i = 0; i < fills.Count; i++) { pt.Adjust(fills[i]); decimal miu = Calc.Sum(Calc.MoneyInUse(pt)); if (miu > mmiu) mmiu = miu; ret.Add(Calc.Sum(Calc.AbsoluteReturn(pt, new decimal[0], true, false))); } decimal maxddval = MaxDDVal(ret.ToArray()); decimal pct = mmiu== 0 ? 0 : maxddval / mmiu; return pct; }
/// <summary> /// flat a symbol and flag it to allow prevention of future trading with status /// </summary> /// <param name="sym"></param> /// <param name="activesym"></param> /// <param name="_pt"></param> /// <param name="sendorder"></param> /// <param name="D"></param> public static void shutdown(string sym, GenericTracker <bool> activesym, PositionTracker _pt, OrderDelegate sendorder, DebugDelegate D) { //send_event(MimeType.shutdown, "foo", "boo".ToJson()); shutdown(sym, activesym, _pt, sendorder, D, string.Empty); }
/// <summary> /// flat a symbol and flag it to allow prevention of future trading with status /// </summary> /// <param name="sym"></param> /// <param name="activesym"></param> /// <param name="_pt"></param> /// <param name="sendorder"></param> /// <param name="D"></param> public static void shutdown(string sym, GenericTracker<bool> activesym, PositionTracker _pt, OrderDelegate sendorder, DebugDelegate D) { //send_event(MimeType.shutdown, "foo", "boo".ToJson()); shutdown(sym, activesym, _pt, sendorder, D, string.Empty); }
/// <summary> /// flat a symbol and flag it to allow prevention of future trading with status /// </summary> /// <param name="sym"></param> /// <param name="activesym"></param> /// <param name="_pt"></param> /// <param name="sendorder"></param> /// <param name="D"></param> public static void shutdown(string sym, GenericTracker <bool> activesym, PositionTracker _pt, OrderDelegate sendorder, DebugDelegate D) { shutdown(sym, activesym, _pt, sendorder, D, string.Empty); }
public static Results FetchResults(List<TradeResult> results, decimal RiskFreeRate, decimal CommissionPerContractShare, bool persymbol, DebugDelegate d) { try { List<Trade> fills = new List<Trade>(); foreach (TradeResult tr in results) fills.Add(tr.Source); List<decimal> _MIU = new List<decimal>(); List<decimal> _grossreturn = new List<decimal>(); List<decimal> _netreturns = new List<decimal>(); List<decimal> pctrets = new List<decimal>(); List<int> days = new List<int>(); //clear position tracker PositionTracker pt = new PositionTracker(results.Count); // setup new results Results r = new Results(); r.ResultsDateTime = Util.ToTLDateTime(); r.ComPerShare = CommissionPerContractShare; int consecWinners = 0; int consecLosers = 0; List<long> exitscounted = new List<long>(); decimal winpl = 0; decimal losepl = 0; Dictionary<string, int> tradecount = new Dictionary<string, int>(); List<decimal> negret = new List<decimal>(results.Count); decimal curcommiss = 0; for (int i = 0; i<results.Count; i++) { var tr = results[i]; if (tradecount.ContainsKey(tr.Source.symbol)) tradecount[tr.Source.symbol]++; else tradecount.Add(tr.Source.symbol, 1); if (!days.Contains(tr.Source.xdate)) days.Add(tr.Source.xdate); var pos = pt[tr.Source.symbol]; int usizebefore = pos.UnsignedSize; decimal pricebefore = pos.AvgPrice; var islongbefore = pos.isLong; var miubefore = pos.isFlat ? 0 : pos.UnsignedSize * pos.AvgPrice; decimal pospl = pt.Adjust(tr.Source); bool islong = pt[tr.Source.symbol].isLong; bool isroundturn = (usizebefore != 0) && (pt[tr.Source.symbol].UnsignedSize == 0); // get comissions curcommiss += Math.Abs(tr.Source.xsize) * CommissionPerContractShare; bool isclosing = pt[tr.Source.symbol].UnsignedSize<usizebefore; // calculate MIU and store on array var miu = pt[tr.Source.symbol].isFlat ? 0 : pt[tr.Source.symbol].AvgPrice * pt[tr.Source.symbol].UnsignedSize; if (miu!=0) _MIU.Add(miu); // get miu up to this point var maxmiu = _MIU.Count == 0 ? 0 : Math.Abs(Calc.Max(_MIU.ToArray())); // if we closed something, update return decimal grosspl = 0; if (isclosing) if (islongbefore) grosspl = (tr.Source.xprice - pricebefore) * Math.Abs(tr.Source.xsize); else grosspl = (pricebefore - tr.Source.xprice) * Math.Abs(tr.Source.xsize); decimal netpl = grosspl - (Math.Abs(tr.Source.xsize) * CommissionPerContractShare); // get p&l for portfolio curcommiss = 0; // count return _grossreturn.Add(grosspl); _netreturns.Add(netpl); // get pct return for portfolio decimal pctret = 0; if (miubefore == 0) pctret = netpl / miu; else pctret = netpl / miubefore; pctrets.Add(pctret); // if it is below our zero, count it as negative return if (pctret < 0) negret.Add(pctret); if (isroundturn) { r.RoundTurns++; if (pospl >= 0) r.RoundWinners++; else if (pospl < 0) r.RoundLosers++; } if (!r.Symbols.Contains(tr.Source.symbol)) r.Symbols += tr.Source.symbol + ","; r.Trades++; r.SharesTraded += Math.Abs(tr.Source.xsize); r.GrossPL += tr.ClosedPL; if ((tr.ClosedPL > 0) && !exitscounted.Contains(tr.Source.id)) { if (tr.Source.side) { r.SellWins++; r.SellPL += tr.ClosedPL; } else { r.BuyWins++; r.BuyPL += tr.ClosedPL; } if (tr.Source.id != 0) exitscounted.Add(tr.id); r.Winners++; consecWinners++; consecLosers = 0; } else if ((tr.ClosedPL < 0) && !exitscounted.Contains(tr.Source.id)) { if (tr.Source.side) { r.SellLosers++; r.SellPL += tr.ClosedPL; } else { r.BuyLosers++; r.BuyPL += tr.ClosedPL; } if (tr.Source.id != 0) exitscounted.Add(tr.id); r.Losers++; consecLosers++; consecWinners = 0; } if (tr.ClosedPL > 0) winpl += tr.ClosedPL; else if (tr.ClosedPL < 0) losepl += tr.ClosedPL; if (consecWinners > r.ConsecWin) r.ConsecWin = consecWinners; if (consecLosers > r.ConsecLose) r.ConsecLose = consecLosers; if ((tr.OpenSize == 0) && (tr.ClosedPL == 0)) r.Flats++; if (tr.ClosedPL > r.MaxWin) r.MaxWin = tr.ClosedPL; if (tr.ClosedPL < r.MaxLoss) r.MaxLoss = tr.ClosedPL; if (tr.OpenPL > r.MaxOpenWin) r.MaxOpenWin = tr.OpenPL; if (tr.OpenPL < r.MaxOpenLoss) r.MaxOpenLoss = tr.OpenPL; } if (r.Trades != 0) { r.AvgPerTrade = Math.Round((losepl + winpl) / r.Trades, 2); r.AvgLoser = r.Losers == 0 ? 0 : Math.Round(losepl / r.Losers, 2); r.AvgWin = r.Winners == 0 ? 0 : Math.Round(winpl / r.Winners, 2); r.MoneyInUse = Math.Round(Calc.Max(_MIU.ToArray()), 2); r.MaxPL = Math.Round(Calc.Max(_grossreturn.ToArray()), 2); r.MinPL = Math.Round(Calc.Min(_grossreturn.ToArray()), 2); r.MaxDD = Calc.MaxDDPct(fills); r.SymbolCount = pt.Count; r.DaysTraded = days.Count; r.GrossPerDay = Math.Round(r.GrossPL / days.Count, 2); r.GrossPerSymbol = Math.Round(r.GrossPL / pt.Count, 2); if (persymbol) { for (int i = 0; i < pt.Count; i++) { r.PerSymbolStats.Add(pt[i].symbol + ": " + tradecount[pt[i].symbol] + " for " + pt[i].ClosedPL.ToString("C2")); } } } else { r.MoneyInUse = 0; r.MaxPL = 0; r.MinPL = 0; r.MaxDD = 0; r.GrossPerDay = 0; r.GrossPerSymbol = 0; } r.PctReturns = pctrets.ToArray(); r.DollarReturns = _netreturns.ToArray(); r.NegPctReturns = negret.ToArray(); try { var fret = Calc.Avg(pctrets.ToArray()); if (pctrets.Count == 0) r.SharpeRatio = 0; else if (pctrets.Count == 1) r.SharpeRatio = Math.Round((fret- RiskFreeRate), 3); else r.SharpeRatio = Math.Round(Calc.SharpeRatio(fret, Calc.StdDev(pctrets.ToArray()), RiskFreeRate), 3); } catch (Exception ex) { if (d != null) d("sharpe error: " + ex.Message); } try { var fret = Calc.Avg(pctrets.ToArray()); if (pctrets.Count == 0) r.SortinoRatio = 0; else if (negret.Count == 1) r.SortinoRatio = (fret - RiskFreeRate) / negret[0]; else if ((negret.Count == 0)) r.SortinoRatio = fret - RiskFreeRate; else r.SortinoRatio = Math.Round(Calc.SortinoRatio(fret, Calc.StdDev(negret.ToArray()), RiskFreeRate ), 3); } catch (Exception ex) { if (d != null) d("sortino error: " + ex.Message); } return r; } catch (Exception ex) { if (d != null) d("error generting report: " + ex.Message + ex.StackTrace); return new Results(); } }
/// <summary> /// creates a trail tracker from an existing position tracker component /// </summary> /// <param name="pt"></param> public TrailTracker(PositionTracker pt, IdTracker id) { _pt = pt; _id = id; }