规划% @' h7 m3 i# X+ B4 E+ |$ P
许多人可能认为规划很容易,特别是,由于它事关转换柱线,即始终把 1-分钟柱线转换为票据(我们稍后会解释)。 然而,模拟比表面看要复杂得多。 主要问题在于我们尚未清晰地理解票据的实际行为,就去创建 1-分钟柱线。 我们只有柱线,和一些它的有关信息,但我们并不知道柱线是如何形成的。 我们将采用 1-分钟柱线,因为它们所需的难度最小。 如果您能创造一个与真实事物非常相似的复杂走位,那么您就能够再现一些非常接近真实事物的东西。
7 ]0 n( g2 M! ^( z# p这个细节似乎并不那么重要,因为我们通常会在市场上看到锯齿形的走位。 无论走位如何复杂,这一切都归结为在 OHCL 点之间创建锯齿形。 它始自柱线的开盘那一刻,且需不少于 9 个走位来创造这个内部的锯齿形。 它总是在柱线收盘时结束,并在下一根柱线上重复该过程。 MetaTrader 5 策略测试器采用相同的逻辑。 更多详细信息,请参阅真实与生成的基差:算法交易。 我们将从这个策略开始。 虽然对于我们的目的来说并不理想,但它将为开拓更多适合的方式提供一个起点。
- V) e" \$ x8 P$ X$ V: n$ E就我而言,测试策略器不太适合回放/模拟系统,因为在策略测试器中,时间问题并不是最重要的。 这就是,没必要以这种方式创建和复现 1-分钟柱线,因为其长度本来就是 1-分钟。 事实上,它甚至更简略,在于它与现实时间并不对应。 如果是这样的话,那么测试策略就变得不可能。 想象一下,依据跨越几天甚至几年的柱运行测试,如果每根柱线复现时间与实际不同。 这将是一项不可能完成的任务。 然而,对于回放/模拟系统,我们正在寻找一种不同的行动。 我们希望按照 1-分钟间隔创建一根 1-分钟柱线,尽可能接近这个目标。
) ]! W3 N: Q3 d3 [4 i# o; q+ d准备奠基& e- g1 E: e3 a/ L3 `4 y R% D
我们的重点将完全放在回放/模拟服务代码上。 此时无需担心其它方面。 故此,我们将开始修改 C_Replay 类的代码,尝试尽可能地优化我们已经开发完成并测试过的东西。 下面是类中出现的第一个过程:
( \. D$ d$ E, c3 U0 n: J( zinline bool CheckFileIsBar(int &file, const string szFileName)
" K8 w9 y) m; P# T8 l{
) i' U& n* K' F& H9 D# s8 k2 rstring szInfo = "";% k! R# Q. |2 \! J# a' u# A, g
bool bRet;
; _1 i1 _0 _+ K" d2 D6 ufor (int c0 = 0; (c0 < 9) && (!FileIsEnding(file)); c0++) szInfo += FileReadString(file);
7 s9 s0 j8 ^$ n3 c. h7 e. cif ((bRet = (szInfo == def_Header_Bar)) == false)- k4 K6 J( Z9 F$ u8 D
{' F; e& u/ D9 e+ n4 o- v: P/ e4 y
Print("File ", szFileName, ".csv is not a file with bars.");
1 }7 G3 O7 r+ M F' w; z( q, XFileClose(file);
( p. N, w; V# q}1 q( T, C u0 N; P7 y" \9 b
return bRet;
M3 C1 i* O: d- U1 J}0 T; G* W3 i% D% v2 W$ X
此处的目标是柱线读取函数,这些测试是为了判定指定文件是否为预览柱线文件。 这是必要的,当使用相同代码来判定柱线文件是否符合我们所需时,如此可避免重复代码。 在此状况下,这些柱线将不会用作预览柱线。 它们将被转换为模拟票据,以便在交易系统中使用。 有基于此,我们引入了另一个函数:. I! r4 m2 [. ^! y) Q0 h9 C
inline void FileReadBars(int &file, MqlRates &rate[])' U! W; }. C8 X% q+ G' H: C
{
1 ~4 ^, r; i1 j2 S3 v/ Hrate[0].time = StringToTime(FileReadString(file) + " " + FileReadString(file));
' J6 C* Y b, Z; S$ V0 ^. urate[0].open = StringToDouble(FileReadString(file));
% \( g q) H3 P- a9 krate[0].high = StringToDouble(FileReadString(file));
3 l$ A; A: p ?0 t2 ^rate[0].low = StringToDouble(FileReadString(file));% R, i8 E( n& B' K8 r
rate[0].close = StringToDouble(FileReadString(file));
( y; F* k. ?, `- U( trate[0].tick_volume = StringToInteger(FileReadString(file));
8 ?/ [3 ~, o5 K! trate[0].real_volume = StringToInteger(FileReadString(file));
9 \" J# X# c9 F/ G7 s4 w u+ t0 Hrate[0].spread = (int) StringToInteger(FileReadString(file));
* L! @& g1 b _9 N2 E% a}6 g$ [+ V+ w- W. k; |; }6 c
它将从指定文件里逐行读取已有柱线的数据。 我想您在理解这段代码时不会遇到任何困难。 继续这个准备阶段,此处是另一个函数:
. J! N9 w& X& T- xinline bool OpenFileBars(int &file, const string szFileName)$ K1 w; e/ X: ]7 g: _5 V- v
{
6 Z% ^- Q7 c, o3 Yif ((file = FileOpen("Market Replay\\Bars\\" + szFileName + ".csv", FILE_CSV | FILE_READ | FILE_ANSI)) != INVALID_HANDLE) @; @6 S4 m# ]
{
6 p; Q2 Z$ u* @( `2 B* Hif (!CheckFileIsBar(file, szFileName))& t( l5 V& s S! t
return false;+ F& o0 a2 Q) H, d; E: z
return true;
3 z8 {* X* {3 \$ y- Z1 f}! E# p2 W3 K7 ^" L, F9 H. x/ M
Print("Falha ao acessar ", szFileName, ".csv de barras."); i6 ~5 e {% m/ |% r# h
return false;: r+ r, m! A4 N3 i3 T$ M
}
) \; S7 o, n* F; b# k我们的系统现在已经完全集中化,能提供对柱线的标准访问:我们即可把它们用作预览柱线时,亦可把它们用于模拟,并转换为票据表示。 因此,之前加载预览柱线的函数也必须修改,如下所示:* |5 x( X) ~) ~$ {* O% \6 t6 J9 S
bool LoadPrevBars(const string szFileNameCSV)9 \! k: ^$ J& S) `- ?
{4 H: Y9 l8 a+ ~
int file,
' @0 |# c5 C$ P3 NiAdjust = 0;2 x$ B! v9 L3 O
datetime dt = 0;" K' _3 P, o4 z" l& l( W
MqlRates Rate[1];/ c* K' g; Q2 j$ V+ p$ J
if (OpenFileBars(file, szFileNameCSV))( T4 q' [6 \# G. K7 R
{+ v1 H6 n- w% j2 k. {$ S5 v
Print("Loading preview bars for Replay. Please wait....");* n1 \# F% m3 x* B& m% |
while ((!FileIsEnding(file)) && (!_StopFlag))
# ^. Y0 g$ J# l5 o6 r{
0 B4 ?' D2 a- S) Q/ d% ^FileReadBars(file, Rate);8 t/ R, s- s. s% \7 u, m
iAdjust = ((dt != 0) && (iAdjust == 0) ? (int)(Rate[0].time - dt) : iAdjust);
9 J O% j$ w5 P# ^dt = (dt == 0 ? Rate[0].time : dt);
' a* @- P( C% h z$ l1 J4 }CustomRatesUpdate(def_SymbolReplay, Rate, 1);) Q+ |9 R0 \, h* O- I( z$ Z- f; p# s
}8 r, G( Y) ^! R) b5 v) c
m_dtPrevLoading = Rate[0].time + iAdjust;
: t1 x) o4 o# s M ZFileClose(file);. D: g+ ^; v) I/ x: ~% C1 ?
return (!_StopFlag);
* S% _2 ~& D" G! s- O7 I}+ ]# E2 D" O8 d, ^& p: x
m_dtPrevLoading = 0;
w6 j- _6 x' F$ rreturn false;3 j; @' b9 s' X2 G
}
% E) ^* ` z3 I! L这个下载函数的工作方式并无变化,尽管现在有更多的调用。 从以前的函数中提取一部分,并在新位置加以运用,为我们提供了更高的安全性,因为所有代码都已经过测试。 以这种方式,我们只需要担心新函数。 现在地基已经准备就绪,我们需要在配置文件中添加新内容呢。 该函数旨在判定哪些柱线文件可用于模拟票据。 为此,我们需要添加一个新定义:- z' p) C+ a4 @4 }) C
#define def_STR_FilesBar "[BARS]"; S5 y* _% B! t1 A0 R9 I
#define def_STR_FilesTicks "[TICKS]"
. ^. ~4 ?; A6 w. h }: W3 c/ d#define def_STR_TicksToBars "[TICKS->BARS]"2 s+ v0 w, }6 A: x- K* F
#define def_STR_BarsToTicks "[BARS->TICKS]"
' y B4 D |( A c这样我们就可以运行一个简单的测试,这正是我们开始进行模拟所需要的。 |