规划" z6 `: {, j! c
许多人可能认为规划很容易,特别是,由于它事关转换柱线,即始终把 1-分钟柱线转换为票据(我们稍后会解释)。 然而,模拟比表面看要复杂得多。 主要问题在于我们尚未清晰地理解票据的实际行为,就去创建 1-分钟柱线。 我们只有柱线,和一些它的有关信息,但我们并不知道柱线是如何形成的。 我们将采用 1-分钟柱线,因为它们所需的难度最小。 如果您能创造一个与真实事物非常相似的复杂走位,那么您就能够再现一些非常接近真实事物的东西。- Z0 S! Y s- o* x
这个细节似乎并不那么重要,因为我们通常会在市场上看到锯齿形的走位。 无论走位如何复杂,这一切都归结为在 OHCL 点之间创建锯齿形。 它始自柱线的开盘那一刻,且需不少于 9 个走位来创造这个内部的锯齿形。 它总是在柱线收盘时结束,并在下一根柱线上重复该过程。 MetaTrader 5 策略测试器采用相同的逻辑。 更多详细信息,请参阅真实与生成的基差:算法交易。 我们将从这个策略开始。 虽然对于我们的目的来说并不理想,但它将为开拓更多适合的方式提供一个起点。0 {0 \9 I& l7 v: ^7 F
就我而言,测试策略器不太适合回放/模拟系统,因为在策略测试器中,时间问题并不是最重要的。 这就是,没必要以这种方式创建和复现 1-分钟柱线,因为其长度本来就是 1-分钟。 事实上,它甚至更简略,在于它与现实时间并不对应。 如果是这样的话,那么测试策略就变得不可能。 想象一下,依据跨越几天甚至几年的柱运行测试,如果每根柱线复现时间与实际不同。 这将是一项不可能完成的任务。 然而,对于回放/模拟系统,我们正在寻找一种不同的行动。 我们希望按照 1-分钟间隔创建一根 1-分钟柱线,尽可能接近这个目标。
% u! L3 n* ?2 d准备奠基0 D6 x" \, x C# ~
我们的重点将完全放在回放/模拟服务代码上。 此时无需担心其它方面。 故此,我们将开始修改 C_Replay 类的代码,尝试尽可能地优化我们已经开发完成并测试过的东西。 下面是类中出现的第一个过程:
/ e4 J0 i. R3 L1 l ] |* c7 ?inline bool CheckFileIsBar(int &file, const string szFileName)0 j: }9 T2 n5 s. |
{
" r4 ]1 O: U6 O: \* M4 G! k. N: ^string szInfo = "";7 W1 @5 ~2 H. x' ?' w2 _
bool bRet;
( f* { v G& m) afor (int c0 = 0; (c0 < 9) && (!FileIsEnding(file)); c0++) szInfo += FileReadString(file);! j% W! }( z8 s
if ((bRet = (szInfo == def_Header_Bar)) == false)
3 q( a# x7 R8 \1 l& r8 |+ U& N& X9 J' V{& p7 K( ^6 q8 f
Print("File ", szFileName, ".csv is not a file with bars.");
$ D. o; x% R. w8 O$ c6 C6 [FileClose(file);
/ ~" C' V0 j ^. P: [; z}! z) Z. t2 d5 N( B" ?
return bRet;6 R2 T9 S; K+ K5 L/ _" Z+ P# k
}" \- f3 e+ d3 I
此处的目标是柱线读取函数,这些测试是为了判定指定文件是否为预览柱线文件。 这是必要的,当使用相同代码来判定柱线文件是否符合我们所需时,如此可避免重复代码。 在此状况下,这些柱线将不会用作预览柱线。 它们将被转换为模拟票据,以便在交易系统中使用。 有基于此,我们引入了另一个函数:1 [# N! C7 w/ i8 Z
inline void FileReadBars(int &file, MqlRates &rate[])8 }4 [. H+ t: a
{
2 B% r2 o1 ?8 g8 N- |7 erate[0].time = StringToTime(FileReadString(file) + " " + FileReadString(file));
0 }0 L2 m. z- \6 u1 rrate[0].open = StringToDouble(FileReadString(file));: a0 l) w" K* d1 T' I* p; {# y
rate[0].high = StringToDouble(FileReadString(file));
: j( h; h3 V8 f7 v3 f: u4 d- }2 Xrate[0].low = StringToDouble(FileReadString(file));
p# ]) U" {' l6 a& L) Krate[0].close = StringToDouble(FileReadString(file));6 m+ p! \/ {+ A
rate[0].tick_volume = StringToInteger(FileReadString(file));
4 W' F' [3 Z5 ^rate[0].real_volume = StringToInteger(FileReadString(file));2 [( L' d4 E! x6 @3 K
rate[0].spread = (int) StringToInteger(FileReadString(file));* R* c, P/ Q/ \4 F x$ o( W. `: ]$ h
}
5 M9 d; h1 r" n B它将从指定文件里逐行读取已有柱线的数据。 我想您在理解这段代码时不会遇到任何困难。 继续这个准备阶段,此处是另一个函数:
* n6 y+ x1 H Vinline bool OpenFileBars(int &file, const string szFileName)
4 V Q n- |5 \5 K* I. m9 |+ e4 Q{' P6 L0 d! s, n, t. f
if ((file = FileOpen("Market Replay\\Bars\\" + szFileName + ".csv", FILE_CSV | FILE_READ | FILE_ANSI)) != INVALID_HANDLE)' j' }: C$ K6 B3 H1 l! Q2 ]
{! S" p- w0 S1 y; y" t: B2 x
if (!CheckFileIsBar(file, szFileName))
1 P G/ [0 M5 l* X" ]: ireturn false;
8 {2 Q) z3 v3 F) e! m& M, A' X. y9 Ereturn true;# V" Y2 Q$ |! f1 e) B
}
7 [! j/ w7 N- I7 n! W: f5 X0 g; ? jPrint("Falha ao acessar ", szFileName, ".csv de barras.");
# m' J; V& h2 h6 }' |. Q1 hreturn false;- W6 K) x, y1 x" T
}' I! u3 t$ [6 s% T l
我们的系统现在已经完全集中化,能提供对柱线的标准访问:我们即可把它们用作预览柱线时,亦可把它们用于模拟,并转换为票据表示。 因此,之前加载预览柱线的函数也必须修改,如下所示:4 M& A* ~% C% j2 u: S* z; g
bool LoadPrevBars(const string szFileNameCSV)9 f( w0 R+ d- U6 g- z
{
0 T- C0 U( \* j u6 Pint file,% ~) W" V+ S, Q3 g
iAdjust = 0;# E! P- v# |, I0 `- O+ s8 C
datetime dt = 0;
2 J8 [# J4 O( X5 m! P2 X0 AMqlRates Rate[1];* I( Q( R2 U6 f0 S
if (OpenFileBars(file, szFileNameCSV))5 j7 s9 j' y, z
{
1 J" N* F1 O1 `# x8 Z2 rPrint("Loading preview bars for Replay. Please wait....");& { [% \- A+ Y5 v! i4 |/ F) U
while ((!FileIsEnding(file)) && (!_StopFlag))
8 |: C# ], @/ }% m/ r5 _" {5 p8 z/ p{8 {" m3 O, V6 @" |$ U& r
FileReadBars(file, Rate);
& P% u. _' _# y5 G7 c2 {9 y$ R' giAdjust = ((dt != 0) && (iAdjust == 0) ? (int)(Rate[0].time - dt) : iAdjust);7 W! o# O) Q3 F$ F6 [3 H
dt = (dt == 0 ? Rate[0].time : dt);, p: t2 J0 p, _, \" A9 S
CustomRatesUpdate(def_SymbolReplay, Rate, 1);
) q# D+ u- l4 Y6 a# N2 ~% g' k0 d+ W}
! w+ E+ d4 J% P5 |4 Sm_dtPrevLoading = Rate[0].time + iAdjust;% X0 N* H! i2 ^
FileClose(file);0 t& n' o' U4 A
return (!_StopFlag);
9 O. B( q" D) e8 v: P5 T}$ H# P* I1 Z9 a' j* ~% Q7 ^
m_dtPrevLoading = 0;
2 { _% j" M& qreturn false;( }% X: k! h' z) M# x$ U# j
}
5 X* o" A: M+ q% K这个下载函数的工作方式并无变化,尽管现在有更多的调用。 从以前的函数中提取一部分,并在新位置加以运用,为我们提供了更高的安全性,因为所有代码都已经过测试。 以这种方式,我们只需要担心新函数。 现在地基已经准备就绪,我们需要在配置文件中添加新内容呢。 该函数旨在判定哪些柱线文件可用于模拟票据。 为此,我们需要添加一个新定义:; j g% I4 J- ]
#define def_STR_FilesBar "[BARS]"
L" ~4 l& O! g- c; f1 q6 F#define def_STR_FilesTicks "[TICKS]"/ e# @1 A% `8 W
#define def_STR_TicksToBars "[TICKS->BARS]"
+ A6 i- F' R5 G, R! w: v7 k#define def_STR_BarsToTicks "[BARS->TICKS]"# n: ~% ^# U8 j4 I+ I3 |
这样我们就可以运行一个简单的测试,这正是我们开始进行模拟所需要的。 |