屏幕坐标与时间/价格的相互转换
由于图表工作区域的测量原理不同,因此需要在不同的测量单位之间进行重新计算。为此提供了两个函数。
bool ChartTimePriceToXY(long chartId, int window, datetime time, double price, int &x, int &y)
bool ChartXYToTimePrice(long chartId, int x, int y, int &window, datetime &time, double &price)
ChartTimePriceToXY函数将图表坐标从时间/价格表示(time/price)转换为以像素为单位的 X 和 Y 坐标(x/y)。ChartXYToTimePrice函数执行相反的操作:将 X 和 Y 坐标转换为时间和价格值。
这两个函数都需要在第一个参数chartId中指定图表 ID。此外,在 ChartTimePriceToXY中还需传入window 子窗口编号(该编号应在窗口数量范围内)。如果存在多个子窗口,每个子窗口都有各自的时间序列和沿垂直轴的刻度(根据参数条件,price参数称为"price")。
在 ChartXYToTimePrice函数中,window 参数是输出参数。该函数会将此参数与 time、price 一同填充。这是因为像素坐标是整个屏幕通用的,其原点 x/y可能位于任意子窗口中。

时间、价格与屏幕坐标
如果成功完成,函数返回 true。
请注意,在两种坐标系中,对应报价或屏幕坐标的可见矩形区域均有范围限制。因此,在特定初始数据下,可能会出现所获取的时间、价格或像素超出可见区域的情况。特别是,也可能获得负值。我们将在 图表事件一章中了解交互式重新计算示例。
在上一节中,我们了解了如何确定 MQL 程序的启动位置。尽管在物理上只有一个最终放置点,但其在报价坐标和屏幕坐标中的表示通常存在计算误差。两个新函数用于像素和价格/时间的双向转换,它们将帮助我们验证这一点。
修改后的脚本名为ChartXY.mq5。它大致可以分为 3 个阶段。在第一阶段,我们将像之前一样导出放置点的坐标。
void OnStart()
|
在第二阶段,我们尝试在时间 (t2) 和价格 (p2) 期间转换屏幕坐标 x1 和 y1,并将其与从上述 OnDropped 函数中获得的坐标进行比较。
int w2;
|
然后我们进行逆变换:使用获得的报价坐标 t1和 p1 来计算屏幕坐标 x2 和 y2,并将其与原始值 x1 和 y1 进行比较。
int x2, y2;
|
正如我们将在后续示例日志中看到的,上述所有检查都将失败(值之间会存在细微差异)。所以,我们需要第三步。
我们重新计算变量名中带有后缀 2 的屏幕坐标和报价坐标,并将它们保存到带有新后缀 3 的变量中。然后,我们将第一阶段和第三阶段的所有值相互进行比较。
int w3;
|
让我们在 XAUUSD, H1 图表上运行该脚本。以下是原始点数据。
ChartWindowOnDropped()=0 / ok
|
将像素转换为报价将得出以下结果。
ChartXYToTimePrice(0,x1,y1,w2,t2,p2)=true / ok
|
在时间和价格上均存在差异。反向计算在精度方面也并非完美。
ChartTimePriceToXY(0,w1,t1,p1,x2,y2)=true / ok
|
精度损失是由于坐标轴上的值按照测量单位(尤其是像素和点)进行量化所导致的。
最后,最后一步证明上述误差并非函数本身的问题,因为循环重新计算会得到原始结果。
ChartXYToTimePrice(0,x2,y2,w3,t3,p3)=true / ok
|
在伪代码中,这可以通过以下等式表示:
ChartTimePriceToXY(ChartXYToTimePrice(XY)) = XY
|
将 ChartTimePriceToXY函数应用于ChartXYToTimePrice 的计算结果时,将得到原始坐标。反向转换同样如此:将 ChartXYToTimePrice函数应用于 ChartTimePriceToXY 的计算结果时,将完全匹配。
因此,如果对使用重新计算函数的算法有更高的精度要求,应谨慎考量这些算法的实现方式。
另一个使用 ChartWindowOnDropped的示例将在脚本 ChartIndicatorMove.mq5 中提供(见 管理图表上的指标章节)。