如何在 C# 中从 cosmos db 获取准确的最新 5 分钟数据
How to get exact latest 5 minutes data from cosmos db in C#
我有一个网络作业,它 运行 不断地从网络套接字 api 读取数据。
下面是每 1 秒后自动 运行 并将报价数据添加到 cosmos db 中的代码。
private static void OnTick(Tick TickData)
{
var latestTickData = new MyObject()
{
InstrumentID = TickData.InstrumentToken,
Close = TickData.LastPrice,
High = TickData.LastPrice,
Low = TickData.LastPrice,
Open = TickData.LastPrice,
TimeStamp = TickData.Timestamp.HasValue ? TickData.Timestamp.Value : DateTime.Now
};
// add data into cosmos
Task.Run(() =>
{
Program.documentClient.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri("tickerDatabase", "tickerContainer"), latestTickData);
}).Wait();
}
现在我想读取最近 5 分钟的数据并获取开盘价高低收盘价。
对于目前最新的 5 分钟数据,我正在 运行每 5 分钟执行一次计时器作业,从 cosmos 读取数据并计算开盘价高低收盘价,但问题是这里的时间。
如果计时器作业 运行 晚 1 分钟,该数据的寡妇也会更改并得到错误的值。
我的问题是,如何从 cosmos 获取准确的 5 分钟最新数据?
当前计时器作业代码 -
myobject.cs
public class MyObject
{
public uint InstrumentID { get; set; }
public decimal Close { get; set; }
public decimal High { get; set; }
public decimal Low { get; set; }
public decimal Open { get; set; }
public DateTime TimeStamp { get; set; }
public uint Volume { get; set; }
public DateTime GetStartOfPeriodByMins(int numMinutes)
{
int oldMinutes = TimeStamp.Minute;
int newMinutes = (oldMinutes / numMinutes) * numMinutes;
DateTime startOfPeriod = new DateTime(TimeStamp.Year, TimeStamp.Month, TimeStamp.Day, TimeStamp.Hour, newMinutes, 0);
return startOfPeriod;
}
}
myfunction.cs
public static void ExecuteProcess([TimerTrigger("0 */5 * * * *")] TimerInfo myTimer)
{
var option = new FeedOptions { EnableCrossPartitionQuery = true };
var queryable = Program.documentClient.CreateDocumentQuery<MyObject>
(UriFactory.CreateDocumentCollectionUri("tickerDatabase", "tickerContainer"), option).ToList();
var resultSet = queryable.GroupBy(i => i.GetStartOfPeriodByMins(5))
.Select(gr =>
new
{
StartOfPeriod = gr.Key,
Low = gr.Min(item => item.Low),
High = gr.Max(item => item.High),
Open = gr.OrderBy(item => item.TimeStamp).First().Open,
Close = gr.OrderBy(item => item.TimeStamp).Last().Close
});
var my5min = resultSet.LastOrDefault();
Console.WriteLine("time " + my5min.StartOfPeriod + " open " + my5min.Open + " high " + my5min.High + " low " + my5min.Low + " close " + my5min.Close);
让我解释一下 1 分钟数据的问题(虽然我原本想要 5 分钟)。
下面是示例记录 -
List<MyObject> test = new List<MyObject>();
test.Add(new MyObject() { Open = 2939, High = 2939, Low = 2939, Close = 2939, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 01) });
test.Add(new MyObject() { Open = 2933, High = 2933, Low = 2933, Close = 2933, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 01) });
test.Add(new MyObject() { Open = 2936, High = 2936, Low = 2936, Close = 2936, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 03) });
test.Add(new MyObject() { Open = 2944, High = 2944, Low = 2944, Close = 2944, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 05) });
test.Add(new MyObject() { Open = 2944, High = 2944, Low = 2944, Close = 2944, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 08) });
test.Add(new MyObject() { Open = 2939, High = 2939, Low = 2939, Close = 2939, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 10) });
test.Add(new MyObject() { Open = 2939, High = 2939, Low = 2939, Close = 2939, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 15) });
test.Add(new MyObject() { Open = 2932, High = 2932, Low = 2932, Close = 2932, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 25) });
test.Add(new MyObject() { Open = 2939, High = 2939, Low = 2939, Close = 2939, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 26) });
test.Add(new MyObject() { Open = 2939, High = 2939, Low = 2939, Close = 2939, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 28) });
test.Add(new MyObject() { Open = 2932, High = 2932, Low = 2932, Close = 2932, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 30) });
test.Add(new MyObject() { Open = 2941, High = 2941, Low = 2941, Close = 2941, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 32) });
test.Add(new MyObject() { Open = 2939, High = 2939, Low = 2939, Close = 2939, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 35) });
test.Add(new MyObject() { Open = 2941, High = 2941, Low = 2941, Close = 2941, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 40) });
test.Add(new MyObject() { Open = 2937, High = 2937, Low = 2937, Close = 2937, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 42) });
test.Add(new MyObject() { Open = 2939, High = 2939, Low = 2939, Close = 2939, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 45) });
test.Add(new MyObject() { Open = 2937, High = 2937, Low = 2937, Close = 2937, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 48) });
test.Add(new MyObject() { Open = 2939, High = 2939, Low = 2939, Close = 2939, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 50) });
test.Add(new MyObject() { Open = 2939, High = 2939, Low = 2939, Close = 2939, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 52) });
test.Add(new MyObject() { Open = 2937, High = 2937, Low = 2937, Close = 2937, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 54) });
test.Add(new MyObject() { Open = 2935, High = 2935, Low = 2935, Close = 2935, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 56) });
test.Add(new MyObject() { Open = 2935, High = 2935, Low = 2935, Close = 2935, TimeStamp = new DateTime(2020, 10, 15, 10, 02, 12) });
将 1 分钟作为参数传递给 GetStartOfPeriodByMins(1) -
这里的记录是时间10:1:56和10:2:12。
现在你可以观察到最后我们将得到 2 条记录集到 resultSet
如果定时器触发功能在特定时间没有运行,它只会对10:2:12的最后一条记录取平均值,这是不对的
所以问题是如何匹配运行ning时间?
5 分钟后也会发生同样的情况。
以上示例数据输出1分钟
所以我们需要确保数据从那个时间段开始就应该是完整的。
一种可能的解决方案是利用 _ts
属性 文档。您可以直接使用 start date
和 end date
进行 5 分钟跨度的 SQL 查询。除此之外,您可以在数据库中将 start date
保存为 last used end date
(可忽略的成本)。查询应如下所示:
SELECT * FROM c where c._ts <= 1601890740 AND c._ts >= 1601890585
另请注意,您将不得不进行一些 POSIX 到 DateTime 的来回转换。
我有一个网络作业,它 运行 不断地从网络套接字 api 读取数据。
下面是每 1 秒后自动 运行 并将报价数据添加到 cosmos db 中的代码。
private static void OnTick(Tick TickData)
{
var latestTickData = new MyObject()
{
InstrumentID = TickData.InstrumentToken,
Close = TickData.LastPrice,
High = TickData.LastPrice,
Low = TickData.LastPrice,
Open = TickData.LastPrice,
TimeStamp = TickData.Timestamp.HasValue ? TickData.Timestamp.Value : DateTime.Now
};
// add data into cosmos
Task.Run(() =>
{
Program.documentClient.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri("tickerDatabase", "tickerContainer"), latestTickData);
}).Wait();
}
现在我想读取最近 5 分钟的数据并获取开盘价高低收盘价。
对于目前最新的 5 分钟数据,我正在 运行每 5 分钟执行一次计时器作业,从 cosmos 读取数据并计算开盘价高低收盘价,但问题是这里的时间。
如果计时器作业 运行 晚 1 分钟,该数据的寡妇也会更改并得到错误的值。
我的问题是,如何从 cosmos 获取准确的 5 分钟最新数据?
当前计时器作业代码 -
myobject.cs
public class MyObject
{
public uint InstrumentID { get; set; }
public decimal Close { get; set; }
public decimal High { get; set; }
public decimal Low { get; set; }
public decimal Open { get; set; }
public DateTime TimeStamp { get; set; }
public uint Volume { get; set; }
public DateTime GetStartOfPeriodByMins(int numMinutes)
{
int oldMinutes = TimeStamp.Minute;
int newMinutes = (oldMinutes / numMinutes) * numMinutes;
DateTime startOfPeriod = new DateTime(TimeStamp.Year, TimeStamp.Month, TimeStamp.Day, TimeStamp.Hour, newMinutes, 0);
return startOfPeriod;
}
}
myfunction.cs
public static void ExecuteProcess([TimerTrigger("0 */5 * * * *")] TimerInfo myTimer)
{
var option = new FeedOptions { EnableCrossPartitionQuery = true };
var queryable = Program.documentClient.CreateDocumentQuery<MyObject>
(UriFactory.CreateDocumentCollectionUri("tickerDatabase", "tickerContainer"), option).ToList();
var resultSet = queryable.GroupBy(i => i.GetStartOfPeriodByMins(5))
.Select(gr =>
new
{
StartOfPeriod = gr.Key,
Low = gr.Min(item => item.Low),
High = gr.Max(item => item.High),
Open = gr.OrderBy(item => item.TimeStamp).First().Open,
Close = gr.OrderBy(item => item.TimeStamp).Last().Close
});
var my5min = resultSet.LastOrDefault();
Console.WriteLine("time " + my5min.StartOfPeriod + " open " + my5min.Open + " high " + my5min.High + " low " + my5min.Low + " close " + my5min.Close);
让我解释一下 1 分钟数据的问题(虽然我原本想要 5 分钟)。
下面是示例记录 -
List<MyObject> test = new List<MyObject>();
test.Add(new MyObject() { Open = 2939, High = 2939, Low = 2939, Close = 2939, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 01) });
test.Add(new MyObject() { Open = 2933, High = 2933, Low = 2933, Close = 2933, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 01) });
test.Add(new MyObject() { Open = 2936, High = 2936, Low = 2936, Close = 2936, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 03) });
test.Add(new MyObject() { Open = 2944, High = 2944, Low = 2944, Close = 2944, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 05) });
test.Add(new MyObject() { Open = 2944, High = 2944, Low = 2944, Close = 2944, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 08) });
test.Add(new MyObject() { Open = 2939, High = 2939, Low = 2939, Close = 2939, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 10) });
test.Add(new MyObject() { Open = 2939, High = 2939, Low = 2939, Close = 2939, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 15) });
test.Add(new MyObject() { Open = 2932, High = 2932, Low = 2932, Close = 2932, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 25) });
test.Add(new MyObject() { Open = 2939, High = 2939, Low = 2939, Close = 2939, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 26) });
test.Add(new MyObject() { Open = 2939, High = 2939, Low = 2939, Close = 2939, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 28) });
test.Add(new MyObject() { Open = 2932, High = 2932, Low = 2932, Close = 2932, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 30) });
test.Add(new MyObject() { Open = 2941, High = 2941, Low = 2941, Close = 2941, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 32) });
test.Add(new MyObject() { Open = 2939, High = 2939, Low = 2939, Close = 2939, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 35) });
test.Add(new MyObject() { Open = 2941, High = 2941, Low = 2941, Close = 2941, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 40) });
test.Add(new MyObject() { Open = 2937, High = 2937, Low = 2937, Close = 2937, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 42) });
test.Add(new MyObject() { Open = 2939, High = 2939, Low = 2939, Close = 2939, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 45) });
test.Add(new MyObject() { Open = 2937, High = 2937, Low = 2937, Close = 2937, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 48) });
test.Add(new MyObject() { Open = 2939, High = 2939, Low = 2939, Close = 2939, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 50) });
test.Add(new MyObject() { Open = 2939, High = 2939, Low = 2939, Close = 2939, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 52) });
test.Add(new MyObject() { Open = 2937, High = 2937, Low = 2937, Close = 2937, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 54) });
test.Add(new MyObject() { Open = 2935, High = 2935, Low = 2935, Close = 2935, TimeStamp = new DateTime(2020, 10, 15, 10, 01, 56) });
test.Add(new MyObject() { Open = 2935, High = 2935, Low = 2935, Close = 2935, TimeStamp = new DateTime(2020, 10, 15, 10, 02, 12) });
将 1 分钟作为参数传递给 GetStartOfPeriodByMins(1) -
这里的记录是时间10:1:56和10:2:12。
现在你可以观察到最后我们将得到 2 条记录集到 resultSet
如果定时器触发功能在特定时间没有运行,它只会对10:2:12的最后一条记录取平均值,这是不对的
所以问题是如何匹配运行ning时间?
5 分钟后也会发生同样的情况。
以上示例数据输出1分钟
所以我们需要确保数据从那个时间段开始就应该是完整的。
一种可能的解决方案是利用 _ts
属性 文档。您可以直接使用 start date
和 end date
进行 5 分钟跨度的 SQL 查询。除此之外,您可以在数据库中将 start date
保存为 last used end date
(可忽略的成本)。查询应如下所示:
SELECT * FROM c where c._ts <= 1601890740 AND c._ts >= 1601890585
另请注意,您将不得不进行一些 POSIX 到 DateTime 的来回转换。