我有一个小程序,我用于算法股票交易。代码必须在我的8核桌面计算机上循环大约192万亿次。我想租用一台64核机器来运行它,但这不符合成本效益。
只是这段代码。但是for循环必须在每个要计算的条上循环(大约180万),然后循环检查匹配的列表大约是800k项。
我现在唯一可以想到加速它的方法是删除匹配的项目,因为它只发生一次(DateTime)。
还有其他人有办法加快这段代码的速度吗?这需要我的桌面野兽大约45个小时来完成程序的一次迭代。
基本上我正在做的是计算每个条形图,寻找当前条形DateTime是否与我手工创建的CSV文件中的DateTime相匹配。然后从列表对象中,我抓住交易方向并设置一个布尔位置。
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using PowerLanguage.Function;
using ATCenterProxy.interop;
using System.IO;
using System.IO.Compression;
namespace PowerLanguage.Strategy
{
public class Ecal_v1 : SignalObject
{
public List<Trades> tradeList = new List<Trades>();
public List<string> csvList = new List<string>();
public bool exitOn24 = false;
public string ecalPath = @"C:\Users\Skynet\OneDrive\Trading\Economic Calendars\backtest1.csv";
PowerLanguage.Indicator.Bollinger_Bands bb;
public Ecal_v1(object _ctx):base(_ctx){}
//[Input]
//public bool exitOn24 { get; set; }
[Input]
public double bbTopOffset { get; set; }
775
[Input]
public double bbBotOffset { get; set; }
[Input]
public double longTPMod { get; set; }
[Input]
public double shortTPMod { get; set; }
[Input]
public double longSLMod { get; set; }
[Input]
public double shortSLMod { get; set; }
//[Input]
//public double buyTrail { get; set; }
//[Input]
//public double sellTrail { get; set; }
double bbUpperDiff;
double bbLowerDiff;
double bbBasis;
double longTP;
double shortTP;
double longSL;
double shortSL;
double ptValue;
public DateTime tradeTime;
private IOrderMarket longEntry, shortEntry, longExit, shortExit;
protected override void Create()
{
// create variable objects, function objects, order objects etc.
bb = ((PowerLanguage.Indicator.Bollinger_Bands)AddIndicator("Bollinger_Bands"));
longEntry = OrderCreator.MarketNextBar(new SOrderParameters(Contracts.Default, EOrderAction.Buy));
shortEntry = OrderCreator.MarketNextBar(new SOrderParameters(Contracts.Default, EOrderAction.SellShort));
longExit = OrderCreator.MarketNextBar(new SOrderParameters(Contracts.Default, EOrderAction.Sell));
shortExit = OrderCreator.MarketNextBar(new SOrderParameters(Contracts.Default, EOrderAction.BuyToCover));
}
protected override void StartCalc()
{
// assign inputs
GetEcal();
ptValue = Bars.Point;
longTP = longTPMod;
longSL = longSLMod;
shortTP = shortTPMod;
shortSL = shortSLMod;
}
protected override void CalcBar()
{
bool LE = false;
bool SE = false;
bool LX = false;
bool SX = false;
for(int i=0; i<tradeList.Count; i++)
{
if(Bars.Time[0] == tradeList.ElementAt(i).time)
{
if (tradeList.ElementAt(i).direction == "Up")
{
LE = true;
tradeList.RemoveAt(i);
}
else if (tradeList.ElementAt(i).direction == "Down")
{
SE = true;
tradeList.RemoveAt(i);
}
else
{
}
}
}
if(exitOn24 == true)
{
if (Bars.Time[0] > tradeTime.AddHours(24))
{
LX = true;
SX = true;
}
}
if (StrategyInfo.MarketPosition == 0)
{
if (LE)
{
longEntry.Send();
tradeTime = Bars.Time[0];
setLongStops();
}
else if (SE)
{
shortEntry.Send();
tradeTime = Bars.Time[0];
setShortStops();
}
}
else if (StrategyInfo.MarketPosition > 0)
{
if (LX)
{
longExit.Send();
}
else if (LE)
{
longEntry.Send();
tradeTime = Bars.Time[0];
setLongStops();
}
else
{
CurSpecOrdersMode = ESpecOrdersMode.PerPosition;
GenerateStopLossPt(longSL);
GenerateProfitTargetPt(longTP);
//GenerateTrailingStopPt(buyTrail);
}
}
else if (StrategyInfo.MarketPosition < 0)
{
if (SX)
{
shortExit.Send();
}
else if (SE)
{
shortEntry.Send();
tradeTime = Bars.Time[0];
setShortStops();
}
else
{
CurSpecOrdersMode = ESpecOrdersMode.PerPosition;
GenerateStopLossPt(shortSL);
GenerateProfitTargetPt(shortTP);
//GenerateTrailingStopPt(sellTrail);
}
}
}
private void GetEcal()
{
csvList = File.ReadAllLines(ecalPath).Skip(1).ToList();
foreach(string line in csvList)
{
string[] values = line.Split(',');
tradeList.Add(new Trades { time = Convert.ToDateTime(values[0]), direction = values[1] });
}
}
}
public class Trades
{
public DateTime time { get; set; }
public string direction { get; set; }
}
}
减速的罪魁祸首是CalcBar()方法中的For循环。
答案 0 :(得分:7)
您是否尝试过分析此方法?我们的信息太少了。例如,最昂贵的操作可能是
Bars.Time[0] == tradeList.ElementAt(i).time
我们不知道。你应该先把它描出来。
下一步是什么......
tradeList.ElementAt(i).direction == "Up"
不要使用字符串。字符串很慢。你可以在这里使用enums,它将优化为整数和整数,比字符串快得多。
不要使用ElementAt方法。只使用[]运算符。它更快。
考虑使用Dictionary而不是List。它比列表快得多。列表必须通过每个元素来找到你需要的东西。字典不是。这可能是非常关键的部分。
考虑使用整数而不是dateTimes。将整数视为秒。它会比DateTime快。
并使用Parallel.ForEach而不是普通的。然后它将使用其他核心。普通的可能仅使用一个核心。
哦,还有一件事。如果它是股票申请,也许你可以尝试使用神经网络?但那是一个完全不同的故事。
答案 1 :(得分:6)
RemoveAt将处理列表的其余部分,以便在每个项目之后移动每个项目 你删除了一个地方。 see here。 在您的情况下,这会产生巨大的成本。
解决方案是使用临时列表添加元素
将在以后删除,退出循环(sourceList.Except(removedList)
);或者只是标记你的物品被移除
并且从不接触来源列表。
您正在将内容中的所有行加载到内存中,只是为了读取它们并在每行中创建一个强类型对象。
您可以逐行阅读文件,并创建对象。
ElementAt可能比索引器慢。由于您使用的是列表,只需使用[]访问项目以避免疑惑。
要使用更少的内存并进行更快的比较,请将direction
设置为带有“向上”“向下”值的枚举。
如果不并行化代码,则不会利用许多核心。
一旦你得到正确的thigs,如果程序仍然需要几个小时,你可以尝试Parallel.For
而不是。在这种情况下,“将项目标记为已删除的解决方案”比使用并发列表更简单,更合适,并将其与要删除的项目一起提供。
答案 2 :(得分:2)
对于大型列表,哈希集通常是实现更好性能的好方法。这里有更多信息:
或者为什么不使用字典并使用DateTime作为您的密钥(如果您不需要存储任何更多细节,您可以使用任何其他类型作为虚拟值)
然后,您根据密钥进行有效匹配,因此您可以点击或遗漏。
答案 3 :(得分:-1)
我的建议是通过分解来优化您的流程。首先,你检查一下:
Bars.Time[0] == tradeList.ElementAt(i).time;
Tom利用它将其添加到LINQ语句中,以便仅使用以下方法过滤满足条件的那些:
tradeList.Where(t => t.time == Bars.Time[0]);
现在你有另一组if条件来控制你是否删除了这个项目:
tradeList.ElementAt(i).direction == "Down" || tradeList.ElementAt(i).direction == "Up";
使用LINQ进一步简化这些:
tradeList.RemoveAll(d => d.direction == "Down" || d => d.Direction == "Up");
现在,您可以在使用Tom的技术过滤后调用RemoveAll方法:
tradeList.Where(t => t.time == Bars.Time[0])
.RemoveAll(d => d.direction == "Up" || d => d.direction == "Down");
此语句用于所有意图和目的与您的相同,它使用foreach循环遍历列表。但现在我们可以使用PLINQ对其进行优化。好吧,所以与PLINQ一起向右移动,你会改变这句话:
tradeList.AsParallel().tradeList.Where(t => t.time != Bars.Time[0]
&& (d => d.direction != "Up" || d => d.direction != "Down"));
我将RemoveAll()中的逻辑与Where()方法结合起来,这个语句应该给出一个不应该被删除的所有条形的列表。现在我不确定你拥有的bool旗帜(LE和SE)的目的是什么,但是在第一次击中后它们会变为真,所以有更好的方法可以做到这一点。但这应该让你在某个地方开始。