Хотя я полностью осознаю, что Python является предпочтительным языком для систем тестирования технического анализа рыночных данных, я все еще нахожусь в процессе обучения программированию с использованием наборов данных и структур данных. Я считаю, что лучше всего работать с нуля, не используя библиотеки, которые берут на себя тяжелую работу. Я все еще нахожусь на стадии практического обучения, и, поскольку язык, который я знаю лучше всего на данный момент, - это JavaScript, я буду продолжать писать код именно на нем. Таким образом, когда я перейду на Python, у меня уже будет основа не только в теории, но и в элементах кодирования.

Вернемся на мгновение назад: вся причина, по которой кто-то может захотеть использовать технический анализ в рыночной торговой системе, состоит в том, чтобы в конечном итоге автоматизировать его. Одна из основных причин, по которой кто-то может захотеть автоматизировать торговую систему, заключается в том, что психология человека часто является его злейшим врагом. Когда я работал проп-трейдером в Нью-Йорке, программа обучения была в значительной степени ориентирована на психологию трейдера. Действительно, среди рекомендуемых к прочтению была книга Марка Дугласа «Торговля в зоне», в которой почти исключительно рассматривались образ мышления, убеждения и предположения трейдера с момента, когда первый звонок прозвучал до закрытия. Поэтому неудивительно, что я хотел бы совместить свое рыночное обучение с моими навыками разработки программного обеспечения и разработать основы математики и алгоритмы для обработки и анализа данных.

Одним из старейших и до сих пор наиболее широко используемых индикаторов технического анализа является простая скользящая средняя. Любая ежедневная газета обычно печатается, если индекс широкого рынка торгуется выше или ниже 200-дневной скользящей средней. Вкратце, это относится к среднему значению всех цен закрытия дня за последние 200 дней. Это вычисление объединяет структуру данных, известную как «стек» (т. Е. Последний пришел, первый ушел), а затем вычисляет простое математическое среднее этих переменных.

Прежде чем мы перейдем к этой формуле, я хотел настроить эти формулы, чтобы иметь возможность обрабатывать наборы данных в правильном формате. Часто рыночные данные содержат по крайней мере четыре значения для любой конкретной точки данных: цена открытия, максимум, минимум, цена закрытия. Каждый из них важен для разных типов анализа. Например, в одном из будущих постов мы рассмотрим ATR, или средний истинный диапазон, который в основном представляет собой среднее значение разницы между максимальной и минимальной ценой для каждой точки данных. Наш набор данных будет состоять из массива объектов, в котором объекты будут следовать шаблону:

let someData = [
    {open: 10, high: 20, low: 5, close: 11}, 
    {open: 11, high: 25, low: 9, close: 20}, 
    {open: 5, high: 29, low: 4, close: 20}, 
    {open: 8, high: 32, low: 5, close: 31}, 
    {open: 9, high: 31, low: 3, close: 30}, 
    {open: 10, high: 29, low: 5, close: 15}, 
    {open: 10, high: 26, low: 5, close: 9}
]

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

function extractData(dataObj, key) {
    closeData = []
    dataObj.forEach(obj => {
        closeData.push(obj[key])
    })
    return closeData
}

Это просто возвращает массив всех значений для конкретного ключа, который вам нужен. Например, если вам нужны все цены открытия точек данных, вы должны запустить extractData (someData, «open»).

Простое скользящее среднее

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

function sma(dataRaw, time_period, parameter) {
}

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

function sma(dataRaw, time_period, parameter) {
    let data = extractData(dataRaw, parameter)
}

Наша переменная data теперь представляет собой простой массив, заполненный значениями выбранного нами параметра.

Далее нам нужно извлечь из этого массива только те точки данных, которые нам нужны. Например, если длина набора данных составляет 300, и мы ищем только последние 200 точек данных, нам нужно обработать это здесь. Но прежде чем мы тратим время на изменение нашего массива, давайте посмотрим, достаточно ли он заполнен. Может случиться так, что нам нужен период ретроспективного анализа 50, когда в нашем массиве всего 30 точек данных. Если это так, мы можем просто вернуть среднее значение в текущем массиве. Давайте сделаем это в первую очередь:

function sma(dataRaw, time_period, parameter) {
    let data = extractData(dataRaw, parameter)
    if (time_period >= data.length) {
    return (  
   ( data.reduce(( accumulator, currentValue ) => accumulator + currentValue, 0 ) ) / data.length
    )} else {
 }
}

Если количество точек данных в нашем наборе данных больше, чем запрашивается, тогда у нас действительно есть сценарий «скользящего среднего», и нам нужно будет извлечь количество time_period самых последних точек данных. . Для этого мы можем использовать функцию .slice и заполнить новый массив только необходимой нам информацией. Поскольку мы знаем точное количество точек данных, которые нам нужны, из аргумента time_period, нам просто нужно вернуть это как отрицательное число в .slice, и у нас есть необходимый набор данных. Чтобы вернуть отрицательное число, вам просто нужно умножить его на отрицательное 1. Как только мы получим это число, мы будем готовы запустить ту же функцию .reduce, чтобы получить среднее значение необходимых нам данных. Наша полная формула простой скользящей средней теперь выглядит так:

function sma(dataRaw, time_period, parameter) {
    let data = extractData(dataRaw, parameter)
    if (time_period >= data.length) {
    return (  
   ( data.reduce(( accumulator, currentValue ) => accumulator + currentValue, 0 ) ) / data.length
    )} else {
        let nData = data.slice((time_period * -1))
        return (
            nData.reduce(( accumulator, currentValue ) => accumulator + currentValue, 0 )  / time_period
        )
        
    }
}

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