规划+ C8 }$ T9 l2 [- Q" \ y/ Z" f
许多人可能认为规划很容易,特别是,由于它事关转换柱线,即始终把 1-分钟柱线转换为票据(我们稍后会解释)。 然而,模拟比表面看要复杂得多。 主要问题在于我们尚未清晰地理解票据的实际行为,就去创建 1-分钟柱线。 我们只有柱线,和一些它的有关信息,但我们并不知道柱线是如何形成的。 我们将采用 1-分钟柱线,因为它们所需的难度最小。 如果您能创造一个与真实事物非常相似的复杂走位,那么您就能够再现一些非常接近真实事物的东西。
( P% u$ d" Y/ E% d5 X这个细节似乎并不那么重要,因为我们通常会在市场上看到锯齿形的走位。 无论走位如何复杂,这一切都归结为在 OHCL 点之间创建锯齿形。 它始自柱线的开盘那一刻,且需不少于 9 个走位来创造这个内部的锯齿形。 它总是在柱线收盘时结束,并在下一根柱线上重复该过程。 MetaTrader 5 策略测试器采用相同的逻辑。 更多详细信息,请参阅真实与生成的基差:算法交易。 我们将从这个策略开始。 虽然对于我们的目的来说并不理想,但它将为开拓更多适合的方式提供一个起点。( n: N# m8 t2 R) t9 H
就我而言,测试策略器不太适合回放/模拟系统,因为在策略测试器中,时间问题并不是最重要的。 这就是,没必要以这种方式创建和复现 1-分钟柱线,因为其长度本来就是 1-分钟。 事实上,它甚至更简略,在于它与现实时间并不对应。 如果是这样的话,那么测试策略就变得不可能。 想象一下,依据跨越几天甚至几年的柱运行测试,如果每根柱线复现时间与实际不同。 这将是一项不可能完成的任务。 然而,对于回放/模拟系统,我们正在寻找一种不同的行动。 我们希望按照 1-分钟间隔创建一根 1-分钟柱线,尽可能接近这个目标。
3 o/ W% l& n* k% D' T准备奠基0 `- M' e/ J: Q
我们的重点将完全放在回放/模拟服务代码上。 此时无需担心其它方面。 故此,我们将开始修改 C_Replay 类的代码,尝试尽可能地优化我们已经开发完成并测试过的东西。 下面是类中出现的第一个过程:
# [" V3 }7 M3 r& Rinline bool CheckFileIsBar(int &file, const string szFileName)6 Z- X% ^6 p/ M- E
{0 a$ y! ^" |+ I3 @# ]3 {. N* ]! h/ R
string szInfo = "";
& J: _) E* s, R4 i3 m- T) F. W" Mbool bRet;( T1 w7 E+ {( `4 e( E0 D
for (int c0 = 0; (c0 < 9) && (!FileIsEnding(file)); c0++) szInfo += FileReadString(file);. K0 L3 K9 w8 D, C5 _
if ((bRet = (szInfo == def_Header_Bar)) == false)4 [) B$ ?% @5 Y, V
{
4 h) B6 r( [- T5 }Print("File ", szFileName, ".csv is not a file with bars.");6 ` }3 b0 r6 C: s t* R$ E) B
FileClose(file);; h6 K+ ? P+ J5 K4 [+ N
} K3 Q7 x; h/ z/ e
return bRet;
9 _7 k8 u+ x4 U}
" `8 V5 j4 I+ I; K2 [此处的目标是柱线读取函数,这些测试是为了判定指定文件是否为预览柱线文件。 这是必要的,当使用相同代码来判定柱线文件是否符合我们所需时,如此可避免重复代码。 在此状况下,这些柱线将不会用作预览柱线。 它们将被转换为模拟票据,以便在交易系统中使用。 有基于此,我们引入了另一个函数:. R/ X/ \2 U+ K0 B/ a0 b
inline void FileReadBars(int &file, MqlRates &rate[])2 u8 t( _5 O3 p2 }- ?+ l, z& R
{! f* I& w4 O: v% d; n
rate[0].time = StringToTime(FileReadString(file) + " " + FileReadString(file));
( k, c7 H+ t( @# g n6 lrate[0].open = StringToDouble(FileReadString(file));
9 t! H+ q7 M" ~1 p& [& c) W' xrate[0].high = StringToDouble(FileReadString(file));& @- d; H. L1 w# @& W: ^
rate[0].low = StringToDouble(FileReadString(file));5 c7 \0 \$ ^: E" o" l3 H" R1 O
rate[0].close = StringToDouble(FileReadString(file));
1 a. I: W* x7 o6 @rate[0].tick_volume = StringToInteger(FileReadString(file));; k) P& {3 z- k5 w9 }/ ^1 @6 |
rate[0].real_volume = StringToInteger(FileReadString(file));7 G3 K) X- Y) h
rate[0].spread = (int) StringToInteger(FileReadString(file));3 J& @! x6 J! w( g) Y! k; r
}
* v) y0 k9 U8 [- ^ M它将从指定文件里逐行读取已有柱线的数据。 我想您在理解这段代码时不会遇到任何困难。 继续这个准备阶段,此处是另一个函数:
/ ?1 d& k; p" b9 Pinline bool OpenFileBars(int &file, const string szFileName)
# L/ X8 L( a5 ?, J/ N& Y+ H. N% O{
/ ]# n/ k) q0 @( h% Cif ((file = FileOpen("Market Replay\\Bars\\" + szFileName + ".csv", FILE_CSV | FILE_READ | FILE_ANSI)) != INVALID_HANDLE)
# i7 o i# [4 K1 s, }6 r) h# K7 k{
3 ?9 D3 C$ z q% Oif (!CheckFileIsBar(file, szFileName))
# s+ A1 @! a& ?! |4 y. Areturn false;
3 n: [8 Q+ H6 W7 X1 Nreturn true;
- f) m! _9 k$ x% C}
0 Y4 ?3 s4 I$ t' n0 L( n; [1 WPrint("Falha ao acessar ", szFileName, ".csv de barras.");
0 m; l8 @% z( I0 Ureturn false;
' y) i9 d, K& \+ i' d}) g' M% |# n) x5 O9 e; H, \5 a
我们的系统现在已经完全集中化,能提供对柱线的标准访问:我们即可把它们用作预览柱线时,亦可把它们用于模拟,并转换为票据表示。 因此,之前加载预览柱线的函数也必须修改,如下所示:
$ E0 P$ i7 L9 r. f2 ebool LoadPrevBars(const string szFileNameCSV)9 V' m* [2 m& r. X7 N5 A* T6 }; c2 m
{
) U' ~* X, x" R q0 T* }int file," s( s; D3 p% ?- ]& F6 T+ J
iAdjust = 0;& t' ?9 P6 V, Y6 I# b4 K) e
datetime dt = 0;/ D4 }3 u& M) f4 b% u, ^2 V
MqlRates Rate[1];. M8 q- u. l+ P$ s6 l |$ ~
if (OpenFileBars(file, szFileNameCSV))
" K' y8 d# Z$ h5 k: ^) s3 {{& o7 I( U$ `% v
Print("Loading preview bars for Replay. Please wait....");
7 {) T$ W S8 \8 ewhile ((!FileIsEnding(file)) && (!_StopFlag)), f$ U0 ?# H/ l+ e; ]8 M* `
{! Q w) q7 m$ l+ d+ f7 y r
FileReadBars(file, Rate);- } U r7 [ J$ Y. J1 T2 U- k% p, W/ N
iAdjust = ((dt != 0) && (iAdjust == 0) ? (int)(Rate[0].time - dt) : iAdjust);
/ i% G c4 r; A0 q1 u- rdt = (dt == 0 ? Rate[0].time : dt);
2 k! C* l- @ j) S" _( ^# r5 KCustomRatesUpdate(def_SymbolReplay, Rate, 1);
0 e- e7 D, }* Q% b}
5 o. Z) v( M7 B% a* Wm_dtPrevLoading = Rate[0].time + iAdjust;5 E, ?- n4 L3 T: P
FileClose(file);9 x5 m- {7 q7 S( ^' y
return (!_StopFlag);( g( B1 H+ I+ [& C& |' h% F. G1 V
}1 k1 a( q. |# ?, P4 N0 H
m_dtPrevLoading = 0;
z6 C( x+ n$ l3 ?% Mreturn false;- }# a4 O) R# `1 B
}4 e# t; y# L' t5 H, }
这个下载函数的工作方式并无变化,尽管现在有更多的调用。 从以前的函数中提取一部分,并在新位置加以运用,为我们提供了更高的安全性,因为所有代码都已经过测试。 以这种方式,我们只需要担心新函数。 现在地基已经准备就绪,我们需要在配置文件中添加新内容呢。 该函数旨在判定哪些柱线文件可用于模拟票据。 为此,我们需要添加一个新定义:7 N" y1 [8 [! C! `+ p
#define def_STR_FilesBar "[BARS]"6 }; X9 F; F; I- F0 X2 _& T
#define def_STR_FilesTicks "[TICKS]"( M& o- v/ N, h- s4 C; P G
#define def_STR_TicksToBars "[TICKS->BARS]"
: M5 o( q) y+ N7 m, I#define def_STR_BarsToTicks "[BARS->TICKS]"
1 Y$ S* h' \0 D) m) C这样我们就可以运行一个简单的测试,这正是我们开始进行模拟所需要的。 |