规划
* G) @4 d6 Q, Y许多人可能认为规划很容易,特别是,由于它事关转换柱线,即始终把 1-分钟柱线转换为票据(我们稍后会解释)。 然而,模拟比表面看要复杂得多。 主要问题在于我们尚未清晰地理解票据的实际行为,就去创建 1-分钟柱线。 我们只有柱线,和一些它的有关信息,但我们并不知道柱线是如何形成的。 我们将采用 1-分钟柱线,因为它们所需的难度最小。 如果您能创造一个与真实事物非常相似的复杂走位,那么您就能够再现一些非常接近真实事物的东西。
0 u* e1 a+ G+ I; S& d. S6 W! F这个细节似乎并不那么重要,因为我们通常会在市场上看到锯齿形的走位。 无论走位如何复杂,这一切都归结为在 OHCL 点之间创建锯齿形。 它始自柱线的开盘那一刻,且需不少于 9 个走位来创造这个内部的锯齿形。 它总是在柱线收盘时结束,并在下一根柱线上重复该过程。 MetaTrader 5 策略测试器采用相同的逻辑。 更多详细信息,请参阅真实与生成的基差:算法交易。 我们将从这个策略开始。 虽然对于我们的目的来说并不理想,但它将为开拓更多适合的方式提供一个起点。# c8 t! ~. d6 a7 `( k6 @7 t5 P
就我而言,测试策略器不太适合回放/模拟系统,因为在策略测试器中,时间问题并不是最重要的。 这就是,没必要以这种方式创建和复现 1-分钟柱线,因为其长度本来就是 1-分钟。 事实上,它甚至更简略,在于它与现实时间并不对应。 如果是这样的话,那么测试策略就变得不可能。 想象一下,依据跨越几天甚至几年的柱运行测试,如果每根柱线复现时间与实际不同。 这将是一项不可能完成的任务。 然而,对于回放/模拟系统,我们正在寻找一种不同的行动。 我们希望按照 1-分钟间隔创建一根 1-分钟柱线,尽可能接近这个目标。# I' j6 c8 F3 |! K; n# W+ f# x0 x
准备奠基4 ^6 B! \- B, i# a! Y, u; U) a U
我们的重点将完全放在回放/模拟服务代码上。 此时无需担心其它方面。 故此,我们将开始修改 C_Replay 类的代码,尝试尽可能地优化我们已经开发完成并测试过的东西。 下面是类中出现的第一个过程:
% `+ c9 T$ e" D/ G- W' D7 G7 hinline bool CheckFileIsBar(int &file, const string szFileName)5 b7 {- h$ K! M, D8 [9 X7 v
{
$ g9 @7 l9 v/ e( Gstring szInfo = "";
& q K4 D. n5 E! Y# D+ T% mbool bRet;
2 R1 Q8 a6 A' D& X- ?for (int c0 = 0; (c0 < 9) && (!FileIsEnding(file)); c0++) szInfo += FileReadString(file);
+ m, s/ q5 R5 G+ e9 R) x$ L' S$ L8 pif ((bRet = (szInfo == def_Header_Bar)) == false)
& P' P8 Z, h; h* C: F$ @% A{) S% q- U7 x# M. B& K2 \! Y
Print("File ", szFileName, ".csv is not a file with bars.");
* L; p$ n( O% t: c6 H8 A2 \- |FileClose(file);
' e6 Y9 g" Q, q2 \+ w: j! u: }$ B, q! l' J}9 q. `% X4 b$ w/ T
return bRet;
; W: Y8 O/ A" s" l& ~}" p! X$ Q; T( W' I
此处的目标是柱线读取函数,这些测试是为了判定指定文件是否为预览柱线文件。 这是必要的,当使用相同代码来判定柱线文件是否符合我们所需时,如此可避免重复代码。 在此状况下,这些柱线将不会用作预览柱线。 它们将被转换为模拟票据,以便在交易系统中使用。 有基于此,我们引入了另一个函数:
0 A5 P, F# y) L) l) u Binline void FileReadBars(int &file, MqlRates &rate[])9 H! |( P: x+ b/ X u+ m
{. ~/ x* g' C: _" ?) U7 A
rate[0].time = StringToTime(FileReadString(file) + " " + FileReadString(file));4 L2 r, x8 d; s ^2 N* J
rate[0].open = StringToDouble(FileReadString(file));
- P9 ^( L$ ~+ \' f n: _1 Drate[0].high = StringToDouble(FileReadString(file));8 d5 z5 v8 f2 I0 d; b' p |
rate[0].low = StringToDouble(FileReadString(file));
3 l, g7 O/ {& F4 |% `rate[0].close = StringToDouble(FileReadString(file));
# {- \. Z4 p+ h- orate[0].tick_volume = StringToInteger(FileReadString(file));
; u$ N/ R2 x9 j' R; [rate[0].real_volume = StringToInteger(FileReadString(file));- B1 v1 x5 n) ~3 S) n s
rate[0].spread = (int) StringToInteger(FileReadString(file));/ ^/ M8 t0 o7 G: ]! _8 @
}8 J/ c- ^8 G. \& Y# B
它将从指定文件里逐行读取已有柱线的数据。 我想您在理解这段代码时不会遇到任何困难。 继续这个准备阶段,此处是另一个函数:( B0 S1 P: w S0 K
inline bool OpenFileBars(int &file, const string szFileName)8 K p8 u! F" ~* t1 t( m& I
{
8 f1 R5 b T; z+ \$ p3 Q! fif ((file = FileOpen("Market Replay\\Bars\\" + szFileName + ".csv", FILE_CSV | FILE_READ | FILE_ANSI)) != INVALID_HANDLE)0 N' a& c$ x8 O
{1 s/ s; U+ s1 s- A5 s7 ]9 ]
if (!CheckFileIsBar(file, szFileName))& J% z4 i2 s2 J9 p2 o# a
return false;6 W/ _( y& Z$ g
return true;
6 L' P" H7 Q4 V}
8 g8 Y9 F0 v$ `7 qPrint("Falha ao acessar ", szFileName, ".csv de barras.");
1 {" j b* g5 A! i. A6 h2 o3 j* dreturn false;! h; }5 F Z" Z6 E4 K& P; f W
}+ w9 C0 O, d5 Q9 N
我们的系统现在已经完全集中化,能提供对柱线的标准访问:我们即可把它们用作预览柱线时,亦可把它们用于模拟,并转换为票据表示。 因此,之前加载预览柱线的函数也必须修改,如下所示:
0 r9 H& ~# U6 G5 J& |bool LoadPrevBars(const string szFileNameCSV)
, u. d l+ c& }* z' K{
0 T0 v% k1 w6 ^' e. G2 u9 Y' Oint file,4 }/ r$ T: l+ S7 s. O | _9 y
iAdjust = 0;" m& X7 Q! D. u+ h' a
datetime dt = 0;8 R8 J+ \* j* I. c' }" O4 N
MqlRates Rate[1];+ C7 A8 p& ]9 q. v$ X
if (OpenFileBars(file, szFileNameCSV))
8 z3 X9 B( E( {9 q; \{
+ P' E* x) O, V* z( uPrint("Loading preview bars for Replay. Please wait....");
7 T8 l) X1 {1 j+ M1 fwhile ((!FileIsEnding(file)) && (!_StopFlag))
; |. x3 [2 G9 G- j8 Z* Q- l7 m{# Y1 x) b. H5 A( ?* D: L$ K
FileReadBars(file, Rate);
6 w" {/ q$ T S4 A. ZiAdjust = ((dt != 0) && (iAdjust == 0) ? (int)(Rate[0].time - dt) : iAdjust);
4 J) a, j( Z0 N: M: A- Udt = (dt == 0 ? Rate[0].time : dt);$ ^3 b- |% s; Q0 P# {6 ]# s: {+ z
CustomRatesUpdate(def_SymbolReplay, Rate, 1);: r" d4 E' v) S
}# {7 E. q9 b& ?' M1 X
m_dtPrevLoading = Rate[0].time + iAdjust;
/ ^! a; |2 q* P9 C0 LFileClose(file);
' ?- P+ D' C+ ~1 G5 Ireturn (!_StopFlag);: C) b7 R; [- l, l2 }
}; a! A) u. ^- ^! }4 a3 A( t+ r7 y1 F
m_dtPrevLoading = 0;! B2 e$ g$ K& S. A5 _4 s2 o
return false;) J5 J% w0 q$ [
}3 _% H, M1 h7 X2 e
这个下载函数的工作方式并无变化,尽管现在有更多的调用。 从以前的函数中提取一部分,并在新位置加以运用,为我们提供了更高的安全性,因为所有代码都已经过测试。 以这种方式,我们只需要担心新函数。 现在地基已经准备就绪,我们需要在配置文件中添加新内容呢。 该函数旨在判定哪些柱线文件可用于模拟票据。 为此,我们需要添加一个新定义:/ Q8 I8 F$ ~2 Y4 d7 R- n! `
#define def_STR_FilesBar "[BARS]"
3 |+ B! u: a1 q& H9 V$ R4 V#define def_STR_FilesTicks "[TICKS]"
$ \4 M e3 A$ c7 v7 F0 W+ Y#define def_STR_TicksToBars "[TICKS->BARS]"+ a0 D a" }3 e+ |; w; o/ z( M
#define def_STR_BarsToTicks "[BARS->TICKS]"
6 t$ y+ }! j, `4 F6 l, r+ p# K这样我们就可以运行一个简单的测试,这正是我们开始进行模拟所需要的。 |