规划
) v; v" [$ O( L; W4 ^& ^, r8 F许多人可能认为规划很容易,特别是,由于它事关转换柱线,即始终把 1-分钟柱线转换为票据(我们稍后会解释)。 然而,模拟比表面看要复杂得多。 主要问题在于我们尚未清晰地理解票据的实际行为,就去创建 1-分钟柱线。 我们只有柱线,和一些它的有关信息,但我们并不知道柱线是如何形成的。 我们将采用 1-分钟柱线,因为它们所需的难度最小。 如果您能创造一个与真实事物非常相似的复杂走位,那么您就能够再现一些非常接近真实事物的东西。! |. X- a3 o) p- Z) M$ k. l, p
这个细节似乎并不那么重要,因为我们通常会在市场上看到锯齿形的走位。 无论走位如何复杂,这一切都归结为在 OHCL 点之间创建锯齿形。 它始自柱线的开盘那一刻,且需不少于 9 个走位来创造这个内部的锯齿形。 它总是在柱线收盘时结束,并在下一根柱线上重复该过程。 MetaTrader 5 策略测试器采用相同的逻辑。 更多详细信息,请参阅真实与生成的基差:算法交易。 我们将从这个策略开始。 虽然对于我们的目的来说并不理想,但它将为开拓更多适合的方式提供一个起点。. E0 A& b( m7 _6 V
就我而言,测试策略器不太适合回放/模拟系统,因为在策略测试器中,时间问题并不是最重要的。 这就是,没必要以这种方式创建和复现 1-分钟柱线,因为其长度本来就是 1-分钟。 事实上,它甚至更简略,在于它与现实时间并不对应。 如果是这样的话,那么测试策略就变得不可能。 想象一下,依据跨越几天甚至几年的柱运行测试,如果每根柱线复现时间与实际不同。 这将是一项不可能完成的任务。 然而,对于回放/模拟系统,我们正在寻找一种不同的行动。 我们希望按照 1-分钟间隔创建一根 1-分钟柱线,尽可能接近这个目标。! c& Q" h7 p! e8 L
准备奠基
; U7 u. G% q6 {8 G6 i3 A: C# a) a我们的重点将完全放在回放/模拟服务代码上。 此时无需担心其它方面。 故此,我们将开始修改 C_Replay 类的代码,尝试尽可能地优化我们已经开发完成并测试过的东西。 下面是类中出现的第一个过程:
& u/ z8 j0 q! N6 {inline bool CheckFileIsBar(int &file, const string szFileName)8 U0 a/ S% \- b# k- I' i$ @
{! [ m' E/ `5 R- H; r8 Y
string szInfo = "";- _8 x' ^$ v& F) s: c
bool bRet;' ^; \7 z1 {& H+ T+ |
for (int c0 = 0; (c0 < 9) && (!FileIsEnding(file)); c0++) szInfo += FileReadString(file);" `2 H# d8 I- t# @
if ((bRet = (szInfo == def_Header_Bar)) == false)1 G4 S. h7 v/ E
{
0 ]$ p- N) I8 P& t0 o* YPrint("File ", szFileName, ".csv is not a file with bars.");6 g. e& W' }3 \6 r6 h
FileClose(file);+ M3 T( i- n' {$ u
}6 ?" N) v3 q( V+ f- I
return bRet;
+ R. v7 h2 D& S$ d}: G; t8 W! y- q4 y, R
此处的目标是柱线读取函数,这些测试是为了判定指定文件是否为预览柱线文件。 这是必要的,当使用相同代码来判定柱线文件是否符合我们所需时,如此可避免重复代码。 在此状况下,这些柱线将不会用作预览柱线。 它们将被转换为模拟票据,以便在交易系统中使用。 有基于此,我们引入了另一个函数:
; o f; `3 [& X+ v& Hinline void FileReadBars(int &file, MqlRates &rate[])9 g9 i2 ^ e2 S8 J; A# `: [
{$ Z3 i) P9 `2 ?# n
rate[0].time = StringToTime(FileReadString(file) + " " + FileReadString(file));
) U- z' A! y/ e+ I& Frate[0].open = StringToDouble(FileReadString(file));( a e0 j5 ]+ E( T K7 m
rate[0].high = StringToDouble(FileReadString(file));
0 }& ~( ] j: D' ?5 a6 grate[0].low = StringToDouble(FileReadString(file));
4 F! n+ Z/ k5 W' Krate[0].close = StringToDouble(FileReadString(file));
- K/ V4 D G& G, j' A1 J, wrate[0].tick_volume = StringToInteger(FileReadString(file));2 x$ ]$ ?% b* O" ?
rate[0].real_volume = StringToInteger(FileReadString(file));1 U( P/ C2 P: w. a; j# z
rate[0].spread = (int) StringToInteger(FileReadString(file));
8 B9 p6 g, [" j3 h- |6 Q. X}
; r% F' ]; ?* y/ p它将从指定文件里逐行读取已有柱线的数据。 我想您在理解这段代码时不会遇到任何困难。 继续这个准备阶段,此处是另一个函数:; F5 i ^' s! t, j
inline bool OpenFileBars(int &file, const string szFileName); \5 G& @1 Q3 E: q* U
{9 I7 r+ ~: z: R! a* e9 W& N
if ((file = FileOpen("Market Replay\\Bars\\" + szFileName + ".csv", FILE_CSV | FILE_READ | FILE_ANSI)) != INVALID_HANDLE)$ I' t8 w% T+ s5 |7 Z4 d+ B
{% ]5 E2 ^3 x) B: W
if (!CheckFileIsBar(file, szFileName)). j- n l2 z' o6 `' a0 X
return false;
6 Q) w3 o& K! Kreturn true;
# W6 W$ t9 i1 x* @& \$ a1 B}
. g% M1 n8 H0 I, t2 @, gPrint("Falha ao acessar ", szFileName, ".csv de barras.");6 m5 `5 R1 R7 w4 S" Z- f& O
return false;: d. Y$ R# { J) j; V2 z
}9 |; _! H! W I8 ?" h' g( V
我们的系统现在已经完全集中化,能提供对柱线的标准访问:我们即可把它们用作预览柱线时,亦可把它们用于模拟,并转换为票据表示。 因此,之前加载预览柱线的函数也必须修改,如下所示:+ G. g D' F ^5 z: V* s
bool LoadPrevBars(const string szFileNameCSV)7 P7 T& [1 e( u. K" ?# S4 {
{$ S# f* O3 g4 a4 {9 I
int file,
! q- E1 S' C0 R/ n1 Q I z2 [iAdjust = 0;
& n/ E ?3 Z% C( U# Jdatetime dt = 0;4 f8 B$ d2 D q* O
MqlRates Rate[1];
: a2 O/ R2 g/ a( t8 v% f, [0 bif (OpenFileBars(file, szFileNameCSV))" }- I+ y" T3 [
{
8 V6 X6 d* r i2 ^/ ?8 @ ZPrint("Loading preview bars for Replay. Please wait....");/ q, n# p* z$ j& `
while ((!FileIsEnding(file)) && (!_StopFlag))
0 P+ M+ H8 Y$ H8 Q* o{8 j9 P) H' h' ^* l
FileReadBars(file, Rate);7 Z% e6 }6 F! G( Z# h- r
iAdjust = ((dt != 0) && (iAdjust == 0) ? (int)(Rate[0].time - dt) : iAdjust);
, ~- [4 O7 ?3 j$ U0 r( @" [& Pdt = (dt == 0 ? Rate[0].time : dt);. J" w3 o' X- S7 p4 {
CustomRatesUpdate(def_SymbolReplay, Rate, 1);
( y$ V$ f2 W5 M; d0 T g}. e+ y$ h+ Y) b% `
m_dtPrevLoading = Rate[0].time + iAdjust;7 c8 r' Q/ m+ x0 \
FileClose(file);
) B: `+ h* `) |return (!_StopFlag);
$ U/ h: e+ E) i* D}
7 e2 m0 @- I9 j) e, X) Om_dtPrevLoading = 0;
! t: G% ]" w* L" F' d; m5 q1 r7 |return false;
* D0 v, Z1 Y# c5 n. n3 Y! O}
2 R6 {# ^9 F S4 Y( ^) X# d这个下载函数的工作方式并无变化,尽管现在有更多的调用。 从以前的函数中提取一部分,并在新位置加以运用,为我们提供了更高的安全性,因为所有代码都已经过测试。 以这种方式,我们只需要担心新函数。 现在地基已经准备就绪,我们需要在配置文件中添加新内容呢。 该函数旨在判定哪些柱线文件可用于模拟票据。 为此,我们需要添加一个新定义:
3 A. q) E5 R. i* t#define def_STR_FilesBar "[BARS]"
% E. l! v9 }* {& T$ o( i. d#define def_STR_FilesTicks "[TICKS]"3 Q: ?; V! h5 } ?6 D
#define def_STR_TicksToBars "[TICKS->BARS]" g5 n, U- d3 g7 B, p/ T7 A* D
#define def_STR_BarsToTicks "[BARS->TICKS]"
$ u* L, [' }( J' ^这样我们就可以运行一个简单的测试,这正是我们开始进行模拟所需要的。 |