예제 #1
0
 static void CreateSolver()
 {
     StaticReset();
     solver = new InteriorPointSolver();
     solver.AddRow("goal", out goal);
     solver.AddGoal(goal, 0, true); //minimizing the goal
 }
예제 #2
0
        static void StaticReset()
        {
#if SIMPLESOLVER
            solver    = null;
            goal      = 0;
            numOfRows = -1;
#else  // SIMPLESOLVER
            solution = null;
            Context.ClearModel();
            model = Context.CreateModel();
            Constraints.Clear();
            goalTerm = null;
            solution = null;
            goal     = null;
#endif // SIMPLESOLVER
        }
예제 #3
0
        /// <summary> Build and run the risk model in multiple iterations.
        ///           Put the results into the plan.
        /// </summary>
        public bool BuildRiskModel(DataTable plan, int iterations)
        {
            int m = _stockNames.Length;

            for (int reqIx = 0; reqIx < iterations; reqIx++)
            {
                InteriorPointSolver solver = new InteriorPointSolver();

                int[] allocations = new int[m];

                for (int invest = 0; invest < m; invest++)
                {
                    string name = _stockNames[invest];
                    solver.AddVariable(name, out allocations[invest]);
                    solver.SetBounds(allocations[invest], 0, 1);
                }

                int expectedReturn;
                solver.AddRow("expectedReturn", out expectedReturn);

                // expected return must beat the minimum asked

                solver.SetBounds(expectedReturn, (double)plan.Rows[reqIx]["minimum"], double.PositiveInfinity);

                int unity;
                solver.AddRow("Investments sum to one", out unity);
                solver.SetBounds(unity, 1, 1);

                // expected return is a weighted linear combination of investments.
                // unity is a simple sum of the investments

                for (int invest = m; 0 <= --invest; )
                {
                    solver.SetCoefficient(expectedReturn, allocations[invest], _means[invest]);
                    solver.SetCoefficient(unity, allocations[invest], 1);
                }

                // The variance of the result is a quadratic combination of the covariants and allocations.

                int variance;
                solver.AddRow("variance", out variance);
                for (int invest = m; 0 <= --invest; )
                {
                    for (int jnvest = m; 0 <= --jnvest; )
                    {
                        solver.SetCoefficient(variance, _covariance[invest, jnvest], allocations[invest], allocations[jnvest]);
                    }
                }

                // the goal is to minimize the variance, given the linear lower bound on asked return.

                solver.AddGoal(variance, 0, true);

                InteriorPointSolverParams lpParams = new InteriorPointSolverParams();

                solver.Solve(lpParams);
                if (solver.Result != LinearResult.Optimal)
                    return false;

                for (int invest = m; 0 <= --invest; )
                {
                    plan.Rows[reqIx][_stockNames[invest]] = (double)solver.GetValue(allocations[invest]);
                }
                plan.Rows[reqIx]["actual"] = (double)solver.GetValue(expectedReturn);
                plan.Rows[reqIx]["Std.Dev."] = Math.Sqrt((double)solver.Statistics.Primal);
                plan.Rows[reqIx]["variance"] = (double)solver.GetValue(variance);
            }
            return true;
        }
예제 #4
0
    public void SolveQuadratic(RvolSolveModel model_)
    {
      int m = model_.CurrencyLines.Length;
      //int iterations = 1;

      //for (int reqIx = 0; reqIx < iterations; ++reqIx)
      {
        InteriorPointSolver solver = new InteriorPointSolver();
        int[] allocations = new int[m];

        for (int invest = 0; invest < m; ++invest)
        {
          string name = model_.CurrencyLines[invest].Ccy.Code;

          solver.AddVariable(name, out allocations[invest]);
          solver.SetBounds(allocations[invest], 
            model_.CurrencyLines[invest].MinWeight,
            model_.CurrencyLines[invest].MaxWeight);
        }

        int expectedReturn;
        solver.AddRow("expectedReturn", out expectedReturn);
        //int sumZero;
        //solver.AddRow("sumZero", out sumZero);

        for (int invest = 0; invest < m; ++invest)
        {
          solver.SetCoefficient(expectedReturn, allocations[invest], model_.CurrencyLines[invest].ExpectedReturn);
          //solver.SetCoefficient(sumZero, allocations[invest], 0);
        }

        int variance;
        solver.AddRow("variance", out variance);

        for (int invest = 0; invest < m; ++invest)
        {
          for (int jnvest = 0; jnvest < m; ++jnvest)
          {
            solver.SetCoefficient(variance, model_.Covar.Data[invest, jnvest], allocations[invest], allocations[jnvest]);
          }
        }

        var varianceTarget = Math.Pow(model_.TargetVol, 2d);

        solver.SetBounds(variance, double.NegativeInfinity, varianceTarget);

        // max expected return
        solver.AddGoal(expectedReturn, 1, false);

        InteriorPointSolverParams lpParams = new InteriorPointSolverParams();

        solver.Solve(lpParams);

        //if (solver.Result != LinearResult.Optimal)

        for (int invest = m; 0 <= --invest;)
        {
          model_.CurrencyLines[invest].Weight = (double) solver.GetValue(allocations[invest]);
        }
      }
    }
예제 #5
0
        public bool ModeloRiesgo(DataTable plan, int iterations)
        {
            int m = Companias.Length;

            for (int reqIx = 0; reqIx < iterations; reqIx++)
            {
                InteriorPointSolver solver = new InteriorPointSolver();

                int[] asignaciones = new int[m];

                for (int i = 0; i < m; i++)
                {
                    solver.AddVariable(Companias[i], out asignaciones[i]);
                    solver.SetBounds(asignaciones[i], 0, 1);
                }

                int rentabilidad;
                solver.AddRow("rentabilidad", out rentabilidad);

                // La rentabilidad debe superar el minimo pedido

                solver.SetBounds(rentabilidad, (double)plan.Rows[reqIx]["minimum"], double.PositiveInfinity);

                int unity;
                solver.AddRow("Invertir la suma a", out unity);
                solver.SetBounds(unity, 1, 1);

                // El rendimiento esperado es una combinacion lineal ponderada de las inversiones
                // unity = suma de inversiones

                for (int invest = m; 0 <= --invest;)
                {
                    solver.SetCoefficient(rentabilidad, asignaciones[invest], media[invest]);
                    solver.SetCoefficient(unity, asignaciones[invest], 1);
                }

                // The variance of the result is a quadratic combination of the covariants and allocations.

                int varianza;
                solver.AddRow("varianza", out varianza);
                for (int invest = m; 0 <= --invest;)
                {
                    for (int jnvest = m; 0 <= --jnvest;)
                    {
                        solver.SetCoefficient(varianza, covarianza[invest, jnvest], asignaciones[invest], asignaciones[jnvest]);
                    }
                }

                // the goal is to minimize the variance, given the linear lower bound on asked return.

                solver.AddGoal(varianza, 0, true);

                InteriorPointSolverParams lpParams = new InteriorPointSolverParams();

                solver.Solve(lpParams);
                if (solver.Result != LinearResult.Optimal)
                {
                    return(false);
                }

                for (int invest = m; 0 <= --invest;)
                {
                    plan.Rows[reqIx][Companias[invest]] = (double)solver.GetValue(asignaciones[invest]);
                }
                plan.Rows[reqIx]["actual"]   = (double)solver.GetValue(rentabilidad);
                plan.Rows[reqIx]["Std.Dev."] = Math.Sqrt((double)solver.Statistics.Primal);
            }
            return(true);
        }
        static void StaticReset()
        {
#if SIMPLESOLVER
            solver = null;
            goal = 0;
            numOfRows = -1;
#else  // SIMPLESOLVER
            solution = null;
            Context.ClearModel();
            model = Context.CreateModel();
            Constraints.Clear();
            goalTerm = null;
            solution = null;
            goal = null;
#endif // SIMPLESOLVER
        }
예제 #7
0
        static void Main(string[] args)
        {
            var wTarget = (new[] { Rational.Get(6, 10), Rational.Get(8, 10), Rational.Get(-5,10) });
              var C = Rational.Get(10,1);
              var w0Target = Rational.Get(1,10);
              var rightness = 0.0;
              var successful = 0.0;
              Datum[] classified={};
              var dump = new Rational[TRIALS,TRIALS];

              for (int experiment = 0; experiment < EXPERIMENTS; experiment++)
              {
            try
            {
              classified = Generate(TRIALS, w0Target, wTarget);
              var testData = Generate(TRIALS * 10, w0Target, wTarget);

              var solver = new InteriorPointSolver();
              Func<Rational[], Rational[], Rational> kernel = Dot;
              int goal;
              solver.AddRow("dual", out goal);
              solver.AddGoal(goal, 0, false);//false to maximize

              //make alphas
              var alphas = classified.Select((_, i) =>
              {
            int tmp;
            solver.AddVariable("alpha" + i, out tmp);
            solver.SetBounds(tmp, Rational.Zero, C);
            return tmp;
              }).ToArray();

              int sumConstraint;
              solver.AddRow("sumConstraint", out sumConstraint);
              //solver.SetBounds(sumConstraint, Rational.Zero, Rational.Zero);
              //TODO: maybe I'm running into numeric issues?
              solver.SetBounds(sumConstraint, Rational.Get(-1, THRESH), Rational.Get(1, THRESH));

              for (int i = 0; i < classified.Length; i++)
              {
            //sum_n (alpha_n*y_n) == 0
            solver.SetCoefficient(sumConstraint, alphas[i], classified[i].y);

            solver.SetCoefficient(goal, alphas[i], 1); //the sum_n (alpha_n) part of the lagrangian

            ////quadratic terms. sometimes not convex, and I don't know why
            for (int j = 0; j <= i; j++)
            {
              // coef = y_i * y_j * Kernel(x_i, x_j). Note that the diagonal is half: the other terms appear twice and are thus doubled
              var coef = (i == j ? -0.5 : -1.0) * classified[i].y * classified[j].y * kernel(classified[i].x, classified[j].x);
              solver.SetCoefficient(goal, coef, alphas[i], alphas[j]);
              dump[i, j] = coef;
              dump[j, i] = coef;
            }
            //This gives better results, but it isn't actually right. :-(
            //for (int j = 0; j < classified.Length; j++)
            //{
            //  // coef = y_i * y_j * Kernel(x_i, x_j). Note that the diagonal is half: the other terms appear twice and are thus doubled
            //  var coef = -0.5  * classified[i].y * classified[j].y * kernel(classified[i].x, classified[j].x);
            //  solver.SetCoefficient(goal, coef, alphas[i], alphas[j]);
            //}
              }

              //now solve
              solver.Solve(new InteriorPointSolverParams());
              var alphaVals = Enumerable.Range(0, classified.Length)
            .Select(i => solver.GetValue(alphas[i])) //using alphas[i] instead of 1..N here
            .ToArray();

              //Console.WriteLine("goal={0}", solver.GetValue(0));

              //get the SVs
              var maxAlpha = alphaVals.Max();
              var threshold = maxAlpha / THRESH;
              var sVecs = alphaVals.Where(a => a > threshold)
            //.Where(a => a + threshold < C) //This may be wrong... yep
            .Select((a, i) => new SVInfo { y = classified[i].y, x = classified[i].x, alpha = a })
            .ToArray();

              //w0, aka b
              var w0s = sVecs.Where(sv => sv.alpha + threshold < C) //this must not be right?
              .Select(sv1 =>
            //note that the inner loop gets alphas == C. Not sure if that's right.
              sv1.y - sVecs.Select(sv2 => sv2.alpha * sv2.y * kernel(sv1.x, sv2.x)).Aggregate((acc, r) => acc + r)
            ).ToArray();

              var w0 = w0s.OrderBy(lf => lf.ToDouble()).ToArray()[w0s.Length / 2]; //too widely dispersed... median here
              //so what does w look like here...?
              List<Rational> wTmp = (new List<Rational> { w0 });
              wTmp.AddRange(
            sVecs.Select(sv =>
              {
            var tmp = sv.alpha * sv.y;
            return sv.x.Select(x => x * tmp);
              }).Aggregate((acc, xs) => acc.Zip(xs, (a, b) => a + b)));
              var wOut = Norm(wTmp.ToArray());

              var classifiedTests = Classify(sVecs, testData, kernel, w0);//They filter out things athat are = C
              //var classifiedTests = Classify(sVecs.Where(sv => sv.alpha + threshold < C).ToArray(), testData, kernel, w0);

              var right = 100.0 * (classifiedTests.Count(b => b)) / (classifiedTests.Length);
              Console.Write(".");
              //Console.WriteLine("Correctly classified {0}%", right);
              rightness += right;
              successful += 1.0;
            }
            catch (Exception ex)
            {
              var sb = new StringBuilder();
              for (int i = 0; i < TRIALS; i++)
              {
            for (int j = 0; j < TRIALS-1; j++)
            {
              sb.Append(dump[i, j]);
              sb.Append(",");
            }
            sb.AppendLine(dump[i, TRIALS - 1].ToString());
              }

              sb.AppendLine();
              foreach (var item in classified)
            sb.AppendLine(item.ToString());

              var p = Path.Combine(Environment.GetFolderPath( Environment.SpecialFolder.CommonApplicationData),
            "SV2",
            DateTime.Now.ToString("yyyyMMdd-HHmmss-") + experiment + ".csv");
              Directory.CreateDirectory(Path.GetDirectoryName(p));
              Console.WriteLine(p);
              using (var sr = new StreamWriter(p))
            sr.Write(sb.ToString());
            }
              }

              Console.WriteLine("Total rightness {0}%", rightness/successful);
              Console.ReadKey(true);
        }
 static void CreateSolver() {
     StaticReset();
     solver = new InteriorPointSolver();
     solver.AddRow("goal", out goal);
     solver.AddGoal(goal, 0, true); //minimizing the goal
 }
예제 #9
0
        static void Main(string[] args)
        {
            var wTarget    = (new[] { Rational.Get(6, 10), Rational.Get(8, 10), Rational.Get(-5, 10) });
            var C          = Rational.Get(10, 1);
            var w0Target   = Rational.Get(1, 10);
            var rightness  = 0.0;
            var successful = 0.0;

            Datum[] classified = {};
            var     dump       = new Rational[TRIALS, TRIALS];

            for (int experiment = 0; experiment < EXPERIMENTS; experiment++)
            {
                try
                {
                    classified = Generate(TRIALS, w0Target, wTarget);
                    var testData = Generate(TRIALS * 10, w0Target, wTarget);

                    var solver = new InteriorPointSolver();
                    Func <Rational[], Rational[], Rational> kernel = Dot;
                    int goal;
                    solver.AddRow("dual", out goal);
                    solver.AddGoal(goal, 0, false);//false to maximize

                    //make alphas
                    var alphas = classified.Select((_, i) =>
                    {
                        int tmp;
                        solver.AddVariable("alpha" + i, out tmp);
                        solver.SetBounds(tmp, Rational.Zero, C);
                        return(tmp);
                    }).ToArray();

                    int sumConstraint;
                    solver.AddRow("sumConstraint", out sumConstraint);
                    //solver.SetBounds(sumConstraint, Rational.Zero, Rational.Zero);
                    //TODO: maybe I'm running into numeric issues?
                    solver.SetBounds(sumConstraint, Rational.Get(-1, THRESH), Rational.Get(1, THRESH));

                    for (int i = 0; i < classified.Length; i++)
                    {
                        //sum_n (alpha_n*y_n) == 0
                        solver.SetCoefficient(sumConstraint, alphas[i], classified[i].y);

                        solver.SetCoefficient(goal, alphas[i], 1); //the sum_n (alpha_n) part of the lagrangian

                        ////quadratic terms. sometimes not convex, and I don't know why
                        for (int j = 0; j <= i; j++)
                        {
                            // coef = y_i * y_j * Kernel(x_i, x_j). Note that the diagonal is half: the other terms appear twice and are thus doubled
                            var coef = (i == j ? -0.5 : -1.0) * classified[i].y * classified[j].y * kernel(classified[i].x, classified[j].x);
                            solver.SetCoefficient(goal, coef, alphas[i], alphas[j]);
                            dump[i, j] = coef;
                            dump[j, i] = coef;
                        }
                        //This gives better results, but it isn't actually right. :-(
                        //for (int j = 0; j < classified.Length; j++)
                        //{
                        //  // coef = y_i * y_j * Kernel(x_i, x_j). Note that the diagonal is half: the other terms appear twice and are thus doubled
                        //  var coef = -0.5  * classified[i].y * classified[j].y * kernel(classified[i].x, classified[j].x);
                        //  solver.SetCoefficient(goal, coef, alphas[i], alphas[j]);
                        //}
                    }

                    //now solve
                    solver.Solve(new InteriorPointSolverParams());
                    var alphaVals = Enumerable.Range(0, classified.Length)
                                    .Select(i => solver.GetValue(alphas[i])) //using alphas[i] instead of 1..N here
                                    .ToArray();

                    //Console.WriteLine("goal={0}", solver.GetValue(0));

                    //get the SVs
                    var maxAlpha  = alphaVals.Max();
                    var threshold = maxAlpha / THRESH;
                    var sVecs     = alphaVals.Where(a => a > threshold)
                                    //.Where(a => a + threshold < C) //This may be wrong... yep
                                    .Select((a, i) => new SVInfo {
                        y = classified[i].y, x = classified[i].x, alpha = a
                    })
                                    .ToArray();

                    //w0, aka b
                    var w0s = sVecs.Where(sv => sv.alpha + threshold < C) //this must not be right?
                              .Select(sv1 =>
                                                                          //note that the inner loop gets alphas == C. Not sure if that's right.
                                      sv1.y - sVecs.Select(sv2 => sv2.alpha * sv2.y * kernel(sv1.x, sv2.x)).Aggregate((acc, r) => acc + r)
                                      ).ToArray();

                    var w0 = w0s.OrderBy(lf => lf.ToDouble()).ToArray()[w0s.Length / 2]; //too widely dispersed... median here
                    //so what does w look like here...?
                    List <Rational> wTmp = (new List <Rational> {
                        w0
                    });
                    wTmp.AddRange(
                        sVecs.Select(sv =>
                    {
                        var tmp = sv.alpha * sv.y;
                        return(sv.x.Select(x => x * tmp));
                    }).Aggregate((acc, xs) => acc.Zip(xs, (a, b) => a + b)));
                    var wOut = Norm(wTmp.ToArray());

                    var classifiedTests = Classify(sVecs, testData, kernel, w0);//They filter out things athat are = C
                    //var classifiedTests = Classify(sVecs.Where(sv => sv.alpha + threshold < C).ToArray(), testData, kernel, w0);

                    var right = 100.0 * (classifiedTests.Count(b => b)) / (classifiedTests.Length);
                    Console.Write(".");
                    //Console.WriteLine("Correctly classified {0}%", right);
                    rightness  += right;
                    successful += 1.0;
                }
                catch (Exception ex)
                {
                    var sb = new StringBuilder();
                    for (int i = 0; i < TRIALS; i++)
                    {
                        for (int j = 0; j < TRIALS - 1; j++)
                        {
                            sb.Append(dump[i, j]);
                            sb.Append(",");
                        }
                        sb.AppendLine(dump[i, TRIALS - 1].ToString());
                    }

                    sb.AppendLine();
                    foreach (var item in classified)
                    {
                        sb.AppendLine(item.ToString());
                    }

                    var p = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
                                         "SV2",
                                         DateTime.Now.ToString("yyyyMMdd-HHmmss-") + experiment + ".csv");
                    Directory.CreateDirectory(Path.GetDirectoryName(p));
                    Console.WriteLine(p);
                    using (var sr = new StreamWriter(p))
                        sr.Write(sb.ToString());
                }
            }

            Console.WriteLine("Total rightness {0}%", rightness / successful);
            Console.ReadKey(true);
        }
예제 #10
0
        public bool BuildRiskModel()
        {
            int m = portfolio.NumPositions;

            InteriorPointSolver solver = new InteriorPointSolver();

            int[] allocations = new int[m];
            int counter = 0;

            foreach (Position p in portfolio.Positions.Values)
            {
                solver.AddVariable(p.Symbol, out allocations[counter]);
                solver.SetBounds(allocations[counter], -1, 1);
                counter++;
            }

            int expectedReturn;
            solver.AddRow("expectedReturn", out expectedReturn);

            // expected return must beat the minimum asked

            solver.SetBounds(expectedReturn, minReturn, double.PositiveInfinity);

            int unity;
            solver.AddRow("Investments sum to one", out unity);
            solver.SetBounds(unity, -1, 1);

            // expected return is a weighted linear combination of investments.
            // unity is a simple sum of the investments

            for (int invest = m; 0 <= --invest;)
            {
                solver.SetCoefficient(expectedReturn, allocations[invest], returns[invest]);
                solver.SetCoefficient(unity, allocations[invest], 1);
            }

            // The variance of the result is a quadratic combination of the covariants and allocations.

            int variance;
            solver.AddRow("variance", out variance);
            for (int invest = m; 0 <= --invest;)
            {
                for (int jnvest = m; 0 <= --jnvest;)
                {
                    solver.SetCoefficient(variance, covariance[invest, jnvest], allocations[invest], allocations[jnvest]);
                }
            }

            // the goal is to minimize the variance, given the linear lower bound on asked return.

            solver.AddGoal(variance, 0, true);

            InteriorPointSolverParams lpParams = new InteriorPointSolverParams();

            solver.Solve(lpParams);
            if (solver.Result != LinearResult.Optimal)
                return false;

            for (int i = 0; i < m; i++)
            {
                
            }

            counter = 0;
            foreach (Position p in portfolio.Positions.Values)
            {
                Console.Write(p.Symbol + " " + (double)solver.GetValue(allocations[counter++]) + " ");
                Console.WriteLine();

            }

            Console.WriteLine((double) solver.GetValue(expectedReturn) + " " +
                              Math.Sqrt((double) solver.Statistics.Primal) + "\n");


            return true;
        }
예제 #11
0
        public bool BuildRiskModel()
        {
            int m = portfolio.NumPositions;

            InteriorPointSolver solver = new InteriorPointSolver();

            int[] allocations = new int[m];
            int   counter     = 0;

            foreach (Position p in portfolio.Positions.Values)
            {
                solver.AddVariable(p.Symbol, out allocations[counter]);
                solver.SetBounds(allocations[counter], -1, 1);
                counter++;
            }

            int expectedReturn;

            solver.AddRow("expectedReturn", out expectedReturn);

            // expected return must beat the minimum asked

            solver.SetBounds(expectedReturn, minReturn, double.PositiveInfinity);

            int unity;

            solver.AddRow("Investments sum to one", out unity);
            solver.SetBounds(unity, -1, 1);

            // expected return is a weighted linear combination of investments.
            // unity is a simple sum of the investments

            for (int invest = m; 0 <= --invest;)
            {
                solver.SetCoefficient(expectedReturn, allocations[invest], returns[invest]);
                solver.SetCoefficient(unity, allocations[invest], 1);
            }

            // The variance of the result is a quadratic combination of the covariants and allocations.

            int variance;

            solver.AddRow("variance", out variance);
            for (int invest = m; 0 <= --invest;)
            {
                for (int jnvest = m; 0 <= --jnvest;)
                {
                    solver.SetCoefficient(variance, covariance[invest, jnvest], allocations[invest], allocations[jnvest]);
                }
            }

            // the goal is to minimize the variance, given the linear lower bound on asked return.

            solver.AddGoal(variance, 0, true);

            InteriorPointSolverParams lpParams = new InteriorPointSolverParams();

            solver.Solve(lpParams);
            if (solver.Result != LinearResult.Optimal)
            {
                return(false);
            }

            for (int i = 0; i < m; i++)
            {
            }

            counter = 0;
            foreach (Position p in portfolio.Positions.Values)
            {
                Console.Write(p.Symbol + " " + (double)solver.GetValue(allocations[counter++]) + " ");
                Console.WriteLine();
            }

            Console.WriteLine((double)solver.GetValue(expectedReturn) + " " +
                              Math.Sqrt((double)solver.Statistics.Primal) + "\n");


            return(true);
        }
        public OptimizationResult OptimizePortfolioAllocation(OptimizationData data)
        {
            int assetCount = data.Stocks.Count;

            InteriorPointSolver solver = new InteriorPointSolver();
            int[] allocations = new int[assetCount];

            for (int i = 0; i < assetCount; i++)
            {
                solver.AddVariable(data.Stocks[i].Symbol, out allocations[i]);
                if (data.Stocks[i].Symbol == "SPY")
                    solver.SetBounds(allocations[i], 0, 0);
                else
                    solver.SetBounds(allocations[i], 0, 1);
            }

            int expectedRateOfReturn;
            solver.AddRow("expectedRateOfReturn", out expectedRateOfReturn);
            solver.SetBounds(expectedRateOfReturn, data.MinimumReturn, double.PositiveInfinity);

            int unity;
            solver.AddRow("Investments sum to one", out unity);
            solver.SetBounds(unity, 1, 1);

            for (int i = 0; i < assetCount; i++)
            {
                solver.SetCoefficient(expectedRateOfReturn, allocations[i], data.Stocks[i].MeanReturnRate);
                solver.SetCoefficient(unity, allocations[i], 1);
            }

            int variance;
            solver.AddRow("variance", out variance);
            for (int i = 0; i < assetCount; i++)
            {
                for (int j = 0; j < assetCount; j++)
                {
                    solver.SetCoefficient(variance, data.Stocks[i].Covariances[data.Stocks[j].Symbol], allocations[i], allocations[j]);
                }
            }

            solver.AddGoal(variance, 0, true);

            InteriorPointSolverParams lpParams = new InteriorPointSolverParams();

            solver.Solve(lpParams);

            bool optimal = false;
            bool feasible = false;
            if (solver.Result == LinearResult.Optimal)
            {
                optimal = feasible = true;
            }
            else if (solver.Result == LinearResult.Feasible)
            {
                optimal = false;
                feasible = true;
            }

            List<AssetResult> assetResults = new List<AssetResult>();
            for (int i = 0; i < assetCount; i++)
            {
                assetResults.Add(new AssetResult
                {
                    Symbol = data.Stocks[i].Symbol,
                    Allocation = (double)solver.GetValue(allocations[i])
                });
            }

            OptimizationResult result = new OptimizationResult
            {
                Optimal = optimal,
                Feasible = feasible,
                ExpectedReturn = (double)solver.GetValue(expectedRateOfReturn),
                Results = assetResults
            };

            return result;
        }