its a calculation behaviour issue
here are the results
Date Open High Low Close preclose TR
2024-01-26 72.25 73.19 70.45 71.28 73.37 2.73
2024-01-29 71.69 73.09 70.78 73.03 68.87 2.31
2024-01-30 73.01 73.62 70.86 71.43 63.40 2.76
2024-01-31 69.74 70.87 68.24 68.53 66.22 2.63
2024-02-01 68.87 69.30 67.66 68.15 55.63 1.64
2024-02-02 67.90 69.06 67.33 68.85 57.23 1.73
2024-02-05 69.15 69.89 67.97 68.37 56.17 1.92
2024-02-06 62.55 62.55 54.82 55.26 53.62 7.73
2024-02-07 55.60 56.21 53.11 53.62 67.38 3.10
2024-02-08 53.81 57.36 53.81 56.25 65.38 3.55
2024-02-09 56.89 59.14 56.80 58.70 54.19 2.35
2024-02-12 58.80 59.74 57.66 58.04 69.35 2.08
2024-02-13 55.03 56.56 54.17 55.22 57.02 2.39
2024-02-14 56.30 57.18 55.57 57.02 66.28 1.61
2024-02-15 57.38 57.58 55.59 56.34 66.70 1.98
now basically, i send a copy of the code, but basically i think the api work properly because it gets the proper values high, close of a stock on a particular day, however my code is not storing the last close day properly for the calculation of the next day. bascially i showed here in the output how the prevclose is not equal to the close, and therefore the calculation is off. with how the true range works, its based on three conditions and the higher number of the 3 conditions becomes the true range and my two conditions are off because of the wrong close value of the previous day
here is my code, now i use two code script. the main script which contain all the functions and calculations and the atr script which does the calculation and bit just for the average true range and true range
main script
package main
import (
“encoding/json”
“fmt”
“net/http”
“strconv”
“time”
atr "example/golang/ATR" // Update with your package path
)
const apiKey = “HBCPDZ4Q7P5W4QNX”
const symbol = “RMBS”
type Candle struct {
High float64
Low float64
Close float64
}
type TimeSeries Candle
func NewTimeSeries(data map[string]interface{}) (TimeSeries, error) {
var series TimeSeries
timeSeriesData, ok := data["Time Series (Daily)"].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("missing or invalid 'Time Series (Daily)' field in data")
}
for date, dailyData := range timeSeriesData {
dailyDataMap, ok := dailyData.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("unexpected data format for date %s: %+v", date, dailyData)
}
high, _ := strconv.ParseFloat(dailyDataMap["2. high"].(string), 64)
low, _ := strconv.ParseFloat(dailyDataMap["3. low"].(string), 64)
close, _ := strconv.ParseFloat(dailyDataMap["4. close"].(string), 64)
candle := Candle{High: high, Low: low, Close: close}
series = append(series, candle)
}
// Reverse the series to have the latest date first
for i, j := 0, len(series)-1; i < j; i, j = i+1, j-1 {
series[i], series[j] = series[j], series[i]
}
return series, nil
}
func main() {
url := fmt.Sprintf(“https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=%s&apikey=%s”, symbol, apiKey)
response, err := http.Get(url)
if err != nil {
panic(err)
}
defer response.Body.Close()
var data map[string]interface{}
err = json.NewDecoder(response.Body).Decode(&data)
if err != nil {
panic(err)
}
series, err := NewTimeSeries(data)
if err != nil {
panic(err)
}
fmt.Printf("%-12s%-12s%-12s%-12s%-12s%-12s%-12s\n", "Date", "Open", "High", "Low", "Close", "preclose", "TR")
// Calculate TR and print data for the last 15 trading days
atrSum := 0.0
validDays := 0
var trValues []float64
for i := 15; i > 0; i-- {
date := getDateNdaysAgo(i)
dailyData, ok := data["Time Series (Daily)"].(map[string]interface{})[date].(map[string]interface{})
if !ok {
// Skip weekends or non-trading days
continue
}
open, _ := strconv.ParseFloat(dailyData["1. open"].(string), 64)
high, _ := strconv.ParseFloat(dailyData["2. high"].(string), 64)
low, _ := strconv.ParseFloat(dailyData["3. low"].(string), 64)
close, _ := strconv.ParseFloat(dailyData["4. close"].(string), 64)
prevClose := getPreviousClose(series, i)
//highLowDiff := high - low
//highCloseDiff := high - close
//lowCloseDiff := low - close
tr := atr.CalculateTrueRange(high, low, close)
fmt.Printf("%-12s%-12.2f%-12.2f%-12.2f%-12.2f%-12.2f%-12.2f\n", date, open, high, low, close, prevClose, tr)
if i > 0 {
atrSum += tr
validDays++
trValues = append(trValues, tr)
}
}
// Calculate ATR
atr := atr.CalculateATR(trValues, validDays)
fmt.Printf("\nAverage True Range (ATR) for the last %d trading days: %.2f\n", validDays, atr)
}
func getPreviousClose(series TimeSeries, index int) float64 {
//if index == 15 {
//return series[index].Close
//}
return series[index].Close
}
func getDateNdaysAgo(n int) string {
today := time.Now()
for i := 0; i <= n; {
previousDate := today.AddDate(0, 0, -i)
if previousDate.Weekday() == time.Saturday || previousDate.Weekday() == time.Sunday {
// Skip weekends
today = today.AddDate(0, 0, -1)
continue
}
i++
}
previousDate := today.AddDate(0, 0, -n)
return previousDate.Format(“2006-01-02”)
}
package main
import (
“encoding/json”
“fmt”
“net/http”
“strconv”
“time”
atr "example/golang/ATR" // Update with your package path
)
const apiKey = "I know the api key "
const symbol = “RMBS”
type Candle struct {
High float64
Low float64
Close float64
}
type TimeSeries Candle
func NewTimeSeries(data map[string]interface{}) (TimeSeries, error) {
var series TimeSeries
timeSeriesData, ok := data["Time Series (Daily)"].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("missing or invalid 'Time Series (Daily)' field in data")
}
for date, dailyData := range timeSeriesData {
dailyDataMap, ok := dailyData.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("unexpected data format for date %s: %+v", date, dailyData)
}
high, _ := strconv.ParseFloat(dailyDataMap["2. high"].(string), 64)
low, _ := strconv.ParseFloat(dailyDataMap["3. low"].(string), 64)
close, _ := strconv.ParseFloat(dailyDataMap["4. close"].(string), 64)
candle := Candle{High: high, Low: low, Close: close}
series = append(series, candle)
}
// Reverse the series to have the latest date first
for i, j := 0, len(series)-1; i < j; i, j = i+1, j-1 {
series[i], series[j] = series[j], series[i]
}
return series, nil
}
func main() {
url := fmt.Sprintf(“https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=%s&apikey=%s”, symbol, apiKey)
response, err := http.Get(url)
if err != nil {
panic(err)
}
defer response.Body.Close()
var data map[string]interface{}
err = json.NewDecoder(response.Body).Decode(&data)
if err != nil {
panic(err)
}
series, err := NewTimeSeries(data)
if err != nil {
panic(err)
}
fmt.Printf("%-12s%-12s%-12s%-12s%-12s%-12s%-12s\n", "Date", "Open", "High", "Low", "Close", "preclose", "TR")
// Calculate TR and print data for the last 15 trading days
atrSum := 0.0
validDays := 0
var trValues []float64
for i := 15; i > 0; i-- {
date := getDateNdaysAgo(i)
dailyData, ok := data["Time Series (Daily)"].(map[string]interface{})[date].(map[string]interface{})
if !ok {
// Skip weekends or non-trading days
continue
}
open, _ := strconv.ParseFloat(dailyData["1. open"].(string), 64)
high, _ := strconv.ParseFloat(dailyData["2. high"].(string), 64)
low, _ := strconv.ParseFloat(dailyData["3. low"].(string), 64)
close, _ := strconv.ParseFloat(dailyData["4. close"].(string), 64)
prevClose := getPreviousClose(series, i)
//highLowDiff := high - low
//highCloseDiff := high - close
//lowCloseDiff := low - close
tr := atr.CalculateTrueRange(high, low, close)
fmt.Printf("%-12s%-12.2f%-12.2f%-12.2f%-12.2f%-12.2f%-12.2f\n", date, open, high, low, close, prevClose, tr)
if i > 0 {
atrSum += tr
validDays++
trValues = append(trValues, tr)
}
}
// Calculate ATR
atr := atr.CalculateATR(trValues, validDays)
fmt.Printf("\nAverage True Range (ATR) for the last %d trading days: %.2f\n", validDays, atr)
}
func getPreviousClose(series TimeSeries, index int) float64 {
//if index == 15 {
//return series[index].Close
//}
return series[index].Close
}
func getDateNdaysAgo(n int) string {
today := time.Now()
for i := 0; i <= n; {
previousDate := today.AddDate(0, 0, -i)
if previousDate.Weekday() == time.Saturday || previousDate.Weekday() == time.Sunday {
// Skip weekends
today = today.AddDate(0, 0, -1)
continue
}
i++
}
previousDate := today.AddDate(0, 0, -n)
return previousDate.Format(“2006-01-02”)
}
atr script
// atr.go
package atr
import (
“math”
)
// CalculateTrueRange calculates the True Range (TR) for a given candle.
func CalculateTrueRange(high, low, close float64) float64 {
highLowDiff := high - low
// theses are wrong, both highclose and lowclose
highCloseDiff := math.Abs(high - close)
lowCloseDiff := math.Abs(low - close)
//fmt.Printf("high-low difference: %.2f\n", highLowDiff)
//fmt.Printf("high-Close difference: %.2f\n", highCloseDiff)
//fmt.Printf("low-close difference: %.2f\n", lowCloseDiff)
tr := highLowDiff
if highCloseDiff > tr {
tr = highCloseDiff
}
if lowCloseDiff > tr {
tr = lowCloseDiff
}
return tr
}
// CalculateATR calculates the Average True Range (ATR) for a given series of TR values and a period.
func CalculateATR(trValues float64, period int) float64 {
// Ensure we don’t exceed the length of trValues
if len(trValues) < period {
period = len(trValues)
}
var sum float64
for i := len(trValues) - period; i < len(trValues); i++ {
sum += trValues[i]
}
return sum / float64(period)
}