首页 > 解决方案 > 四个循环使用两个范围来找到最高 PNL

问题描述

我试图让这段代码运行得更快,因为它有数十亿种组合。我需要查看四个循环,并根据这些参数找到最高的利润。字典可能有 500 条记录,我通常使用 excel 来查找表现最好的设置的模式,几分钟后我最终得到了大约 100 个条目。你们认为哪种方法最适合我,或者您有什么建议?

using System;
using System.Collections.Concurrent;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Windows.Forms;

namespace WindowsFormsApp1 {
    public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        public class Stock {
            public double PNL { get; set; }
            public double OD { get; set; }
            public double DR { get; set; }
            public double Volume { get; set; }
            public double Liquidity { get; set; }

            public Stock(double iPNL, double iOD, double iDR, double iVolume, double iLiquidity) {
                PNL = iPNL;
                OD = iOD;
                DR = iDR;
                Volume = iVolume;
                Liquidity = iLiquidity;
            }
        }

        private ConcurrentDictionary<string, Stock> StockData = new();

        private void button1_Click(object sender, EventArgs e) {

            StockData["A"] = new Stock(-109, 60, 0.92, 32, 5.99);
            StockData["B"] = new Stock(41, 25, 0.96, 37, 6.75);
            StockData["C"] = new Stock(41, 62, 1.12, 79, 8.4);


            long iLoops = 0;
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            double iODMax = 999;
            double iDRMax = 1500;
            double iVolMax = 999;
            double iLiqMax = 2000;
            double PNL = 0;

            for (int iODMin = 0; iODMin <= iODMax; iODMin += 6) {

                for (double iDRMin = 1; iDRMin <= iDRMax; iDRMin += 6) {

                    for (int iVolMin = 0; iVolMin <= iVolMax; iVolMin += 6) {

                        for (double iLiqMin = 1; iLiqMin <= iLiqMax; iLiqMin += 6) {

                            iLoops += 1;

                            var aaaa = StockData.Where(n => n.Value.OD >= iODMin && n.Value.OD <= iODMax &&
                                                    n.Value.DR >= iDRMin && n.Value.DR <= (iDRMax / 100) &&
                                                    n.Value.Liquidity >= iLiqMin && n.Value.Liquidity <= (iLiqMax / 100) &&
                                                    n.Value.Volume >= iVolMin && n.Value.Volume <= iVolMax).FirstOrDefault();
                            if (aaaa.Value != null) {
                                PNL += aaaa.Value.PNL;
                            }

                        }
                    }
                }
            }

            stopwatch.Stop();

            Debug.Print(stopwatch.Elapsed.ToString() + " | " + iLoops.ToString("#,#") + " | " + PNL.ToString("#,#"));
        }
    }
}

标签: c#

解决方案


这是您可以在逻辑中实现并行性的一种方法,它可以为您提供更好的性能。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    public class Stock
    {
        public double PNL { get; set; }
        public double OD { get; set; }
        public double DR { get; set; }
        public double Volume { get; set; }
        public double Liquidity { get; set; }

        public Stock(double iPNL, double iOD, double iDR, double iVolume, double iLiquidity)
        {
            PNL = iPNL;
            OD = iOD;
            DR = iDR;
            Volume = iVolume;
            Liquidity = iLiquidity;
        }
    }
    private ConcurrentDictionary<string, Stock> StockData = new ConcurrentDictionary<string, Stock>();
    public IEnumerable<int> SteppedIntegerList(int startIndex,
        double endEndex, int stepSize)
    {
        for (int i = startIndex; i < endEndex; i += stepSize)
        {
            yield return i;
        }
    }
    object lockObject = new object();
    private void button1_Click(object sender, EventArgs e)
    {
        StockData["A"] = new Stock(-109, 60, 0.92, 32, 5.99);
        StockData["B"] = new Stock(41, 25, 0.96, 37, 6.75);
        StockData["C"] = new Stock(41, 62, 1.12, 79, 8.4);


        long iLoops = 0;
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();
        double iODMax = 499;
        double iDRMax = 1000;
        double iVolMax = 499;
        double iLiqMax = 1500;
        double PNL = 0;


        Parallel.ForEach(SteppedIntegerList(0, iODMax, 6), iODMin =>
        {
            Parallel.ForEach(SteppedIntegerList(1, iDRMax, 6), iDRMin =>
            {
                Parallel.ForEach(SteppedIntegerList(0, iVolMax, 6), iVolMin =>
                {
                    Parallel.ForEach(SteppedIntegerList(1, iLiqMax, 6), iLiqMin =>
                    {
                        Interlocked.Increment(ref iLoops);
                        var aaaa = StockData.Where(n => n.Value.OD >= iODMin && n.Value.OD <= iODMax &&
                                                n.Value.DR >= iDRMin && n.Value.DR <= (iDRMax / 100) &&
                                                n.Value.Liquidity >= iLiqMin && n.Value.Liquidity <= (iLiqMax / 100) &&
                                                n.Value.Volume >= iVolMin && n.Value.Volume <= iVolMax).FirstOrDefault();
                        if (aaaa.Value != null)
                        {
                            lock (lockObject)
                            {
                                PNL += aaaa.Value.PNL;
                            }
                        }
                    });
                });
            });
        });
        stopwatch.Stop();
        Debug.Print(stopwatch.Elapsed.ToString() + " | " + iLoops.ToString("#,#") + " | " + PNL.ToString("#,#"));
    }
}

//输出分析

我已经减少了下面提到的样本数据来测试我自己的目的

double iODMax = 499;
double iDRMax = 1000;
double iVolMax = 499;
double iLiqMax = 1500;

有了上面的数据出来是这样的:

// 你给出的逻辑:00:03:44.7069770 | 294,588,000 | 12,628

// 具有并行性的新逻辑:00:01:13.9397930 | 294,588,000 | 12,628

我需要引入对象锁定,因为这些是所有线程之间的共享资源,因此您可以相应地扩展您的逻辑。

有关Parallel.For的更多信息


推荐阅读