规划
$ {* i) ?3 |2 {8 S5 c许多人可能认为规划很容易,特别是,由于它事关转换柱线,即始终把 1-分钟柱线转换为票据(我们稍后会解释)。 然而,模拟比表面看要复杂得多。 主要问题在于我们尚未清晰地理解票据的实际行为,就去创建 1-分钟柱线。 我们只有柱线,和一些它的有关信息,但我们并不知道柱线是如何形成的。 我们将采用 1-分钟柱线,因为它们所需的难度最小。 如果您能创造一个与真实事物非常相似的复杂走位,那么您就能够再现一些非常接近真实事物的东西。3 g% @- c# x, G j
这个细节似乎并不那么重要,因为我们通常会在市场上看到锯齿形的走位。 无论走位如何复杂,这一切都归结为在 OHCL 点之间创建锯齿形。 它始自柱线的开盘那一刻,且需不少于 9 个走位来创造这个内部的锯齿形。 它总是在柱线收盘时结束,并在下一根柱线上重复该过程。 MetaTrader 5 策略测试器采用相同的逻辑。 更多详细信息,请参阅真实与生成的基差:算法交易。 我们将从这个策略开始。 虽然对于我们的目的来说并不理想,但它将为开拓更多适合的方式提供一个起点。/ P5 g0 b8 e7 H$ |) l7 D
就我而言,测试策略器不太适合回放/模拟系统,因为在策略测试器中,时间问题并不是最重要的。 这就是,没必要以这种方式创建和复现 1-分钟柱线,因为其长度本来就是 1-分钟。 事实上,它甚至更简略,在于它与现实时间并不对应。 如果是这样的话,那么测试策略就变得不可能。 想象一下,依据跨越几天甚至几年的柱运行测试,如果每根柱线复现时间与实际不同。 这将是一项不可能完成的任务。 然而,对于回放/模拟系统,我们正在寻找一种不同的行动。 我们希望按照 1-分钟间隔创建一根 1-分钟柱线,尽可能接近这个目标。
: `% \ k' K9 T- m! D准备奠基. t" P* Q, \! x% W
我们的重点将完全放在回放/模拟服务代码上。 此时无需担心其它方面。 故此,我们将开始修改 C_Replay 类的代码,尝试尽可能地优化我们已经开发完成并测试过的东西。 下面是类中出现的第一个过程:4 _* Q0 y. c( X z8 E# S7 ?6 K
inline bool CheckFileIsBar(int &file, const string szFileName) ^' J3 s3 H" Q* y! b% ]
{1 _! V7 x" l: D! E- C
string szInfo = "";: H( ~2 G7 `: I6 {5 v" B, V* a
bool bRet;
/ |2 e* U, c8 T* lfor (int c0 = 0; (c0 < 9) && (!FileIsEnding(file)); c0++) szInfo += FileReadString(file);. e9 W# S+ _ A( N+ {
if ((bRet = (szInfo == def_Header_Bar)) == false)3 x1 r- q( D# o# [: p
{# E, N/ @1 O. [5 L( B6 `/ R$ S
Print("File ", szFileName, ".csv is not a file with bars.");
8 W4 `8 a/ \- s2 E; u4 h! o9 QFileClose(file);
* q0 q7 A2 r" g5 ~/ I( n. c9 {}* b; a4 T# U3 S, H0 F! R; d+ D
return bRet;
7 c, l, q9 h y' }9 t2 H9 R}3 s6 \" |" _' w G, E; u1 p
此处的目标是柱线读取函数,这些测试是为了判定指定文件是否为预览柱线文件。 这是必要的,当使用相同代码来判定柱线文件是否符合我们所需时,如此可避免重复代码。 在此状况下,这些柱线将不会用作预览柱线。 它们将被转换为模拟票据,以便在交易系统中使用。 有基于此,我们引入了另一个函数:9 q' z* _, q( h% y5 y" A# }1 v5 H6 ]
inline void FileReadBars(int &file, MqlRates &rate[])9 \; ^0 t/ `' t! o
{
( Y1 o, V( v5 N" z- O+ \% u1 Jrate[0].time = StringToTime(FileReadString(file) + " " + FileReadString(file));
w+ {# K }, u% R% xrate[0].open = StringToDouble(FileReadString(file));$ S2 p# f3 A& R x# D* v4 Y& d
rate[0].high = StringToDouble(FileReadString(file));% S" o4 G; v8 t
rate[0].low = StringToDouble(FileReadString(file));
- J' x5 Y8 d8 l; n% v3 {: L6 srate[0].close = StringToDouble(FileReadString(file));7 Y. d" W" S$ H& @
rate[0].tick_volume = StringToInteger(FileReadString(file));5 c! p# M+ |5 `' W8 r0 A: g4 z4 D
rate[0].real_volume = StringToInteger(FileReadString(file));
$ i" l, T7 P$ I' [: hrate[0].spread = (int) StringToInteger(FileReadString(file)); o7 u8 y* }" N0 f1 h
}
. ^; r3 d8 m: C它将从指定文件里逐行读取已有柱线的数据。 我想您在理解这段代码时不会遇到任何困难。 继续这个准备阶段,此处是另一个函数:
& L7 T# P0 S! H; T$ Uinline bool OpenFileBars(int &file, const string szFileName)& i3 e8 u' D' W. g6 y3 a
{
1 ]3 ^2 v; L# V6 b; u/ bif ((file = FileOpen("Market Replay\\Bars\\" + szFileName + ".csv", FILE_CSV | FILE_READ | FILE_ANSI)) != INVALID_HANDLE)2 {& \( ?6 x6 @" s( C
{
* t- P, U3 z# L) @if (!CheckFileIsBar(file, szFileName))
$ v, e$ I- n/ F9 k/ Creturn false;
7 Q; H8 F& U9 i6 creturn true;5 M( g9 D$ m Q" D- q
}5 }/ x0 F' r; g7 U/ r& }
Print("Falha ao acessar ", szFileName, ".csv de barras.");/ P) H5 S$ C4 a
return false;7 f2 C, D$ T4 }. r e: W
}
w9 E+ E2 v+ N我们的系统现在已经完全集中化,能提供对柱线的标准访问:我们即可把它们用作预览柱线时,亦可把它们用于模拟,并转换为票据表示。 因此,之前加载预览柱线的函数也必须修改,如下所示:0 q# Z9 n# D. ?
bool LoadPrevBars(const string szFileNameCSV)1 }: M. j/ \; r; D9 j4 d+ Y
{
: i1 X* ?" B& i0 uint file,( W4 P0 c. i* F
iAdjust = 0;
5 ^1 ^! w2 G: |8 d5 U; Kdatetime dt = 0;
! F4 n. a6 P% h" |+ xMqlRates Rate[1];6 C' @" f9 f& ]7 q4 Y& z5 y( H& A
if (OpenFileBars(file, szFileNameCSV))1 X: c- T0 L( A* ~
{
: J3 m% ]9 c# CPrint("Loading preview bars for Replay. Please wait....");: b* ^3 O! x- I3 a8 \* D
while ((!FileIsEnding(file)) && (!_StopFlag)): V8 F# F, K9 |+ [' }5 B: D( K- U
{5 o/ P ?2 S" F
FileReadBars(file, Rate);5 O! U; V& R8 M# H5 ^
iAdjust = ((dt != 0) && (iAdjust == 0) ? (int)(Rate[0].time - dt) : iAdjust);. ]/ X- e, s( v! Y* }) `0 s
dt = (dt == 0 ? Rate[0].time : dt);% G& ?0 ^ B+ `* E# I5 T
CustomRatesUpdate(def_SymbolReplay, Rate, 1);
; ^6 j% s* @/ s}" x5 x0 Y2 T1 x% j2 \
m_dtPrevLoading = Rate[0].time + iAdjust;$ l7 o) \, R7 w8 h
FileClose(file);
& o1 Q5 e6 ?" T0 U( n3 V- \' E8 ~+ yreturn (!_StopFlag);
1 w; X, S5 m& ?2 H( S}
0 C o0 z6 `' a8 T& Q! G) m( j7 Tm_dtPrevLoading = 0;4 ]( ?! \ ^! S/ D
return false;: p; I! |. I* q. ^+ l
}
" v% s: B7 W: `- V. i这个下载函数的工作方式并无变化,尽管现在有更多的调用。 从以前的函数中提取一部分,并在新位置加以运用,为我们提供了更高的安全性,因为所有代码都已经过测试。 以这种方式,我们只需要担心新函数。 现在地基已经准备就绪,我们需要在配置文件中添加新内容呢。 该函数旨在判定哪些柱线文件可用于模拟票据。 为此,我们需要添加一个新定义:& t; |6 ~3 J, h5 u
#define def_STR_FilesBar "[BARS]"
/ ?( T" w" Q9 |5 C#define def_STR_FilesTicks "[TICKS]"
1 p4 ^! a9 p5 W3 C& a#define def_STR_TicksToBars "[TICKS->BARS]"5 j+ R3 R- Q" d% E' M
#define def_STR_BarsToTicks "[BARS->TICKS]"
" q/ @) ^; u7 {" [# \8 `- a- G这样我们就可以运行一个简单的测试,这正是我们开始进行模拟所需要的。 |