2013-01-31 29 views
18

Poniżej możesz zobaczyć moją metodę C# do obliczania pasm Bollingera dla każdego punktu (średnia ruchoma, zakres górny, pasmo dolne).Jak skutecznie obliczyć ruchome odchylenie standardowe

Jak widać, ta metoda wykorzystuje 2 pętle do obliczenia ruchomego odchylenia standardowego przy użyciu średniej ruchomej. Zwykle zawierał dodatkową pętlę do obliczania średniej ruchomej w ciągu ostatnich n okresów. Ten mógłbym usunąć dodając nową wartość punktu do total_average na początku pętli i usuwając wartość punktu i - n na końcu pętli.

Moje pytanie brzmi: Czy mogę usunąć pozostałą pętlę wewnętrzną w podobny sposób, jak udało mi się z ruchomą średnią?

public static void AddBollingerBands(SortedList<DateTime, Dictionary<string, double>> data, int period, int factor) 
    { 
     double total_average = 0; 

     for (int i = 0; i < data.Count(); i++) 
     { 
      total_average += data.Values[i]["close"]; 

      if (i >= period - 1) 
      { 
       double total_bollinger = 0; 
       double average = total_average/period; 

       for (int x = i; x > (i - period); x--) 
       { 
        total_bollinger += Math.Pow(data.Values[x]["close"] - average, 2); 
       } 

       double stdev = Math.Sqrt(total_bollinger/period); 

       data.Values[i]["bollinger_average"] = average; 
       data.Values[i]["bollinger_top"] = average + factor * stdev; 
       data.Values[i]["bollinger_bottom"] = average - factor * stdev; 

       total_average -= data.Values[i - period + 1]["close"]; 
      } 
     } 
    } 

Odpowiedz

20

Odpowiedź brzmi: tak, możesz. W połowie lat 80-tych opracowałem właśnie taki algorytm (prawdopodobnie nie oryginalny) w FORTRAN dla aplikacji monitorowania i sterowania procesem. Niestety, to było ponad 25 lat temu i nie pamiętam dokładnych wzorów, ale technika była przedłużeniem średniej ruchomej, z kalkulacjami drugiego rzędu zamiast liniowych.


Po przyjrzeniu się twojemu kodowi, myślę, że mogę wyluzować, jak to zrobiłem. Zauważ, jak twoja wewnętrzna pętla robi suma kwadratów ?:

  for (int x = i; x > (i - period); x--) 
      { 
       total_bollinger += Math.Pow(data.Values[x]["close"] - average, 2); 
      } 

w taki sam sposób, że średnia musi mieć pierwotnie miał suma wartości? Jedyne dwie różnice to kolejność (jej moc 2 zamiast 1) i odejmowanie średniej każdej wartości przed jej wyrównywaniem. Teraz, że może wyglądać nierozłączne, ale w rzeczywistości mogą być rozdzielone: ​​

SUM(i=1; n){ (v[i] - k)^2 } 

jest

SUM(i=1..n){v[i]^2 -2*v[i]*k + k^2} 

która staje

SUM(i=1..n){v[i]^2 -2*v[i]*k} + k^2*n 

który jest

SUM(i=1..n){v[i]^2} + SUM(i=1..n){-2*v[i]*k} + k^2*n 

który jest również

SUM(i=1..n){v[i]^2} + SUM(i=1..n){-2*v[i]}*k + k^2*n 

Teraz pierwszy termin to tylko Suma kwadratów, traktujesz to w taki sam sposób, w jaki robisz sumę wartości dla średniej. Ostatni termin (k^2*n) to tylko średnia kwadratowa razy period. Ponieważ mimo wszystko dzielisz wynik na okres, możesz po prostu dodać nowy średni kwadrat bez dodatkowej pętli.

Wreszcie, w drugim terminie (SUM(-2*v[i]) * k), ponieważ SUM(v[i]) = total = k*n można następnie zmienić go w ten sposób:

-2 * k * k * n 

czy tylko -2*k^2*n, który jest -2 razy średnia kwadratu, po okresie (n) jest ponownie podzielony.Tak więc końcowy połączony formuła brzmi:

SUM(i=1..n){v[i]^2} - n*k^2 

lub

SUM(i=1..n){values[i]^2} - period*(average^2) 

(należy sprawdzić ważność tego, ponieważ jestem wyprowadzania go na czubku głowy)

i włączenie kod powinien wyglądać mniej więcej tak:

public static void AddBollingerBands(ref SortedList<DateTime, Dictionary<string, double>> data, int period, int factor) 
{ 
    double total_average = 0; 
    double total_squares = 0; 

    for (int i = 0; i < data.Count(); i++) 
    { 
     total_average += data.Values[i]["close"]; 
     total_squares += Math.Pow(data.Values[i]["close"], 2); 

     if (i >= period - 1) 
     { 
      double total_bollinger = 0; 
      double average = total_average/period; 

      double stdev = Math.Sqrt((total_squares - Math.Pow(total_average,2)/period)/period); 
      data.Values[i]["bollinger_average"] = average; 
      data.Values[i]["bollinger_top"] = average + factor * stdev; 
      data.Values[i]["bollinger_bottom"] = average - factor * stdev; 

      total_average -= data.Values[i - period + 1]["close"]; 
      total_squares -= Math.Pow(data.Values[i - period + 1]["close"], 2); 
     } 
    } 
} 
+4

Wielkie dzięki! Patrzyłem na to niewidomym. Tylko zapomniałeś zmniejszyć liczbę całkowitą na końcu: total_squares - = Math.Pow (dane.Wartości [i - kropka + 1] ["zamknij"], 2); – ChrisW

+2

http://www.johndcook.com/blog/standard_deviation/ – odyth

+0

@odyth Ładny odnośnik! Nie zdawałem sobie sprawy, że to było w Knuth. Właściwie to czytałem TAoCP na kilka lat przed napisaniem tego w latach 80., a teraz zastanawiam się, czy podświadomie go zszargałem. – RBarryYoung

1

Używam commons-matem (i przyczyniły się do tej biblioteki!) Na coś bardzo podobnego do tego. To jest open-source, przeniesienie do C# powinno być łatwe, jak kupione w sklepie ciasto (czy próbowałeś zrobić ciasto od zera !?). Sprawdź to: http://commons.apache.org/math/api-3.1.1/index.html. Mają klasę StandardDeviation. Być rozrzutnym!

+0

Dzięki, ale nie potrzebuję biblioteki matematycznej do prostych obliczeń, takich jak ta. – ChrisW

+0

Nie ma za co! Niestety nie znalazłem odpowiedzi, której szukasz. Zdecydowanie nie chciałem sugerować przeniesienia całej biblioteki! Wystarczy minimalny niezbędny kod, który powinien wynosić kilkaset linii. Zauważ, że nie mam pojęcia, jakie ograniczenia prawne/autorskie dotyczą tego kodu, więc musisz to sprawdzić. W przypadku, gdy go prześladujesz, oto [link] (http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math3/stat/descriptive /moment/StandardDeviation.java?revision=1416643&view=markup). Tak więc + Variance + FastMath? –

25

Problem z podejściami obliczającymi sumę kwadratów ares to, że i kwadrat sum może stać się całkiem duży, a obliczenie ich różnicy może wprowadzić very large error, więc pomyślmy o czymś lepszym. Dlaczego jest to potrzebne, zobacz artykuł z Wikipedii na Algorithms for computing variance i John Cook na Theoretical explanation for numerical results)

Po pierwsze, zamiast obliczać stddev, skupmy się na wariancji. Kiedy już mamy wariancję, stddev jest tylko pierwiastkiem kwadratowym wariancji.

Załóżmy, że dane znajdują się w tablicy o nazwie x; rolowanie okna o wielkości n o jeden może być traktowane jako usunięcie wartości x[0] i dodanie wartości x[n]. Oznaczmy średnie wartości x[0]..x[n-1] i x[1]..x[n] odpowiednio przez μ i μ '. Różnica między wariancji x[0]..x[n-1]x[1]..x[n] i jest, po anulowaniu pewnych terminów i stosowania (a²-b²) = (a+b)(a-b):

Var[x[1],..,x[n]] - Var[x[0],..,x[n-1]] 
= (\sum_1^n x[i]² - n µ’²)/(n-1) - (\sum_0^{n-1} x[i]² - n µ²)/(n-1) 
= (x[n]² - x[0]² - n(µ’² - µ²))/(n-1) 
= (x[n]-µ’ + x[0]-µ)(x[n]-x[0])/(n-1) 

Zatem wariancja jest zaburzony przez coś, co nie wymaga, aby utrzymać sumę kwadratów, co jest lepsze dla dokładności liczbowej.

Można obliczyć średnią i wariancję raz na początku za pomocą odpowiedniego algorytmu (Welford's method). Po tym, za każdym razem trzeba zastąpić wartości w oknie x[0] innym x[n] zaktualizować średnią i wariancję takiego:

new_Avg = Avg + (x[n]-x[0])/n 
new_Var = Var + (x[n]-new_Avg + x[0]-Avg)(x[n] - x[0])/(n-1) 
new_StdDev = sqrt(new_Var) 
+1

Dzięki za to. Użyłem go jako podstawy implementacji w C# dla CLR. Odkryłem, że w praktyce można aktualizować tak, że 'new_Var' jest małą liczbą ujemną, a sqrt kończy się niepowodzeniem. Wprowadziłem "if", aby ograniczyć wartość do zera dla tego przypadku. Nie pomysł, ale stabilny. Stało się tak, gdy każda wartość w moim oknie miała tę samą wartość (użyłem okna o rozmiarze 20, a wartość, o której mowa, wynosi 0,5, na wypadek, gdyby ktoś chciał to odtworzyć). –

0

najważniejsze informacje zostały już podane powyżej --- ale może to jest nadal użyteczności publicznej.

Maleńki biblioteki Java do obliczania średniej ruchomej i odchylenia standardowego jest dostępna tutaj: https://github.com/tools4j/meanvar

Wdrożenie opiera się na wariancie sposobu Welford w wymienionym powyżej. Wyodrębniono metody usuwania i zastępowania wartości, które można wykorzystać do przesuwania okien wartości.

Powiązane problemy