Метод поворотных точек (Turning point test)

Теоретические сведения

Количество поворотных точек (локальных максимумов и минимумов) последовательности случайных чисел в пределе имеет нормальное распределение со средним значением \(2 \, (n - 2) / 3\) и дисперсией \((16 n - 29) / 90.\)

Доказательство:

Если пронумеровать числа в каждой тройке по возрастанию, то получится всего 6 различных вариантов: (1, 2, 3); (1, 3, 2); (2, 1, 3); (2, 3, 1); (3, 2, 1) и (3, 1, 2). Все варианты показаны на рис. 1.

../../_images/TurningPoint1.png

Рис. 1. Варианты расположения трёх точек.

В последовательности из \(n\) элементов имеется \((n-2)\) тройки значений, т.к. самый первый и самый последний элемент не имеют одного из соседей. В четырёх из шести случаев формируется поворотная точка.

Следовательно, среднее число поворотных точек равно

\[4 \, (n - 2) / 6 \; = \; 2 \, (n - 2) / 3 .\]

Громоздкие выкладки для вычисления дисперсии опускаем.

Замечание: Если фактическое число поворотных точек в некоторой последовательности значений лежит вне диапазона

\[\frac{2}{3} \, (n - 2) \; \pm 1.96\cdot\sqrt{\frac{16 n - 29}{90}},\]

то гипотезу о случайности данной последовательности можно отвергнуть с вероятностью 0.95.

Ссылки:

Текст программы на языке Python

#!/usr/bin/python
#-*- coding: utf-8 -*-

import math
import random

# Функция turningPoint - определяет локальные экстремумы.
# Вход: три числа.
# Выход: 1 - второе число больше двух других;
#       -1 - второе число меньше двух других;
#        0 - в остальных случаях.
def turningPoint(a, b, c):
    result = 0       # не поворотная точка
    if ((a < b) and (b > c)):
       result = 1    # пик
    elif ((a > b) and (b < c)):
       result = -1   # впадина
    return result

# Функция readData - чтение данных из текстового файла.
# В каждой строке файла - одно число.
# Вход:  имя файла.
# Выход: число прочитанных значений и список самих значений.
def readData(filename):
    a = []
    n = 0
    with open(filename) as f:
        for line in f:
            a.append(float(line))
            n += 1
    return [n, a]
    
# Функция turningPointsCounter - тест на случайность методом поворотных точек.
# Вход:  число значений; список значений.
# Выход: фактическое число поворотных точек; теоретическое число; дисперсия; 
# пороговое отклонение для уровня значимости 0.95.
def turningPointsCounter(n, a):
    k = 0  # счётчик поворотных точек
    for i in xrange(1, n-1): # для всех элементов, кроме 0-го и последнего
        k += abs(turningPoint(a[i-1], a[i], a[i+1]))

    k0 = 2.0 * (n - 2) / 3.0  # теоретическое среднее значение числа поворотных точек
    d = (16.0 * n - 29.0) / 90.0  # дисперсия 
    dk95 = 1.96 * math.sqrt(d)    # пороговое отклонение для вероятности 0.95
    return [k, k0, d, dk95]
    
# Функция printResult - вывод результатов
def printResult(k, k0, dk95):
    print("Число поворотных точек = %d" % k)
    print("отличается от теоретического значения %1.2f" % k0)
    dk = k - k0
    print("на %1.2f" % dk)
    print("Пороговая разница = %1.2f" % dk95) 
    if (abs(dk) < dk95):
        print("Не можем отвергнуть гипотезу о случайности данной последовательности") 
    else:
        print("Последовательность не является случайной")
    

#-------------------------------
if __name__ == '__main__':
    print("\nТест на случайность методом поворотных точек\n")
    print("Вариант 1. Используется генератор случайных чисел из библиотеки Python random")
    a = []
    n = 1000
    random.seed() # инициализация генератора случайных чисел
    for i in xrange(0, n):
        # случайное число от 0 до 1 с шагом 0.0001
        a.append( random.randrange(0, 10000, 1) / 10000.0 ) 
    print("Сгенерировано %d значений" % n)
    k, k0, d, dk95 = turningPointsCounter(n, a)
    printResult(k, k0, dk95)
    
    print("----------------------------------------\n")
    
    print("Вариант 2. Используются случайные числа с сайта random.org")
    n, a = readData("d:/random.org(0-1000).txt") # читаем числа из файла
    print("Прочитано %d значений" % n)
    k, k0, d, dk95 = turningPointsCounter(n, a)
    printResult(k, k0, dk95)
    
    print("----------------------------------------\n")
    
    print("Вариант 3. Используются фунция sin")
    n = 1000
    a = []
    for i in xrange(0, n):
        a.append(math.sin(i/math.pi))
    print("Сегерировано %d значений" % n)
    k, k0, d, dk95 = turningPointsCounter(n, a)
    printResult(k, k0, dk95)
    
    print("----------------------------------------\n")
    
    print("Вариант 4. Используются цены закрытия валютной пары GBP/USD")
    n, a = readData("d:/GBPUSD-60.txt") # читаем числа из файла
    print("Прочитано %d значений" % n)
    k, k0, d, dk95 = turningPointsCounter(n, a)
    printResult(k, k0, dk95)
    
    print("----------------------------------------\n")
        
    print("Вариант 5. Используются приращения цен закрытия валютной пары GBP/USD")
    n, a = readData("d:/GBPUSD-60.txt") # читаем числа из файла
    x = a[0]
    n -= 1
    for i in xrange(0, n):
        a[i] = a[i+1] - x
        x = a[i+1]
    print("Прочитано %d значений" % n)
    k, k0, d, dk95 = turningPointsCounter(n, a)
    printResult(k, k0, dk95)

    print("----------------------------------------\n") 
    
    print("Вариант 6. Используются приращения логарифмов цен закрытия валютной пары GBP/USD")
    n, a = readData("d:/GBPUSD-60.txt") # читаем числа из файла
    x = a[0]
    n -= 1
    for i in xrange(0, n):
        a[i] = math.log(a[i+1] / x)
        x = a[i+1]
    print("Прочитано %d значений" % n)
    k, k0, d, dk95 = turningPointsCounter(n, a)
    printResult(k, k0, dk95)
    

Результат работы программы

Тест на случайность методом поворотных точек

Вариант 1. Используется генератор случайных чисел из библиотеки Python random
Сгенерировано 1000 значений
Число поворотных точек = 671
отличается от теоретического значения 665.33
на 5.67
Пороговая разница = 26.11
Не можем отвергнуть гипотезу о случайности данной последовательности
----------------------------------------
Вариант 2. Используются случайные числа с сайта random.org
Прочитано 1000 значений
Число поворотных точек = 679
отличается от теоретического значения 665.33
на 13.67
Пороговая разница = 26.11
Не можем отвергнуть гипотезу о случайности данной последовательности
----------------------------------------
Вариант 3. Используются фунция sin
Сегерировано 1000 значений
Число поворотных точек = 101
отличается от теоретического значения 665.33
на -564.33
Пороговая разница = 26.11
Последовательность не является случайной
----------------------------------------
Вариант 4. Используются цены закрытия валютной пары GBP/USD
Прочитано 13417 значений
Число поворотных точек = 6980
отличается от теоретического значения 8943.33
на -1963.33
Пороговая разница = 95.72
Последовательность не является случайной
----------------------------------------
Вариант 5. Используются приращения цен закрытия валютной пары GBP/USD
Прочитано 13416 значений
Число поворотных точек = 9073
отличается от теоретического значения 8942.67
на 130.33
Пороговая разница = 95.71
Последовательность не является случайной
----------------------------------------
Вариант 6. Используются приращения логарифмов цен закрытия валютной пары GBP/USD
Прочитано 13416 значений
Число поворотных точек = 9097
отличается от теоретического значения 8942.67
на 154.33
Пороговая разница = 95.71
Последовательность не является случайной

Тест на случайность с использованием R

В программе на языке R для проверки гипотезы о случайности заданной последовательности можно использовать пакет randtests. Метод поворотных точек реализуется функцией turning.point.test:

require(randtests)
require(rusquant)

d = getSymbols("GBPUSD", src = 'Finam', period="hour", from = '2014-10-01', to = '2014-10-31', auto.assign = F)

d1 = as.numeric(Cl(d)) # Цены закрытия
turning.point.test(d1)

d2 = as.numeric(ROC(Cl(d), type='discrete', na.pad=F)) # Относительные приращения цен закрытия
turning.point.test(d2)

d3 = as.numeric(ROC(Cl(d), type='continuous', na.pad=F)) # Приращения логарифмов цен закрытия
turning.point.test(d3)

Вывод

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



Комментарии

Комментариев пока нет.

* Обязательные поля
(Не публикуется)
 
Жирный Курсив Подчеркнутый Перечеркнутый Степень Индекс Код PHP Код Кавычки Вставить линию Вставить маркированный список Вставить нумерованный список Вставить ссылку Вставить e-mail Вставить изображение Вставить видео
 
Улыбка Печаль Удивление Смех Злость Язык Возмущение Ухмылка Подмигнуть Испуг Круто Скука Смущение Несерьёзно Шокирован
 
1000
Captcha
Refresh
 
Введите код:
 
Запомнить информацию введенную в поля формы.