private void PrepareStatData(IPortfolioBuilderBuildParams builderParams) { int nInstruments = builderParams.Instruments.Count; rets = new double[nInstruments]; stDevs = new double[nInstruments]; matCov = new double[nInstruments, nInstruments]; // getting instruments and calculating avg returns and stdevs for given time period IQuotesDalGetTimeSeriesValuesParams getQuotesParams = _quotesDal.CreateGetQuotesParams(); getQuotesParams.PeriodStart = builderParams.PeriodStart; getQuotesParams.PeriodEnd = builderParams.PeriodEnd; getQuotesParams.TimeFrame = (QuotesInterfaces.ETimeFrame)builderParams.TimeFrame; getQuotesParams.Tickers.AddRange(builderParams.Instruments); IQuotesDalGetTimeseriesValuesResult getQuotesResult = _quotesDal.GetTimseriesValues(getQuotesParams); Dictionary <string, List <double> > returns = new Dictionary <string, List <double> >(); // calculating returns & stdev for (int i = 0; i < nInstruments; ++i) { double avgRet = 0; returns.Add(getQuotesResult.Quotes[i].Ticker, new List <double>()); for (int d = 0; d < getQuotesResult.Quotes[i].Quotes.Count(); ++d) { double ret = (double)(getQuotesResult.Quotes[i].Quotes[d]["Close"] - getQuotesResult.Quotes[i].Quotes[d]["Open"]) / (double)getQuotesResult.Quotes[i].Quotes[d]["Open"]; returns[getQuotesResult.Quotes[i].Ticker].Add(ret); avgRet += ret; } avgRet /= getQuotesResult.Quotes[i].Quotes.Count() - 1; switch (builderParams.TimeFrame) { case PortfolioInterfaces.ETimeFrame.Monthly: avgRet = Math.Pow(1 + avgRet, 12) - 1; break; } rets[i] = avgRet; stDevs[i] = MathNet.Numerics.Statistics.Statistics.PopulationStandardDeviation(returns[getQuotesResult.Quotes[i].Ticker]); switch (builderParams.TimeFrame) { case PortfolioInterfaces.ETimeFrame.Monthly: stDevs[i] *= Math.Sqrt(12); break; } } // calculating covariances for (int k = 0; k < returns.Keys.Count; ++k) { for (int o = k; o < returns.Keys.Count; ++o) { matCov[k, o] = MathNet.Numerics.Statistics.Statistics.PopulationCovariance(returns[returns.Keys.ElementAt(k)], returns[returns.Keys.ElementAt(o)]); matCov[o, k] = matCov[k, o]; } } }
public void BuildPortfolio_5Tickers_MaxReturn_Success() { IQuotesDal dal = PrepareQuotesDal(); IPortfolioBuilder builder = PreparePortfolioBuilder(dal); // preparing list of instruments IPortfolioBuilderBuildParams buildParams = builder.CreatePortfolioBuilderBuildParams(); buildParams.Goal = EOptimizationGoal.Max; // maximizing portfolio return buildParams.OptimizationTarget = EProtfolioProperty.Return; buildParams.Instruments.Add(ConfigurationManager.AppSettings["TickerSPY"]); buildParams.Instruments.Add(ConfigurationManager.AppSettings["TickerQQQ"]); buildParams.Instruments.Add(ConfigurationManager.AppSettings["TickerGLD"]); buildParams.Instruments.Add(ConfigurationManager.AppSettings["TickerSLV"]); buildParams.Instruments.Add(ConfigurationManager.AppSettings["TickerTLT"]); buildParams.TimeFrame = PortfolioInterfaces.ETimeFrame.Monthly; buildParams.PeriodStart = DateTime.Parse(ConfigurationManager.AppSettings["PeriodStart"]); buildParams.PeriodEnd = DateTime.Parse(ConfigurationManager.AppSettings["PeriodEnd"]); // setting constraints foreach (var i in buildParams.Instruments) { IPortfolioBuilderConstraint cl = builder.CreateConstraint(); cl.Operation = EConstraintOp.GreaterOrEqual; cl.Property = EProtfolioProperty.Instrument; cl.Ticker = i; cl.Value = 0; IPortfolioBuilderConstraint cu = builder.CreateConstraint(); cu.Operation = EConstraintOp.LessOrEqual; cu.Property = EProtfolioProperty.Instrument; cu.Ticker = i; cu.Value = 1; buildParams.Constraints.Add(cl); buildParams.Constraints.Add(cu); } IPortfolioBuilderConstraint cstdev = builder.CreateConstraint(); cstdev.Operation = EConstraintOp.LessOrEqual; cstdev.Property = EProtfolioProperty.StDev; cstdev.Value = (decimal)0.1; // limiting stdev to 10% buildParams.Constraints.Add(cstdev); IPortfolioBuilderBuildResult result = builder.Build(buildParams); Assert.IsTrue(result.Success); Assert.IsNotNull(result.Portfolio); Assert.IsNotNull(result.Portfolio.Instruments); Assert.AreEqual(result.Portfolio.Instruments.Count, 5); Assert.AreEqual(result.Portfolio.Instruments.Values.ElementAt(0) + result.Portfolio.Instruments.Values.ElementAt(1) + result.Portfolio.Instruments.Values.ElementAt(2) + result.Portfolio.Instruments.Values.ElementAt(3) + result.Portfolio.Instruments.Values.ElementAt(4), (decimal)1, "Sum of weights is not 100%"); }
public IPortfolioBuilderBuildResult Build(IPortfolioBuilderBuildParams buildParams) { IPortfolioBuilderBuildResult result = null; PrepareStatData(buildParams); fReturn = PrepareReturnFunction(); fStDev = PrepareStDevFunction(); //lower bound is 0 - we cannot have negative weights double[] bndl = new double[buildParams.Instruments.Count]; for (int i = 0; i < buildParams.Instruments.Count; ++i) { string symbol = buildParams.Instruments[i]; IPortfolioBuilderConstraint constr = buildParams.Constraints.FirstOrDefault(x => x.Property == EProtfolioProperty.Instrument && x.Ticker == symbol && (x.Operation == EConstraintOp.Greater || x.Operation == EConstraintOp.GreaterOrEqual || x.Operation == EConstraintOp.Equal)); bndl[i] = constr != null ? (constr.Value >= 0 ? (double)constr.Value : 0) : 0; } //upper bound double[] bndu = new double[buildParams.Instruments.Count]; for (int i = 0; i < buildParams.Instruments.Count; ++i) { string symbol = buildParams.Instruments[i]; IPortfolioBuilderConstraint constr = buildParams.Constraints.FirstOrDefault(x => x.Property == EProtfolioProperty.Instrument && x.Ticker == symbol && (x.Operation == EConstraintOp.Less || x.Operation == EConstraintOp.LessOrEqual || x.Operation == EConstraintOp.Equal)); bndu[i] = constr != null ? (constr.Value >= 0 ? (double)constr.Value : 1) : 1; } // constraining weights' sum to 100% double[,] c = new double[1, buildParams.Instruments.Count + 1]; //{ 1, 1, 1, 1, 1, 1 } for (int i = 0; i < buildParams.Instruments.Count + 1; ++i) { c[0, i] = 1; } int[] ct = { 0 }; // array to hold weights double[] w = new double[buildParams.Instruments.Count]; alglib.minbleicstate state; alglib.minbleicreport rep; double epsg = 0.000001; double epsf = 0; double epsx = 0; int maxits = 0; for (int i = 0; i < buildParams.MaxIterations; ++i) { GenerateRandomWeights(w); alglib.minbleiccreate(w, out state); alglib.minbleicsetbc(state, bndl, bndu); alglib.minbleicsetlc(state, c, ct); alglib.minbleicsetcond(state, epsg, epsf, epsx, maxits); switch (buildParams.OptimizationTarget) { case EProtfolioProperty.Return: alglib.minbleicoptimize(state, FunReturn, null, null); break; default: throw new InvalidOperationException(string.Format("Optimization for {0} is not supported", buildParams.OptimizationTarget.ToString())); } alglib.minbleicresults(state, out w, out rep); } TPortfolio bestPortfolio = null; switch (buildParams.OptimizationTarget) { case EProtfolioProperty.Return: IPortfolioBuilderConstraint stDevConstr = buildParams.Constraints.FirstOrDefault(x => x.Property == EProtfolioProperty.StDev); IEnumerable <TPortfolio> orderedPortfolios = null; switch (buildParams.Goal) { case EOptimizationGoal.Max: orderedPortfolios = portfolios.OrderByDescending(x => x.Return); break; case EOptimizationGoal.Min: orderedPortfolios = portfolios.OrderBy(x => x.Return); break; } if (stDevConstr != null) { switch (stDevConstr.Operation) { case EConstraintOp.Greater: orderedPortfolios = orderedPortfolios.Where(x => x.StDev > (double)stDevConstr.Value); break; case EConstraintOp.Equal: orderedPortfolios = orderedPortfolios.Where(x => Double.Equals(x.StDev, (double)stDevConstr.Value)); break; case EConstraintOp.GreaterOrEqual: orderedPortfolios = orderedPortfolios.Where(x => x.StDev >= (double)stDevConstr.Value); break; case EConstraintOp.Less: orderedPortfolios = orderedPortfolios.Where(x => x.StDev < (double)stDevConstr.Value); break; case EConstraintOp.LessOrEqual: orderedPortfolios = orderedPortfolios.Where(x => x.StDev <= (double)stDevConstr.Value); break; } } bestPortfolio = orderedPortfolios.FirstOrDefault(); break; } // getting result result = new PortfolioBuilderBuildResult(); if (bestPortfolio != null) { result.Success = true; result.Portfolio = new Portfolio(); for (int i = 0; i < bestPortfolio.Weights.Count; ++i) { result.Portfolio.Instruments.Add(buildParams.Instruments[i], Math.Round((decimal)bestPortfolio.Weights[i], 4)); } result.Return = (decimal)bestPortfolio.Return; result.StDev = (decimal)bestPortfolio.StDev; } else { result.Success = false; } return(result); }