Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий
fxsaber, 2016.10.04 11:28
// Индикатор в виде гистограммы показывает проторгованный оборот BUY и SELL #property indicator_separate_window #property indicator_buffers 2 #property indicator_plots 2 #property indicator_label1 "TurnOver_BUY" #property indicator_type1 DRAW_HISTOGRAM #property indicator_style1 STYLE_SOLID #property indicator_color1 clrYellow #property indicator_label2 "TurnOver_SELL" #property indicator_type2 DRAW_HISTOGRAM #property indicator_style2 STYLE_SOLID #property indicator_color2 clrRed long LastTime = 0; // time_msc-время последнего тика (самого свежего), полученного из истории int Count = 0; // Количество тиков в последенем запросе, у которых time_msc == LastTime double Buffer0[]; double Buffer1[]; // Возвращает свежие тики, пришедшие после предыдущего вызова int GetFreshTicks( MqlTick &Ticks[], const uint flags = COPY_TICKS_TRADE, const uint count = 100000 ) { int Res = 0; MqlTick NewTicks[]; const int NewAmount = CopyTicks(Symbol(), NewTicks, flags, LastTime, count); if ((NewAmount > 0) && (Count < NewAmount)) { Res = ArrayCopy(Ticks, NewTicks, 0, Count); // Взяли крайнее время из текущей истории LastTime = NewTicks[NewAmount - 1].time_msc; Count = 1; // Находим (Count) в текущей истории количество тиков со временем LastTime for (int i = NewAmount - 2; i >= 0; i--) { if (NewTicks[i].time_msc < LastTime) break; Count++; } } return(Res); } // Ширина гистограммы void SetScale() { const int Width = 9 / (1 << (5 - (int)ChartGetInteger(0, CHART_SCALE))); if (Width != PlotIndexGetInteger(0, PLOT_LINE_WIDTH)) { PlotIndexSetInteger(0, PLOT_LINE_WIDTH, Width); PlotIndexSetInteger(1, PLOT_LINE_WIDTH, Width); ChartRedraw(); } } void OnInit() { SetIndexBuffer(0, Buffer0); SetIndexBuffer(1, Buffer1); } void OnChartEvent( const int id, const long &lparam, const double &dparam, const string &sparam ) { if (id == CHARTEVENT_CHART_CHANGE) SetScale(); } // Заполняет бар соответствующими ему тиками void SetBarData( const datetime TimeBar, const MqlTick &Ticks[], int &Pos, double &TurnOverBuy, double &TurnOverSell ) { const datetime NextTime = TimeBar + PeriodSeconds(); const int Amount = ArraySize(Ticks); while (Pos < Amount) { const MqlTick Tick = Ticks[Pos]; if (Tick.time >= NextTime) break; if ((bool)(Tick.flags & TICK_FLAG_BUY)) TurnOverBuy += (double)Tick.volume; else if ((bool)(Tick.flags & TICK_FLAG_SELL)) TurnOverSell -= (double)Tick.volume; Pos++; } return; } int OnCalculate( const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[] ) { if (prev_calculated == 0) { // возьмем тики с начала утренней сессии LastTime = (TimeCurrent() - (TimeCurrent() % (24 * 3600))) * 1000; Count = 0; } // Зануляем новые бары if (rates_total > prev_calculated) { ArrayFill(Buffer0, prev_calculated, rates_total - prev_calculated, 0); ArrayFill(Buffer1, prev_calculated, rates_total - prev_calculated, 0); } MqlTick Ticks[]; // Взяли свеженькие тики const int Amount = GetFreshTicks(Ticks); if (Amount > 0) { int Pos; const datetime LastTime2 = Ticks[0].time - (Ticks[0].time % PeriodSeconds()); // Находим бар (Pos), включающий первый свежий тик for (Pos = rates_total - 1; Pos >= 0; Pos--) if (time[Pos] == LastTime2) break; if (Pos >= 0) { int i = 0; // Заполняем бары своими тиками while ((Pos < rates_total) && (i < Amount)) { SetBarData(time[Pos], Ticks, i, Buffer0[Pos], Buffer1[Pos]); Pos++; } } } return(rates_total); }
Вопросы:
1.Какой размер массива должен быть, чтобы не пропустить сделки?
2. Как быстро "вытащить" НОВЫЕ сделки, если предыдущие тики храняться в mem_ticks_array такого же размера ?
3. Может быть есть альтернативное решение быстрого получкения новых сделок, после срабатывания OnBookEvent ?
1. Для этого, полагаю, нужно написать тестовый индикатор и оставить его хотя бы на день на интересующем Вас символе. Индикатор как раз должен считать сколько максимально пришло сделок с момента последнего запроса сделок (как сохранить время/считать показал fxsaber);
2 и 3. Какая задача? Просто, по факту размер сделок, полученных с последнего запроса часто будет разным (вероятно, что даже 100 может не всегда хватить).
1. Для этого, полагаю, нужно написать тестовый индикатор и оставить его хотя бы на день на интересующем Вас символе. Индикатор как раз должен считать сколько максимально пришло сделок с момента последнего запроса сделок (как сохранить время/считать показал fxsaber);
2 и 3. Какая задача? Просто, по факту размер сделок, полученных с последнего запроса часто будет разным (вероятно, что даже 100 может не всегда хватить).
2 и 3 Есть два одинаковых массива с предыдущими и текущими тиками.
Как быстро "отсеять" совпадающие данные. (т.е получить свежие сделки)?
2 и 3 Есть два одинаковых массива с предыдущими и текущими тиками.
Как быстро "отсеять" совпадающие данные. (т.е получить свежие сделки)?
Свежие сделки, исключительно свежие сделки получать нужно так, как у fxsaber'a. Т.е. делаете запрос, запоминаете время последней сделки (savedTime) в запросе и количество сделок (savedNum) в этот момент (момент последней сделки). Далее снова делаете запрос c момента времени savedTime (т.к. сразу может прийти не вся пачка миллисекунды savedTime). И вычитаете сохраненное (ранее полученное savedNum) количество сделок первой миллисекунды - считаете только новые сделки. И так по кругу.
Правда, я бы еще проверил (на всякий случай) последовательность сделок момента savedTime. Вдруг что..
Свежие сделки, исключительно свежие сделки получать нужно так, как у fxsaber'a. Т.е. делаете запрос, запоминаете время последней сделки (savedTime) в запросе и количество сделок (savedNum) в этот момент (момент последней сделки). Далее снова делаете запрос c момента времени savedTime (т.к. сразу может прийти не вся пачка миллисекунды savedTime). И вычитаете сохраненное (ранее полученное savedNum) количество сделок первой миллисекунды - считаете только новые сделки. И так по кругу.
Правда, я бы еще проверил (на всякий случай) последовательность сделок момента savedTime. Вдруг что..
Думакется, что нельзя полагатьься что в новых данных нет старых.
Допустим, при инициализации мы запомлили стартовое время.
В следующем проходе мы получили, например 10 сделок, а в последующем 8.
Иак вот в этих 8 сделках могут быть 2 сделки из предыдущего прохода с совпадающим временем, т.к
на высоколиквидных инструментах много сделок с одинаковым временем.
Думакется, что нельзя полагатьься что в новых данных нет старых.
Допустим, при инициализации мы запомлили стартовое время.
В следующем проходе мы получили, например 10 сделок, а в последующем 8.
Иак вот в этих 8 сделках могут быть 2 сделки из предыдущего прохода с совпадающим временем, т.к
на высоколиквидных инструментах много сделок с одинаковым временем.
Можете посмотреть в моей статье алгоритм, как я получаю сделки. Правда у меня конкретно вопрос пачки (когда несколько сделок с одинаковым временем) решен иначе. Я учитываю пачку только тогда, когда она полностью сформирована (т.е. началась следующая пачка). И порядок сделок в пачке мне не важен.
Опять таки, если Вам нужны строго новые тики Вы все равно должны делать запрос с момента последнего тика предыдущей пачки. Только вот сделки, которые были получены в предыдущем запросе, Вы должны идентифицировать и не учитывать их. По идее (повторюсь, это надо проверить) тики должны идти по порядку.
Можете посмотреть в моей статье алгоритм, как я получаю сделки. Правда у меня конкретно вопрос пачки (когда несколько сделок с одинаковым временем) решен иначе. Я учитываю пачку только тогда, когда она полностью сформирована (т.е. началась следующая пачка). И порядок сделок в пачке мне не важен.
Опять таки, если Вам нужны строго новые тики Вы все равно должны делать запрос с момента последнего тика предыдущей пачки. Только вот сделки, которые были получены в предыдущем запросе, Вы должны идентифицировать и не учитывать их. По идее (повторюсь, это надо проверить) тики должны идти по порядку.
Вспомнил, я же писал Лента всех сделок
Спасибо всем
Работает точно и быстро
Желтым - изменения
//+------------------------------------------------------------------+ //| Stakan.mqh | //| Copyright 2018 prostotrader | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2018 prostotrader" #property link "https://www.mql5.com" //--- #define BOOK_DEPTH 20 #define TICKS_COUNT 100 //--- struct STAKAN_ELEMENT { double price; long volume; }; struct DEALS { long sell_cnt; long buy_cnt; long sell_volume; long buy_volume; }; struct OPERATION_TIME { ulong start_count; ulong end_count;; ulong last_tick_time; }; struct STAKAN_DATA { STAKAN_ELEMENT sell_data[BOOK_DEPTH]; STAKAN_ELEMENT buy_data[BOOK_DEPTH]; DEALS deals; int sell_cnt; int buy_cnt; int max_sell_index; int min_sell_index; int max_buy_index; int min_buy_index; long ses_deals; long ses_sell_orders; long ses_buy_orders; long symb_vol_high; long symb_vol_low; int spread; double best_bid; double bid_high; double best_ask; double ask_high; double ask_low; double bid_low; double last; double last_high; double last_low; double ses_volume; double ses_turmover; double ses_buy_ord_volume; double ses_sell_ord_volume; double ses_interest; double ses_aw; }; //--- class class CStakan { private: //--- Variables STAKAN_ELEMENT temp_data[BOOK_DEPTH]; STAKAN_DATA stakan_data; OPERATION_TIME oper_time; bool set_book; string st_symbol; MqlBookInfo book_data[]; MqlTick ticks_array[]; MqlTick mem_tick_array[]; uint ticks_cnt; //--- functions void GetOtherData(); public: CStakan(void); ~CStakan(void); //---Functions bool SetStakanSymbol(const string st_symbol); bool RefreshStData(); STAKAN_DATA GetStData(); }; //+------------------------------------------------------------------+ // Constructor | //+------------------------------------------------------------------+ CStakan::CStakan(void) { ticks_cnt = uint(TICKS_COUNT); set_book = false; ZeroMemory(stakan_data); } //+------------------------------------------------------------------+ // Destructor | //+------------------------------------------------------------------+ CStakan::~CStakan(void) { if(set_book == true) MarketBookRelease(st_symbol); } //+------------------------------------------------------------------+ //| Set stakan symnol function | //+------------------------------------------------------------------+ bool CStakan::SetStakanSymbol(const string st_symb) { if(SymbolSelect(st_symb, true) == true) { st_symbol = st_symb; set_book = MarketBookAdd(st_symbol); //--- Set ticks oper_time.start_count = GetMicrosecondCount(); uint result = uint(CopyTicks(st_symbol, mem_tick_array, COPY_TICKS_TRADE, 0, 1)); if(result < 1) return(false); oper_time.last_tick_time = mem_tick_array[0].time_msc; } return(set_book); } //+------------------------------------------------------------------+ //| Refresh Stakan Data function | //+------------------------------------------------------------------+ bool CStakan::RefreshStData(void) { if((MarketBookGet(st_symbol, book_data) == true) && (set_book == true))//getBook ) { int size = ArraySize(book_data); if(size > 0) { ZeroMemory(temp_data); long max_vol = 0; long min_vol = 0; stakan_data.max_buy_index = -1; stakan_data.min_buy_index = -1; stakan_data.max_sell_index = -1; stakan_data.min_sell_index = -1; stakan_data.buy_cnt = 0; stakan_data.sell_cnt = 0; for(int i = 0; i < size; i++) { if((book_data[i].type == BOOK_TYPE_SELL) || (book_data[i].type == BOOK_TYPE_SELL_MARKET)) { temp_data[stakan_data.sell_cnt].price = book_data[i].price; temp_data[stakan_data.sell_cnt].volume = book_data[i].volume; if(stakan_data.sell_cnt == 0) { max_vol = book_data[i].volume; min_vol = book_data[i].volume; stakan_data.max_sell_index = stakan_data.sell_cnt; stakan_data.min_sell_index = stakan_data.sell_cnt; } else { if(book_data[i].volume > max_vol) { stakan_data.max_sell_index = stakan_data.sell_cnt; } else if(book_data[i].volume < min_vol) { stakan_data.min_sell_index = stakan_data.sell_cnt; } } stakan_data.sell_cnt++; } else if((book_data[i].type == BOOK_TYPE_BUY) || (book_data[i].type == BOOK_TYPE_BUY_MARKET)) { //--- get buy data stakan_data.buy_data[stakan_data.buy_cnt].price = book_data[i].price; stakan_data.buy_data[stakan_data.buy_cnt].volume = book_data[i].volume; if(stakan_data.buy_cnt == 0) { max_vol = book_data[i].volume; min_vol = book_data[i].volume; stakan_data.max_buy_index = stakan_data.buy_cnt; stakan_data.min_buy_index = stakan_data.buy_cnt; } else { if(book_data[i].volume > max_vol) { stakan_data.max_buy_index = stakan_data.buy_cnt; } else if(book_data[i].volume < min_vol) { stakan_data.min_buy_index = stakan_data.buy_cnt; } } stakan_data.buy_cnt++; } } //--- set sell data stakan_data.max_sell_index = stakan_data.sell_cnt - 1 - stakan_data.max_sell_index; stakan_data.min_sell_index = stakan_data.sell_cnt - 1 - stakan_data.min_sell_index; for(int j = 0; j < stakan_data.sell_cnt; j++) { stakan_data.sell_data[j].price = temp_data[stakan_data.sell_cnt - 1 - j].price; stakan_data.sell_data[j].volume = temp_data[stakan_data.sell_cnt - 1 - j].volume; } GetOtherData(); return(true); } else ZeroMemory(stakan_data); } // GetOtherData(); //DEBUG return(false); } //+------------------------------------------------------------------+ //| Get Other Data function | //+------------------------------------------------------------------+ void CStakan::GetOtherData(void) { stakan_data.ses_deals = SymbolInfoInteger(st_symbol, SYMBOL_SESSION_DEALS); stakan_data.ses_sell_orders = SymbolInfoInteger(st_symbol, SYMBOL_SESSION_SELL_ORDERS); stakan_data.ses_buy_orders = SymbolInfoInteger(st_symbol, SYMBOL_SESSION_BUY_ORDERS); stakan_data.symb_vol_high = SymbolInfoInteger(st_symbol, SYMBOL_VOLUMEHIGH); stakan_data.symb_vol_low = SymbolInfoInteger(st_symbol, SYMBOL_VOLUMELOW); stakan_data.spread = int(SymbolInfoInteger(st_symbol, SYMBOL_SPREAD)); //--- stakan_data.best_ask = SymbolInfoDouble(st_symbol, SYMBOL_ASK); stakan_data.best_bid = SymbolInfoDouble(st_symbol, SYMBOL_BID); stakan_data.ask_high = SymbolInfoDouble(st_symbol, SYMBOL_ASKHIGH); stakan_data.bid_high = SymbolInfoDouble(st_symbol, SYMBOL_BIDHIGH); stakan_data.ask_low = SymbolInfoDouble(st_symbol, SYMBOL_ASKLOW); stakan_data.bid_low = SymbolInfoDouble(st_symbol, SYMBOL_BIDLOW); stakan_data.last = SymbolInfoDouble(st_symbol, SYMBOL_LAST); stakan_data.last_high = SymbolInfoDouble(st_symbol, SYMBOL_LASTHIGH); stakan_data.last_low = SymbolInfoDouble(st_symbol, SYMBOL_LASTLOW); stakan_data.ses_volume = SymbolInfoDouble(st_symbol, SYMBOL_SESSION_VOLUME); stakan_data.ses_turmover = SymbolInfoDouble(st_symbol, SYMBOL_SESSION_TURNOVER); stakan_data.ses_buy_ord_volume = SymbolInfoDouble(st_symbol, SYMBOL_SESSION_BUY_ORDERS_VOLUME); stakan_data.ses_sell_ord_volume = SymbolInfoDouble(st_symbol, SYMBOL_SESSION_SELL_ORDERS_VOLUME); stakan_data.ses_interest = SymbolInfoDouble(st_symbol, SYMBOL_SESSION_INTEREST); stakan_data.ses_aw = SymbolInfoDouble(st_symbol, SYMBOL_SESSION_AW); //---Deals TODO stakan_data.deals.buy_cnt = 0; stakan_data.deals.sell_cnt = 0; stakan_data.deals.sell_volume = 0; stakan_data.deals.buy_volume = 0; oper_time.end_count = GetMicrosecondCount(); ulong delta_time = ulong(MathAbs(double(oper_time.end_count - oper_time.start_count))); int result = CopyTicksRange(st_symbol, ticks_array, COPY_TICKS_TRADE, oper_time.last_tick_time, oper_time.last_tick_time + delta_time); if(result > 0) { for(int i = 0; i < result; i++) { if((ticks_array[i].flags&TICK_FLAG_SELL)==TICK_FLAG_SELL) { stakan_data.deals.sell_cnt++; stakan_data.deals.sell_volume += long(ticks_array[i].volume); } else if((ticks_array[i].flags&TICK_FLAG_BUY)==TICK_FLAG_BUY) { stakan_data.deals.buy_cnt++; stakan_data.deals.buy_volume += long(ticks_array[i].volume); } } oper_time.start_count = GetMicrosecondCount(); oper_time.last_tick_time = ticks_array[0].time_msc + 1; } } //+------------------------------------------------------------------+ //| Get stakan data function | //+------------------------------------------------------------------+ STAKAN_DATA CStakan::GetStData(void) { return(stakan_data); }
Вспомнил, я же писал Лента всех сделок
Спасибо всем
Работает точно и быстро
Желтым - изменения
oper_time.last_tick_time = ticks_array[0].time_msc + 1;
Нельзя добавлять +1, иначе будете пропускать сделки!
Нельзя добавлять +1, иначе будете пропускать сделки!
Именно так нужно делать, чтобы не дублировать сделки.
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Пишу анализатор стакана, увязанного со сделками и "уткнулся" в прблему получения новых сделок.
Функция RefreshStData() вызывается из класса при каждом срабатывании OnBookEvent
Вопросы:
1.Какой размер массива должен быть, чтобы не пропустить сделки?
2. Как быстро "вытащить" НОВЫЕ сделки, если предыдущие тики храняться в mem_ticks_array такого же размера ?
3. Может быть есть альтернативное решение быстрого получкения новых сделок, после срабатывания OnBookEvent ?