规划
5 s( C7 W8 L3 X& C# M6 `( ? N许多人可能认为规划很容易,特别是,由于它事关转换柱线,即始终把 1-分钟柱线转换为票据(我们稍后会解释)。 然而,模拟比表面看要复杂得多。 主要问题在于我们尚未清晰地理解票据的实际行为,就去创建 1-分钟柱线。 我们只有柱线,和一些它的有关信息,但我们并不知道柱线是如何形成的。 我们将采用 1-分钟柱线,因为它们所需的难度最小。 如果您能创造一个与真实事物非常相似的复杂走位,那么您就能够再现一些非常接近真实事物的东西。7 U+ S* v( _5 {1 H* _! @
这个细节似乎并不那么重要,因为我们通常会在市场上看到锯齿形的走位。 无论走位如何复杂,这一切都归结为在 OHCL 点之间创建锯齿形。 它始自柱线的开盘那一刻,且需不少于 9 个走位来创造这个内部的锯齿形。 它总是在柱线收盘时结束,并在下一根柱线上重复该过程。 MetaTrader 5 策略测试器采用相同的逻辑。 更多详细信息,请参阅真实与生成的基差:算法交易。 我们将从这个策略开始。 虽然对于我们的目的来说并不理想,但它将为开拓更多适合的方式提供一个起点。
, Z! j g+ L$ F就我而言,测试策略器不太适合回放/模拟系统,因为在策略测试器中,时间问题并不是最重要的。 这就是,没必要以这种方式创建和复现 1-分钟柱线,因为其长度本来就是 1-分钟。 事实上,它甚至更简略,在于它与现实时间并不对应。 如果是这样的话,那么测试策略就变得不可能。 想象一下,依据跨越几天甚至几年的柱运行测试,如果每根柱线复现时间与实际不同。 这将是一项不可能完成的任务。 然而,对于回放/模拟系统,我们正在寻找一种不同的行动。 我们希望按照 1-分钟间隔创建一根 1-分钟柱线,尽可能接近这个目标。* y. F6 R& O, l! ^8 r" B3 g
准备奠基
. i. f# m5 o, Z我们的重点将完全放在回放/模拟服务代码上。 此时无需担心其它方面。 故此,我们将开始修改 C_Replay 类的代码,尝试尽可能地优化我们已经开发完成并测试过的东西。 下面是类中出现的第一个过程:
% X6 M7 f$ M- s, a1 d; ~& j6 E% T; t0 jinline bool CheckFileIsBar(int &file, const string szFileName)# C/ a' H0 Z/ s6 Z) l$ ~
{
9 U6 O, j H/ e3 j6 I( T3 wstring szInfo = "";
9 L: D: [/ t7 r+ Sbool bRet;
5 A0 ^ X* ^2 J2 Lfor (int c0 = 0; (c0 < 9) && (!FileIsEnding(file)); c0++) szInfo += FileReadString(file);7 x$ `! G9 @& C3 }, i: k/ I+ P
if ((bRet = (szInfo == def_Header_Bar)) == false)1 k4 h: a% L* _, s
{
7 O' x/ `) P+ L$ ?Print("File ", szFileName, ".csv is not a file with bars.");* P7 F: X% S( q- B# P
FileClose(file);- T6 H# E* e. T- a$ \2 K% N
}
' }% _% [, ~7 C. Ireturn bRet;! [# l$ u% z& U$ t$ R; K1 L
}" s( k6 Z2 K" Z9 z
此处的目标是柱线读取函数,这些测试是为了判定指定文件是否为预览柱线文件。 这是必要的,当使用相同代码来判定柱线文件是否符合我们所需时,如此可避免重复代码。 在此状况下,这些柱线将不会用作预览柱线。 它们将被转换为模拟票据,以便在交易系统中使用。 有基于此,我们引入了另一个函数:
; S$ ^4 A8 W5 r! Q( j" Z U1 Z+ Pinline void FileReadBars(int &file, MqlRates &rate[])
$ V* A- O6 L8 @' X) Z; V) V{
! G$ Y c+ w( k) i. Brate[0].time = StringToTime(FileReadString(file) + " " + FileReadString(file));$ T3 v9 y9 Z" V
rate[0].open = StringToDouble(FileReadString(file));
6 c e3 \% Q0 r/ hrate[0].high = StringToDouble(FileReadString(file));' c0 u6 b6 }+ B8 n3 B
rate[0].low = StringToDouble(FileReadString(file));
/ M! [5 O! S* V0 Urate[0].close = StringToDouble(FileReadString(file));/ L2 ?. @3 Q3 o: p) n z8 a3 I
rate[0].tick_volume = StringToInteger(FileReadString(file));. p# A; A& G" R* {
rate[0].real_volume = StringToInteger(FileReadString(file));
, T# f/ L5 }8 z0 X; ~rate[0].spread = (int) StringToInteger(FileReadString(file));" ~. C% O1 c- y: [( \% n
}
) ]$ T/ F# M9 [* q8 N1 `它将从指定文件里逐行读取已有柱线的数据。 我想您在理解这段代码时不会遇到任何困难。 继续这个准备阶段,此处是另一个函数:; D' F2 T0 G7 u" c
inline bool OpenFileBars(int &file, const string szFileName)0 u @: ^2 [& d) H8 R& a# _! e
{
1 K! g/ g) y3 W/ f& Zif ((file = FileOpen("Market Replay\\Bars\\" + szFileName + ".csv", FILE_CSV | FILE_READ | FILE_ANSI)) != INVALID_HANDLE)5 K9 y" q* V9 X. K% P/ l
{
8 C9 c5 b9 z' Vif (!CheckFileIsBar(file, szFileName))3 O2 W1 s! j3 d0 N' B8 n2 N4 y
return false;
4 p; t4 I6 G/ ~& q0 Ureturn true;7 I9 j3 |, F3 O# y8 r6 i
}
/ q# G. L5 _4 z7 D4 r# fPrint("Falha ao acessar ", szFileName, ".csv de barras.");8 D! N* _! r) E6 O" i
return false;
' ]# A# r4 U% f' F4 a7 K7 @& k}
8 _, V) H2 x+ q( w8 D7 r我们的系统现在已经完全集中化,能提供对柱线的标准访问:我们即可把它们用作预览柱线时,亦可把它们用于模拟,并转换为票据表示。 因此,之前加载预览柱线的函数也必须修改,如下所示:
1 o* s _- x" R& Q2 Nbool LoadPrevBars(const string szFileNameCSV)! F1 M9 R g$ G2 g/ f- c% s1 j f
{
% }6 C! k8 o* b, \' Y; \( G6 Fint file,
4 l# A6 ] H% i, _: F+ niAdjust = 0;& c4 [! F: }5 l9 v& a5 d
datetime dt = 0;; Y2 C9 S, s" d' k: ]# _
MqlRates Rate[1];
- O' X, e- K0 g* L. A4 Rif (OpenFileBars(file, szFileNameCSV)) o/ t! v* g' y' E( B9 t5 y
{
; P2 `) X9 A, E: T1 s$ r& y$ oPrint("Loading preview bars for Replay. Please wait....");
3 s8 m7 t/ C$ B jwhile ((!FileIsEnding(file)) && (!_StopFlag))! s1 p7 i1 C( K' d; x* c5 ~. W& L- z( U
{
$ |4 Z/ \# ^2 K oFileReadBars(file, Rate);
1 {# a2 y4 r4 a! J3 ^4 a+ YiAdjust = ((dt != 0) && (iAdjust == 0) ? (int)(Rate[0].time - dt) : iAdjust);
5 Z; ~$ Q( u2 J/ n3 X) Jdt = (dt == 0 ? Rate[0].time : dt);
# G H' Y' e. E. j- q; e5 L6 nCustomRatesUpdate(def_SymbolReplay, Rate, 1);; ], ~1 ^! A, |, S: ?( K- l, e& U
}* a" i, l/ J, B" O
m_dtPrevLoading = Rate[0].time + iAdjust;
( ?$ D7 e+ e a0 l. uFileClose(file);! ] B1 J( Q8 n5 t7 F" p
return (!_StopFlag);" k1 ]. ~& t6 y0 ^0 @3 `
}' R4 m: D6 q- S1 y. @
m_dtPrevLoading = 0;9 ~7 K4 s& I! P+ \, v) R- `
return false;0 k) f! h- `1 w) R+ A5 J
}
* g6 n( b5 q9 [* }& L) d3 G4 }这个下载函数的工作方式并无变化,尽管现在有更多的调用。 从以前的函数中提取一部分,并在新位置加以运用,为我们提供了更高的安全性,因为所有代码都已经过测试。 以这种方式,我们只需要担心新函数。 现在地基已经准备就绪,我们需要在配置文件中添加新内容呢。 该函数旨在判定哪些柱线文件可用于模拟票据。 为此,我们需要添加一个新定义:5 l8 L$ B6 _* T+ R) X* q) [
#define def_STR_FilesBar "[BARS]"! o# m* X* b8 d( ^
#define def_STR_FilesTicks "[TICKS]") A7 c4 a4 X0 R& I9 C* r
#define def_STR_TicksToBars "[TICKS->BARS]" t& r" ^4 I5 v
#define def_STR_BarsToTicks "[BARS->TICKS]"
) G; T) _2 h; T- H i这样我们就可以运行一个简单的测试,这正是我们开始进行模拟所需要的。 |