规划
* s. ?5 W! @$ ? C6 ?4 ]0 w许多人可能认为规划很容易,特别是,由于它事关转换柱线,即始终把 1-分钟柱线转换为票据(我们稍后会解释)。 然而,模拟比表面看要复杂得多。 主要问题在于我们尚未清晰地理解票据的实际行为,就去创建 1-分钟柱线。 我们只有柱线,和一些它的有关信息,但我们并不知道柱线是如何形成的。 我们将采用 1-分钟柱线,因为它们所需的难度最小。 如果您能创造一个与真实事物非常相似的复杂走位,那么您就能够再现一些非常接近真实事物的东西。* ]2 o5 B$ Z0 P$ [( ^! d
这个细节似乎并不那么重要,因为我们通常会在市场上看到锯齿形的走位。 无论走位如何复杂,这一切都归结为在 OHCL 点之间创建锯齿形。 它始自柱线的开盘那一刻,且需不少于 9 个走位来创造这个内部的锯齿形。 它总是在柱线收盘时结束,并在下一根柱线上重复该过程。 MetaTrader 5 策略测试器采用相同的逻辑。 更多详细信息,请参阅真实与生成的基差:算法交易。 我们将从这个策略开始。 虽然对于我们的目的来说并不理想,但它将为开拓更多适合的方式提供一个起点。
% f8 r% K5 ^$ y8 l% y; X5 L就我而言,测试策略器不太适合回放/模拟系统,因为在策略测试器中,时间问题并不是最重要的。 这就是,没必要以这种方式创建和复现 1-分钟柱线,因为其长度本来就是 1-分钟。 事实上,它甚至更简略,在于它与现实时间并不对应。 如果是这样的话,那么测试策略就变得不可能。 想象一下,依据跨越几天甚至几年的柱运行测试,如果每根柱线复现时间与实际不同。 这将是一项不可能完成的任务。 然而,对于回放/模拟系统,我们正在寻找一种不同的行动。 我们希望按照 1-分钟间隔创建一根 1-分钟柱线,尽可能接近这个目标。
* B g4 x$ S) b$ |9 F' s' i2 @准备奠基( F( j: X, r8 h0 h6 T4 @" |) f" {
我们的重点将完全放在回放/模拟服务代码上。 此时无需担心其它方面。 故此,我们将开始修改 C_Replay 类的代码,尝试尽可能地优化我们已经开发完成并测试过的东西。 下面是类中出现的第一个过程:
: r' h1 h* M% b3 [4 g9 B minline bool CheckFileIsBar(int &file, const string szFileName)- N$ G$ @' [% _& g/ Y7 B
{
7 V6 l5 ~0 ~$ c- c+ j |7 mstring szInfo = "";
2 k+ T: |# ]- ]- i% ubool bRet;
, ^) W- N! j: N7 jfor (int c0 = 0; (c0 < 9) && (!FileIsEnding(file)); c0++) szInfo += FileReadString(file);* G' a5 y! o# C0 C" L& p
if ((bRet = (szInfo == def_Header_Bar)) == false)+ [. a* s4 E2 U: p' \
{
5 C4 a. a! `% Y# G# WPrint("File ", szFileName, ".csv is not a file with bars.");
9 @3 W8 y, ?- W" m+ D, [6 qFileClose(file);
1 E1 U& R% {* g9 n( H0 f' ~) z}
) Q3 v) n1 \3 E; `3 Z" Ireturn bRet;% Y5 _% G( d7 w5 [# g
}) g9 K1 @, V4 L7 ^3 k
此处的目标是柱线读取函数,这些测试是为了判定指定文件是否为预览柱线文件。 这是必要的,当使用相同代码来判定柱线文件是否符合我们所需时,如此可避免重复代码。 在此状况下,这些柱线将不会用作预览柱线。 它们将被转换为模拟票据,以便在交易系统中使用。 有基于此,我们引入了另一个函数:
7 }1 u. S" R0 q$ s1 z1 B6 xinline void FileReadBars(int &file, MqlRates &rate[])6 p, A \7 Q+ q2 N( C
{
' M& W5 ^8 F( T x$ qrate[0].time = StringToTime(FileReadString(file) + " " + FileReadString(file));
, C2 K# _5 I1 h* J2 }6 Urate[0].open = StringToDouble(FileReadString(file));! }3 x' ~6 a. p2 D V& Y
rate[0].high = StringToDouble(FileReadString(file));8 X: O/ e3 q) [: [6 f6 w
rate[0].low = StringToDouble(FileReadString(file));
4 F" M+ T2 X$ s/ krate[0].close = StringToDouble(FileReadString(file));
+ R. }- b, ]- o4 _4 F5 V. Z9 \8 x& w6 orate[0].tick_volume = StringToInteger(FileReadString(file));" k( h9 _) {0 {# \$ ^
rate[0].real_volume = StringToInteger(FileReadString(file));0 Q7 ^& I* d' n7 d3 T& _
rate[0].spread = (int) StringToInteger(FileReadString(file));
\1 h0 z) M) q! h}
2 g! m4 U1 E& r i1 T: n) y, o它将从指定文件里逐行读取已有柱线的数据。 我想您在理解这段代码时不会遇到任何困难。 继续这个准备阶段,此处是另一个函数:
. ]4 I6 w s* i, L5 Q% Zinline bool OpenFileBars(int &file, const string szFileName)
, I1 k1 n6 f: K7 B{
2 V8 N) G9 U& \$ e( T! iif ((file = FileOpen("Market Replay\\Bars\\" + szFileName + ".csv", FILE_CSV | FILE_READ | FILE_ANSI)) != INVALID_HANDLE)
6 \% [. M M5 ~% ]( U{
9 X5 k( M% @6 y+ Zif (!CheckFileIsBar(file, szFileName))4 `( x( d1 |/ h1 f1 T N
return false;) Y1 t7 l" {. y
return true;
) `& q0 Z1 l" s}
5 ?3 c5 S- \- ~' c$ D* F' YPrint("Falha ao acessar ", szFileName, ".csv de barras.");
1 Q4 ^! p4 c& L$ `return false;
& g: t( ~0 g) H/ z) T}
( u7 t9 g- { Y4 W4 G- G$ y1 ^9 c我们的系统现在已经完全集中化,能提供对柱线的标准访问:我们即可把它们用作预览柱线时,亦可把它们用于模拟,并转换为票据表示。 因此,之前加载预览柱线的函数也必须修改,如下所示:& ]$ p4 s p3 m" n
bool LoadPrevBars(const string szFileNameCSV)
& X& Z Q5 }' F' [# q1 H0 k{! k @1 C9 C9 Q
int file,: P0 Q" m+ k& ], [2 F8 N
iAdjust = 0;( a* Z1 N6 Q! B3 o0 H# _, H u. B6 J
datetime dt = 0;) h* q I+ ~( t$ `" Y5 i
MqlRates Rate[1];; x& _2 [6 N( x e3 ]* h, I1 k/ Q
if (OpenFileBars(file, szFileNameCSV))
6 k2 D% W8 q! J# y2 C# a{
- T& _" W. M zPrint("Loading preview bars for Replay. Please wait....");- y2 b( B% |. }% b% u
while ((!FileIsEnding(file)) && (!_StopFlag))
9 |. O, F& a- o$ [9 f( l- I{
5 U. a; W3 L3 `! c: ^7 v& BFileReadBars(file, Rate);
- Z/ o" R6 H9 k& liAdjust = ((dt != 0) && (iAdjust == 0) ? (int)(Rate[0].time - dt) : iAdjust);) t, B# ^; l( ]
dt = (dt == 0 ? Rate[0].time : dt);' }( H$ c" S, C" @8 Y' J. g% R# t
CustomRatesUpdate(def_SymbolReplay, Rate, 1);
2 j& R$ A' U1 [: a' F}+ E, }, f: f6 h4 j2 D2 U% N9 Z& W
m_dtPrevLoading = Rate[0].time + iAdjust;8 E6 |: M4 E" s% b4 Z
FileClose(file);; D k* w/ C1 O+ e r
return (!_StopFlag);
$ q5 A( K' M$ i- q- l' b}
/ p) `5 G9 a, T1 |% U! j% ~$ Bm_dtPrevLoading = 0;+ h( x- r4 U/ ~. e5 A' q) k: S/ U
return false;2 g3 \- r4 u! [/ ]: x
}$ i. A* Q3 l8 C) Y+ ?5 _
这个下载函数的工作方式并无变化,尽管现在有更多的调用。 从以前的函数中提取一部分,并在新位置加以运用,为我们提供了更高的安全性,因为所有代码都已经过测试。 以这种方式,我们只需要担心新函数。 现在地基已经准备就绪,我们需要在配置文件中添加新内容呢。 该函数旨在判定哪些柱线文件可用于模拟票据。 为此,我们需要添加一个新定义:7 ]* T, h7 V7 H! x4 T! U
#define def_STR_FilesBar "[BARS]"
0 p7 \$ k- \( ^6 P }8 l/ A: u#define def_STR_FilesTicks "[TICKS]"5 z2 C7 N) v: o4 B
#define def_STR_TicksToBars "[TICKS->BARS]"
9 t; o. H, l( O#define def_STR_BarsToTicks "[BARS->TICKS]"& t5 s I- g9 U" E
这样我们就可以运行一个简单的测试,这正是我们开始进行模拟所需要的。 |