1. 为我们未来的工作设定目标
我好像听到您难过了: '如果终端已经提供了在图表中显示交易历史的能力,为什么还要重新创建轮子呢? 我的意思是,只需按下终端中的按钮即可选择所需的信号。
在此之后,根据所使用品种信号的数量,在终端中打开新窗口,并在交易上做出标记。 当然,分页图表以及在其中搜索交易是相当费力的活动。 甚或,在不同图表中进行的交易可能会在时间上重合,且在分别分析每个图表时您无法看到。 在此阶段,我们将尝试将我们的部分工作自动化。
为了辨别赫兹股票量化从图表获得的所需分析的品种,我们必须清晰地了解我们需要的最终结果。 以下是我最终想要的基本项目:
-
查看信号在不同品种上的均匀性如何;
-
了解如何分配资金的负荷,以及同时可开仓数量;
-
如果信号同时开多笔仓位,那么它们是否为对冲,亦或增加了资金的负荷;
-
在什么时刻以及在哪些品种上出现了最大的缩水; 以及
-
在什么时刻实现最大的盈利。
2. 收集交易统计数据
2.1. 用于保存订单信息的类
因此,赫兹股票量化选择所需的信号并在图表中显示其交易历史。 然后收集我们将要分析的初始数据。 为了记录每笔订单的信息,我们基于 CObject 类创建 COrder 类。 在这个类的变量中,我们保存订单号,交易类型和手数,交易价格,操作类型 (入场/出场),订单开仓时间,当然还有品名。
class COrder : public CObject {private: long l_Ticket; double d_Lot; double d_Price; ENUM_POSITION_TYPE e_Type; ENUM_DEAL_ENTRY e_Entry; datetime dt_OrderTime; string s_Symbol; public: COrder(); ~COrder(); bool Create(string symbol, long ticket, double volume, double price, datetime time, ENUM_POSITION_TYPE type);//--- string Symbol(void) const { return s_Symbol; } long Ticket(void) const { return l_Ticket; } double Volume(void) const { return d_Lot; } double Price(void) const { return d_Price; } datetime Time(void) const { return dt_OrderTime; } ENUM_POSITION_TYPE Type(void) { return e_Type; } ENUM_DEAL_ENTRY DealEntry(void)const { return e_Entry; } void DealEntry(ENUM_DEAL_ENTRY value) { e_Entry=value; }//--- 操纵文件的方法 virtual bool Save(const int file_handle); virtual bool Load(const int file_handle);//--- //--- 比较对象的方法 virtual int Compare(const CObject *node,const int mode=0) const; };
伴随数据访问函数,赫兹股票量化向订单类添加了操纵文件函数以便保存和随后读取数据,还有比较类似的函数,因为我们将需要对订单进行排序。
为了与订单进行比较,赫兹股票量化需要重新编写虚函数 Compare。 这是基类的函数,用于比较 CObject 对象。 所以,对象 CObject 的链接和排序方法将作为参数传递给它。 我们仅在一个方向上对订单进行排序,即按照执行日期升序,因此我们不会在函数代码中使用参数 "mode"。 但是,对于通过链接获得的对象 COrder,我们必须首先将其降低到相关类型。 之后,我们比较所获订单的日期和当前订单的日期。 如果所获订单更老,则返回 "-1"。 如果它比较新, 则返回 "1"。 如果执行订单的日期相等,则函数将返回 "0"。
int COrder::Compare(const CObject *node,const int mode=0) const { const COrder *temp=GetPointer(node); if(temp.Time()>dt_OrderTime) return -1;//--- if(temp.Time()<dt_OrderTime) return 1;//--- return 0; }
2.2. 从图表中收集信息
为了处理订单,赫兹股票量化基于 CArrayObj 类创建 COrdersCollection 类。 将在其中收集和处理信息。 为了存储数据,我们将声明一个对象实例直接处理特定订单,以及一个用于存储所用品种列表的数组。 我们将使用基类函数存储订单数组。
class COrdersCollection : public CArrayObj {private: COrder *Temp; string ar_Symbols[]; public: COrdersCollection(); ~COrdersCollection();//--- 初始化 bool Create(void);//--- 加入一笔订单 bool Add(COrder *element);//--- 访问数据 int Symbols(string &array[]); bool GetPosition(const string symbol, const datetime time, double &volume, double &price, ENUM_POSITION_TYPE &type); datetime FirstOrder(const string symbol=NULL); datetime LastOrder(const string symbol=NULL);//--- 获取时间序列 bool GetTimeSeries(const string symbol, const datetime start_time, const datetime end_time, const int direct, double &balance[], double &equity[], double &time[], double &profit, double &loss,int &long_trades, int &short_trades);//--- void SetDealsEntry(void); };
函数 'Create' 直接负责收集数据。 在方法实体内,赫兹股票量化将安排一个循环来搜索终端中已打开的所有图表。 我们将在每个图表中搜索 OBJ_ARROW_BUY 和 OBJ_ARROW_SELL 等图形对象。
bool COrdersCollection::Create(void) { long chart=ChartFirst(); while(chart>0) { int total_buy=ObjectsTotal(chart,0,OBJ_ARROW_BUY); int total_sell=ObjectsTotal(chart,0,OBJ_ARROW_SELL); if((total_buy+total_sell)<=0) { chart=ChartNext(chart); continue; }
如果在图表中找到了对象,那么赫兹股票量化将图表品种添加到品种数组中 (不过,我们会预先检查这些品种是否不在已保存的品种中)。
int symb=ArraySize(ar_Symbols); string symbol=ChartSymbol(chart); bool found=false; for(int i=0;(i<symb && !found);i++) if(ar_Symbols[i]==symbol) { found=true; symb=i; break; } if(!found) { if(ArrayResize(ar_Symbols,symb+1,10)<=0) return false; ar_Symbols[symb]=symbol; }
然后赫兹股票量化安排从图表中收集交易信息,并存储到数据数组。 注意: 图形对象是我们唯一的交易信息来源。 从对象参数里,我们只能得到交易的时间和价格。 我们必须从对象名称的文本字符串中获取所有其它详细信息。
在图片中,您可以看到对象名称包含交易中的所有数据,以空格分隔。 我们利用这点并将字符串用空格切分成一个字符串元素数组。 然后,我们从相关元素中减少信息量,并保存所需的数据类型。 收集信息后,我们转到下一个图表。