我有一些我试图使用linq排序的股票数据,但是我对linq非常不熟悉并且无法理解文档。
现在我有一个酒吧列表(我创建了一个保存库存数据的类别),它是自1990年以来每天的所有库存数据。现在我正在尝试按年和月分组此库存数据,以便我可以将每日库存数据转换为月库存数据(库存的分辨率)。
public class Stock
{
private string stockSymboll;
private string period;
private List<bar> aBar = new List<bar>();
private DateTime startingDate;
private DateTime endingDate;
enum period { DAILY, WEEKLY, MONTHLY };
private period PeriodType;
}
public class bar
{
private double open;
private double high;
private double low;
private double close;
private double volume;
private DateTime stockDate;
}
在库存类中我有一个功能,我尝试使用它从每日数据列表转换为月度数据列表,按照从最近到最近的顺序。
以下是我的尝试:
stock convertPeriod(Period pt)
{
stock newStock = new Stock(stockName, startingDate, endingDate, period);
if (pt == Periode.MONTHLY)
{
List<bar> monthlyGroup = new List<bar>();
var group1 = (from b in bar group c by b.getDate().Month);
var group2 = from g in group1 group g by g.getDate().Year)
return...;
}
}
但是我发现你无法对var进行排序。所以我认为最好的方法是在linq中尝试嵌套查询,但是我甚至无法获得基本查询。任何帮助将不胜感激。
答案 0 :(得分:1)
我不是100%明确你应该建立什么合同,但据我所知,你想:
IEnumerable<Bar>
和IEnumerable<Bar>
,以便StockDate
是汇总库存的年份和月份的第一天,所有按日期降序排列。据我了解,Stock
或多或少与您的真实问题无关。如果我不对,请告诉我,我可以帮助你更进一步。
你在LINQ中有一个良好的开端。我对你在类型中使用private
字段感到有些困惑,但我假设这些字段是拼写错误,而你的实际代码使用public
,可能是属性。
我会做几个单独的方法,只是为了让我做得更清楚,但为了表现,你可能想把它们放在一起。特别是因为我有效地订购了两次。
此方法根据其年月份设置的第一天对数据进行分组和排序。请注意,您实际上可以对匿名类型执行分组。正如您所指出的,排序对匿名对象本身不起作用,但它确实对其属性起作用。
public IEnumerable<IEnumerable<Bar>> GroupIntoMonths(IEnumerable<Bar> bars)
{
return bars.GroupBy(c => new { c.StockDate.Year, c.StockDate.Month })
.OrderByDescending(c => c.Key.Year)
.ThenByDescending(c => c.Key.Month);
}
无论您是希望将日期设置为1的实例化DateTime
对象分组,还是我在此处所做的事情,您都可以选择。我没有再触摸Key
属性,因此在我离开方法后,我很好地失去了对它的追踪。其他实现可能会促使您做出不同的决定。
一旦你有了这个,就可以将IEnumerable<Bar>
转换成一个Bar
来概括整个时期。
public IEnumerable<Bar> GroupIntoBars(IEnumerable<IGrouping<DateTime, Bar>> groups)
{
return groups.Select(GetBar);
}
public Bar GetBar(IEnumerable<Bar> bars)
{
Bar ret = new Bar();
Bar last = null;
int index = -1;
foreach(var v in bars.OrderBy(c => c.StartingDate))
{
index++;
if(index == 0)
{
ret.Open = v.Open;
ret.StockDate = v.StockDate;
ret.High = v.High;
ret.Low = v.Low;
}
else
{
ret.High = Math.Max(ret.High, v.High);
ret.Low= Math.Max(ret.Low, v.Low);
}
last = v;
}
if(last == null) throw new ArgumentException("Collection cannot be empty!");
ret.Close = last.Close;
return ret;
}
我认为这种方法很简单,但如果我能清除任何内容,请告诉我。
答案 1 :(得分:0)
您可以通过在将成为组密钥的匿名对象中指定多个属性来一次分组多个属性:
var monthlyGroup = aBar.OrderBy(bar => bar.stockDate)
.GroupBy(bar => new { Year = bar.stockDate.Year, Month = bar.stockDate.Month })
//create bars from groups
.Select(g => new bar()
{
open = g.First().open,
high = g.Max(b => b.high),
low = g.Min(b => b.low),
close = g.Last().close,
volume = g.Average(b => b.volume),
stockDate = new DateTime(g.Key.Year, g.Key.Month, 1)
})
.ToList();
很抱歉,我更喜欢linq的函数语法。
我注意到bar
课程中的字段为private
,因此无法访问。但是,我假设您拥有每个字段的属性。
在这种情况下,您必须使用上面代码中的属性名称替换字段名称。