Exemple #1
0
        /// <summary>
        /// maximum drawdown as a percentage
        /// </summary>
        /// <param name="fills"></param>
        /// <returns></returns>
        public static decimal MaxDDPct(List <Trade> fills)
        {
            Portfolio      portfolio = new Portfolio();
            List <decimal> ret       = new List <decimal>();
            decimal        mmiu      = 0;

            for (int i = 0; i < fills.Count; i++)
            {
                portfolio.Adjust(fills[i]);
                decimal miu = Calc.Sum(Calc.MoneyInUse(portfolio.Positions));
                if (miu > mmiu)
                {
                    mmiu = miu;
                }
                ret.Add(Calc.Sum(Calc.AbsoluteReturn(portfolio.Positions, new Dictionary <string, decimal>(), true)));
            }
            decimal maxddval = MaxDDVal(ret.ToArray());
            decimal pct      = mmiu == 0 ? 0 : maxddval / mmiu;

            return(pct);
        }
        /// <summary>
        /// Generate reports
        /// call after InitializePosition
        /// </summary>
        public void GenerateReports(List <Trade> trades)
        {
            try
            {
                _fills.Clear();
                _fills.AddRange(trades);

                List <decimal>           moneyinuse = new List <decimal>();           // money in use
                List <decimal>           tradepnl   = new List <decimal>();
                List <int>               days       = new List <int>();               // hostorical trading days when the trades happened
                Dictionary <string, int> tradecount = new Dictionary <string, int>(); // symbol --> trade count
                List <decimal>           negret     = new List <decimal>(_fills.Count);

                int         consecWinners = 0;
                int         consecLosers  = 0;
                List <long> exitscounted  = new List <long>();
                decimal     winpl         = 0;
                decimal     losepl        = 0;

                foreach (Trade trade in _fills)
                {
                    if (tradecount.ContainsKey(trade.FullSymbol))
                    {
                        tradecount[trade.FullSymbol]++;
                    }
                    else
                    {
                        tradecount.Add(trade.FullSymbol, 1);
                    }

                    if (!days.Contains(trade.TradeDate))
                    {
                        days.Add(trade.TradeDate);
                    }

                    int     usizebefore            = 0;
                    decimal closedpnlfromthistrade = 0;
                    if (_positions.ContainsKey(trade.FullSymbol))
                    {
                        usizebefore            = _positions[trade.FullSymbol].UnsignedSize;
                        closedpnlfromthistrade = _positions[trade.FullSymbol].Adjust(trade);                           // closed pnl
                    }
                    else
                    {
                        // add the trade to position
                        _positions.Add(trade.FullSymbol, new Position(trade));
                        usizebefore            = 0;
                        closedpnlfromthistrade = 0;
                    }

                    bool isroundturn = (usizebefore != 0) && (_positions[trade.FullSymbol].UnsignedSize == 0);      // end at exact 0

                    bool isclosing = _positions[trade.FullSymbol].UnsignedSize < usizebefore;

                    // calculate MIU and store on array
                    decimal miu = Calc.Sum(Calc.MoneyInUse(_positions));
                    if (miu != 0)
                    {
                        moneyinuse.Add(miu);
                    }
                    // if we closed something, update return
                    if (isclosing)
                    {
                        // get p&l for portfolio
                        decimal pl = Calc.Sum(Calc.AbsoluteReturn(_positions));         // with one param, AbsoluteReturn returns closed pnl
                        // count return
                        tradepnl.Add(pl);
                        // get pct return for portfolio
                        decimal pctret = moneyinuse[moneyinuse.Count - 1] == 0 ? 0 : pl / moneyinuse[moneyinuse.Count - 1];
                        // if it is below our zero, count it as negative return
                        if (pctret < 0)
                        {
                            negret.Add(pl);
                        }
                    }
                    if (isroundturn)            // # of RoundTurns = RoundWinners + RoundLosers
                    {
                        RoundTurns++;
                        if (closedpnlfromthistrade >= 0)
                        {
                            RoundWinners++;
                        }
                        else if (closedpnlfromthistrade < 0)
                        {
                            RoundLosers++;
                        }
                    }

                    Trades++;
                    SharesTraded += Math.Abs(trade.TradeSize);
                    Commissions  += CalculateIBCommissions(trade);
                    GrossPL      += closedpnlfromthistrade;



                    if ((closedpnlfromthistrade > 0) && !exitscounted.Contains(trade.Id))
                    {
                        if (trade.Side)
                        {
                            SellWins++;
                            SellPL += closedpnlfromthistrade;
                        }
                        else
                        {
                            BuyWins++;
                            BuyPL += closedpnlfromthistrade;
                        }
                        if (trade.Id != 0)
                        {
                            exitscounted.Add(trade.Id);
                        }
                        Winners++;
                        consecWinners++;
                        consecLosers = 0;
                    }
                    else if ((closedpnlfromthistrade < 0) && !exitscounted.Contains(trade.Id))
                    {
                        if (trade.Side)
                        {
                            SellLosers++;
                            SellPL += closedpnlfromthistrade;
                        }
                        else
                        {
                            BuyLosers++;
                            BuyPL += closedpnlfromthistrade;
                        }
                        if (trade.Id != 0)
                        {
                            exitscounted.Add(trade.Id);
                        }
                        Losers++;
                        consecLosers++;
                        consecWinners = 0;
                    }
                    if (closedpnlfromthistrade > 0)
                    {
                        winpl += closedpnlfromthistrade;
                    }
                    else if (closedpnlfromthistrade < 0)
                    {
                        losepl += closedpnlfromthistrade;
                    }

                    if (consecWinners > ConsecWin)
                    {
                        ConsecWin = consecWinners;
                    }
                    if (consecLosers > ConsecLose)
                    {
                        ConsecLose = consecLosers;
                    }
                    if ((_positions[trade.FullSymbol].Size == 0) && (closedpnlfromthistrade == 0))
                    {
                        Flats++;
                    }
                    if (closedpnlfromthistrade > MaxWin)
                    {
                        MaxWin = closedpnlfromthistrade;
                    }
                    if (closedpnlfromthistrade < MaxLoss)
                    {
                        MaxLoss = closedpnlfromthistrade;
                    }
                    if (_positions[trade.FullSymbol].OpenPL > MaxOpenWin)
                    {
                        MaxOpenWin = _positions[trade.FullSymbol].OpenPL;
                    }
                    if (_positions[trade.FullSymbol].OpenPL < MaxOpenLoss)
                    {
                        MaxOpenLoss = _positions[trade.FullSymbol].OpenPL;
                    }
                }   // end of loop over trades

                if (Trades != 0)
                {
                    AvgPerTrade    = Math.Round((losepl + winpl) / Trades, 2);
                    AvgLoser       = Losers == 0 ? 0 : Math.Round(losepl / Losers, 2);
                    AvgWin         = Winners == 0 ? 0 : Math.Round(winpl / Winners, 2);
                    MoneyInUse     = Math.Round(Calc.Max(moneyinuse.ToArray()), 2);
                    MaxPL          = Math.Round(Calc.Max(tradepnl.ToArray()), 2);
                    MinPL          = Math.Round(Calc.Min(tradepnl.ToArray()), 2);
                    MaxDD          = Calc.MaxDDPct(_fills);
                    SymbolCount    = _positions.Count;
                    DaysTraded     = days.Count;
                    GrossPerDay    = Math.Round(GrossPL / days.Count, 2);
                    GrossPerSymbol = Math.Round(GrossPL / _positions.Count, 2);
                    if (PerSymbol)
                    {
                        foreach (KeyValuePair <string, Position> item in _positions)
                        {
                            PerSymbolStats.Add(item.Value.FullSymbol, tradecount[item.Value.FullSymbol] + " for " + item.Value.ClosedPL.ToString("C2"));
                        }
                    }
                }
                else
                {
                    MoneyInUse     = 0;
                    MaxPL          = 0;
                    MinPL          = 0;
                    MaxDD          = 0;
                    GrossPerDay    = 0;
                    GrossPerSymbol = 0;
                }

                // ratio measures
                try
                {
                    SharpeRatio = tradepnl.Count < 2 ? 0 : Math.Round(Calc.SharpeRatio(tradepnl[tradepnl.Count - 1], Calc.StdDev(tradepnl.ToArray()), (RiskFreeRate * MoneyInUse * DaysTraded / 252)), 3);
                }
                catch (Exception ex)
                {
                    Debug("sharpe error: " + ex.Message);
                }

                try
                {
                    if (tradepnl.Count == 0)
                    {
                        SortinoRatio = 0;
                    }
                    else if (negret.Count == 1)
                    {
                        SortinoRatio = 0;
                    }
                    else if ((negret.Count == 0) && (tradepnl[tradepnl.Count - 1] == 0))
                    {
                        SortinoRatio = 0;
                    }
                    else if ((negret.Count == 0) && (tradepnl[tradepnl.Count - 1] > 0))
                    {
                        SortinoRatio = 0;
                    }
                    //SortinoRatio = decimal.MaxValue;
                    else if ((negret.Count == 0) && (tradepnl[tradepnl.Count - 1] < 0))
                    {
                        SortinoRatio = 0;
                    }
                    //SortinoRatio = decimal.MinValue;
                    else
                    {
                        SortinoRatio = Math.Round(Calc.SortinoRatio(tradepnl[tradepnl.Count - 1], Calc.StdDev(negret.ToArray()), (RiskFreeRate * MoneyInUse)), 3);
                    }
                }
                catch (Exception ex)
                {
                    Debug("sortino error: " + ex.Message);
                }
            }
            catch (Exception ex)
            {
                Debug("error in generating performance report" + ex.Message);
            }
        }