当前位置: 首页 > news >正文

基于MT5的K线处理逻辑

一、数据存储结构

ConcurrentDictionary<string, LockSortedDictionary<DateTime, PriceInfo>>

该结构主要用来存储如下k线周期数据:

  • M1 (1分钟), M5, M15, M30
  • H1 (1小时), H2, H4
  • D1 (日线), W1 (周线), MN1 (月线)

比如:

lstM1 = {// 第一层:品种维度["EURUSD"] => {// 第二层:时间维度(自动排序)[2025-10-24 09:00:00] => PriceInfo {OpenPrice: 1.0850,HighPrice: 1.0855,LowPrice: 1.0848,ClosePrice: 1.0852,Volume: 125000,PriceTime: 2025-10-24 09:00:00},[2025-10-24 09:01:00] => PriceInfo {OpenPrice: 1.0852,HighPrice: 1.0858,LowPrice: 1.0850,ClosePrice: 1.0856,Volume: 98000,PriceTime: 2025-10-24 09:01:00},[2025-10-24 09:02:00] => PriceInfo { ... },...},["GBPUSD"] => {[2025-10-24 09:00:00] => PriceInfo {OpenPrice: 1.2650,HighPrice: 1.2658,LowPrice: 1.2648,ClosePrice: 1.2655,Volume: 156000,PriceTime: 2025-10-24 09:00:00},[2025-10-24 09:01:00] => PriceInfo { ... },...}
}
lstM5 = {["EURUSD"] => {// 注意:5分钟K线的时间是5的倍数[2025-10-24 09:00:00] => PriceInfo {OpenPrice: 1.0850,  // 来自09:00的M1HighPrice: 1.0860,  // 09:00-09:04这5根M1的最高价LowPrice: 1.0845,   // 09:00-09:04这5根M1的最低价ClosePrice: 1.0858, // 来自09:04的M1Volume: 550000,     // 5根M1的Volume累加PriceTime: 2025-10-24 09:00:00},[2025-10-24 09:05:00] => PriceInfo {OpenPrice: 1.0858,HighPrice: 1.0865,LowPrice: 1.0855,ClosePrice: 1.0863,Volume: 480000,PriceTime: 2025-10-24 09:05:00},...}
}

二、 历史数据加载:RefreshChartData

1、根据配置文件获取需要品种加载行情的开始时间和加载月份数
<add key="ChartStartTime" value="2025-08-12 00:00" />  // 从2025-08-12 00:00开始
<add key="ChartFetchMonthEachTime" value="1" />  // 加载一个月的数据
DateTime endSynTime = DateTime.Now.Add(-serverTimeSpan);
auto startTime = symbolFefreshTimes[symbolName];
DateTime endTime = startTime.AddMonths(fetchMonthEachTime) > endSynTime ? endSynTime : startTime.AddMonths(fetchMonthEachTime);
// 如果加载月份超过当前时间,则以当前时间为结束时间
RequestChartData(symbolName, startTime, endTime);
最后调用MT5的ChartRequest接口
2、数据存储在内存中,最多保留6000条 (AddPriceInfo)

由于从MT5获取的是1分钟k线数据,所以转化成5分钟k线、小时k线、日k、周k还涉及到k线数据合并与归档逻辑
处理过程分为 三个阶段:

(1)按周期对时间戳对齐 (ConvertPeriodTime)
获取对应周期的历史数据表
auto symbolPriceList = GetSymbolPriceList(symbolName, period);

GetSymbolPriceList 会:

  • 按周期选择对应的全局字典(lstM1, lstH1, lstD1 等)

  • 从该字典中取出某个 symbol 对应的价格表(类型为 LockSortedDictionary<DateTime, PriceInfo>)

  • 如果不存在则创建一个新的空表

每个 symbol、每个周期都有独立的时间序列表,例如:

lstH1[“EURUSD”] => LockSortedDictionary<时间, PriceInfo>

把任意时刻的数据对齐到周期的开始时间
DateTime keyTime = ConvertPeriodTime(priceKV.Key, period);
原始时间周期对齐后时间
2025-10-24 14:07M52025-10-24 14:05
2025-10-24 14:59H12025-10-24 14:00
2025-10-24 14:00D12025-10-24 00:00
2025-10-24 14:00W12025-10-19 (周日)
2025-10-24 14:00MN12025-10-01

保证数据被合并进同一K线的时间桶中。

实现逻辑:

  • 如果是分钟线:1分钟/5分钟/15分钟/30分钟
    计算公式:
    当前时间 - (当前时间的分钟数%k线周期)
    time.AddMinutes(-time.Minute % (int)period);
    比如5分钟:
当前时间time.Minute偏移量 (minute % 5)结果时间(周期起点)
10:033310:00
10:077210:05
10:1919410:15
10:3030010:30
  • 如果是小时线:
    计算公式:
    当前时间 - (当前时间的分钟数 + 当前时间的小时数 * 60 % k线周期)
    time.AddMinutes(-(time.Minute + time.Hour * 60 % (int)period));

    解释:把“当前小时 + 当前分钟”都换算成“从当天 00:00 起的总分钟数”。
    举例(H4 = 240分钟):
时间从0点起的分钟数偏移量(%240)当前属于哪个周期
01:157575第1段 (00:00–03:59)
03:59239239仍在第1段
04:002400第2段开始 (04:00–07:59)
06:10370130第2段 (04:00–07:59)
08:014811第3段 (08:00–11:59)
  • 如果是周线:
    计算公式:
    当前时间 - 当前是周几 (周日为0,周一为1…) 即对齐到周日
    time.Date.AddDays(-(int)time.DayOfWeek);

如果是月线:
计算公式:
当前时间 - 当前是本月的第几天+1,即对齐到本月第一天
time.Date.AddDays(-time.Day + 1);

周期类型逻辑示例输入输出(对齐后的起始时间)
W1(周线)回退到本周星期日零点2025-10-27(周一)2025-10-26(周日)
MN1(月线)回退到本月第一天零点2025-10-272025-10-01
(2)合并新旧K线数据(更新OHLCV)

对每个 priceKV(时间+PriceInfo):
✅ 如果当前时间桶已有K线:

if (symbolPriceList.TryGetValue(keyTime, out PriceInfo priceInfo))
{priceInfo.ClosePrice = priceKV.Value.ClosePrice;if (priceInfo.LowPrice > priceKV.Value.LowPrice){priceInfo.LowPrice = priceKV.Value.LowPrice;}else if (priceInfo.HighPrice < priceKV.Value.HighPrice){priceInfo.HighPrice = priceKV.Value.HighPrice;}priceInfo.Volume += priceKV.Value.Volume == 0 ? CreateRandomNumber() : priceKV.Value.Volume;
}

则执行 更新逻辑:

  • ClosePrice 始终更新为最新
  • LowPrice 更新为较低者
  • HighPrice 更新为较高者
  • Volume 累加,若为0则生成随机数(CreateRandomNumber())
  • 不改变Open

❌ 如果不存在该时间桶:

else
{auto addPrice = new PriceInfo(){ClosePrice = priceKV.Value.ClosePrice,OpenPrice = priceKV.Value.OpenPrice,HighPrice = priceKV.Value.HighPrice,LowPrice = priceKV.Value.LowPrice,PriceTime = keyTime,Volume = priceKV.Value.Volume == 0 ? CreateRandomNumber() : priceKV.Value.Volume};symbolPriceList.Add(keyTime, addPrice);
}

就新建一根K线(通常代表新的周期开始)。

(3)清理过期缓存数据(数量控制)
if (symbolPriceList.Count > maxCachedPriceInfoCount + 30)
  • 超过缓存阈值则清理多余K线(FIFO原则)
  • Take(symbolPriceList.Count - maxCachedPriceInfoCount) 取出最旧的部分删除

这样可避免内存无限增长。

3、支持持久化到文件(ChartLoadModel模式)

需要导出行情的时候使用

if (isChartLoadModel)
{StoreChart(symbolName);base.ClearChart(symbolName);
}

功能:

  • 将指定品种(symbolName)的所有周期的K线缓存,打包保存成一个文件。

存储结构:

  • 每个品种对应一个独立文件;
  • 文件内容是多个周期(M1、M5、H1、D1…)的K线数据集合;
  • 每个周期包含 PriceInfo 列表。
    在这里插入图片描述

三、实时报价处理:AddRealtimeQuote

1、从MT5的行情回调函数OnTick接收到行情数据

public override void OnTick(string symbol, MTTickShort tick)
{
....Quote quote = tick.ToQuote(symbol, manager);
.....
}

2、对价格进行检查(10%、900%波动警告)
3、将TICK数据聚合成1分钟K线
4、更新当前分钟的OHLC(开高低收)
5、通过定时任务PushQuoteToChart 每10秒将实时数据推送到主K线存储

四、k线数据查询:GetChartList

1、LatestCount > 0 的情况下,如果EndTime == default(DateTime), 查询最近LatestCount条记录;否则查询EndTime之前的最新LatestCount条记录。
2、LatestCount <= 0的情况下,查询StartTime,EndTime范围内的记录。
3、每次返回记录不超过1500条。

// 参数校验逻辑
if (!symbolNameList.Contains(request.Symbol))
{log.ErrorFormat("RequestChart Wrong symbol, rquest: {0}", JsonConvert.SerializeObject(request));return null;
}
if (!Enum.IsDefined(typeof(ChartPeriods), request.Period))
{log.ErrorFormat("RequestChart Wrong period, rquest: {0}", JsonConvert.SerializeObject(request));return null;
}
if (request.LatestCount > MAX_RECORD_SIZE)
{log.ErrorFormat("RequestChart Wrong LatestCount, rquest: {0}", JsonConvert.SerializeObject(request));return null;
}
if (request.LatestCount == 0 &&(request.StartTime == default(DateTime) || request.EndTime == default(DateTime) || // 时间之一为空request.StartTime > request.EndTime // 开始时间大于截止时间))
{log.ErrorFormat("RequestChart Wrong, rquest: {0}", JsonConvert.SerializeObject(request));request.LatestCount = MAX_RECORD_SIZE;
}

五、K线数据修复:AddReplaceChart

1、清除指定时间范围的K线数据

ClearChart(request.Symbol, request.StartTime, request.EndTime);

2、自动调整为按月批量重新拉取

while (lastDataTime < request.EndTime)
{lastDataTime = request.StartTime.AddMonths(fetchMonthEachTime) > request.EndTime ? request.EndTime : request.StartTime.AddMonths(fetchMonthEachTime);auto chartData = RequestChartData(request.Symbol, request.StartTime, request.EndTime);...
}

3、从MT5重新拉取该时间段数据

manager.ChartRequest(symbol, from, to, out code);

4、转换为有序字典并去重(价格时间为 key)

5、修复后触发数据完整性检查

六、K线完整性检查:CheckChart

1、遍历所有K线周期(除W1和MN1)
2、根据品种的交易时段检查缺失的K线
3、记录缺失时间段

{List<DateTime> missChart = new List<DateTime>();auto priceTimeList = chartService.GetAllPriceTimeList(symbol.Name, period).Where(x => x > chartCheckStartTime);         if (priceTimeList.Count() == 0){return missChart;}DateTime startTime = priceTimeList.First();DateTime endTime = priceTimeList.Last();DateTime checkTime = GetNextPeriodTime(startTime, period);while (checkTime < endTime){bool isQuoteTime = true;if (!priceTimeList.Contains(checkTime)){isQuoteTime = IsQuoteTime(checkTime, symbol, period);if (isQuoteTime){missChart.Add(checkTime);}}checkTime = isQuoteTime ? GetNextPeriodTime(checkTime, period) : GetNextQuoteTimeForPeriod(checkTime, symbol, period);}return missChart;
}

4、提供导出 ExportMissChart和查询接口GetMissSymbolList
5、每天自动检查一次

七、定时任务

1、存储线程: 每60分钟持久化K线到文件 (StoreChart)
2、推送线程: 每10秒将实时报价聚合到K线存储 (PushQuoteToChart)
3、修复线程: 每10秒处理K线修复请求 (ReplaceChart)

http://www.dtcms.com/a/537317.html

相关文章:

  • 河南郑州网站建设哪家公司好免费wordpress主题下载地址
  • 低空经济网络安全的政策体系构建
  • 网页设计网站规划深圳设计网站公司哪家好
  • 【Etcd 】Etcd 详解以及安装教程
  • 文交所网站建设方案饰品企业网站建设
  • 郑州网站建设市场陕西省建设工程信息网官网
  • 中国电商网站排行榜绍兴百度推广优化排名
  • 网站 用php asp源码 比较好建设部执业考试网站
  • 宜家有做自己的网站吗眼镜厂官网
  • JAVA1027抽象类;抽象类继承
  • AD22更新网表时总是显示 net with name XXX In already exists
  • 推荐一个免费的IP地址库:纯真社区版IP库
  • 4.前缀和
  • 网站开发技术 北京国内网站建设排名
  • 南通网站建设兼职中国沈阳app在哪里下载
  • MinIo纯前端使用文件上传预览
  • 学习记录-package.json的scripts添加参数的方式有那些
  • 【前端】avue组件分页勾选
  • 个人网站主页设计模板台州建网站
  • 修改网站主目录的位置云闪付当前页面设计隐私
  • 计算机图形学:【Games101】学习笔记02——变换(二维与三维、模型、视图、投影)
  • 解码固相萃取仪:如何实现复杂样品前处理的高效与重现性
  • Easyx图形库应用(直接显存操作)
  • 网站翻书效果网站建设费用 会计分录
  • Langchain从零开始到应用落地案例[AI智能助手]【4】---优化ocr识别编写,实现按文件类型进行调用识别
  • 如何添加网站logo天津网站定制公司
  • 做网站需要规划哪些内容南宁网站seo大概多少钱
  • 第15天:网络基础与故障排除
  • confluence or 语雀 or sward,知识管理工具一文全方位对比
  • 易语言中函数参数“参考”的基本概念