Торговый бот на Node.js и Tinkoff API (Часть 2) Кластеры

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Кластеры?

Говоря совсем просто, в нашем проекте кластер это ситуация на рынке, склонная к повтору. Изначально существует лишь 1 критерий - схожесть комбинации из 4х свечей. Если критерий совпал присваиваем нынешней ситуации кластер. Сейчас в структуру кластера добавляются и другие критерии, но об это позже.

Мы уже видели стандартную структуру по которой мы собираем комбинации. Что бы собрать кластеры, нам как минимум нужен инструмент сравнения одной комбинации с другой.

Алгоритм сравнения

Стандартизация

У каждой свечи 4 значения, в комбинации 4 свечи. Нам надо сравнить насколько сильно отличается друг от друга набор из 16 цифр. Первый этап - подготовка комбинации к сравнению, цена варьируется от инструмента к инструменту, из-за этого было решено представить каждое из 16 значений как число от 0 до 1, где 0 минимальное значение комбинации, а 1 максимальное.

Основная формула для этого этапа:

// el - свеча
// min, max - значения всей комбинации
newС[kObj] = (el[kObj] - min) / (max - min);

Стандартизированная комбинация:

'combo': [
	{ 'o': 0.98356, 'c': 0.51233, 'l': 0.51233, 'h': 1 },
	{ 'o': 0.52603, 'c': 0.21096, 'l': 0.20822, 'h': 0.55068 },
	{ 'o': 0.21096, 'c': 0.22466, 'l': 0.09589, 'h': 0.49589 },
	{ 'o': 0.21918, 'c': 0.09589, 'l': 0, 'h': 0.28493 }
],

Сравнение

Самая первая версия алгоритма просто считала % разницы между каждым значением и возвращала массив из 16 процентных значений. Затем мы считали средние отличие и все, на основе этого решали похожа комбинация или нет.

Недавно алгоритм был переработан, теперь мы сравниваем каждую свечу с другой свечей по нескольким параметрам: размер свечи и верхнего нижнего хвостов, а так же расположение свечи относительно комбинации. Все это легко считается по изначальным 4м параметрам свечи. Самое главное - возможность настроить максимальное отклонение для каждого параметра отдельно. Сейчас настройки выставлены так:

maxBD: 100, // максимальная разница размеров свечей (10%)
maxH: 200, // максимальная разница верхнего хвоста (20%)
maxL: 200, // максимальная разница нижнего хвоста (20%)
maxBP: 150, // максимальная разница в положении
maxCBP: 200 // maxBP, но без учета хвостов

Иногда хвост свечи может сильно подвинуть всю комбинацию вверх или вниз и, даже если все очень похоже, положение свечей будет сильно отличаться, поэтому CBP (Clear Body Position) считается без учета верхних и нижних хвостов.

По итогу сравнения мы получаем объект с параметрами и средним значением. Среднее значение будет использоваться для вычисления общего процента схожести комбинаций.

Сборка в кластеры

Представим песочницу в которую хаотично высыпали фигуры разных цветов и форм. Наша задача собрать все фигуры в группы. Уже на этом этапе необходимо определится какие мы хотим получить группы. Например, что бы в одной группе были максимально похожие фигуры, или же что бы группы были максимально большими. Трудно сказать какой метод подходит для наших целей лучше всего, собирая максимально похожие группы, результат будет - множество небольших групп с очень похожими фигурами, с одной стороны чем больше схожесть тем лучше, с другой лучше если группа больше. Перебирая комбинации одна за другой, мы рано или поздно добавим в кластер комбинацию, а затем создадим новый кластер к которому та самая комбинация подошла бы лучше. Вот тут и необходимо решить - либо переносим комбинацию в новый кластер, либо не трогаем.

Для начала делаем первый перебор по нашей импровизированной базе из JSON файлов.

let cur = 1;
const g_excluded = new Set();
const clusters = [];
while (cur <= last) { // last - последний файл
  const prepdata = getDocVal(pathToF(cur)); // Данные из файла
	for (let i = 0; i < prepdata.mLength; i++) {
    const realElem1 = prepdata.main[i].candles;
		const elem1 = makeMeOld(realElem1); // упращенная версия комбинации
		// первая комбинация
		g_excluded.add(transForEx(elem1)); // добавляем в сет
		const cluster = [firstClusterEl(realElem1)];
		// каждый кластер имеет главный (0) элемент
		let cur2 = 1;
		// Next code goes here...
	}
	cur++;
}
return clusters;

После определения первой комбинации для сравнения начинаем перебор для второй комбинации, внутри этого перебора мы и будем сравнивать первую комбинацию с кучей вторых комбинаций и те что похожи записывать в кластер к первой. Сет g_excluded хранит в себе все использованные комбинации и перед каждым сравнением проверяет не использовалась ли она ранее.

while (true) { 
	let best_comb = null;
	while (cur2 <= last) {
		const prepdata2 = getDocVal(pathToF(cur2)); // данные из файла  
		for (let ii = 0; ii < prepdata2.mLength; ii++) {      
			const realElem2 = prepdata2.main[ii];       
			const elem2 = makeMeOld(realElem2.candles);             
			// Второая комбинация             
			let d;       
			if (getCollorsOfCombo(elem1) == getCollorsOfCombo(elem2)) {     
				// получаем результат сравнивания двух комбинаций           
				d = newDecoder(newPrepareToCompare(elem1, elem2), 1);       
			} else d = false;
			if (d != false) {  // если комбинации похожи       
				const cAfterCandle = realElem2.afterCandle; // свеча после комбо
      	if (g_excluded.has(transForEx(elem2))) continue;
				if (d.is) {
					best_comb = elem2; // запоминаем комбинацию
					g_excluded.add(transForEx(best_comb)); // исключаем 
				}         
			}         
		}   
		cur2++;
	}
	if (best_comb == null) break; // если не нашли комбо
}
if (cluster.length > 1) { // если кластер не пустой - добавляем
    clusters.push(cluster); 
}

Метод transForEx - очень простая хеш-функция, она делает из комбинации уникальную строку что бы добавить ее в сет исключенных комбинаций. Цикл while(true) может быть прерван если комбинация не найдена после всего обхода. Комбинация пропускается если исключена. Вместо пропуска комбинаций, мы можем перезаписывать ее в новый кластер, если она подходит ему больше, но об этом чуть позже. Объект d - результаты сравнения двух комбинаций, чтобы значительно уменьшить количество сравнений, мы сначала быстро проверяем цвета свечей в комбинации, и при совпадении, сравниваем уже детально.

По итогу обработки всей базы у нас имеется файл со всеми кластерами. Метод сборки уже готового элемента сверху не описан и может сильно отличаться в зависимости от его применения, у нас это массив комбинаций где 0 элемент всегда тело кластера, в нем хранится вся статистика по комбинациям, хранящимся далее в массиве. Это сделано что бы можно было безболезненно оторвать тело кластера от остальных комбинаций и использовать для сравнения в реальном времени.

Пример кластера:

[
  { // тело кластера
    "up": 1,
 		"uAv": 0,
    "down": 0,
    "dAv": 0,
    "total": 1,
    "allGPers": [32.804],
    "allNPers": [],
    "theCombo": [ // главная комбинация
      { "o": 44.4125, "c": 44.395, "l": 44.395, "h": 44.4175 },
      { "o": 44.3825, "c": 44.3725, "l": 44.3725, "h": 44.4 },
      { "o": 44.3775, "c": 44.375, "l": 44.375, "h": 44.3975 },
      { "o": 44.365, "c": 44.395, "l": 44.365, "h": 44.395 }
    ]
  }, // комбинации в кластере
  {
    "combo": [
      { "o": 0.77249, "c": 0.50265, "l": 0.38624, "h": 1 },
      { "o": 0.48677, "c": 0.21164, "l": 0.15344, "h": 0.65608 },
      { "o": 0.2328, "c": 0.10582, "l": 0, "h": 0.44444 },
      { "o": 0.10053, "c": 0.61376, "l": 0.06349, "h": 0.66667 }
    ],
    "extra": { // у каждой комбинации свои extra данные
      "aftrO": 114.6325, // данные свечи после комбинации
      "aftrC": 114.7875,
      "min": 114.35,
      "max": 114.8225,
      "indexID": "f17i658" // используется для простого удаления комбо
    }
  }, 
  ...
], ...

indexID это короткий пусть к комбинации в первичной базе данных, в нашем случае (f17i658) это файл 17, комбинация 658. Если мы заменяем комбинацию вместо пропуска, нам нужен этот ID.

Заключение

В следующей части мы детальнее посмотрим на некоторые вспомогательные функции и разберем часть проекта, которая отвечает за поиск кластера и отображение всех необходимых данных на странице.

Источник: https://habr.com/ru/post/594051/


Интересные статьи

Интересные статьи

Во втором разделе учебных материалах о моделях sequence-to-sequence с использованием PyTorch и TorchText мы будем реализовывать модель из работы Learning Phrase Represent...
Как по мне достаточно важная, хотя и холиварная тема. Думаю каждый из нас задавал себе вопросы: "можно не дублировать проверки?", "а не усложняю ли я?", "да как же это сделать нормально?!...
В первой части статьи мы реализовали простой (и не очень эффективный) рендерер сетки ГУТ, и пообещали, что оптимизируем рендерер настолько, что он сможет отобразить заявл...
Это пошаговая инструкция создания приложение на Node.JS, с использованием typescript и express. Новое приложение создается не часто, отсюда забываются шаги по его созданию. И я решил напи...
Маркетплейс – это сервис от 1С-Битрикс, который позволяет разработчикам делиться своими решениями с широкой аудиторией, состоящей из клиентов и других разработчиков.