规划
) o8 Z1 s; x' W9 m' y许多人可能认为规划很容易,特别是,由于它事关转换柱线,即始终把 1-分钟柱线转换为票据(我们稍后会解释)。 然而,模拟比表面看要复杂得多。 主要问题在于我们尚未清晰地理解票据的实际行为,就去创建 1-分钟柱线。 我们只有柱线,和一些它的有关信息,但我们并不知道柱线是如何形成的。 我们将采用 1-分钟柱线,因为它们所需的难度最小。 如果您能创造一个与真实事物非常相似的复杂走位,那么您就能够再现一些非常接近真实事物的东西。
! @4 B1 x0 c' ^5 e0 [这个细节似乎并不那么重要,因为我们通常会在市场上看到锯齿形的走位。 无论走位如何复杂,这一切都归结为在 OHCL 点之间创建锯齿形。 它始自柱线的开盘那一刻,且需不少于 9 个走位来创造这个内部的锯齿形。 它总是在柱线收盘时结束,并在下一根柱线上重复该过程。 MetaTrader 5 策略测试器采用相同的逻辑。 更多详细信息,请参阅真实与生成的基差:算法交易。 我们将从这个策略开始。 虽然对于我们的目的来说并不理想,但它将为开拓更多适合的方式提供一个起点。
1 F: W& T {% Z# @5 f t9 @ D% Z9 r就我而言,测试策略器不太适合回放/模拟系统,因为在策略测试器中,时间问题并不是最重要的。 这就是,没必要以这种方式创建和复现 1-分钟柱线,因为其长度本来就是 1-分钟。 事实上,它甚至更简略,在于它与现实时间并不对应。 如果是这样的话,那么测试策略就变得不可能。 想象一下,依据跨越几天甚至几年的柱运行测试,如果每根柱线复现时间与实际不同。 这将是一项不可能完成的任务。 然而,对于回放/模拟系统,我们正在寻找一种不同的行动。 我们希望按照 1-分钟间隔创建一根 1-分钟柱线,尽可能接近这个目标。. Q3 w) C2 [- ~7 @6 s5 |0 Q
准备奠基3 A1 S# u% z8 J* ~
我们的重点将完全放在回放/模拟服务代码上。 此时无需担心其它方面。 故此,我们将开始修改 C_Replay 类的代码,尝试尽可能地优化我们已经开发完成并测试过的东西。 下面是类中出现的第一个过程:9 ?3 s9 ]* \* l/ a6 w( b, u
inline bool CheckFileIsBar(int &file, const string szFileName)6 x' M, B: `. O1 A$ N: R1 P& E
{9 G& P q3 n$ v; r$ {# }/ r1 J
string szInfo = "";
. o* Z- p: b3 F' |" k4 R) ^bool bRet;7 G/ X+ ~1 V) _& e. `
for (int c0 = 0; (c0 < 9) && (!FileIsEnding(file)); c0++) szInfo += FileReadString(file);
" D- A) \ o$ }- n7 @3 u! Gif ((bRet = (szInfo == def_Header_Bar)) == false)! ~- Y Z1 E T/ z
{* p* K4 I7 P$ v6 i/ G- A
Print("File ", szFileName, ".csv is not a file with bars.");
3 o) |9 s6 M# L# b! T: oFileClose(file);4 X; J/ i: p- F
}$ G6 ?2 e3 k+ @& W" m
return bRet;$ p9 `1 y2 j9 `- a) e
}& G: q8 \' U; ^3 W1 S' h" T; t/ }
此处的目标是柱线读取函数,这些测试是为了判定指定文件是否为预览柱线文件。 这是必要的,当使用相同代码来判定柱线文件是否符合我们所需时,如此可避免重复代码。 在此状况下,这些柱线将不会用作预览柱线。 它们将被转换为模拟票据,以便在交易系统中使用。 有基于此,我们引入了另一个函数:
: h3 d/ H2 \) C3 m! Tinline void FileReadBars(int &file, MqlRates &rate[])
8 h5 U- I5 ~2 Y! O- l" Y* L0 W2 Y{
( d4 H5 j% [& Xrate[0].time = StringToTime(FileReadString(file) + " " + FileReadString(file));
m8 y. R* D7 g7 Nrate[0].open = StringToDouble(FileReadString(file));
8 x: Y: b+ h9 x; l; O# prate[0].high = StringToDouble(FileReadString(file));
4 Z _: [4 h/ vrate[0].low = StringToDouble(FileReadString(file));: Y9 ]0 O/ \3 E
rate[0].close = StringToDouble(FileReadString(file));
0 { H/ E0 X9 @; S' [rate[0].tick_volume = StringToInteger(FileReadString(file)); x" Y$ @1 ^7 B# H
rate[0].real_volume = StringToInteger(FileReadString(file));3 G) [1 {/ H5 U* p @! N* v* J) R9 N
rate[0].spread = (int) StringToInteger(FileReadString(file));" o# J5 A: o/ V% S8 n5 x! q4 y) C/ F- x1 U
}4 B* d0 h! A5 k9 R
它将从指定文件里逐行读取已有柱线的数据。 我想您在理解这段代码时不会遇到任何困难。 继续这个准备阶段,此处是另一个函数:0 P* g6 ]# g+ ~+ K% [6 I
inline bool OpenFileBars(int &file, const string szFileName)+ B$ T1 J: j+ H' a2 r& R) B) e
{* |! Y) q) x- w) f) U& ?, n
if ((file = FileOpen("Market Replay\\Bars\\" + szFileName + ".csv", FILE_CSV | FILE_READ | FILE_ANSI)) != INVALID_HANDLE)$ A, ]# {6 U/ H. ~
{
' \+ V r; x# K d/ z- d7 bif (!CheckFileIsBar(file, szFileName))1 Q6 [) A2 U# s& Q1 L' n, \6 i
return false;* l, l, Q2 c, X+ r3 S7 D
return true;0 @) u/ P' Q, H
}, r+ |) r' _" a b9 t# w
Print("Falha ao acessar ", szFileName, ".csv de barras.");
) z* j# |& g6 o, z6 l5 Lreturn false;3 z, h* Q$ x' U4 Z: c* l$ S* V( i
}
* Q8 H) S$ ?3 i; c- ]- S5 M我们的系统现在已经完全集中化,能提供对柱线的标准访问:我们即可把它们用作预览柱线时,亦可把它们用于模拟,并转换为票据表示。 因此,之前加载预览柱线的函数也必须修改,如下所示:
( Y5 h# c% i0 B( a! Sbool LoadPrevBars(const string szFileNameCSV)
5 u; [( ] z8 v& R0 m) ?% F{
1 F) }9 Y% i4 qint file,9 @) ?/ S" x/ ^+ B" L7 T( a7 s& ~2 Y
iAdjust = 0;6 {. |/ t9 Q1 P e' ~2 p# J7 N. X
datetime dt = 0;
% L% s$ }, Z4 x4 T; ^1 j/ }7 UMqlRates Rate[1];
% @) {1 z# K D8 Z: j& Cif (OpenFileBars(file, szFileNameCSV))+ ^& u7 e; f4 a- T5 l" l. r
{) o8 x5 X" m; i
Print("Loading preview bars for Replay. Please wait....");
# p# v7 {9 G$ O5 s. dwhile ((!FileIsEnding(file)) && (!_StopFlag)) t% u* V* q7 r
{
% h8 M; X7 H; N; _FileReadBars(file, Rate);
9 R9 J: k6 z' ^iAdjust = ((dt != 0) && (iAdjust == 0) ? (int)(Rate[0].time - dt) : iAdjust);, U4 J" e8 O/ Z3 a. Q
dt = (dt == 0 ? Rate[0].time : dt);# T+ [" |# m" N2 C, i
CustomRatesUpdate(def_SymbolReplay, Rate, 1);' R4 i" U/ S& o# o) i" ~
}7 \/ b& [& d$ C3 h0 w# Y
m_dtPrevLoading = Rate[0].time + iAdjust;# A, m: V+ }* d) o% O. g/ U! W
FileClose(file);
! ?4 c {6 L0 x( oreturn (!_StopFlag);; a( F+ P" f p& l( s
} n$ X# q6 M; c* h8 N/ ~7 b# |
m_dtPrevLoading = 0;
3 Z O- @7 \" e, Yreturn false;+ F0 q& n# d- x L* u. `$ ~+ ~
}
8 n, O3 i3 ]. ?6 c6 V) I ?. @这个下载函数的工作方式并无变化,尽管现在有更多的调用。 从以前的函数中提取一部分,并在新位置加以运用,为我们提供了更高的安全性,因为所有代码都已经过测试。 以这种方式,我们只需要担心新函数。 现在地基已经准备就绪,我们需要在配置文件中添加新内容呢。 该函数旨在判定哪些柱线文件可用于模拟票据。 为此,我们需要添加一个新定义:+ N! O4 `' @5 U- ?8 w; {1 j
#define def_STR_FilesBar "[BARS]"' V& t: R3 `+ R; m: R( {
#define def_STR_FilesTicks "[TICKS]"! |7 q$ R6 }( L$ E
#define def_STR_TicksToBars "[TICKS->BARS]"& R9 a6 a5 _9 ~" D, D5 M( h
#define def_STR_BarsToTicks "[BARS->TICKS]"; S% |$ u9 j0 H3 V: X, `
这样我们就可以运行一个简单的测试,这正是我们开始进行模拟所需要的。 |