规划3 m: v: k3 `* @: n7 h$ N; g6 R3 T+ a
许多人可能认为规划很容易,特别是,由于它事关转换柱线,即始终把 1-分钟柱线转换为票据(我们稍后会解释)。 然而,模拟比表面看要复杂得多。 主要问题在于我们尚未清晰地理解票据的实际行为,就去创建 1-分钟柱线。 我们只有柱线,和一些它的有关信息,但我们并不知道柱线是如何形成的。 我们将采用 1-分钟柱线,因为它们所需的难度最小。 如果您能创造一个与真实事物非常相似的复杂走位,那么您就能够再现一些非常接近真实事物的东西。) ?# H6 L7 B+ N
这个细节似乎并不那么重要,因为我们通常会在市场上看到锯齿形的走位。 无论走位如何复杂,这一切都归结为在 OHCL 点之间创建锯齿形。 它始自柱线的开盘那一刻,且需不少于 9 个走位来创造这个内部的锯齿形。 它总是在柱线收盘时结束,并在下一根柱线上重复该过程。 MetaTrader 5 策略测试器采用相同的逻辑。 更多详细信息,请参阅真实与生成的基差:算法交易。 我们将从这个策略开始。 虽然对于我们的目的来说并不理想,但它将为开拓更多适合的方式提供一个起点。
; v, f& g D8 k! o9 ?5 @7 w. c就我而言,测试策略器不太适合回放/模拟系统,因为在策略测试器中,时间问题并不是最重要的。 这就是,没必要以这种方式创建和复现 1-分钟柱线,因为其长度本来就是 1-分钟。 事实上,它甚至更简略,在于它与现实时间并不对应。 如果是这样的话,那么测试策略就变得不可能。 想象一下,依据跨越几天甚至几年的柱运行测试,如果每根柱线复现时间与实际不同。 这将是一项不可能完成的任务。 然而,对于回放/模拟系统,我们正在寻找一种不同的行动。 我们希望按照 1-分钟间隔创建一根 1-分钟柱线,尽可能接近这个目标。# \1 f3 r# m$ p# J. C- U6 t
准备奠基
1 ~3 |$ y v8 X( Z4 ]8 Y我们的重点将完全放在回放/模拟服务代码上。 此时无需担心其它方面。 故此,我们将开始修改 C_Replay 类的代码,尝试尽可能地优化我们已经开发完成并测试过的东西。 下面是类中出现的第一个过程:' N, O) V4 W* k2 g6 u
inline bool CheckFileIsBar(int &file, const string szFileName)3 C) L W: h8 H* A% f
{
5 N4 h. r8 h% V: v+ Rstring szInfo = "";6 j: N' y# ~2 ?. T# A3 \! @7 |
bool bRet;
/ B9 C; G" m6 a yfor (int c0 = 0; (c0 < 9) && (!FileIsEnding(file)); c0++) szInfo += FileReadString(file);
& `) u% |+ }! K; |6 `! Uif ((bRet = (szInfo == def_Header_Bar)) == false)9 t, t5 W3 M m! Y9 S% J
{4 F2 Q2 I$ r! G6 J
Print("File ", szFileName, ".csv is not a file with bars.");: F0 L; G. L6 m/ Z! J2 C2 b
FileClose(file);' C. y) N% D/ |0 v
}
/ W* C2 [8 {- f! F8 f" }return bRet;7 b# f" S8 m4 O1 G) h
}+ b- s" Z2 h1 F0 A: _
此处的目标是柱线读取函数,这些测试是为了判定指定文件是否为预览柱线文件。 这是必要的,当使用相同代码来判定柱线文件是否符合我们所需时,如此可避免重复代码。 在此状况下,这些柱线将不会用作预览柱线。 它们将被转换为模拟票据,以便在交易系统中使用。 有基于此,我们引入了另一个函数:
, @6 k: [5 k; N9 e( l' P' Ginline void FileReadBars(int &file, MqlRates &rate[])
+ {9 ]5 d! I1 V1 P j2 Y{
$ K4 A9 X) _) V$ `rate[0].time = StringToTime(FileReadString(file) + " " + FileReadString(file));
3 t, I2 t: j$ ~( k; e# Zrate[0].open = StringToDouble(FileReadString(file));
: y* ^1 y& g, {* s$ arate[0].high = StringToDouble(FileReadString(file));7 I1 J! ^9 F5 s9 p: J
rate[0].low = StringToDouble(FileReadString(file));' x+ F" d! @% _1 z
rate[0].close = StringToDouble(FileReadString(file));
7 r; g) m# d4 X; s0 a; V6 y. \rate[0].tick_volume = StringToInteger(FileReadString(file));
W3 V$ X; Q/ I5 i6 ?. `4 I# Crate[0].real_volume = StringToInteger(FileReadString(file));
! q0 B2 b# L' a6 ?' mrate[0].spread = (int) StringToInteger(FileReadString(file));& c# f0 V. q/ F$ r. o. [6 A; f
}
8 ]6 n0 G4 {5 O* o# p L. f2 S它将从指定文件里逐行读取已有柱线的数据。 我想您在理解这段代码时不会遇到任何困难。 继续这个准备阶段,此处是另一个函数:+ h) P0 Y; d5 |
inline bool OpenFileBars(int &file, const string szFileName)& t5 c, F2 d- \! Y- w8 u# Q
{
/ ~( }6 d7 ?1 i& j% A, iif ((file = FileOpen("Market Replay\\Bars\\" + szFileName + ".csv", FILE_CSV | FILE_READ | FILE_ANSI)) != INVALID_HANDLE). W% {7 f% v" h$ J& h2 l: F
{, Z7 P" b3 g3 o& q" Y* [
if (!CheckFileIsBar(file, szFileName))0 Z# ? }* g# t# Q, c, u
return false;
0 M5 L) n' g$ j6 \! m4 y& Jreturn true;
1 ^3 g5 ?! H8 W9 X. E}
: v0 G% h$ r! U8 q. vPrint("Falha ao acessar ", szFileName, ".csv de barras.");
/ ^8 |+ O4 F4 w$ x2 W: N: n5 _0 c* Ireturn false;8 d) H. R$ ]; h8 \3 I9 e( ^& P
}+ d& ^% I6 { S$ M! C
我们的系统现在已经完全集中化,能提供对柱线的标准访问:我们即可把它们用作预览柱线时,亦可把它们用于模拟,并转换为票据表示。 因此,之前加载预览柱线的函数也必须修改,如下所示:
0 Q& J& s5 q$ C8 `+ Zbool LoadPrevBars(const string szFileNameCSV)
, X. v8 M) N3 @% ^{
1 [: C& x/ }& C% H0 s' \int file,
" W. S. _; n- ^2 tiAdjust = 0;# D5 |2 t+ |4 ~' g3 o
datetime dt = 0;
2 x0 N* C5 g# w- j e* V# JMqlRates Rate[1];
7 {" d8 A$ `* W, P8 K2 A Jif (OpenFileBars(file, szFileNameCSV)) }* m' V, ~1 ?! z5 o5 p P
{
* K( Z" e4 W, p( J; `" e; DPrint("Loading preview bars for Replay. Please wait....");
5 x8 r2 C% B5 W' E1 ^% ~* l9 Hwhile ((!FileIsEnding(file)) && (!_StopFlag))
3 j Y: d+ D; J, S7 a6 V{
" R) E8 ^; A2 e6 [FileReadBars(file, Rate);- t' o' `3 U% v, t, z( q; K0 A9 B0 H
iAdjust = ((dt != 0) && (iAdjust == 0) ? (int)(Rate[0].time - dt) : iAdjust);! C4 ^" D0 I5 d2 I
dt = (dt == 0 ? Rate[0].time : dt);3 A3 b2 ^ ]' O* l% w! ^5 ~
CustomRatesUpdate(def_SymbolReplay, Rate, 1);$ c9 r+ \5 ~: l. d
}
4 ~0 y8 q v3 i5 _0 ?1 Wm_dtPrevLoading = Rate[0].time + iAdjust;" s: S% \- E, m" X
FileClose(file);* Q) K% q8 |. L: h7 p5 v4 v% l n
return (!_StopFlag);4 n* X( S* d. S3 G. L
}# }$ R9 U! P' x
m_dtPrevLoading = 0;2 @6 c# x2 F0 l7 y" \. D
return false;
+ P2 t% w; R/ t& @}* m5 f6 E4 ?% ]6 n! K. D1 c$ O1 e
这个下载函数的工作方式并无变化,尽管现在有更多的调用。 从以前的函数中提取一部分,并在新位置加以运用,为我们提供了更高的安全性,因为所有代码都已经过测试。 以这种方式,我们只需要担心新函数。 现在地基已经准备就绪,我们需要在配置文件中添加新内容呢。 该函数旨在判定哪些柱线文件可用于模拟票据。 为此,我们需要添加一个新定义:. `1 s0 H( B1 [' y
#define def_STR_FilesBar "[BARS]"& R6 M* H$ h" \: i6 r
#define def_STR_FilesTicks "[TICKS]"
: {" a/ |6 z, u t8 h; x0 N8 \ Z Q+ M* O#define def_STR_TicksToBars "[TICKS->BARS]"
- e O6 M* I/ k Y* a, a4 b5 h7 T#define def_STR_BarsToTicks "[BARS->TICKS]". d/ G# K5 m/ _
这样我们就可以运行一个简单的测试,这正是我们开始进行模拟所需要的。 |