通用类库 - 错误、说明、问题、使用功能和建议 - 页 12

 
谢尔盖-迪尤布利 克。

如果没有明确实现T类型的GetHashCode 函数,你明白代码是怎么做的吗?
答案是:这是一个无奈之举,因为缺少执行的问题被压制了。同类的所有对象将返回相同的哈希值。

实施(身体)与此有什么关系?我加了这个。

int GetHashCode(T & value)

我从球中插入了身体。

 
fxsaber:

实施(身体)与此有什么关系?我加了这个。

我把身体从头开始装进去。


有人告诉你关于苍蝇的事(你不应该那样做,代码会让你将来生病),而你则继续谈论炸鸡排。
好了,有一个好的胃口。

 

决定看一下所提出的解决方案的速度特性。测试员的专家顾问

#include <MT4Orders.mqh>
#include <Generic\HashMap.mqh>

CHashMap<ulong, double> DealsProfit;

// Создаем историю из Amount сделок в тестере
void CreateHistory( const int Amount, const double Lots = 0.1 )
{
  MqlTick Tick;
  
  if (SymbolInfoTick(_Symbol, Tick) && Tick.ask && Tick.bid)
    for (int i = (Amount >> 1) - 1; i >= 0; i--)
      OrderClose(OrderSend(_Symbol, OP_BUY, Lots, Tick.ask, 0, 0, 0), Lots, Tick.bid, 0);
}

// Заполняем массив случайно выбранными сделками
void GetDeals( const int Amount, const int MaxDealTicket, ulong &Deals[] )
{
  for (int i = ArrayResize(Deals, Amount) - 1; i >= 0; i--)  
    Deals[i] = MathRand() * MaxDealTicket / SHORT_MAX;
}

// Заполнили HashMap
void SetHashMap()
{
  if (HistorySelect(0, INT_MAX))
    for (int i = HistoryDealsTotal() - 1; i >= 0; i--)
    {
      const ulong DealTicket = HistoryDealGetTicket(i);
      
      DealsProfit.Add(DealTicket, HistoryDealGetDouble(DealTicket, DEAL_PROFIT));
    }
}

double GetDealProfitHashClear( const ulong Deal )
{
  static double Profit = 0;
  
  return(DealsProfit.TryGetValue(Deal, Profit) ? Profit : 0);
}

double GetDealProfitFull( const ulong Deal )
{
  return(HistoryDealSelect(Deal) ? HistoryDealGetDouble(Deal, DEAL_PROFIT) : 0);
}

double GetDealProfitClear( const ulong Deal )
{
  return(HistoryDealGetDouble(Deal, DEAL_PROFIT));
}

typedef double (*GetDealProfit)( const ulong );

// Находим суммарный профит сделок из массива
double SumProfit( const ulong &Deals[], GetDealProfit DealProfit )
{
  double Profit = 0;
  
  for (int i = ArraySize(Deals) - 1; i >= 0; i--)
    Profit += DealProfit(Deals[i]);
    
  return(Profit);
}

#define  BENCH(A)                                                              \
{                                                                             \
  const ulong StartTime = GetMicrosecondCount();                              \
  A;                                                                          \
  Print("Time[" + #A + "] = " + (string)(GetMicrosecondCount() - StartTime)); \
} 

int OnInit()
{
  const int Amount = 100000;  
  CreateHistory(Amount); // Создаем историю из Amount сделок в тестере
  
  ulong Deals[];
  GetDeals(Amount, Amount, Deals); // Заполняем массив случайно выбранными сделками

  // Находим суммарный профит сделок из массива
  
  BENCH(Print(SumProfit(Deals, GetDealProfitFull))); // Полноценная классическая реализация
  
  BENCH(SetHashMap()); // Заполнили HashMap
  BENCH(Print(SumProfit(Deals, GetDealProfitHashClear))); // Реализация через HashMap
  
  BENCH(HistorySelect(0, INT_MAX));
  BENCH(Print(SumProfit(Deals, GetDealProfitClear))); // Реализация с предварительно загруженной историей
  
  return(INIT_FAILED);
}

专家顾问开启了100,000次交易,然后用各种方法搜索随机交易的总利润(见评论)。其结果是

2017.12.05 00:00:00   -13133.19999999244
2017.12.05 00:00:00   Time[Print(SumProfit(Deals,GetDealProfitFull))] = 38082
2017.12.05 00:00:00   Time[SetHashMap()] = 57849
2017.12.05 00:00:00   -13133.19999999244
2017.12.05 00:00:00   Time[Print(SumProfit(Deals,GetDealProfitHashClear))] = 7437
2017.12.05 00:00:00   Time[HistorySelect(0,INT_MAX)] = 1
2017.12.05 00:00:00   -13133.19999999244
2017.12.05 00:00:00   Time[Print(SumProfit(Deals,GetDealProfitClear))] = 31669

在这里,我们比较两个突出的数值。事实证明,HashMap的访问速度是开发者的4倍。但在开发商那里,它已经包括了历史...

在这种情况下,4倍的速度是够快还是不够快?那么这里是24毫秒。如果你经常访问历史,你可能可以节省很多。但我不确定。


对于一个更实际的测试案例(2000次交易和1 000 000次单一历史访问),结果如下

2017.12.05 00:00:00   Time[Print(SumProfit(Deals,GetDealProfitFull))] = 122969
2017.12.05 00:00:00   Time[SetHashMap()] = 816
2017.12.05 00:00:00   4829800340.792288
2017.12.05 00:00:00   Time[Print(SumProfit(Deals,GetDealProfitHashClear))] = 23852
2017.12.05 00:00:00   Time[HistorySelect(0,INT_MAX)] = 1
2017.12.05 00:00:00   4829800340.792288
2017.12.05 00:00:00   Time[Print(SumProfit(Deals,GetDealProfitClear))] = 114427

每道工序节省了近100毫秒!如果说,我们做了10000次完整的优化,那么Hash变体最终会快15分钟。

现在给开发商的历史打上 "A "还为时过早。很明显,他们可以加快速度,因为即使是MQL解决方案也越来越快。

 
fxsaber:

决定看一下所提出的解决方案的速度特性。测试员的专家顾问

专家顾问开启了100,000次交易,然后用各种方法搜索随机交易的总利润(见评论)。其结果是

在这里,我们比较两个突出的数值。事实证明,HashMap的访问速度是开发者的4倍。但在开发商那里,它已经包括了历史...

在这种情况下,4倍的速度是够快还是不够快?那么这里是24毫秒。如果你访问历史的次数非常多,你可能可以节省很多钱。但我不确定。

在通过平台的调用中,你在GetDealProfitFull和GetDealProfitClear中分别传递了两次同步对象,并且在每次迭代中都有很多强制性检查。

因此,速度是出了名的慢,比在干净和优化的情况下,一堆嵌套在预先准备好的本地哈希图上工作要慢。

 
Renat Fatkhullin:

当通过平台调用时,你在GetDealProfitFull和GetDealProfitClear中分别传递了两次同步对象,并且在每次迭代中都有很多强制性检查。

因此,与基于预先准备好的带有一堆嵌套的本地哈希图的干净和优化的工作相比,速度本来就比较慢。

纠正了我之前的帖子。这就是为什么双重检查被称为满分。

我不太明白在HistoryDealGetDouble 的Strategy Tester中,我们所说的昂贵的同步对象和大量的检查是什么?
 
fxsaber:

纠正了我之前的帖子。双重检查是它被称为满分的原因。

我不太清楚Tester所说的HistoryDealGetDouble的昂贵同步对象和大量检查是什么?

有什么区别。

  1. 对于hashmap测试,你已经预先将数据填充到本地存储中,具有快速的非同步访问,而在平台调用中,你需要落在存储的内部
  2. 在平台上,存储是另一种类型,不是哈希姆普。
  3. 当在平台上检索一个单一的值时,你需要把请求当作 "第一次",仔细检查所有数据是否正确和存在。


我看了一下我们的代码--有一种方法可以优化对贸易 基础的调用。我们将尝试在下周的发布中实施。

 

雷纳特-法特库林

在平台中,当提取一个单一的值时,我们需要把查询 "当作第一次 "来处理,反复检查正确性和所有数据的存在性。

但是TryGetValue的调用不是没有检查正确性吗?根据日志,你可以看到测试器中的HistorySelect 是免费的。

我不明白的是,为什么要获得一个交易的所有数据,你需要调用一堆昂贵的HistoryDealGet*-函数?只有一次调用来填充MqlDeal-结构。

很明显,当用户想通过HashMap处理历史时,他将填写CHashMap<ulong, MqlDeal>。

也许我们应该制作MqlDealInteger、MqlDealDouble、MqlDealString或类似的东西,以便不增加昂贵的单元调用,因为反正所有历史表都在测试器中?而且,只需检查一次DealTicket的正确性即可,不必每次都检查。

 
fxsaber:

TryGetValue的调用不是为了检查正确性吗?根据日志,你可以看到HistorySelect在测试器中是免费的。

怎么会是免费的呢?这根本就不是免费的。


我不明白的是,为什么要获得一个交易的所有数据,你需要调用一堆昂贵的HistoryDealGet*-函数?只有一次调用来填充MqlDeal-结构。

很明显,当用户想通过HashMap处理历史时,要填入CHashMap<ulong, MqlDeal>。

我们没有一个MqlDeal结构,因为交易记录格式是浮动的,而且是周期性的扩展。没有它,就不可能扩展平台的功能。

因此,唯一的选择是通过Get函数访问它们。而且对以前访问的记录的其他字段的访问比第一次访问快很多倍,因为记录被缓存了。

而且只需检查一次DealTicket的正确性即可,不必每次都检查。

在上面的测试中,每次交易的数量都是新的,这就不断打乱了之前选择的交易 的缓存。另外,不能保证两次通话之间没有发生变化。你仍然可以在请求的历史之间进行交易。

 
雷纳特-法特库林
它怎么会是免费的?一点都不免费。

关于交易、自动交易系统和交易策略测试的论坛

通用类库 - 错误、描述、问题、用例和建议

fxsaber, 2017.12.08 22:46

顾问开了10万笔交易,然后用不同的方法寻找随机交易的总利润(见评论)。结果

2017.12.05 00:00:00   Time[HistorySelect(0,INT_MAX)] = 1

每100,000次交易(和相同数量的订单)1微秒是免费的。这都是关于测试者的。

在上面的测试中,每次的交易数量都是新的,这就不断打乱了以前选择的交易的 缓存。另外,不能保证两次通话之间没有发生变化。你可以在请求之间交换历史。

所以历史(特别是在测试器中)只是补充,旧的记录没有改变。我们谈论的是透明变体。


在真实的市场上,似乎即使一个订单被部分执行并产生了几笔交易,它也不会进入历史,直到它被完全填补或取消。也就是说,冻结的历史规则得到了维护。

 
fxsaber:

对于100,000次交易(和相同数量的订单),1微秒是免费的。所有的时间都是关于测试者的。

测试器中的HistorySelect绝对是虚拟的/假的,在参数0,INT_MAX 的情况下更是如此。这在很久以前就已经被优化了。

你不能比较HistorySelect(把访问范围放在测试器中)和HistoryDealSelect(ticket),后者实际上是在寻找一个特定的票据并将其缓存。