规划- b a# B. ~$ }' d6 R$ s
许多人可能认为规划很容易,特别是,由于它事关转换柱线,即始终把 1-分钟柱线转换为票据(我们稍后会解释)。 然而,模拟比表面看要复杂得多。 主要问题在于我们尚未清晰地理解票据的实际行为,就去创建 1-分钟柱线。 我们只有柱线,和一些它的有关信息,但我们并不知道柱线是如何形成的。 我们将采用 1-分钟柱线,因为它们所需的难度最小。 如果您能创造一个与真实事物非常相似的复杂走位,那么您就能够再现一些非常接近真实事物的东西。
9 d7 h/ h4 x: C: Q0 g! b2 |" r这个细节似乎并不那么重要,因为我们通常会在市场上看到锯齿形的走位。 无论走位如何复杂,这一切都归结为在 OHCL 点之间创建锯齿形。 它始自柱线的开盘那一刻,且需不少于 9 个走位来创造这个内部的锯齿形。 它总是在柱线收盘时结束,并在下一根柱线上重复该过程。 MetaTrader 5 策略测试器采用相同的逻辑。 更多详细信息,请参阅真实与生成的基差:算法交易。 我们将从这个策略开始。 虽然对于我们的目的来说并不理想,但它将为开拓更多适合的方式提供一个起点。
' ?8 t% b$ v0 ]/ g就我而言,测试策略器不太适合回放/模拟系统,因为在策略测试器中,时间问题并不是最重要的。 这就是,没必要以这种方式创建和复现 1-分钟柱线,因为其长度本来就是 1-分钟。 事实上,它甚至更简略,在于它与现实时间并不对应。 如果是这样的话,那么测试策略就变得不可能。 想象一下,依据跨越几天甚至几年的柱运行测试,如果每根柱线复现时间与实际不同。 这将是一项不可能完成的任务。 然而,对于回放/模拟系统,我们正在寻找一种不同的行动。 我们希望按照 1-分钟间隔创建一根 1-分钟柱线,尽可能接近这个目标。
' A- u0 ]" x0 _- S. @准备奠基2 f3 e+ Y3 |' t+ c3 p. `7 Y1 I
我们的重点将完全放在回放/模拟服务代码上。 此时无需担心其它方面。 故此,我们将开始修改 C_Replay 类的代码,尝试尽可能地优化我们已经开发完成并测试过的东西。 下面是类中出现的第一个过程:
4 s3 e# I- d0 x2 X, Y0 u4 \, Q4 @/ tinline bool CheckFileIsBar(int &file, const string szFileName)
4 k/ r/ A/ X9 d, ^8 y9 @ y{( E" e% Z+ z; B
string szInfo = "";$ r5 T! e, y3 a, w O
bool bRet;, \" J( |$ p; q( [) G2 B% Z9 {
for (int c0 = 0; (c0 < 9) && (!FileIsEnding(file)); c0++) szInfo += FileReadString(file);! @/ ~/ h- f1 L! n% O# Q
if ((bRet = (szInfo == def_Header_Bar)) == false): a- a, Q0 j0 z; m
{. Z2 w1 Q1 g) j! e
Print("File ", szFileName, ".csv is not a file with bars.");. ]. k8 G0 ^4 s2 {+ ~% r% U: r
FileClose(file);
. H" g" a! C; f* y! r4 ~}
9 S7 B* F; h: \% V6 zreturn bRet; D# N3 `3 i8 t6 z( a' T
}
: Y: i! {- c. Z) C3 d0 L此处的目标是柱线读取函数,这些测试是为了判定指定文件是否为预览柱线文件。 这是必要的,当使用相同代码来判定柱线文件是否符合我们所需时,如此可避免重复代码。 在此状况下,这些柱线将不会用作预览柱线。 它们将被转换为模拟票据,以便在交易系统中使用。 有基于此,我们引入了另一个函数:
9 q" j3 {# D1 z+ f9 t- K' yinline void FileReadBars(int &file, MqlRates &rate[])
% L0 w1 V+ K# E; Y0 m {3 d{7 F& s- T/ N+ X1 V8 c6 M K
rate[0].time = StringToTime(FileReadString(file) + " " + FileReadString(file));
# k" c8 F0 K# a! drate[0].open = StringToDouble(FileReadString(file));
, ^" R7 y( g8 N! N7 X! Frate[0].high = StringToDouble(FileReadString(file));
$ A# ? \3 K' trate[0].low = StringToDouble(FileReadString(file));( ]( c6 a) k" Q, _- m. I. _
rate[0].close = StringToDouble(FileReadString(file));
& c9 n7 j% o1 D; o# o3 ^# srate[0].tick_volume = StringToInteger(FileReadString(file));9 s% u. j$ [, i% H
rate[0].real_volume = StringToInteger(FileReadString(file));& L; j: U) ~1 B3 b
rate[0].spread = (int) StringToInteger(FileReadString(file));2 w4 ?) O% r& |+ j" K1 _
}
* p+ x$ H, D+ M# @, @9 @) |6 C它将从指定文件里逐行读取已有柱线的数据。 我想您在理解这段代码时不会遇到任何困难。 继续这个准备阶段,此处是另一个函数:
5 w g$ Z9 ~6 ~" ~0 ^inline bool OpenFileBars(int &file, const string szFileName)
' `' ~' O5 V/ _# E2 R1 S0 h{
1 Z6 D9 G& N' J) y; I+ dif ((file = FileOpen("Market Replay\\Bars\\" + szFileName + ".csv", FILE_CSV | FILE_READ | FILE_ANSI)) != INVALID_HANDLE)( | J) n+ \3 k* O; n
{
7 H. G( z3 Z8 h1 j$ u/ mif (!CheckFileIsBar(file, szFileName))
- o) g \2 l0 x" t0 vreturn false;, J2 v, w, h1 f1 C
return true;3 z7 P5 _2 J1 t6 Q8 o, o
}+ ~2 S$ |+ B( A: u( h6 v3 y1 T
Print("Falha ao acessar ", szFileName, ".csv de barras.");
7 d7 L# U$ K5 p2 L5 Vreturn false;" M$ ^' Z" G1 d& _% M
}
8 ?8 S2 r; z5 s7 g E. ?& T我们的系统现在已经完全集中化,能提供对柱线的标准访问:我们即可把它们用作预览柱线时,亦可把它们用于模拟,并转换为票据表示。 因此,之前加载预览柱线的函数也必须修改,如下所示:
- o( }+ V2 ?7 T) R. z1 J1 s: pbool LoadPrevBars(const string szFileNameCSV)$ G2 K" W- B% U- m7 i8 G
{3 Y" n3 L' O" f6 o
int file,- u% t2 r$ e' g; C5 U6 J
iAdjust = 0;
! I& W& x& w5 b- s' s$ Zdatetime dt = 0;% h* D/ B2 P/ T% N# K
MqlRates Rate[1];' U0 |; q; F, a* N2 i
if (OpenFileBars(file, szFileNameCSV)) \" T0 R7 e4 k( ~+ V
{
- J. J9 s* l) d/ p7 C m/ V' BPrint("Loading preview bars for Replay. Please wait....");
. @, C) {% W7 L/ x7 h# Xwhile ((!FileIsEnding(file)) && (!_StopFlag))
/ r# Z [5 W- i" d0 E{
% t( h0 t, V5 H+ n3 HFileReadBars(file, Rate);
0 `# l! M& z4 ~iAdjust = ((dt != 0) && (iAdjust == 0) ? (int)(Rate[0].time - dt) : iAdjust);
2 M8 D0 D& y7 odt = (dt == 0 ? Rate[0].time : dt);
0 M: k& [: @# w9 K; mCustomRatesUpdate(def_SymbolReplay, Rate, 1);
) d/ `% j) e5 B' ?0 g% h}
3 @6 M- ^. J6 x7 K8 gm_dtPrevLoading = Rate[0].time + iAdjust;
9 g5 H+ F2 A' f3 nFileClose(file);1 w3 X% n; ^# K3 l, |- T
return (!_StopFlag);
( h# ~7 p7 ~9 b4 E4 d2 \$ {5 \5 t}
+ m9 k- v. N: zm_dtPrevLoading = 0;
/ o# l/ E" P$ h1 S; w( Breturn false;8 z6 P& e* L, \" v9 l# K% ^
}
" ?2 l7 r/ a; p( {# D- ^+ A9 ]这个下载函数的工作方式并无变化,尽管现在有更多的调用。 从以前的函数中提取一部分,并在新位置加以运用,为我们提供了更高的安全性,因为所有代码都已经过测试。 以这种方式,我们只需要担心新函数。 现在地基已经准备就绪,我们需要在配置文件中添加新内容呢。 该函数旨在判定哪些柱线文件可用于模拟票据。 为此,我们需要添加一个新定义:# V) y `4 c+ L
#define def_STR_FilesBar "[BARS]"
5 ~: \/ `) A- U6 ~#define def_STR_FilesTicks "[TICKS]"( T# C, g9 E T5 e2 ~4 L' _- |
#define def_STR_TicksToBars "[TICKS->BARS]"2 b5 X; ~- h& c
#define def_STR_BarsToTicks "[BARS->TICKS]"( i2 O2 P4 @ S3 k3 G& L) V c
这样我们就可以运行一个简单的测试,这正是我们开始进行模拟所需要的。 |