规划
+ F+ p, ?/ u# ?5 H7 j" Y$ o ^许多人可能认为规划很容易,特别是,由于它事关转换柱线,即始终把 1-分钟柱线转换为票据(我们稍后会解释)。 然而,模拟比表面看要复杂得多。 主要问题在于我们尚未清晰地理解票据的实际行为,就去创建 1-分钟柱线。 我们只有柱线,和一些它的有关信息,但我们并不知道柱线是如何形成的。 我们将采用 1-分钟柱线,因为它们所需的难度最小。 如果您能创造一个与真实事物非常相似的复杂走位,那么您就能够再现一些非常接近真实事物的东西。" z& G; P6 [9 o' u5 ^4 B: D3 \. J
这个细节似乎并不那么重要,因为我们通常会在市场上看到锯齿形的走位。 无论走位如何复杂,这一切都归结为在 OHCL 点之间创建锯齿形。 它始自柱线的开盘那一刻,且需不少于 9 个走位来创造这个内部的锯齿形。 它总是在柱线收盘时结束,并在下一根柱线上重复该过程。 MetaTrader 5 策略测试器采用相同的逻辑。 更多详细信息,请参阅真实与生成的基差:算法交易。 我们将从这个策略开始。 虽然对于我们的目的来说并不理想,但它将为开拓更多适合的方式提供一个起点。, y1 p B5 i/ ~, x
就我而言,测试策略器不太适合回放/模拟系统,因为在策略测试器中,时间问题并不是最重要的。 这就是,没必要以这种方式创建和复现 1-分钟柱线,因为其长度本来就是 1-分钟。 事实上,它甚至更简略,在于它与现实时间并不对应。 如果是这样的话,那么测试策略就变得不可能。 想象一下,依据跨越几天甚至几年的柱运行测试,如果每根柱线复现时间与实际不同。 这将是一项不可能完成的任务。 然而,对于回放/模拟系统,我们正在寻找一种不同的行动。 我们希望按照 1-分钟间隔创建一根 1-分钟柱线,尽可能接近这个目标。
; K u' g, j' e% m准备奠基/ I8 T7 L1 u9 r: l" q# d# s
我们的重点将完全放在回放/模拟服务代码上。 此时无需担心其它方面。 故此,我们将开始修改 C_Replay 类的代码,尝试尽可能地优化我们已经开发完成并测试过的东西。 下面是类中出现的第一个过程:5 J, q6 a& A! M/ t8 V6 r2 D6 H3 e
inline bool CheckFileIsBar(int &file, const string szFileName)
1 F) N$ M7 p/ L/ l2 b{* n. z! a5 }% G. o
string szInfo = "";
; u; m% ~9 F% n! m9 x, u( bbool bRet;
. v( i: t$ F W/ ? P- \6 sfor (int c0 = 0; (c0 < 9) && (!FileIsEnding(file)); c0++) szInfo += FileReadString(file);
8 w. x. V& v8 \- [5 R9 G) t: K8 Nif ((bRet = (szInfo == def_Header_Bar)) == false)
2 f7 W4 r6 N; r" H{! b9 a8 o* _, ?6 A; z2 O& }
Print("File ", szFileName, ".csv is not a file with bars.");
- z# O6 }4 W% S: \FileClose(file);
: Q% ?' k5 U6 O: A}1 h4 _$ [+ n! I. E2 z, G( l
return bRet;
# l1 ^9 L1 W+ j W- o7 l}" M: [9 k. C/ F: P& M
此处的目标是柱线读取函数,这些测试是为了判定指定文件是否为预览柱线文件。 这是必要的,当使用相同代码来判定柱线文件是否符合我们所需时,如此可避免重复代码。 在此状况下,这些柱线将不会用作预览柱线。 它们将被转换为模拟票据,以便在交易系统中使用。 有基于此,我们引入了另一个函数:
% s- u4 b5 H, f+ }" p; }inline void FileReadBars(int &file, MqlRates &rate[])% ^% l2 u1 V" ~+ ]$ H2 K, F1 w
{
% T- h1 [' I- y+ S2 S7 ^7 a" t& V: Irate[0].time = StringToTime(FileReadString(file) + " " + FileReadString(file));
) l) g `' |- Z1 H# frate[0].open = StringToDouble(FileReadString(file));
& x$ H! S, X3 v. K) Nrate[0].high = StringToDouble(FileReadString(file));) W4 }" B8 l9 P# O, P2 H" @9 y$ b+ f0 ^
rate[0].low = StringToDouble(FileReadString(file));
) I% V6 T$ J2 _1 l0 }rate[0].close = StringToDouble(FileReadString(file));; T0 X7 c1 k5 L) B" G9 f
rate[0].tick_volume = StringToInteger(FileReadString(file));* m9 t8 P. j# D4 V( k2 M6 E
rate[0].real_volume = StringToInteger(FileReadString(file));9 ]; X6 H' M7 L$ ~% |, t* r
rate[0].spread = (int) StringToInteger(FileReadString(file));) R- `/ g V2 U& y* {' j2 n* g
}
; z1 {& h) a& Q! j8 V它将从指定文件里逐行读取已有柱线的数据。 我想您在理解这段代码时不会遇到任何困难。 继续这个准备阶段,此处是另一个函数:5 l, q& w- d7 S* B- H2 P
inline bool OpenFileBars(int &file, const string szFileName)
2 B3 X0 F2 E( U{0 Z Y6 U! _5 ?4 Q7 d" ^
if ((file = FileOpen("Market Replay\\Bars\\" + szFileName + ".csv", FILE_CSV | FILE_READ | FILE_ANSI)) != INVALID_HANDLE)
" `/ C5 b [% _0 Y/ n% f5 Y0 B8 ^6 H{/ `+ S" M! @( V4 M1 n6 D
if (!CheckFileIsBar(file, szFileName))
* F- n7 |) Q; }$ s1 ?$ ^3 u- wreturn false;" }% {; z% Y' q% i! \! u) B
return true;
$ p, B$ i1 D4 n/ F}
& c( \6 V9 n% w- i+ E7 {Print("Falha ao acessar ", szFileName, ".csv de barras.");
0 f5 @3 j7 Z/ M3 @return false;
# Q7 B6 P$ z& ?5 Y8 G3 E}: E q! X* b) v* h
我们的系统现在已经完全集中化,能提供对柱线的标准访问:我们即可把它们用作预览柱线时,亦可把它们用于模拟,并转换为票据表示。 因此,之前加载预览柱线的函数也必须修改,如下所示:2 g) l3 q! ^7 z* R* K
bool LoadPrevBars(const string szFileNameCSV)( g: i3 J$ w7 G1 g0 ]5 ~; x+ ]
{
5 w9 E2 X5 ?2 r- @% l+ Sint file,% }: y$ e9 J/ ]( ]! ]
iAdjust = 0;
; _' S& a6 @7 vdatetime dt = 0;
- _+ ?1 g" ?) Y0 EMqlRates Rate[1];
O6 E! ]! t, A+ ^$ [9 h( Zif (OpenFileBars(file, szFileNameCSV))5 e3 `6 r* b) }6 Q- Y1 S/ T% g
{! l* ]' P, M! N# \/ B8 e" M7 j
Print("Loading preview bars for Replay. Please wait....");0 P( g2 \+ `$ M$ s- t* ~
while ((!FileIsEnding(file)) && (!_StopFlag))! d& {0 c$ H7 c( }+ q' g3 M0 u
{
( X( s' ?( y6 E& o8 z9 e- xFileReadBars(file, Rate);! ^/ `# P3 g: C* L) c
iAdjust = ((dt != 0) && (iAdjust == 0) ? (int)(Rate[0].time - dt) : iAdjust);
$ R' a) I+ f8 u! @; J" K, B4 x3 i Ndt = (dt == 0 ? Rate[0].time : dt);# U5 p$ |# g' [6 _
CustomRatesUpdate(def_SymbolReplay, Rate, 1);
^/ m5 F4 @9 c0 b! s: o# s}6 q% @ ^& j5 a2 j: R' v, L
m_dtPrevLoading = Rate[0].time + iAdjust;
. o9 A8 Z" G- F4 }- _, @5 UFileClose(file);" n, h4 w; F9 j$ k2 L4 R
return (!_StopFlag);0 U6 q$ x- n) V. ?) O8 j1 E
}
! C9 @4 c" U. M4 o$ v1 Pm_dtPrevLoading = 0;
0 K, s/ ~) f1 _% k$ T4 Rreturn false;
\2 @; c0 [0 T2 P6 f3 L& {}* M D! t0 a" Z% G* G# K: ~3 e7 o
这个下载函数的工作方式并无变化,尽管现在有更多的调用。 从以前的函数中提取一部分,并在新位置加以运用,为我们提供了更高的安全性,因为所有代码都已经过测试。 以这种方式,我们只需要担心新函数。 现在地基已经准备就绪,我们需要在配置文件中添加新内容呢。 该函数旨在判定哪些柱线文件可用于模拟票据。 为此,我们需要添加一个新定义:
: w2 x* B- }- o1 p4 `3 K) d#define def_STR_FilesBar "[BARS]"0 R' I8 g1 r b3 R, A4 U0 t" k
#define def_STR_FilesTicks "[TICKS]"
9 A& O) ]" ~9 ?- E' \#define def_STR_TicksToBars "[TICKS->BARS]"* v2 T, p9 ?7 `0 a' j" K' y
#define def_STR_BarsToTicks "[BARS->TICKS]". Y6 k1 s# j- {) V1 d
这样我们就可以运行一个简单的测试,这正是我们开始进行模拟所需要的。 |