规划
9 P. f9 {# O- D9 ?, b x许多人可能认为规划很容易,特别是,由于它事关转换柱线,即始终把 1-分钟柱线转换为票据(我们稍后会解释)。 然而,模拟比表面看要复杂得多。 主要问题在于我们尚未清晰地理解票据的实际行为,就去创建 1-分钟柱线。 我们只有柱线,和一些它的有关信息,但我们并不知道柱线是如何形成的。 我们将采用 1-分钟柱线,因为它们所需的难度最小。 如果您能创造一个与真实事物非常相似的复杂走位,那么您就能够再现一些非常接近真实事物的东西。% S, F/ ^0 e7 T
这个细节似乎并不那么重要,因为我们通常会在市场上看到锯齿形的走位。 无论走位如何复杂,这一切都归结为在 OHCL 点之间创建锯齿形。 它始自柱线的开盘那一刻,且需不少于 9 个走位来创造这个内部的锯齿形。 它总是在柱线收盘时结束,并在下一根柱线上重复该过程。 MetaTrader 5 策略测试器采用相同的逻辑。 更多详细信息,请参阅真实与生成的基差:算法交易。 我们将从这个策略开始。 虽然对于我们的目的来说并不理想,但它将为开拓更多适合的方式提供一个起点。
; p' R q1 z) ^. U, o5 \就我而言,测试策略器不太适合回放/模拟系统,因为在策略测试器中,时间问题并不是最重要的。 这就是,没必要以这种方式创建和复现 1-分钟柱线,因为其长度本来就是 1-分钟。 事实上,它甚至更简略,在于它与现实时间并不对应。 如果是这样的话,那么测试策略就变得不可能。 想象一下,依据跨越几天甚至几年的柱运行测试,如果每根柱线复现时间与实际不同。 这将是一项不可能完成的任务。 然而,对于回放/模拟系统,我们正在寻找一种不同的行动。 我们希望按照 1-分钟间隔创建一根 1-分钟柱线,尽可能接近这个目标。
' p* K; ^* P5 {; t% l准备奠基/ }6 G* ]0 z: C) W
我们的重点将完全放在回放/模拟服务代码上。 此时无需担心其它方面。 故此,我们将开始修改 C_Replay 类的代码,尝试尽可能地优化我们已经开发完成并测试过的东西。 下面是类中出现的第一个过程:
; l% K) w }" [* [1 q; T+ I# _/ ]. uinline bool CheckFileIsBar(int &file, const string szFileName)
P1 p) U9 U2 g& e% L7 [# `{5 o2 E7 `9 x" E; Z/ u
string szInfo = "";
" ^8 X' [" h/ Z) y r% X7 ybool bRet;3 b5 J# i$ \& O; ]
for (int c0 = 0; (c0 < 9) && (!FileIsEnding(file)); c0++) szInfo += FileReadString(file);
" R1 X% U9 W& P; S- e# ]if ((bRet = (szInfo == def_Header_Bar)) == false) @( P# ]3 C: y/ y2 r
{3 L) g5 e: R' V: X0 `. A
Print("File ", szFileName, ".csv is not a file with bars.");- k! O0 o( m. q9 H. E" ~) W
FileClose(file);
& i. D; m6 X, U0 Q}
3 K. ~1 q7 Z# c8 B& Sreturn bRet;
' z- g4 ?9 D. m- P1 u! } B}
, s0 J. m) t) k5 [$ ^此处的目标是柱线读取函数,这些测试是为了判定指定文件是否为预览柱线文件。 这是必要的,当使用相同代码来判定柱线文件是否符合我们所需时,如此可避免重复代码。 在此状况下,这些柱线将不会用作预览柱线。 它们将被转换为模拟票据,以便在交易系统中使用。 有基于此,我们引入了另一个函数:
" h8 U) k% p5 e5 tinline void FileReadBars(int &file, MqlRates &rate[])/ M# j1 N0 j% @& {
{
/ c$ K1 \5 p9 n6 C& O: s- x$ u6 R/ Rrate[0].time = StringToTime(FileReadString(file) + " " + FileReadString(file));
4 E6 u7 |% Y# J. C6 Y4 Irate[0].open = StringToDouble(FileReadString(file));
2 U9 t7 P& t# ]' w7 _rate[0].high = StringToDouble(FileReadString(file));
" |. R3 m C* u Z ^( m2 c8 D& Prate[0].low = StringToDouble(FileReadString(file));
5 Q) h- G2 ~+ }' Xrate[0].close = StringToDouble(FileReadString(file));
) e& l- W. U9 A+ Z& Zrate[0].tick_volume = StringToInteger(FileReadString(file));0 Z% o3 t$ u: Y6 L
rate[0].real_volume = StringToInteger(FileReadString(file));8 v1 e5 D6 g6 z4 T
rate[0].spread = (int) StringToInteger(FileReadString(file));
' E/ g& ?0 R3 u- ^8 z+ d5 p}5 Y8 I- E3 P; u
它将从指定文件里逐行读取已有柱线的数据。 我想您在理解这段代码时不会遇到任何困难。 继续这个准备阶段,此处是另一个函数:
/ G" w/ e8 ]9 h% V) s) yinline bool OpenFileBars(int &file, const string szFileName): T8 n5 t( }$ t3 u/ P: e
{
: s% W. `2 K1 z6 _, ~' y ?' qif ((file = FileOpen("Market Replay\\Bars\\" + szFileName + ".csv", FILE_CSV | FILE_READ | FILE_ANSI)) != INVALID_HANDLE)
# f' v1 `8 p. p2 f% f) l5 ]% v{* m* o- C1 B7 {. t1 G
if (!CheckFileIsBar(file, szFileName))
) v* T8 E0 I. J! ~return false;
) ~* _: [, P+ l3 |- {5 W7 xreturn true;
* {& s2 F2 k% P7 n1 ~) B: f}9 Q- `( b% `6 _+ x8 M
Print("Falha ao acessar ", szFileName, ".csv de barras.");
2 I5 B9 T x( X1 l/ D c7 ?" Vreturn false;
! M- M" m8 L& a( S4 d$ u}6 x7 [) C7 F* ]" p* n* J0 y7 l0 v
我们的系统现在已经完全集中化,能提供对柱线的标准访问:我们即可把它们用作预览柱线时,亦可把它们用于模拟,并转换为票据表示。 因此,之前加载预览柱线的函数也必须修改,如下所示:+ h: Q# R3 j. d; C$ A! g- a& _
bool LoadPrevBars(const string szFileNameCSV)* _" D( U. l3 z$ v/ T+ D E6 E
{
- t! g; Q+ D* [+ X; \4 H. eint file,9 }% N) d g' C
iAdjust = 0;
$ \! F# |" R, D7 k7 h- H/ edatetime dt = 0;' F2 Q. h# q- E$ A3 v# {. B: \
MqlRates Rate[1];
, ^; h# h+ z3 i0 r8 x0 lif (OpenFileBars(file, szFileNameCSV))7 S: a# I$ S- u4 l. Y! ~7 l6 H
{
( k, r. O! T. l5 d$ E" p* r1 `Print("Loading preview bars for Replay. Please wait....");
5 g" D! I4 ~' b0 _9 ] P# j8 xwhile ((!FileIsEnding(file)) && (!_StopFlag))
; {; t0 m2 x- @! _+ D. x# D{
! c5 { c" b | EFileReadBars(file, Rate);) R, ~: ~6 R' W" D k
iAdjust = ((dt != 0) && (iAdjust == 0) ? (int)(Rate[0].time - dt) : iAdjust);
. W! b/ z( ?% tdt = (dt == 0 ? Rate[0].time : dt);
5 B3 `# w3 t! b; g$ {2 NCustomRatesUpdate(def_SymbolReplay, Rate, 1);2 u' B9 B. |5 K- O
}
$ d7 t. j j1 n* G1 x2 i7 e) s- bm_dtPrevLoading = Rate[0].time + iAdjust;
. N2 X) r* b. _" n! C fFileClose(file);' \! E4 q+ _, Q' o. n( ~( k3 C
return (!_StopFlag);
H+ U$ \% u' b( R}
& A3 d0 R- R& [5 K! }* @! fm_dtPrevLoading = 0;- x1 A6 b: K( C, w% n1 Q9 y
return false;
5 N7 I6 {' O# d% s9 a3 u2 k}! \5 z, d9 t: }3 n" o
这个下载函数的工作方式并无变化,尽管现在有更多的调用。 从以前的函数中提取一部分,并在新位置加以运用,为我们提供了更高的安全性,因为所有代码都已经过测试。 以这种方式,我们只需要担心新函数。 现在地基已经准备就绪,我们需要在配置文件中添加新内容呢。 该函数旨在判定哪些柱线文件可用于模拟票据。 为此,我们需要添加一个新定义:
, c/ m1 S7 H0 L! w/ R' o#define def_STR_FilesBar "[BARS]"
' G p" H& N G) ^$ Y* W( Z#define def_STR_FilesTicks "[TICKS]"
, B6 c% r2 ] d: k- i: B#define def_STR_TicksToBars "[TICKS->BARS]"" k# P+ P9 ]# @# P0 l; r6 `
#define def_STR_BarsToTicks "[BARS->TICKS]"$ r, p7 \9 }; J! A! E7 L# P
这样我们就可以运行一个简单的测试,这正是我们开始进行模拟所需要的。 |