merge: quick/fase-funciones-y-wal — 54 funciones, 12 tipos, WAL mode
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
package core
|
||||
|
||||
// Chunk splits xs into sub-slices of the given size.
|
||||
// The last chunk may contain fewer than size elements.
|
||||
// Returns nil if xs is empty. Panics if size <= 0.
|
||||
func Chunk[T any](xs []T, size int) [][]T {
|
||||
if size <= 0 {
|
||||
panic("chunk size must be > 0")
|
||||
}
|
||||
n := len(xs)
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
numChunks := (n + size - 1) / size
|
||||
chunks := make([][]T, 0, numChunks)
|
||||
for i := 0; i < n; i += size {
|
||||
end := i + size
|
||||
if end > n {
|
||||
end = n
|
||||
}
|
||||
chunks = append(chunks, xs[i:end])
|
||||
}
|
||||
return chunks
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: chunk
|
||||
kind: function
|
||||
lang: go
|
||||
domain: core
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func Chunk[T any](xs []T, size int) [][]T"
|
||||
description: "Divide un slice en trozos (sub-slices) de tamanio N. El ultimo trozo puede contener menos de N elementos. Retorna nil si el slice esta vacio. Entra en panic si size <= 0."
|
||||
tags: [slice, chunk, batch, generic]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/core/chunk.go"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
chunks := Chunk([]int{1, 2, 3, 4, 5}, 2)
|
||||
// chunks = [[1, 2], [3, 4], [5]]
|
||||
|
||||
chunks = Chunk([]string{"a", "b", "c", "d"}, 4)
|
||||
// chunks = [["a", "b", "c", "d"]]
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Funcion pura generica. Los sub-slices comparten memoria con el slice original (son slices del mismo backing array). Pre-calcula la capacidad del slice de chunks para evitar reasignaciones. Si size es mayor que len(xs), retorna un unico chunk con todos los elementos.
|
||||
@@ -0,0 +1,43 @@
|
||||
package core
|
||||
|
||||
import "sync"
|
||||
|
||||
// MapConcurrent applies fn to each element of xs using a pool of worker goroutines.
|
||||
// The number of concurrent workers is capped by the workers parameter.
|
||||
// Results preserve the original index order. If workers <= 0, it defaults to 1.
|
||||
func MapConcurrent[T any, U any](xs []T, fn func(T) U, workers int) []U {
|
||||
if workers <= 0 {
|
||||
workers = 1
|
||||
}
|
||||
n := len(xs)
|
||||
if n == 0 {
|
||||
return []U{}
|
||||
}
|
||||
|
||||
results := make([]U, n)
|
||||
var wg sync.WaitGroup
|
||||
ch := make(chan int, n)
|
||||
|
||||
// Feed indices into the channel.
|
||||
for i := range xs {
|
||||
ch <- i
|
||||
}
|
||||
close(ch)
|
||||
|
||||
// Spawn workers.
|
||||
if workers > n {
|
||||
workers = n
|
||||
}
|
||||
wg.Add(workers)
|
||||
for w := 0; w < workers; w++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for i := range ch {
|
||||
results[i] = fn(xs[i])
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
return results
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: map_concurrent
|
||||
kind: function
|
||||
lang: go
|
||||
domain: core
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "func MapConcurrent[T any, U any](xs []T, fn func(T) U, workers int) []U"
|
||||
description: "Aplica una funcion a cada elemento de un slice usando un pool de goroutines como workers. Los resultados preservan el orden original del slice de entrada."
|
||||
tags: [map, concurrent, parallel, generic]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/core/map_concurrent.go"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
squares := MapConcurrent([]int{1, 2, 3, 4, 5}, func(n int) int {
|
||||
return n * n
|
||||
}, 3)
|
||||
// squares = [1, 4, 9, 16, 25] (orden preservado)
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Funcion impura generica que usa goroutines y sync.WaitGroup. Los workers se alimentan de un canal con indices, garantizando que cada resultado se escribe en su posicion correcta sin race conditions (cada goroutine escribe en un indice unico). Si workers <= 0 se usa 1. Si workers > len(xs) se ajusta a len(xs).
|
||||
@@ -0,0 +1,16 @@
|
||||
package core
|
||||
|
||||
// Memoize wraps a pure function fn so that results are cached by key.
|
||||
// Subsequent calls with the same key return the cached value without calling fn again.
|
||||
// The returned function is safe for single-goroutine use.
|
||||
func Memoize[K comparable, V any](fn func(K) V) func(K) V {
|
||||
cache := make(map[K]V)
|
||||
return func(key K) V {
|
||||
if val, ok := cache[key]; ok {
|
||||
return val
|
||||
}
|
||||
val := fn(key)
|
||||
cache[key] = val
|
||||
return val
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
name: memoize
|
||||
kind: function
|
||||
lang: go
|
||||
domain: core
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func Memoize[K comparable, V any](fn func(K) V) func(K) V"
|
||||
description: "Cachea resultados de una funcion pura. Retorna una nueva funcion que almacena en un mapa interno los resultados ya calculados, evitando recalculos para la misma clave."
|
||||
tags: [cache, memoize, functional, generic]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/core/memoize.go"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
fib := Memoize(func(n int) int {
|
||||
if n <= 1 {
|
||||
return n
|
||||
}
|
||||
// nota: para recursion memoizada se necesita declarar la variable antes
|
||||
return n // simplificado
|
||||
})
|
||||
doubled := Memoize(func(x int) int { return x * 2 })
|
||||
doubled(5) // calcula: 10
|
||||
doubled(5) // cache hit: 10
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Funcion pura generica (referencialmente transparente). El cache interno es un map[K]V sin sincronizacion, por lo que la funcion retornada es segura solo para uso en una sola goroutine. K debe ser comparable para usarse como clave del mapa.
|
||||
@@ -0,0 +1,16 @@
|
||||
package core
|
||||
|
||||
// Partition splits xs into two slices: the first contains elements where pred returns true,
|
||||
// the second contains elements where pred returns false. Original order is preserved in both.
|
||||
func Partition[T any](xs []T, pred func(T) bool) ([]T, []T) {
|
||||
trueSlice := make([]T, 0)
|
||||
falseSlice := make([]T, 0)
|
||||
for _, x := range xs {
|
||||
if pred(x) {
|
||||
trueSlice = append(trueSlice, x)
|
||||
} else {
|
||||
falseSlice = append(falseSlice, x)
|
||||
}
|
||||
}
|
||||
return trueSlice, falseSlice
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: partition
|
||||
kind: function
|
||||
lang: go
|
||||
domain: core
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func Partition[T any](xs []T, pred func(T) bool) ([]T, []T)"
|
||||
description: "Divide un slice en dos segun un predicado. El primer slice contiene los elementos que cumplen el predicado, el segundo los que no. Se preserva el orden original."
|
||||
tags: [slice, partition, functional, generic]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/core/partition.go"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
evens, odds := Partition([]int{1, 2, 3, 4, 5}, func(n int) bool { return n%2 == 0 })
|
||||
// evens = [2, 4]
|
||||
// odds = [1, 3, 5]
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Funcion pura generica. Ambos slices retornados son nuevos (no muta el slice original). Si el slice de entrada esta vacio, retorna dos slices vacios. Los elementos mantienen su orden relativo original en cada particion.
|
||||
@@ -0,0 +1,13 @@
|
||||
package core
|
||||
|
||||
// Pipeline composes a sequence of functions T -> T into a single function.
|
||||
// The functions are applied left to right: Pipeline(f, g, h)(x) == h(g(f(x))).
|
||||
// Returns the identity function if no functions are provided.
|
||||
func Pipeline[T any](fns ...func(T) T) func(T) T {
|
||||
return func(val T) T {
|
||||
for _, fn := range fns {
|
||||
val = fn(val)
|
||||
}
|
||||
return val
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
name: pipeline
|
||||
kind: function
|
||||
lang: go
|
||||
domain: core
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func Pipeline[T any](fns ...func(T) T) func(T) T"
|
||||
description: "Compone funciones T -> T en secuencia de izquierda a derecha. Pipeline(f, g, h)(x) equivale a h(g(f(x))). Sin funciones retorna la identidad."
|
||||
tags: [pipeline, compose, functional, generic]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/core/pipeline.go"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
transform := Pipeline(
|
||||
func(s string) string { return strings.TrimSpace(s) },
|
||||
func(s string) string { return strings.ToUpper(s) },
|
||||
func(s string) string { return "[" + s + "]" },
|
||||
)
|
||||
transform(" hello ") // "[HELLO]"
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Funcion pura generica. Las funciones se aplican en orden de izquierda a derecha (no es composicion matematica tradicional). Si se pasa un slice vacio de funciones, la funcion retornada actua como identidad.
|
||||
@@ -0,0 +1,31 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// RetryWithBackoff retries fn up to maxRetries times with exponential backoff.
|
||||
// The delay between attempt i and i+1 is baseDelay * 2^i.
|
||||
// Returns the first successful result or the last error after all retries are exhausted.
|
||||
func RetryWithBackoff[T any](fn func() (T, error), maxRetries int, baseDelay time.Duration) (T, error) {
|
||||
var zero T
|
||||
if maxRetries < 0 {
|
||||
return zero, fmt.Errorf("maxRetries must be >= 0, got %d", maxRetries)
|
||||
}
|
||||
|
||||
var lastErr error
|
||||
for attempt := 0; attempt <= maxRetries; attempt++ {
|
||||
result, err := fn()
|
||||
if err == nil {
|
||||
return result, nil
|
||||
}
|
||||
lastErr = err
|
||||
|
||||
if attempt < maxRetries {
|
||||
delay := baseDelay * (1 << uint(attempt))
|
||||
time.Sleep(delay)
|
||||
}
|
||||
}
|
||||
return zero, fmt.Errorf("all %d retries exhausted: %w", maxRetries+1, lastErr)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
name: retry_with_backoff
|
||||
kind: function
|
||||
lang: go
|
||||
domain: core
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "func RetryWithBackoff[T any](fn func() (T, error), maxRetries int, baseDelay time.Duration) (T, error)"
|
||||
description: "Reintenta una funcion impura con backoff exponencial. El delay entre intento i e i+1 es baseDelay * 2^i. Retorna el primer resultado exitoso o el ultimo error tras agotar los reintentos."
|
||||
tags: [retry, backoff, resilience, generic]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/core/retry_with_backoff.go"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
result, err := RetryWithBackoff(func() (string, error) {
|
||||
resp, err := http.Get("https://api.example.com/data")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return "ok", nil
|
||||
}, 3, 100*time.Millisecond)
|
||||
// Intenta hasta 4 veces (1 inicial + 3 reintentos)
|
||||
// Delays: 100ms, 200ms, 400ms
|
||||
```
|
||||
|
||||
## Notas
|
||||
|
||||
Funcion impura generica que usa time.Sleep para el backoff. El numero total de intentos es maxRetries + 1 (el intento inicial mas los reintentos). Si maxRetries es negativo retorna error inmediatamente.
|
||||
@@ -0,0 +1,33 @@
|
||||
package cybersecurity
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var sqliPatterns = []struct {
|
||||
name string
|
||||
re *regexp.Regexp
|
||||
}{
|
||||
{"union_select", regexp.MustCompile(`(?i)\bunion\s+(all\s+)?select\b`)},
|
||||
{"or_1_eq_1", regexp.MustCompile(`(?i)\bor\s+1\s*=\s*1`)},
|
||||
{"comment_injection", regexp.MustCompile(`(--|#|/\*)\s*$`)},
|
||||
{"single_quote_or", regexp.MustCompile(`(?i)'\s*(or|and)\s+'`)},
|
||||
{"drop_table", regexp.MustCompile(`(?i)\bdrop\s+(table|database)\b`)},
|
||||
{"sleep_benchmark", regexp.MustCompile(`(?i)\b(sleep|benchmark)\s*\(`)},
|
||||
{"exec_xp", regexp.MustCompile(`(?i)\b(exec|xp_)\w*`)},
|
||||
{"tautology", regexp.MustCompile(`(?i)\bor\s+['"]?\w+['"]?\s*=\s*['"]?\w+['"]?`)},
|
||||
{"stacked_query", regexp.MustCompile(`;\s*(select|insert|update|delete|drop|alter)\b`)},
|
||||
}
|
||||
|
||||
// DetectSQLInjection analiza un input en busca de patrones heuristicos de inyeccion SQL.
|
||||
// Devuelve si se detecto una amenaza y el nombre del patron encontrado.
|
||||
func DetectSQLInjection(input string) (isThreat bool, pattern string) {
|
||||
normalized := strings.TrimSpace(input)
|
||||
for _, p := range sqliPatterns {
|
||||
if p.re.MatchString(normalized) {
|
||||
return true, p.name
|
||||
}
|
||||
}
|
||||
return false, ""
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: detect_sql_injection
|
||||
kind: function
|
||||
lang: go
|
||||
domain: cybersecurity
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func DetectSQLInjection(input string) (isThreat bool, pattern string)"
|
||||
description: "Analiza un input en busca de patrones heuristicos de inyeccion SQL y devuelve si se detecto amenaza y el patron encontrado."
|
||||
tags: [cybersecurity, sqli, detection, security]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [regexp, strings]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/cybersecurity/detect_sql_injection.go"
|
||||
---
|
||||
@@ -0,0 +1,26 @@
|
||||
package cybersecurity
|
||||
|
||||
import "math"
|
||||
|
||||
// EntropyShannon calcula la entropia de Shannon de los datos proporcionados.
|
||||
// Devuelve un valor entre 0 (datos uniformes) y 8 (datos completamente aleatorios para bytes).
|
||||
func EntropyShannon(data []byte) float64 {
|
||||
if len(data) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var freq [256]float64
|
||||
for _, b := range data {
|
||||
freq[b]++
|
||||
}
|
||||
|
||||
n := float64(len(data))
|
||||
entropy := 0.0
|
||||
for _, f := range freq {
|
||||
if f > 0 {
|
||||
p := f / n
|
||||
entropy -= p * math.Log2(p)
|
||||
}
|
||||
}
|
||||
return entropy
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: entropy_shannon
|
||||
kind: function
|
||||
lang: go
|
||||
domain: cybersecurity
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func EntropyShannon(data []byte) float64"
|
||||
description: "Calcula la entropia de Shannon de un slice de bytes. Retorna un valor entre 0 y 8 bits por byte."
|
||||
tags: [cybersecurity, entropy, shannon, analysis]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [math]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/cybersecurity/entropy_shannon.go"
|
||||
---
|
||||
@@ -0,0 +1,14 @@
|
||||
package cybersecurity
|
||||
|
||||
import "regexp"
|
||||
|
||||
var urlRegex = regexp.MustCompile(`https?://[^\s<>"'` + "`" + `\)\]\}]+`)
|
||||
|
||||
// ExtractURLs extrae todas las URLs (http/https) encontradas en el texto proporcionado.
|
||||
func ExtractURLs(text string) []string {
|
||||
matches := urlRegex.FindAllString(text, -1)
|
||||
if matches == nil {
|
||||
return []string{}
|
||||
}
|
||||
return matches
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: extract_urls
|
||||
kind: function
|
||||
lang: go
|
||||
domain: cybersecurity
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func ExtractURLs(text string) []string"
|
||||
description: "Extrae todas las URLs HTTP/HTTPS de un texto usando expresiones regulares."
|
||||
tags: [cybersecurity, extract, url, parse]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [regexp]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/cybersecurity/extract_urls.go"
|
||||
---
|
||||
@@ -0,0 +1,27 @@
|
||||
package cybersecurity
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// FetchHTTPHeaders realiza una solicitud HTTP HEAD a la URL y devuelve los headers de respuesta.
|
||||
func FetchHTTPHeaders(url string) (map[string][]string, error) {
|
||||
client := &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
resp, err := client.Head(url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error realizando solicitud HEAD a %s: %w", url, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
headers := make(map[string][]string, len(resp.Header))
|
||||
for k, v := range resp.Header {
|
||||
headers[k] = v
|
||||
}
|
||||
|
||||
return headers, nil
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: fetch_http_headers
|
||||
kind: function
|
||||
lang: go
|
||||
domain: cybersecurity
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "func FetchHTTPHeaders(url string) (map[string][]string, error)"
|
||||
description: "Realiza una solicitud HTTP HEAD a una URL y devuelve los headers de la respuesta."
|
||||
tags: [cybersecurity, io, http, headers]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [fmt, net/http, time]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/cybersecurity/fetch_http_headers.go"
|
||||
---
|
||||
@@ -0,0 +1,12 @@
|
||||
package cybersecurity
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// HashMD5 calcula el hash MD5 de los datos proporcionados y devuelve el resultado como string hexadecimal.
|
||||
func HashMD5(data []byte) string {
|
||||
h := md5.Sum(data)
|
||||
return hex.EncodeToString(h[:])
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: hash_md5
|
||||
kind: function
|
||||
lang: go
|
||||
domain: cybersecurity
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func HashMD5(data []byte) string"
|
||||
description: "Calcula el hash MD5 de un slice de bytes y devuelve el resultado como string hexadecimal."
|
||||
tags: [cybersecurity, hash, md5, crypto]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [crypto/md5, encoding/hex]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/cybersecurity/hash_md5.go"
|
||||
---
|
||||
@@ -0,0 +1,12 @@
|
||||
package cybersecurity
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// HashSHA256 calcula el hash SHA-256 de los datos proporcionados y devuelve el resultado como string hexadecimal.
|
||||
func HashSHA256(data []byte) string {
|
||||
h := sha256.Sum256(data)
|
||||
return hex.EncodeToString(h[:])
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: hash_sha256
|
||||
kind: function
|
||||
lang: go
|
||||
domain: cybersecurity
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func HashSHA256(data []byte) string"
|
||||
description: "Calcula el hash SHA-256 de un slice de bytes y devuelve el resultado como string hexadecimal."
|
||||
tags: [cybersecurity, hash, sha256, crypto]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [crypto/sha256, encoding/hex]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/cybersecurity/hash_sha256.go"
|
||||
---
|
||||
@@ -0,0 +1,16 @@
|
||||
package cybersecurity
|
||||
|
||||
import "net"
|
||||
|
||||
// IPInRange verifica si una direccion IP esta dentro de un rango CIDR dado.
|
||||
func IPInRange(ip, cidr string) bool {
|
||||
parsedIP := net.ParseIP(ip)
|
||||
if parsedIP == nil {
|
||||
return false
|
||||
}
|
||||
_, ipNet, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return ipNet.Contains(parsedIP)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: ip_in_range
|
||||
kind: function
|
||||
lang: go
|
||||
domain: cybersecurity
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func IPInRange(ip, cidr string) bool"
|
||||
description: "Verifica si una direccion IP se encuentra dentro de un rango CIDR dado."
|
||||
tags: [cybersecurity, network, cidr, check]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [net]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/cybersecurity/ip_in_range.go"
|
||||
---
|
||||
@@ -0,0 +1,12 @@
|
||||
package cybersecurity
|
||||
|
||||
import "encoding/base64"
|
||||
|
||||
// IsBase64 verifica si el string proporcionado es una cadena base64 valida (standard encoding).
|
||||
func IsBase64(s string) bool {
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
_, err := base64.StdEncoding.DecodeString(s)
|
||||
return err == nil
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: is_base64
|
||||
kind: function
|
||||
lang: go
|
||||
domain: cybersecurity
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func IsBase64(s string) bool"
|
||||
description: "Valida si un string es una cadena base64 valida segun el encoding estandar."
|
||||
tags: [cybersecurity, validation, base64, format]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [encoding/base64]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/cybersecurity/is_base64.go"
|
||||
---
|
||||
@@ -0,0 +1,15 @@
|
||||
package cybersecurity
|
||||
|
||||
// IsHex verifica si el string proporcionado es una cadena hexadecimal valida.
|
||||
// Requiere longitud par y que todos los caracteres sean digitos hex (0-9, a-f, A-F).
|
||||
func IsHex(s string) bool {
|
||||
if len(s) == 0 || len(s)%2 != 0 {
|
||||
return false
|
||||
}
|
||||
for _, c := range s {
|
||||
if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: is_hex
|
||||
kind: function
|
||||
lang: go
|
||||
domain: cybersecurity
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func IsHex(s string) bool"
|
||||
description: "Valida si un string es una cadena hexadecimal valida (longitud par, caracteres 0-9 a-f A-F)."
|
||||
tags: [cybersecurity, validation, hex, format]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/cybersecurity/is_hex.go"
|
||||
---
|
||||
@@ -0,0 +1,37 @@
|
||||
package cybersecurity
|
||||
|
||||
// JaccardSimilarity calcula la similitud de Jaccard entre dos conjuntos de tokens.
|
||||
// Devuelve un valor entre 0.0 (sin interseccion) y 1.0 (conjuntos identicos).
|
||||
func JaccardSimilarity(a, b []string) float64 {
|
||||
if len(a) == 0 && len(b) == 0 {
|
||||
return 1.0
|
||||
}
|
||||
if len(a) == 0 || len(b) == 0 {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
setA := make(map[string]struct{}, len(a))
|
||||
for _, s := range a {
|
||||
setA[s] = struct{}{}
|
||||
}
|
||||
|
||||
setB := make(map[string]struct{}, len(b))
|
||||
for _, s := range b {
|
||||
setB[s] = struct{}{}
|
||||
}
|
||||
|
||||
intersection := 0
|
||||
for k := range setA {
|
||||
if _, ok := setB[k]; ok {
|
||||
intersection++
|
||||
}
|
||||
}
|
||||
|
||||
// Union = |A| + |B| - |A intersect B| (usando conjuntos sin duplicados)
|
||||
union := len(setA) + len(setB) - intersection
|
||||
if union == 0 {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
return float64(intersection) / float64(union)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: jaccard_similarity
|
||||
kind: function
|
||||
lang: go
|
||||
domain: cybersecurity
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func JaccardSimilarity(a, b []string) float64"
|
||||
description: "Calcula la similitud de Jaccard entre dos conjuntos de tokens. Retorna un valor entre 0.0 y 1.0."
|
||||
tags: [cybersecurity, similarity, jaccard, tokens]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/cybersecurity/jaccard_similarity.go"
|
||||
---
|
||||
@@ -0,0 +1,49 @@
|
||||
package cybersecurity
|
||||
|
||||
// LevenshteinDistance calcula la distancia de edicion (Levenshtein) entre dos strings.
|
||||
func LevenshteinDistance(a, b string) int {
|
||||
ra := []rune(a)
|
||||
rb := []rune(b)
|
||||
la := len(ra)
|
||||
lb := len(rb)
|
||||
|
||||
if la == 0 {
|
||||
return lb
|
||||
}
|
||||
if lb == 0 {
|
||||
return la
|
||||
}
|
||||
|
||||
// Usar solo dos filas para optimizar memoria
|
||||
prev := make([]int, lb+1)
|
||||
curr := make([]int, lb+1)
|
||||
|
||||
for j := 0; j <= lb; j++ {
|
||||
prev[j] = j
|
||||
}
|
||||
|
||||
for i := 1; i <= la; i++ {
|
||||
curr[0] = i
|
||||
for j := 1; j <= lb; j++ {
|
||||
cost := 1
|
||||
if ra[i-1] == rb[j-1] {
|
||||
cost = 0
|
||||
}
|
||||
del := prev[j] + 1
|
||||
ins := curr[j-1] + 1
|
||||
sub := prev[j-1] + cost
|
||||
|
||||
m := del
|
||||
if ins < m {
|
||||
m = ins
|
||||
}
|
||||
if sub < m {
|
||||
m = sub
|
||||
}
|
||||
curr[j] = m
|
||||
}
|
||||
prev, curr = curr, prev
|
||||
}
|
||||
|
||||
return prev[lb]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: levenshtein_distance
|
||||
kind: function
|
||||
lang: go
|
||||
domain: cybersecurity
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func LevenshteinDistance(a, b string) int"
|
||||
description: "Calcula la distancia de edicion de Levenshtein entre dos strings. Util para deteccion de typosquatting."
|
||||
tags: [cybersecurity, string, distance, typosquatting]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/cybersecurity/levenshtein_distance.go"
|
||||
---
|
||||
@@ -0,0 +1,33 @@
|
||||
package cybersecurity
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LookupWhois realiza una consulta WHOIS para el dominio proporcionado conectandose al servidor whois.iana.org.
|
||||
func LookupWhois(domain string) (string, error) {
|
||||
conn, err := net.DialTimeout("tcp", "whois.iana.org:43", 10*time.Second)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error conectando al servidor WHOIS: %w", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
_ = conn.SetDeadline(time.Now().Add(10 * time.Second))
|
||||
|
||||
_, err = fmt.Fprintf(conn, "%s\r\n", domain)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error enviando consulta WHOIS: %w", err)
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
_, err = io.Copy(&sb, conn)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error leyendo respuesta WHOIS: %w", err)
|
||||
}
|
||||
|
||||
return sb.String(), nil
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: lookup_whois
|
||||
kind: function
|
||||
lang: go
|
||||
domain: cybersecurity
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "func LookupWhois(domain string) (string, error)"
|
||||
description: "Realiza una consulta WHOIS para un dominio conectandose al servidor whois.iana.org por TCP."
|
||||
tags: [cybersecurity, io, whois, recon]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [fmt, io, net, strings, time]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/cybersecurity/lookup_whois.go"
|
||||
---
|
||||
@@ -0,0 +1,44 @@
|
||||
package cybersecurity
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// trackingParams lista de parametros de tracking comunes a eliminar.
|
||||
var trackingParams = map[string]bool{
|
||||
"utm_source": true,
|
||||
"utm_medium": true,
|
||||
"utm_campaign": true,
|
||||
"utm_term": true,
|
||||
"utm_content": true,
|
||||
"fbclid": true,
|
||||
"gclid": true,
|
||||
"ref": true,
|
||||
"mc_cid": true,
|
||||
"mc_eid": true,
|
||||
}
|
||||
|
||||
// NormalizeURL normaliza una URL: convierte el host a minusculas, elimina fragmentos
|
||||
// y remueve parametros de tracking comunes.
|
||||
func NormalizeURL(rawURL string) string {
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return rawURL
|
||||
}
|
||||
|
||||
// Lowercase host
|
||||
u.Host = strings.ToLower(u.Host)
|
||||
|
||||
// Eliminar fragmento
|
||||
u.Fragment = ""
|
||||
|
||||
// Eliminar parametros de tracking
|
||||
q := u.Query()
|
||||
for param := range trackingParams {
|
||||
q.Del(param)
|
||||
}
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
return u.String()
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: normalize_url
|
||||
kind: function
|
||||
lang: go
|
||||
domain: cybersecurity
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func NormalizeURL(rawURL string) string"
|
||||
description: "Normaliza una URL: convierte el host a minusculas, elimina fragmentos y remueve parametros de tracking comunes."
|
||||
tags: [cybersecurity, url, normalize, sanitize]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [net/url, strings]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/cybersecurity/normalize_url.go"
|
||||
---
|
||||
@@ -0,0 +1,44 @@
|
||||
package cybersecurity
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
// ParseIPCIDR parsea una notacion CIDR y devuelve la direccion de red, broadcast, cantidad de hosts y error.
|
||||
func ParseIPCIDR(cidr string) (network string, broadcast string, hosts int, err error) {
|
||||
ip, ipNet, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
return "", "", 0, fmt.Errorf("CIDR invalido: %w", err)
|
||||
}
|
||||
|
||||
// Solo soportamos IPv4
|
||||
ip4 := ip.To4()
|
||||
if ip4 == nil {
|
||||
return "", "", 0, fmt.Errorf("solo se soporta IPv4")
|
||||
}
|
||||
|
||||
mask := ipNet.Mask
|
||||
networkIP := ipNet.IP.To4()
|
||||
network = networkIP.String()
|
||||
|
||||
// Calcular broadcast
|
||||
broadcastIP := make(net.IP, 4)
|
||||
for i := 0; i < 4; i++ {
|
||||
broadcastIP[i] = networkIP[i] | ^mask[i]
|
||||
}
|
||||
broadcast = broadcastIP.String()
|
||||
|
||||
// Calcular hosts usables
|
||||
netInt := binary.BigEndian.Uint32(networkIP)
|
||||
bcastInt := binary.BigEndian.Uint32(broadcastIP)
|
||||
total := int(bcastInt - netInt + 1)
|
||||
if total > 2 {
|
||||
hosts = total - 2 // excluir network y broadcast
|
||||
} else {
|
||||
hosts = total // /31 o /32
|
||||
}
|
||||
|
||||
return network, broadcast, hosts, nil
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: parse_ip_cidr
|
||||
kind: function
|
||||
lang: go
|
||||
domain: cybersecurity
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func ParseIPCIDR(cidr string) (network string, broadcast string, hosts int, err error)"
|
||||
description: "Parsea una notacion CIDR IPv4 y devuelve la direccion de red, broadcast y cantidad de hosts usables."
|
||||
tags: [cybersecurity, network, cidr, parse]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [encoding/binary, fmt, net]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/cybersecurity/parse_ip_cidr.go"
|
||||
---
|
||||
@@ -0,0 +1,15 @@
|
||||
package cybersecurity
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
// ResolveDNS resuelve un hostname a sus direcciones IP usando el resolver del sistema.
|
||||
func ResolveDNS(host string) ([]string, error) {
|
||||
ips, err := net.LookupHost(host)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error resolviendo DNS para %s: %w", host, err)
|
||||
}
|
||||
return ips, nil
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: resolve_dns
|
||||
kind: function
|
||||
lang: go
|
||||
domain: cybersecurity
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "func ResolveDNS(host string) ([]string, error)"
|
||||
description: "Resuelve un hostname a sus direcciones IP usando el resolver DNS del sistema."
|
||||
tags: [cybersecurity, io, dns, resolve]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [fmt, net]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/cybersecurity/resolve_dns.go"
|
||||
---
|
||||
@@ -0,0 +1,34 @@
|
||||
package cybersecurity
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ScanPortTCP intenta conectarse a un puerto TCP y devuelve el estado ("open", "closed", "filtered"),
|
||||
// un banner si el puerto esta abierto, y un posible error.
|
||||
func ScanPortTCP(host string, port int, timeoutMs int) (status string, banner string, err error) {
|
||||
address := fmt.Sprintf("%s:%d", host, port)
|
||||
timeout := time.Duration(timeoutMs) * time.Millisecond
|
||||
|
||||
conn, err := net.DialTimeout("tcp", address, timeout)
|
||||
if err != nil {
|
||||
// Distinguir entre conexion rechazada (closed) y timeout (filtered)
|
||||
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
||||
return "filtered", "", nil
|
||||
}
|
||||
return "closed", "", nil
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// Intentar leer un banner con timeout corto
|
||||
_ = conn.SetReadDeadline(time.Now().Add(2 * time.Second))
|
||||
buf := make([]byte, 1024)
|
||||
n, _ := conn.Read(buf)
|
||||
if n > 0 {
|
||||
banner = string(buf[:n])
|
||||
}
|
||||
|
||||
return "open", banner, nil
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: scan_port_tcp
|
||||
kind: function
|
||||
lang: go
|
||||
domain: cybersecurity
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "func ScanPortTCP(host string, port int, timeoutMs int) (status string, banner string, err error)"
|
||||
description: "Escanea un puerto TCP en un host dado. Devuelve el estado (open/closed/filtered) y un banner si esta abierto."
|
||||
tags: [cybersecurity, io, port, scan]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [fmt, net, time]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/cybersecurity/scan_port_tcp.go"
|
||||
---
|
||||
@@ -0,0 +1,12 @@
|
||||
package datascience
|
||||
|
||||
// Autocorrelation calcula la autocorrelación de data con el desfase (lag) dado.
|
||||
// Usa la correlación de Pearson entre data[0:n-lag] y data[lag:n].
|
||||
// Si lag es inválido, retorna 0.
|
||||
func Autocorrelation(data []float64, lag int) float64 {
|
||||
n := len(data)
|
||||
if lag < 0 || lag >= n {
|
||||
return 0
|
||||
}
|
||||
return Pearson(data[:n-lag], data[lag:])
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: autocorrelation
|
||||
kind: function
|
||||
lang: go
|
||||
domain: datascience
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func Autocorrelation(data []float64, lag int) float64"
|
||||
description: "Calcula la autocorrelación de una serie temporal con un desfase (lag) dado, usando correlación de Pearson."
|
||||
tags: [datascience, statistics, autocorrelation, timeseries]
|
||||
uses_functions: [pearson_go_datascience]
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/datascience/autocorrelation.go"
|
||||
---
|
||||
@@ -0,0 +1,17 @@
|
||||
package datascience
|
||||
|
||||
// Clip recorta cada valor del slice para que quede dentro del rango [min, max].
|
||||
func Clip(data []float64, min, max float64) []float64 {
|
||||
result := make([]float64, len(data))
|
||||
for i, v := range data {
|
||||
switch {
|
||||
case v < min:
|
||||
result[i] = min
|
||||
case v > max:
|
||||
result[i] = max
|
||||
default:
|
||||
result[i] = v
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: clip
|
||||
kind: function
|
||||
lang: go
|
||||
domain: datascience
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func Clip(data []float64, min, max float64) []float64"
|
||||
description: "Recorta cada valor del slice para que quede dentro del rango [min, max]."
|
||||
tags: [datascience, clamp, clip, range]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/datascience/clip.go"
|
||||
---
|
||||
@@ -0,0 +1,38 @@
|
||||
package datascience
|
||||
|
||||
import "math"
|
||||
|
||||
// DetectOutliers devuelve un []bool donde true indica que el valor es un outlier
|
||||
// según z-score. Un valor es outlier si |z-score| > threshold.
|
||||
func DetectOutliers(data []float64, threshold float64) []bool {
|
||||
n := len(data)
|
||||
if n == 0 {
|
||||
return []bool{}
|
||||
}
|
||||
|
||||
var sum float64
|
||||
for _, v := range data {
|
||||
sum += v
|
||||
}
|
||||
mean := sum / float64(n)
|
||||
|
||||
var sqSum float64
|
||||
for _, v := range data {
|
||||
d := v - mean
|
||||
sqSum += d * d
|
||||
}
|
||||
stddev := math.Sqrt(sqSum / float64(n))
|
||||
|
||||
result := make([]bool, n)
|
||||
if stddev == 0 {
|
||||
return result
|
||||
}
|
||||
for i, v := range data {
|
||||
z := (v - mean) / stddev
|
||||
if z < 0 {
|
||||
z = -z
|
||||
}
|
||||
result[i] = z > threshold
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: detect_outliers
|
||||
kind: function
|
||||
lang: go
|
||||
domain: datascience
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func DetectOutliers(data []float64, threshold float64) []bool"
|
||||
description: "Detecta outliers en un slice de float64 usando z-score. Devuelve true para valores cuyo |z-score| supera el umbral."
|
||||
tags: [datascience, statistics, outlier, anomaly]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: ["math"]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/datascience/detect_outliers.go"
|
||||
---
|
||||
@@ -0,0 +1,8 @@
|
||||
package datascience
|
||||
|
||||
import "fmt"
|
||||
|
||||
// FetchDataFrame ejecuta una consulta SQL contra un DSN y retorna los resultados como slice de mapas.
|
||||
func FetchDataFrame(dsn, query string) ([]map[string]any, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: fetch_data_frame
|
||||
kind: function
|
||||
lang: go
|
||||
domain: datascience
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "func FetchDataFrame(dsn, query string) ([]map[string]any, error)"
|
||||
description: "Ejecuta una consulta SQL contra un DSN y retorna los resultados como slice de mapas columna-valor."
|
||||
tags: [datascience, io, bigquery, fetch]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: ["fmt"]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/datascience/fetch_data_frame.go"
|
||||
---
|
||||
@@ -0,0 +1,61 @@
|
||||
package datascience
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/cmplx"
|
||||
)
|
||||
|
||||
// FFT calcula la Fast Fourier Transform usando el algoritmo Cooley-Tukey radix-2.
|
||||
// Si la longitud de data no es potencia de 2, se rellena con ceros (zero-padding).
|
||||
func FFT(data []float64) []complex128 {
|
||||
n := len(data)
|
||||
if n == 0 {
|
||||
return []complex128{}
|
||||
}
|
||||
|
||||
// Calcular la siguiente potencia de 2.
|
||||
size := nextPow2(n)
|
||||
|
||||
// Convertir a complex128 con zero-padding.
|
||||
x := make([]complex128, size)
|
||||
for i := 0; i < n; i++ {
|
||||
x[i] = complex(data[i], 0)
|
||||
}
|
||||
|
||||
fftRecursive(x)
|
||||
return x
|
||||
}
|
||||
|
||||
// nextPow2 retorna la menor potencia de 2 >= n.
|
||||
func nextPow2(n int) int {
|
||||
p := 1
|
||||
for p < n {
|
||||
p <<= 1
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// fftRecursive aplica Cooley-Tukey radix-2 DIT in-place.
|
||||
func fftRecursive(x []complex128) {
|
||||
n := len(x)
|
||||
if n <= 1 {
|
||||
return
|
||||
}
|
||||
|
||||
// Separar pares e impares.
|
||||
even := make([]complex128, n/2)
|
||||
odd := make([]complex128, n/2)
|
||||
for i := 0; i < n/2; i++ {
|
||||
even[i] = x[2*i]
|
||||
odd[i] = x[2*i+1]
|
||||
}
|
||||
|
||||
fftRecursive(even)
|
||||
fftRecursive(odd)
|
||||
|
||||
for k := 0; k < n/2; k++ {
|
||||
t := cmplx.Rect(1, -2*math.Pi*float64(k)/float64(n)) * odd[k]
|
||||
x[k] = even[k] + t
|
||||
x[k+n/2] = even[k] - t
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: fft
|
||||
kind: function
|
||||
lang: go
|
||||
domain: datascience
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func FFT(data []float64) []complex128"
|
||||
description: "Calcula la Transformada Rápida de Fourier (FFT) usando el algoritmo Cooley-Tukey radix-2. Aplica zero-padding si la longitud no es potencia de 2."
|
||||
tags: [datascience, dsp, fft, fourier, frequency]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: ["math", "math/cmplx"]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/datascience/fft.go"
|
||||
---
|
||||
@@ -0,0 +1,11 @@
|
||||
package datascience
|
||||
|
||||
// GroupBy agrupa los elementos de un slice según la clave devuelta por keyFn.
|
||||
func GroupBy[T any, K comparable](xs []T, keyFn func(T) K) map[K][]T {
|
||||
groups := make(map[K][]T)
|
||||
for _, x := range xs {
|
||||
k := keyFn(x)
|
||||
groups[k] = append(groups[k], x)
|
||||
}
|
||||
return groups
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: group_by
|
||||
kind: function
|
||||
lang: go
|
||||
domain: datascience
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func GroupBy[T any, K comparable](xs []T, keyFn func(T) K) map[K][]T"
|
||||
description: "Agrupa los elementos de un slice según una función clave, devolviendo un mapa de clave a slice de elementos."
|
||||
tags: [datascience, group, aggregate, generic]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/datascience/group_by.go"
|
||||
---
|
||||
@@ -0,0 +1,39 @@
|
||||
package datascience
|
||||
|
||||
import "math"
|
||||
|
||||
// Histogram calcula las frecuencias de data distribuidas en la cantidad de buckets indicada.
|
||||
// Retorna un slice de longitud buckets con el conteo de elementos por cada intervalo equiespaciado.
|
||||
func Histogram(data []float64, buckets int) []int {
|
||||
if buckets <= 0 || len(data) == 0 {
|
||||
return make([]int, buckets)
|
||||
}
|
||||
|
||||
minVal := math.Inf(1)
|
||||
maxVal := math.Inf(-1)
|
||||
for _, v := range data {
|
||||
if v < minVal {
|
||||
minVal = v
|
||||
}
|
||||
if v > maxVal {
|
||||
maxVal = v
|
||||
}
|
||||
}
|
||||
|
||||
counts := make([]int, buckets)
|
||||
rang := maxVal - minVal
|
||||
if rang == 0 {
|
||||
// Todos los valores son iguales; poner todo en el primer bucket.
|
||||
counts[0] = len(data)
|
||||
return counts
|
||||
}
|
||||
|
||||
for _, v := range data {
|
||||
idx := int(float64(buckets) * (v - minVal) / rang)
|
||||
if idx >= buckets {
|
||||
idx = buckets - 1
|
||||
}
|
||||
counts[idx]++
|
||||
}
|
||||
return counts
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: histogram
|
||||
kind: function
|
||||
lang: go
|
||||
domain: datascience
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func Histogram(data []float64, buckets int) []int"
|
||||
description: "Calcula las frecuencias de un slice de float64 distribuidas en un número dado de buckets equiespaciados."
|
||||
tags: [datascience, statistics, histogram, frequency]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: ["math"]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/datascience/histogram.go"
|
||||
---
|
||||
@@ -0,0 +1,18 @@
|
||||
package datascience
|
||||
|
||||
import "math"
|
||||
|
||||
// Impute rellena valores NaN usando forward-fill.
|
||||
// Cada NaN se reemplaza con el último valor válido (no NaN) anterior.
|
||||
// Si el primer valor es NaN y no hay valor anterior, se mantiene como NaN.
|
||||
func Impute(data []float64) []float64 {
|
||||
result := make([]float64, len(data))
|
||||
last := math.NaN()
|
||||
for i, v := range data {
|
||||
if !math.IsNaN(v) {
|
||||
last = v
|
||||
}
|
||||
result[i] = last
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: impute
|
||||
kind: function
|
||||
lang: go
|
||||
domain: datascience
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func Impute(data []float64) []float64"
|
||||
description: "Rellena valores NaN en un slice de float64 usando forward-fill, reemplazando cada NaN con el último valor válido anterior."
|
||||
tags: [datascience, impute, missing, fill]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: ["math"]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/datascience/impute.go"
|
||||
---
|
||||
@@ -0,0 +1,8 @@
|
||||
package datascience
|
||||
|
||||
import "fmt"
|
||||
|
||||
// LoadCSV carga un archivo CSV y lo retorna como slice de mapas (columna -> valor).
|
||||
func LoadCSV(path string) ([]map[string]string, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: load_csv
|
||||
kind: function
|
||||
lang: go
|
||||
domain: datascience
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "func LoadCSV(path string) ([]map[string]string, error)"
|
||||
description: "Carga un archivo CSV desde disco y lo retorna como slice de mapas columna-valor."
|
||||
tags: [datascience, io, csv, load]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: ["fmt"]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/datascience/load_csv.go"
|
||||
---
|
||||
@@ -0,0 +1,8 @@
|
||||
package datascience
|
||||
|
||||
import "fmt"
|
||||
|
||||
// LoadParquet carga un archivo Parquet y lo retorna como slice de mapas.
|
||||
func LoadParquet(path string) ([]map[string]any, error) {
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: load_parquet
|
||||
kind: function
|
||||
lang: go
|
||||
domain: datascience
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "func LoadParquet(path string) ([]map[string]any, error)"
|
||||
description: "Carga un archivo Parquet desde disco y lo retorna como slice de mapas columna-valor."
|
||||
tags: [datascience, io, parquet, load]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: ["fmt"]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/datascience/load_parquet.go"
|
||||
---
|
||||
@@ -0,0 +1,33 @@
|
||||
package datascience
|
||||
|
||||
import "math"
|
||||
|
||||
// MinMaxScale escala los valores al rango [0, 1] usando min-max normalización.
|
||||
// Si min == max, retorna un slice de ceros.
|
||||
func MinMaxScale(data []float64) []float64 {
|
||||
n := len(data)
|
||||
if n == 0 {
|
||||
return []float64{}
|
||||
}
|
||||
|
||||
minVal := math.Inf(1)
|
||||
maxVal := math.Inf(-1)
|
||||
for _, v := range data {
|
||||
if v < minVal {
|
||||
minVal = v
|
||||
}
|
||||
if v > maxVal {
|
||||
maxVal = v
|
||||
}
|
||||
}
|
||||
|
||||
rang := maxVal - minVal
|
||||
result := make([]float64, n)
|
||||
if rang == 0 {
|
||||
return result
|
||||
}
|
||||
for i, v := range data {
|
||||
result[i] = (v - minVal) / rang
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: min_max_scale
|
||||
kind: function
|
||||
lang: go
|
||||
domain: datascience
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func MinMaxScale(data []float64) []float64"
|
||||
description: "Escala los valores de un slice al rango [0, 1] usando normalización min-max."
|
||||
tags: [datascience, statistics, normalize, scale]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: ["math"]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/datascience/min_max_scale.go"
|
||||
---
|
||||
@@ -0,0 +1,39 @@
|
||||
package datascience
|
||||
|
||||
import "math"
|
||||
|
||||
// Pearson calcula el coeficiente de correlación de Pearson entre dos slices.
|
||||
// Si los slices tienen distinta longitud, usa la longitud mínima.
|
||||
// Retorna 0 si alguna desviación estándar es 0.
|
||||
func Pearson(xs, ys []float64) float64 {
|
||||
n := len(xs)
|
||||
if len(ys) < n {
|
||||
n = len(ys)
|
||||
}
|
||||
if n == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var sumX, sumY float64
|
||||
for i := 0; i < n; i++ {
|
||||
sumX += xs[i]
|
||||
sumY += ys[i]
|
||||
}
|
||||
meanX := sumX / float64(n)
|
||||
meanY := sumY / float64(n)
|
||||
|
||||
var num, denomX, denomY float64
|
||||
for i := 0; i < n; i++ {
|
||||
dx := xs[i] - meanX
|
||||
dy := ys[i] - meanY
|
||||
num += dx * dy
|
||||
denomX += dx * dx
|
||||
denomY += dy * dy
|
||||
}
|
||||
|
||||
denom := math.Sqrt(denomX * denomY)
|
||||
if denom == 0 {
|
||||
return 0
|
||||
}
|
||||
return num / denom
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: pearson
|
||||
kind: function
|
||||
lang: go
|
||||
domain: datascience
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func Pearson(xs, ys []float64) float64"
|
||||
description: "Calcula el coeficiente de correlación de Pearson entre dos slices de float64."
|
||||
tags: [datascience, statistics, correlation, pearson]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: ["math"]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/datascience/pearson.go"
|
||||
---
|
||||
@@ -0,0 +1,17 @@
|
||||
package datascience
|
||||
|
||||
// RollingWindow genera ventanas deslizantes de tamaño size sobre el slice xs.
|
||||
// Si size <= 0 o size > len(xs), retorna nil.
|
||||
func RollingWindow[T any](xs []T, size int) [][]T {
|
||||
n := len(xs)
|
||||
if size <= 0 || size > n {
|
||||
return nil
|
||||
}
|
||||
windows := make([][]T, 0, n-size+1)
|
||||
for i := 0; i <= n-size; i++ {
|
||||
w := make([]T, size)
|
||||
copy(w, xs[i:i+size])
|
||||
windows = append(windows, w)
|
||||
}
|
||||
return windows
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: rolling_window
|
||||
kind: function
|
||||
lang: go
|
||||
domain: datascience
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func RollingWindow[T any](xs []T, size int) [][]T"
|
||||
description: "Genera ventanas deslizantes de tamaño fijo sobre un slice genérico."
|
||||
tags: [datascience, window, rolling, sliding, generic]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/datascience/rolling_window.go"
|
||||
---
|
||||
@@ -0,0 +1,35 @@
|
||||
package datascience
|
||||
|
||||
import "math"
|
||||
|
||||
// Standardize aplica Z-score normalización a un slice de float64.
|
||||
// Cada valor se transforma a (x - mean) / stddev.
|
||||
// Si stddev es 0, retorna un slice de ceros.
|
||||
func Standardize(data []float64) []float64 {
|
||||
n := len(data)
|
||||
if n == 0 {
|
||||
return []float64{}
|
||||
}
|
||||
|
||||
var sum float64
|
||||
for _, v := range data {
|
||||
sum += v
|
||||
}
|
||||
mean := sum / float64(n)
|
||||
|
||||
var sqSum float64
|
||||
for _, v := range data {
|
||||
d := v - mean
|
||||
sqSum += d * d
|
||||
}
|
||||
stddev := math.Sqrt(sqSum / float64(n))
|
||||
|
||||
result := make([]float64, n)
|
||||
if stddev == 0 {
|
||||
return result
|
||||
}
|
||||
for i, v := range data {
|
||||
result[i] = (v - mean) / stddev
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: standardize
|
||||
kind: function
|
||||
lang: go
|
||||
domain: datascience
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func Standardize(data []float64) []float64"
|
||||
description: "Aplica Z-score normalización a un slice de float64, transformando cada valor a (x - media) / desviación estándar."
|
||||
tags: [datascience, statistics, normalize, zscore]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: ["math"]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/datascience/standardize.go"
|
||||
---
|
||||
@@ -0,0 +1,15 @@
|
||||
package datascience
|
||||
|
||||
// ZipSlices combina dos slices de float64 en pares [2]float64.
|
||||
// El resultado tiene longitud igual al menor de los dos slices.
|
||||
func ZipSlices(as, bs []float64) [][2]float64 {
|
||||
n := len(as)
|
||||
if len(bs) < n {
|
||||
n = len(bs)
|
||||
}
|
||||
result := make([][2]float64, n)
|
||||
for i := 0; i < n; i++ {
|
||||
result[i] = [2]float64{as[i], bs[i]}
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: zip_slices
|
||||
kind: function
|
||||
lang: go
|
||||
domain: datascience
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func ZipSlices(as, bs []float64) [][2]float64"
|
||||
description: "Combina dos slices de float64 en un slice de pares [2]float64, truncando al más corto."
|
||||
tags: [datascience, zip, combine, pair]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/datascience/zip_slices.go"
|
||||
---
|
||||
@@ -0,0 +1,25 @@
|
||||
package finance
|
||||
|
||||
import "math"
|
||||
|
||||
// AnnualizedVolatility calcula la volatilidad anualizada a partir de una serie de retornos.
|
||||
// periodsPerYear indica cuantos periodos hay en un anio (e.g. 252 para retornos diarios).
|
||||
// Retorna stddev(returns) * sqrt(periodsPerYear).
|
||||
func AnnualizedVolatility(returns []float64, periodsPerYear float64) float64 {
|
||||
n := len(returns)
|
||||
if n < 2 {
|
||||
return 0
|
||||
}
|
||||
var sum float64
|
||||
for _, r := range returns {
|
||||
sum += r
|
||||
}
|
||||
mean := sum / float64(n)
|
||||
var variance float64
|
||||
for _, r := range returns {
|
||||
diff := r - mean
|
||||
variance += diff * diff
|
||||
}
|
||||
variance /= float64(n - 1)
|
||||
return math.Sqrt(variance) * math.Sqrt(periodsPerYear)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: annualized_volatility
|
||||
kind: function
|
||||
lang: go
|
||||
domain: finance
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func AnnualizedVolatility(returns []float64, periodsPerYear float64) float64"
|
||||
description: "Calcula la volatilidad anualizada a partir de una serie de retornos y la frecuencia de los periodos."
|
||||
tags: [finance, volatility, risk, annualized]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [math]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/finance/annualized_volatility.go"
|
||||
---
|
||||
|
||||
# annualized_volatility
|
||||
|
||||
Calcula la volatilidad anualizada como `stddev(returns) * sqrt(periodsPerYear)`. Usa desviacion estandar muestral (n-1).
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
vol := finance.AnnualizedVolatility([]float64{0.01, -0.02, 0.015, 0.005, -0.01}, 252)
|
||||
```
|
||||
@@ -0,0 +1,32 @@
|
||||
package finance
|
||||
|
||||
import "math"
|
||||
|
||||
// BollingerBands calcula las bandas de Bollinger: upper, middle (SMA), lower.
|
||||
// Los primeros period-1 elementos de cada slice son 0.
|
||||
func BollingerBands(data []float64, period int, numStdDev float64) (upper, middle, lower []float64) {
|
||||
n := len(data)
|
||||
upper = make([]float64, n)
|
||||
middle = make([]float64, n)
|
||||
lower = make([]float64, n)
|
||||
if period <= 0 || period > n {
|
||||
return
|
||||
}
|
||||
for i := period - 1; i < n; i++ {
|
||||
var sum float64
|
||||
for j := i - period + 1; j <= i; j++ {
|
||||
sum += data[j]
|
||||
}
|
||||
mean := sum / float64(period)
|
||||
var variance float64
|
||||
for j := i - period + 1; j <= i; j++ {
|
||||
diff := data[j] - mean
|
||||
variance += diff * diff
|
||||
}
|
||||
std := math.Sqrt(variance / float64(period))
|
||||
middle[i] = mean
|
||||
upper[i] = mean + numStdDev*std
|
||||
lower[i] = mean - numStdDev*std
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: bollinger_bands
|
||||
kind: function
|
||||
lang: go
|
||||
domain: finance
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func BollingerBands(data []float64, period int, numStdDev float64) (upper, middle, lower []float64)"
|
||||
description: "Calcula las bandas de Bollinger (upper, middle, lower) para una serie de precios."
|
||||
tags: [finance, indicator, bollinger, bands]
|
||||
uses_functions: []
|
||||
uses_types: [bollinger_result_go_finance]
|
||||
returns: [bollinger_result_go_finance]
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [math]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/finance/bollinger_bands.go"
|
||||
---
|
||||
|
||||
# bollinger_bands
|
||||
|
||||
Calcula las bandas de Bollinger. La banda media es la SMA, y las bandas superior e inferior estan a `numStdDev` desviaciones estandar de la media. Usa desviacion estandar poblacional.
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
upper, middle, lower := finance.BollingerBands(
|
||||
[]float64{22, 24, 23, 25, 26, 28, 27, 29, 30, 28},
|
||||
5, 2.0,
|
||||
)
|
||||
```
|
||||
@@ -0,0 +1,23 @@
|
||||
package finance
|
||||
|
||||
// EMA calcula la media movil exponencial de data con el periodo dado.
|
||||
// El primer valor valido se inicializa con la SMA de los primeros period elementos.
|
||||
// Los primeros period-1 elementos del resultado son 0.
|
||||
func EMA(data []float64, period int) []float64 {
|
||||
n := len(data)
|
||||
result := make([]float64, n)
|
||||
if period <= 0 || period > n {
|
||||
return result
|
||||
}
|
||||
k := 2.0 / float64(period+1)
|
||||
// Seed con SMA
|
||||
var sum float64
|
||||
for i := 0; i < period; i++ {
|
||||
sum += data[i]
|
||||
}
|
||||
result[period-1] = sum / float64(period)
|
||||
for i := period; i < n; i++ {
|
||||
result[i] = data[i]*k + result[i-1]*(1-k)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: ema
|
||||
kind: function
|
||||
lang: go
|
||||
domain: finance
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func EMA(data []float64, period int) []float64"
|
||||
description: "Calcula la media movil exponencial (EMA) sobre una serie de datos con un periodo dado."
|
||||
tags: [finance, indicator, ema, moving-average]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/finance/ema.go"
|
||||
---
|
||||
|
||||
# ema
|
||||
|
||||
Calcula la media movil exponencial (Exponential Moving Average). Se inicializa con la SMA de los primeros `period` elementos. El multiplicador es `2 / (period + 1)`.
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
result := finance.EMA([]float64{10, 11, 12, 13, 14, 15}, 3)
|
||||
// result[2] = SMA de los primeros 3 = 11.0
|
||||
// result[3] en adelante usa suavizado exponencial
|
||||
```
|
||||
@@ -0,0 +1,9 @@
|
||||
package finance
|
||||
|
||||
import "fmt"
|
||||
|
||||
// FetchOHLCV obtiene datos OHLCV de un exchange para un simbolo e intervalo dados.
|
||||
// TODO: implementar conexion real al exchange.
|
||||
func FetchOHLCV(symbol, interval string) ([][]float64, error) {
|
||||
return nil, fmt.Errorf("not implemented: FetchOHLCV(%s, %s)", symbol, interval)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: fetch_ohlcv
|
||||
kind: function
|
||||
lang: go
|
||||
domain: finance
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "func FetchOHLCV(symbol, interval string) ([][]float64, error)"
|
||||
description: "Obtiene datos OHLCV de un exchange para un simbolo e intervalo dados."
|
||||
tags: [finance, io, exchange, fetch]
|
||||
uses_functions: []
|
||||
uses_types: [ohlcv_go_finance]
|
||||
returns: [ohlcv_go_finance]
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [fmt]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/finance/fetch_ohlcv.go"
|
||||
---
|
||||
|
||||
# fetch_ohlcv
|
||||
|
||||
Stub para obtener datos OHLCV de un exchange. Pendiente de implementacion.
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
data, err := finance.FetchOHLCV("BTC/USDT", "1h")
|
||||
```
|
||||
@@ -0,0 +1,9 @@
|
||||
package finance
|
||||
|
||||
import "fmt"
|
||||
|
||||
// LoadOHLCVFromDuckDB carga datos OHLCV ejecutando una query en una base DuckDB.
|
||||
// TODO: implementar conexion real a DuckDB.
|
||||
func LoadOHLCVFromDuckDB(dbPath, query string) ([][]float64, error) {
|
||||
return nil, fmt.Errorf("not implemented: LoadOHLCVFromDuckDB(%s, %s)", dbPath, query)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: load_ohlcv_from_duckdb
|
||||
kind: function
|
||||
lang: go
|
||||
domain: finance
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "func LoadOHLCVFromDuckDB(dbPath, query string) ([][]float64, error)"
|
||||
description: "Carga datos OHLCV ejecutando una query SQL en una base de datos DuckDB."
|
||||
tags: [finance, io, duckdb, load]
|
||||
uses_functions: []
|
||||
uses_types: [ohlcv_go_finance]
|
||||
returns: [ohlcv_go_finance]
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [fmt]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/finance/load_ohlcv_from_duckdb.go"
|
||||
---
|
||||
|
||||
# load_ohlcv_from_duckdb
|
||||
|
||||
Stub para cargar datos OHLCV desde DuckDB. Pendiente de implementacion.
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
data, err := finance.LoadOHLCVFromDuckDB("/data/market.duckdb", "SELECT * FROM ohlcv WHERE symbol='BTC'")
|
||||
```
|
||||
@@ -0,0 +1,12 @@
|
||||
package finance
|
||||
|
||||
import "math"
|
||||
|
||||
// LogReturn calcula el retorno logaritmico entre dos precios.
|
||||
// Retorna ln(priceEnd / priceStart). Si priceStart <= 0 o priceEnd <= 0, retorna 0.
|
||||
func LogReturn(priceStart, priceEnd float64) float64 {
|
||||
if priceStart <= 0 || priceEnd <= 0 {
|
||||
return 0
|
||||
}
|
||||
return math.Log(priceEnd / priceStart)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: log_return
|
||||
kind: function
|
||||
lang: go
|
||||
domain: finance
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func LogReturn(priceStart, priceEnd float64) float64"
|
||||
description: "Calcula el retorno logaritmico entre un precio inicial y un precio final."
|
||||
tags: [finance, return, logarithmic, atomic]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [math]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/finance/log_return.go"
|
||||
---
|
||||
|
||||
# log_return
|
||||
|
||||
Calcula el retorno logaritmico: `ln(priceEnd / priceStart)`. Si alguno de los precios es <= 0, retorna 0.
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
r := finance.LogReturn(100.0, 110.0)
|
||||
// r ~ 0.09531 (aprox 9.53%)
|
||||
```
|
||||
@@ -0,0 +1,27 @@
|
||||
package finance
|
||||
|
||||
// MaxDrawdown calcula el maximo drawdown de una serie de valores (e.g. equity curve).
|
||||
// Retorna la magnitud del drawdown (valor positivo entre 0 y 1 como fraccion del pico),
|
||||
// y los indices de inicio (pico) y fin (valle) del peor drawdown.
|
||||
// Si la serie tiene menos de 2 elementos, retorna 0, 0, 0.
|
||||
func MaxDrawdown(values []float64) (maxDD float64, start, end int) {
|
||||
n := len(values)
|
||||
if n < 2 {
|
||||
return 0, 0, 0
|
||||
}
|
||||
peakIdx := 0
|
||||
peak := values[0]
|
||||
for i := 1; i < n; i++ {
|
||||
if values[i] > peak {
|
||||
peak = values[i]
|
||||
peakIdx = i
|
||||
}
|
||||
dd := (peak - values[i]) / peak
|
||||
if dd > maxDD {
|
||||
maxDD = dd
|
||||
start = peakIdx
|
||||
end = i
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: max_drawdown
|
||||
kind: function
|
||||
lang: go
|
||||
domain: finance
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func MaxDrawdown(values []float64) (maxDD float64, start, end int)"
|
||||
description: "Calcula el maximo drawdown de una curva de equity, retornando la magnitud y los indices pico-valle."
|
||||
tags: [finance, drawdown, risk, metric]
|
||||
uses_functions: []
|
||||
uses_types: [drawdown_result_go_finance]
|
||||
returns: [drawdown_result_go_finance]
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/finance/max_drawdown.go"
|
||||
---
|
||||
|
||||
# max_drawdown
|
||||
|
||||
Calcula el maximo drawdown (caida maxima desde un pico hasta un valle) como fraccion del pico. Retorna la magnitud (0 a 1) y los indices de inicio y fin.
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
dd, s, e := finance.MaxDrawdown([]float64{100, 120, 90, 110, 80})
|
||||
// dd = (120-80)/120 = 0.3333, s = 1, e = 4
|
||||
```
|
||||
@@ -0,0 +1,17 @@
|
||||
package finance
|
||||
|
||||
// NormalizeOHLCV ajusta slices de precios OHLCV multiplicando por un factor.
|
||||
func NormalizeOHLCV(open, high, low, close []float64, factor float64) ([]float64, []float64, []float64, []float64) {
|
||||
n := len(open)
|
||||
nOpen := make([]float64, n)
|
||||
nHigh := make([]float64, n)
|
||||
nLow := make([]float64, n)
|
||||
nClose := make([]float64, n)
|
||||
for i := 0; i < n; i++ {
|
||||
nOpen[i] = open[i] * factor
|
||||
nHigh[i] = high[i] * factor
|
||||
nLow[i] = low[i] * factor
|
||||
nClose[i] = close[i] * factor
|
||||
}
|
||||
return nOpen, nHigh, nLow, nClose
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: normalize_ohlcv
|
||||
kind: function
|
||||
lang: go
|
||||
domain: finance
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func NormalizeOHLCV(open, high, low, close []float64, factor float64) ([]float64, []float64, []float64, []float64)"
|
||||
description: "Ajusta slices de precios OHLCV multiplicando cada valor por un factor dado."
|
||||
tags: [finance, ohlcv, normalize, adjust]
|
||||
uses_functions: []
|
||||
uses_types: [ohlcv_go_finance]
|
||||
returns: [ohlcv_go_finance]
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/finance/normalize_ohlcv.go"
|
||||
---
|
||||
|
||||
# normalize_ohlcv
|
||||
|
||||
Ajusta slices de precios OHLCV (open, high, low, close) multiplicando cada elemento por un factor escalar. Util para ajustes por splits, conversiones de divisa, o normalizacion de series.
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
o, h, l, c := finance.NormalizeOHLCV(
|
||||
[]float64{100, 200},
|
||||
[]float64{110, 210},
|
||||
[]float64{90, 190},
|
||||
[]float64{105, 205},
|
||||
2.0,
|
||||
)
|
||||
// o = [200, 400], h = [220, 420], l = [180, 380], c = [210, 410]
|
||||
```
|
||||
@@ -0,0 +1,47 @@
|
||||
package finance
|
||||
|
||||
// RSI calcula el Relative Strength Index.
|
||||
// Usa el metodo de suavizado de Wilder (EMA con alpha = 1/period).
|
||||
// Los primeros period elementos del resultado son 0.
|
||||
func RSI(data []float64, period int) []float64 {
|
||||
n := len(data)
|
||||
result := make([]float64, n)
|
||||
if period <= 0 || n < period+1 {
|
||||
return result
|
||||
}
|
||||
var gainSum, lossSum float64
|
||||
for i := 1; i <= period; i++ {
|
||||
change := data[i] - data[i-1]
|
||||
if change > 0 {
|
||||
gainSum += change
|
||||
} else {
|
||||
lossSum -= change
|
||||
}
|
||||
}
|
||||
avgGain := gainSum / float64(period)
|
||||
avgLoss := lossSum / float64(period)
|
||||
if avgLoss == 0 {
|
||||
result[period] = 100
|
||||
} else {
|
||||
rs := avgGain / avgLoss
|
||||
result[period] = 100 - 100/(1+rs)
|
||||
}
|
||||
for i := period + 1; i < n; i++ {
|
||||
change := data[i] - data[i-1]
|
||||
var gain, loss float64
|
||||
if change > 0 {
|
||||
gain = change
|
||||
} else {
|
||||
loss = -change
|
||||
}
|
||||
avgGain = (avgGain*float64(period-1) + gain) / float64(period)
|
||||
avgLoss = (avgLoss*float64(period-1) + loss) / float64(period)
|
||||
if avgLoss == 0 {
|
||||
result[i] = 100
|
||||
} else {
|
||||
rs := avgGain / avgLoss
|
||||
result[i] = 100 - 100/(1+rs)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: rsi
|
||||
kind: function
|
||||
lang: go
|
||||
domain: finance
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func RSI(data []float64, period int) []float64"
|
||||
description: "Calcula el Relative Strength Index (RSI) usando suavizado de Wilder."
|
||||
tags: [finance, indicator, rsi, momentum]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/finance/rsi.go"
|
||||
---
|
||||
|
||||
# rsi
|
||||
|
||||
Calcula el Relative Strength Index (RSI) con el metodo de suavizado de Wilder. Los primeros `period` elementos son 0. El RSI oscila entre 0 y 100.
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
result := finance.RSI([]float64{44, 44.34, 44.09, 43.61, 44.33, 44.83, 45.10, 45.42, 45.84}, 5)
|
||||
```
|
||||
@@ -0,0 +1,29 @@
|
||||
package finance
|
||||
|
||||
import "math"
|
||||
|
||||
// SharpeRatio calcula el ratio de Sharpe.
|
||||
// riskFreeRate es la tasa libre de riesgo por periodo.
|
||||
// periodsPerYear indica cuantos periodos hay en un anio (e.g. 252 para diario).
|
||||
// Formula: (mean(returns) - riskFreeRate) / stddev(returns) * sqrt(periodsPerYear)
|
||||
func SharpeRatio(returns []float64, riskFreeRate float64, periodsPerYear float64) float64 {
|
||||
n := len(returns)
|
||||
if n < 2 {
|
||||
return 0
|
||||
}
|
||||
var sum float64
|
||||
for _, r := range returns {
|
||||
sum += r
|
||||
}
|
||||
mean := sum / float64(n)
|
||||
var variance float64
|
||||
for _, r := range returns {
|
||||
diff := r - mean
|
||||
variance += diff * diff
|
||||
}
|
||||
std := math.Sqrt(variance / float64(n-1))
|
||||
if std == 0 {
|
||||
return 0
|
||||
}
|
||||
return (mean - riskFreeRate) / std * math.Sqrt(periodsPerYear)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: sharpe_ratio
|
||||
kind: function
|
||||
lang: go
|
||||
domain: finance
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func SharpeRatio(returns []float64, riskFreeRate float64, periodsPerYear float64) float64"
|
||||
description: "Calcula el ratio de Sharpe anualizado a partir de retornos, tasa libre de riesgo y frecuencia."
|
||||
tags: [finance, sharpe, risk, ratio]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: [math]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/finance/sharpe_ratio.go"
|
||||
---
|
||||
|
||||
# sharpe_ratio
|
||||
|
||||
Calcula el ratio de Sharpe: `(mean(returns) - riskFreeRate) / stddev(returns) * sqrt(periodsPerYear)`. Usa desviacion estandar muestral.
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
sr := finance.SharpeRatio([]float64{0.01, 0.02, -0.005, 0.015, 0.008}, 0.0001, 252)
|
||||
```
|
||||
@@ -0,0 +1,22 @@
|
||||
package finance
|
||||
|
||||
// SMA calcula la media movil simple de data con el periodo dado.
|
||||
// Retorna un slice de longitud len(data) donde los primeros period-1
|
||||
// elementos son 0 (sin datos suficientes).
|
||||
func SMA(data []float64, period int) []float64 {
|
||||
n := len(data)
|
||||
result := make([]float64, n)
|
||||
if period <= 0 || period > n {
|
||||
return result
|
||||
}
|
||||
var sum float64
|
||||
for i := 0; i < period; i++ {
|
||||
sum += data[i]
|
||||
}
|
||||
result[period-1] = sum / float64(period)
|
||||
for i := period; i < n; i++ {
|
||||
sum += data[i] - data[i-period]
|
||||
result[i] = sum / float64(period)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: sma
|
||||
kind: function
|
||||
lang: go
|
||||
domain: finance
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "func SMA(data []float64, period int) []float64"
|
||||
description: "Calcula la media movil simple (SMA) sobre una serie de datos con un periodo dado."
|
||||
tags: [finance, indicator, sma, moving-average]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/finance/sma.go"
|
||||
---
|
||||
|
||||
# sma
|
||||
|
||||
Calcula la media movil simple (Simple Moving Average). Los primeros `period-1` elementos del resultado son 0 ya que no hay datos suficientes para calcular la media.
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
result := finance.SMA([]float64{1, 2, 3, 4, 5}, 3)
|
||||
// result = [0, 0, 2, 3, 4]
|
||||
```
|
||||
@@ -0,0 +1,10 @@
|
||||
package finance
|
||||
|
||||
import "fmt"
|
||||
|
||||
// StreamTicks abre un stream de ticks en tiempo real para un simbolo.
|
||||
// Retorna un canal de [2]float64 donde [0] es precio y [1] es volumen.
|
||||
// TODO: implementar conexion real via websocket.
|
||||
func StreamTicks(symbol string) (<-chan [2]float64, error) {
|
||||
return nil, fmt.Errorf("not implemented: StreamTicks(%s)", symbol)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: stream_ticks
|
||||
kind: function
|
||||
lang: go
|
||||
domain: finance
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "func StreamTicks(symbol string) (<-chan [2]float64, error)"
|
||||
description: "Abre un stream de ticks en tiempo real para un simbolo via websocket."
|
||||
tags: [finance, io, stream, realtime]
|
||||
uses_functions: []
|
||||
uses_types: [tick_go_finance]
|
||||
returns: [tick_go_finance]
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [fmt]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/finance/stream_ticks.go"
|
||||
---
|
||||
|
||||
# stream_ticks
|
||||
|
||||
Stub para abrir un stream de ticks en tiempo real. Pendiente de implementacion.
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
ch, err := finance.StreamTicks("ETH/USDT")
|
||||
```
|
||||
@@ -0,0 +1,53 @@
|
||||
package finance
|
||||
|
||||
// TickToOHLCV agrega datos de ticks en velas OHLCV segun un intervalo en segundos.
|
||||
// prices, volumes y timestamps deben tener la misma longitud.
|
||||
// timestamps son unix seconds. intervalSecs define el ancho de cada vela.
|
||||
// Retorna slices de open, high, low, close, volume para cada vela generada.
|
||||
func TickToOHLCV(prices, volumes []float64, timestamps []int64, intervalSecs int64) (open, high, low, close, vol []float64) {
|
||||
n := len(prices)
|
||||
if n == 0 || intervalSecs <= 0 {
|
||||
return
|
||||
}
|
||||
bucketStart := timestamps[0] - (timestamps[0] % intervalSecs)
|
||||
o := prices[0]
|
||||
h := prices[0]
|
||||
l := prices[0]
|
||||
c := prices[0]
|
||||
v := volumes[0]
|
||||
|
||||
for i := 1; i < n; i++ {
|
||||
currentBucket := timestamps[i] - (timestamps[i] % intervalSecs)
|
||||
if currentBucket != bucketStart {
|
||||
// Cerrar la vela anterior
|
||||
open = append(open, o)
|
||||
high = append(high, h)
|
||||
low = append(low, l)
|
||||
close = append(close, c)
|
||||
vol = append(vol, v)
|
||||
// Nueva vela
|
||||
bucketStart = currentBucket
|
||||
o = prices[i]
|
||||
h = prices[i]
|
||||
l = prices[i]
|
||||
c = prices[i]
|
||||
v = volumes[i]
|
||||
} else {
|
||||
if prices[i] > h {
|
||||
h = prices[i]
|
||||
}
|
||||
if prices[i] < l {
|
||||
l = prices[i]
|
||||
}
|
||||
c = prices[i]
|
||||
v += volumes[i]
|
||||
}
|
||||
}
|
||||
// Cerrar ultima vela
|
||||
open = append(open, o)
|
||||
high = append(high, h)
|
||||
low = append(low, l)
|
||||
close = append(close, c)
|
||||
vol = append(vol, v)
|
||||
return
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user