规划- X( T# b. u" B2 v; E) H
许多人可能认为规划很容易,特别是,由于它事关转换柱线,即始终把 1-分钟柱线转换为票据(我们稍后会解释)。 然而,模拟比表面看要复杂得多。 主要问题在于我们尚未清晰地理解票据的实际行为,就去创建 1-分钟柱线。 我们只有柱线,和一些它的有关信息,但我们并不知道柱线是如何形成的。 我们将采用 1-分钟柱线,因为它们所需的难度最小。 如果您能创造一个与真实事物非常相似的复杂走位,那么您就能够再现一些非常接近真实事物的东西。! i6 N }) f8 T! W3 L: H
这个细节似乎并不那么重要,因为我们通常会在市场上看到锯齿形的走位。 无论走位如何复杂,这一切都归结为在 OHCL 点之间创建锯齿形。 它始自柱线的开盘那一刻,且需不少于 9 个走位来创造这个内部的锯齿形。 它总是在柱线收盘时结束,并在下一根柱线上重复该过程。 MetaTrader 5 策略测试器采用相同的逻辑。 更多详细信息,请参阅真实与生成的基差:算法交易。 我们将从这个策略开始。 虽然对于我们的目的来说并不理想,但它将为开拓更多适合的方式提供一个起点。
2 ]! D; v1 l2 a4 ?0 ^" {就我而言,测试策略器不太适合回放/模拟系统,因为在策略测试器中,时间问题并不是最重要的。 这就是,没必要以这种方式创建和复现 1-分钟柱线,因为其长度本来就是 1-分钟。 事实上,它甚至更简略,在于它与现实时间并不对应。 如果是这样的话,那么测试策略就变得不可能。 想象一下,依据跨越几天甚至几年的柱运行测试,如果每根柱线复现时间与实际不同。 这将是一项不可能完成的任务。 然而,对于回放/模拟系统,我们正在寻找一种不同的行动。 我们希望按照 1-分钟间隔创建一根 1-分钟柱线,尽可能接近这个目标。
. o7 z. @# v; G# n准备奠基
4 M3 {- C- \. L/ q G1 p6 K我们的重点将完全放在回放/模拟服务代码上。 此时无需担心其它方面。 故此,我们将开始修改 C_Replay 类的代码,尝试尽可能地优化我们已经开发完成并测试过的东西。 下面是类中出现的第一个过程:0 J5 [+ N1 G. i# B: I4 p/ N
inline bool CheckFileIsBar(int &file, const string szFileName)2 y/ o5 r: K. G) u
{7 q! C- Y' W7 ~% r+ A O, q$ P
string szInfo = "";1 j; Y3 l# I$ j5 N3 m
bool bRet;. P/ v' x/ A5 f/ r- ? z
for (int c0 = 0; (c0 < 9) && (!FileIsEnding(file)); c0++) szInfo += FileReadString(file);9 Q! i3 d/ L K" j! E
if ((bRet = (szInfo == def_Header_Bar)) == false)4 W' ], [) ~9 `' y
{" E5 i" |% x6 |
Print("File ", szFileName, ".csv is not a file with bars.");
4 ]: N8 b: J4 q8 l& RFileClose(file);
) u- R; A8 \4 k8 a, o, V}
) {/ e U6 [" |( Z1 I; b9 u) R3 Sreturn bRet;3 f4 t# {5 i2 @3 V- s
}
3 k& r- Q! j1 C/ {# H1 }) ^此处的目标是柱线读取函数,这些测试是为了判定指定文件是否为预览柱线文件。 这是必要的,当使用相同代码来判定柱线文件是否符合我们所需时,如此可避免重复代码。 在此状况下,这些柱线将不会用作预览柱线。 它们将被转换为模拟票据,以便在交易系统中使用。 有基于此,我们引入了另一个函数:
, b/ u2 M4 b4 F, G4 winline void FileReadBars(int &file, MqlRates &rate[])
( o4 k) ?( g5 G7 w" R$ ]{8 u6 v" M8 ]3 W
rate[0].time = StringToTime(FileReadString(file) + " " + FileReadString(file));1 A1 K* r. {, a0 S4 }% b {2 i0 R
rate[0].open = StringToDouble(FileReadString(file));
$ k; S8 F" e. |7 O. t/ ~7 _rate[0].high = StringToDouble(FileReadString(file));
- F- l" |! B2 b2 ^) O: E8 ?8 Trate[0].low = StringToDouble(FileReadString(file));
' K# @% F, R4 M7 G. B! E: Orate[0].close = StringToDouble(FileReadString(file));! V; v9 s0 k) ^2 H1 b/ n' c2 r6 v
rate[0].tick_volume = StringToInteger(FileReadString(file));3 a: ?4 U `5 g" A# ?
rate[0].real_volume = StringToInteger(FileReadString(file));
# _3 o9 b: P* G+ zrate[0].spread = (int) StringToInteger(FileReadString(file));
" Y n2 W) p( Z" _ Z}
* x. T7 @7 ~- U/ F4 O, I它将从指定文件里逐行读取已有柱线的数据。 我想您在理解这段代码时不会遇到任何困难。 继续这个准备阶段,此处是另一个函数:
5 X' w, M1 J2 ]( H$ [2 S8 S4 `) linline bool OpenFileBars(int &file, const string szFileName)# R5 E9 r1 A) ]0 P
{9 l4 u+ M; _9 N
if ((file = FileOpen("Market Replay\\Bars\\" + szFileName + ".csv", FILE_CSV | FILE_READ | FILE_ANSI)) != INVALID_HANDLE): v0 Z% J3 K m7 U1 f
{& m2 t8 U% k7 ^# P. X9 X9 l* v- i
if (!CheckFileIsBar(file, szFileName))
2 `) I8 Q& t8 }. V$ Mreturn false;
- w8 S; v8 b$ c+ B9 }% b; treturn true; q9 p& Z: C [
}! `- q* O$ H3 E8 c0 f6 E
Print("Falha ao acessar ", szFileName, ".csv de barras.");
' p2 p3 d! `& K" Vreturn false;1 {6 k4 d. E0 o( [- }: `
}, D9 Z$ S/ C3 n$ G! H
我们的系统现在已经完全集中化,能提供对柱线的标准访问:我们即可把它们用作预览柱线时,亦可把它们用于模拟,并转换为票据表示。 因此,之前加载预览柱线的函数也必须修改,如下所示:1 y* ^* T5 c! Y, ]4 |$ }: n4 \4 Y
bool LoadPrevBars(const string szFileNameCSV)5 B* ^/ [' Z; U$ t! {
{
0 V% c8 b, J+ n- C) \7 Jint file,1 Q4 o. [. @" r$ N
iAdjust = 0;
% x# |# L) N+ h. D& bdatetime dt = 0;
3 k& x& m' F* ]" D1 VMqlRates Rate[1];% N, } f g9 Y: D
if (OpenFileBars(file, szFileNameCSV))
; j n& W4 @8 Z{
( w6 g1 l3 P# T/ }& u; v3 }1 J+ f4 TPrint("Loading preview bars for Replay. Please wait....");2 ?8 d- P2 j+ c
while ((!FileIsEnding(file)) && (!_StopFlag))
" Z5 b! Y d0 C, i: o{: j0 @/ h' o+ a T
FileReadBars(file, Rate);1 R$ x5 P6 |! H/ f( K% j+ M7 G
iAdjust = ((dt != 0) && (iAdjust == 0) ? (int)(Rate[0].time - dt) : iAdjust);# z4 ^" z+ Y4 E
dt = (dt == 0 ? Rate[0].time : dt);$ t3 t* o `/ G8 q' U( C
CustomRatesUpdate(def_SymbolReplay, Rate, 1);
" l) o& e1 Q! e& s6 _}- X4 j( P2 K8 `0 E; |
m_dtPrevLoading = Rate[0].time + iAdjust;4 N( O6 |# [; F3 p" h- M$ t
FileClose(file);% X$ \" F* H; |
return (!_StopFlag);
) y$ t B9 o }( G: D" V}
" c: E e9 a0 _+ }" ?9 Rm_dtPrevLoading = 0;
) O& K! L. b3 M' ]return false;4 R: N! m# ]8 O7 c) ^9 V) ^
}
9 ^/ G8 X: i8 D) s0 q6 Y这个下载函数的工作方式并无变化,尽管现在有更多的调用。 从以前的函数中提取一部分,并在新位置加以运用,为我们提供了更高的安全性,因为所有代码都已经过测试。 以这种方式,我们只需要担心新函数。 现在地基已经准备就绪,我们需要在配置文件中添加新内容呢。 该函数旨在判定哪些柱线文件可用于模拟票据。 为此,我们需要添加一个新定义:
: L! ~ Y- M3 D/ _: d8 p#define def_STR_FilesBar "[BARS]"+ ^: }( ]. w% K2 |6 ?
#define def_STR_FilesTicks "[TICKS]"; {" ~1 |+ E4 `& u( p, Z
#define def_STR_TicksToBars "[TICKS->BARS]"
_! {8 H. {& h x#define def_STR_BarsToTicks "[BARS->TICKS]"
2 R2 y& x6 m! W. u _# C这样我们就可以运行一个简单的测试,这正是我们开始进行模拟所需要的。 |