2013-01-31 42 views
18

Dưới đây bạn có thể thấy phương pháp C# của tôi để tính Bollinger Bands cho mỗi điểm (trung bình di chuyển, băng lên, băng xuống).Cách tính hiệu quả độ lệch chuẩn di chuyển

Như bạn có thể thấy, phương pháp này sử dụng 2 cho các vòng để tính toán độ lệch chuẩn di chuyển bằng cách sử dụng trung bình di chuyển. Nó được sử dụng để chứa một vòng lặp bổ sung để tính toán di chuyển trung bình trong n thời gian qua. Điều này tôi có thể loại bỏ bằng cách thêm giá trị điểm mới vào total_average ở đầu vòng lặp và loại bỏ giá trị điểm i - n ở cuối vòng lặp.

Câu hỏi của tôi bây giờ về cơ bản: Tôi có thể xóa vòng lặp bên trong còn lại theo cách tương tự mà tôi đã quản lý với mức trung bình di chuyển không?

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"]; 
      } 
     } 
    } 

Trả lời

20

Câu trả lời là có, bạn có thể. Vào giữa những năm 80, tôi đã phát triển một thuật toán như vậy (có thể không phải là bản gốc) trong FORTRAN cho một ứng dụng giám sát và kiểm soát quy trình. Thật không may, đó là hơn 25 năm trước và tôi không nhớ các công thức chính xác, nhưng kỹ thuật này là một phần mở rộng của một cho di chuyển trung bình, với tính toán thứ tự thay vì chỉ những tuyến tính.


Sau khi xem mã của bạn một số, tôi nghĩ rằng tôi có thể lo lắng về cách tôi đã làm lại. Lưu ý cách vòng lặp bên trong của bạn tạo một Tổng số hình vuông như thế nào ?:

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

giống như cách mà giá trị trung bình của bạn phải có tổng giá trị trung bình? Hai sự khác biệt duy nhất là thứ tự (sức mạnh của nó 2 thay vì 1) và bạn trừ đi giá trị trung bình của mỗi giá trị trước khi bạn đặt nó. Bây giờ có thể trông không thể tách rời, nhưng trong thực tế, họ có thể được tách ra:

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

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

mà trở nên

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

đó là

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

đó cũng là

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

Bây giờ cụm từ đầu tiên chỉ là một tổng của hình vuông, bạn xử lý theo cùng một cách mà bạn thực hiện tổng giá trị cho giá trị trung bình. Thuật ngữ cuối cùng (k^2*n) chỉ là bình phương bình phương lần số period. Vì bạn chia kết quả theo khoảng thời gian, bạn chỉ có thể thêm bình phương mới mà không có vòng lặp thêm.

Cuối cùng, trong nhiệm kỳ thứ hai (SUM(-2*v[i]) * k), vì SUM(v[i]) = total = k*n bạn có thể sau đó thay đổi nó vào đây:

-2 * k * k * n 

hoặc chỉ -2*k^2*n, đó là -2 lần mức trung bình bình phương, một khi giai đoạn (n) được chia ra lần nữa.Vì vậy, các công thức kết hợp cuối cùng là:

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

hoặc

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

(hãy chắc chắn để kiểm tra tính hợp lệ của việc này, vì tôi đang phát sinh nó ra khỏi đỉnh đầu của tôi)

Và kết hợp vào mã của bạn sẽ trông giống như sau:

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

Cảm ơn rất nhiều! Tôi đã nhìn chằm chằm vào cái này. Bạn chỉ quên giảm total_squares ở cuối: total_squares - = Math.Pow (dữ liệu.Giá trị [i - period + 1] ["close"], 2); – ChrisW

+2

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

+0

@odyth Tham chiếu hấp dẫn! Tôi đã không nhận ra điều này là ở Knuth. Tôi đã thực sự đọc TAoCP vài năm trước khi tôi viết điều này vào những năm 80, và bây giờ tôi tự hỏi liệu tôi có vô thức ăn cắp nó hay không. – RBarryYoung

1

Tôi đã sử dụng toán học commons (và đóng góp vào thư viện đó!) Cho một cái gì đó rất giống với điều này. Đó là mã nguồn mở, việc chuyển sang C# sẽ dễ dàng như là chiếc bánh được mua tại cửa hàng (bạn đã thử chế biến một chiếc bánh từ đầu rồi sao !?). Hãy khám phá: http://commons.apache.org/math/api-3.1.1/index.html. Họ có một lớp StandardDeviation. Tới thị trấn!

+0

Cảm ơn, nhưng tôi không nghĩ rằng tôi cần một thư viện toán học để tính toán đơn giản như thế này. – ChrisW

+0

Bạn được chào đón! Xin lỗi tôi không có câu trả lời bạn đang tìm kiếm. Tôi chắc chắn không có ý định đề nghị chuyển toàn bộ thư viện! Chỉ cần mã tối thiểu cần thiết, mà phải là một vài trăm dòng hoặc lâu hơn. Lưu ý rằng tôi không có ý tưởng apache pháp lý/bản quyền hạn chế có trên mã đó, vì vậy bạn sẽ phải kiểm tra xem ra. Trong trường hợp bạn theo đuổi, đây là [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). Vì vậy, + Variance + FastMath? –

25

Vấn đề với cách tiếp cận tính tổng của squ ares là nó và hình vuông của các khoản tiền có thể nhận được khá lớn, và tính toán sự khác biệt của họ có thể giới thiệu một very large error, vì vậy hãy nghĩ về một cái gì đó tốt hơn. Để biết tại sao điều này là cần thiết, hãy xem bài viết trên Wikipedia về Algorithms for computing variance và John Cook trên Theoretical explanation for numerical results)

Đầu tiên, thay vì tính toán stddev hãy tập trung vào phương sai. Khi chúng ta có phương sai, stddev chỉ là căn bậc hai của phương sai.

Giả sử dữ liệu nằm trong một mảng có tên là x; cán một cửa sổ cỡ n bằng một có thể được coi là loại bỏ giá trị của x[0] và thêm giá trị của x[n]. Hãy biểu thị mức trung bình của x[0]..x[n-1]x[1]..x[n] tương ứng với µ và µ '. Sự khác biệt giữa phương sai của x[0]..x[n-1]x[1]..x[n] là, sau khi huỷ ra một số thuật ngữ và áp dụng (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) 

Do đó phương sai là bị nhiễu loạn bởi một cái gì đó mà không yêu cầu bạn phải duy trì tổng bình phương, mà là tốt hơn cho độ chính xác số.

Bạn có thể tính giá trị trung bình và phương sai một lần ngay từ đầu bằng thuật toán thích hợp (Welford's method). Sau đó, mỗi khi bạn phải thay thế một giá trị trong cửa sổ x[0] bởi x[n] khác bạn cập nhật tỷ lệ trung bình và phương sai như thế này:

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

Cảm ơn vì điều này. Tôi đã sử dụng nó làm cơ sở cho việc triển khai trong C# cho CLR. Tôi phát hiện ra rằng, trên thực tế, bạn có thể cập nhật sao cho 'new_Var' là một số âm nhỏ _very_ và sqrt không thành công. Tôi đã giới thiệu một 'if' để giới hạn giá trị bằng 0 cho trường hợp này. Không ý tưởng, nhưng ổn định. Điều này xảy ra khi mọi giá trị trong cửa sổ của tôi có cùng giá trị (tôi đã sử dụng kích thước cửa sổ là 20 và giá trị được đề cập là 0,5, trong trường hợp ai đó muốn thử tạo lại điều này). –

0

thông tin quan trọng nhất đã được đưa ra ở trên --- nhưng có lẽ đây vẫn là lợi ích chung.

Một thư viện Java nhỏ để tính toán chuyển động lệch trung bình và tiêu chuẩn có sẵn ở đây: https://github.com/tools4j/meanvar

Việc thực hiện dựa trên một biến thể của phương pháp Welford của nêu trên. Các phương pháp để loại bỏ và thay thế các giá trị đã được bắt nguồn có thể được sử dụng để di chuyển các cửa sổ giá trị.

Các vấn đề liên quan