19 changed files with 426 additions and 146 deletions
@ -0,0 +1,96 @@
|
||||
package indicator |
||||
|
||||
import ( |
||||
"math" |
||||
"sig-pub/pkg/types" |
||||
) |
||||
|
||||
// BollMB 布林带中轨
|
||||
type BollMB struct { |
||||
} |
||||
|
||||
func (c *BollMB) Meta() IndicatorMeta { |
||||
return IndicatorMeta{ |
||||
Name: "BollMB", |
||||
Input: []types.InputArg{ |
||||
{Name: "window", Type: types.InputTypeUInt, Desc: "窗口大小"}, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
func (c *BollMB) CandlePeriods(ctx IIndicatorContext) int16 { |
||||
return ctx.Input().Int16("window") |
||||
} |
||||
|
||||
func (c *BollMB) Calculate(ctx IIndicatorContext) (vector float64) { |
||||
window := ctx.Input().Int16("window") |
||||
closeSeries := ctx.Series(0, int16(window)).Close() |
||||
vector = closeSeries.Avg() |
||||
return |
||||
} |
||||
|
||||
// BollUB 布林带上轨
|
||||
type BollUB struct { |
||||
} |
||||
|
||||
func (c *BollUB) Meta() IndicatorMeta { |
||||
return IndicatorMeta{ |
||||
Name: "BollUB", |
||||
Input: []types.InputArg{ |
||||
{Name: "window", Type: types.InputTypeUInt, Desc: "窗口大小"}, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
func (c *BollUB) CandlePeriods(ctx IIndicatorContext) int16 { |
||||
return ctx.Input().Int16("window") |
||||
} |
||||
|
||||
func (c *BollUB) Calculate(ctx IIndicatorContext) (vector float64) { |
||||
window := ctx.Input().Int16("window") |
||||
closeSeries := ctx.Series(0, int16(window)).Close() |
||||
mb := closeSeries.Avg() |
||||
|
||||
// 标准差σ_t = sqrt(∑(P-MB)^2 / (n-1))
|
||||
sst := float64(0) |
||||
for _, p := range closeSeries { |
||||
sst += math.Pow(p-mb, 2) |
||||
} |
||||
sigma := math.Sqrt(sst / float64(window-1)) |
||||
|
||||
vector = mb + 2*sigma |
||||
return |
||||
} |
||||
|
||||
// BollLB 布林带下轨
|
||||
type BollLB struct { |
||||
} |
||||
|
||||
func (c *BollLB) Meta() IndicatorMeta { |
||||
return IndicatorMeta{ |
||||
Name: "BollLB", |
||||
Input: []types.InputArg{ |
||||
{Name: "window", Type: types.InputTypeUInt, Desc: "窗口大小"}, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
func (c *BollLB) CandlePeriods(ctx IIndicatorContext) int16 { |
||||
return ctx.Input().Int16("window") |
||||
} |
||||
|
||||
func (c *BollLB) Calculate(ctx IIndicatorContext) (vector float64) { |
||||
window := ctx.Input().Int16("window") |
||||
closeSeries := ctx.Series(0, int16(window)).Close() |
||||
mb := closeSeries.Avg() |
||||
|
||||
// 标准差σ_t = sqrt(∑(P-MB)^2 / (n-1))
|
||||
sst := float64(0) |
||||
for _, p := range closeSeries { |
||||
sst += math.Pow(p-mb, 2) |
||||
} |
||||
sigma := math.Sqrt(sst / float64(window-1)) |
||||
|
||||
vector = mb - 2*sigma |
||||
return |
||||
} |
||||
@ -0,0 +1,61 @@
|
||||
package persist |
||||
|
||||
import ( |
||||
"fmt" |
||||
"reflect" |
||||
"strings" |
||||
) |
||||
|
||||
// ReflectGormData todo cache data type info
|
||||
func ReflectGormData(data any) (table string, columns []string, values map[string]any, err error) { |
||||
defer func() { |
||||
if r := recover(); r != nil { |
||||
err = fmt.Errorf("%T parse gorm data error %v", data, r) |
||||
} |
||||
}() |
||||
|
||||
values = make(map[string]any) |
||||
refValue := reflect.ValueOf(data) |
||||
refType := reflect.TypeOf(data) |
||||
|
||||
tableNameM := refValue.MethodByName("TableName") |
||||
if !tableNameM.IsValid() { |
||||
err = fmt.Errorf("type %T not has method TableName", data) |
||||
return |
||||
} |
||||
rsp := tableNameM.Call(nil) |
||||
table = rsp[0].Interface().(string) |
||||
|
||||
if refValue.Kind() == reflect.Ptr { |
||||
refValue = refValue.Elem() |
||||
} |
||||
if refType.Kind() == reflect.Ptr { |
||||
refType = refType.Elem() |
||||
} |
||||
|
||||
for i := 0; i < refType.NumField(); i++ { |
||||
field := refType.Field(i) |
||||
value := refValue.Field(i).Interface() |
||||
// value
|
||||
tag := field.Tag.Get("gorm") |
||||
column := getGormTagColumnName(tag) |
||||
if column == "" || strings.HasPrefix(column, "-") { // 忽略字段
|
||||
continue |
||||
} |
||||
columns = append(columns, column) |
||||
values[column] = value |
||||
} |
||||
return |
||||
} |
||||
|
||||
func getGormTagColumnName(tag string) (column string) { |
||||
i1 := strings.Index(tag, "column:") |
||||
if i1 < 0 { |
||||
return |
||||
} |
||||
i2 := strings.Index(tag[i1+7:], ";") |
||||
if i2 < 0 { |
||||
return tag[i1+7:] |
||||
} |
||||
return tag[i1+7 : i2+7] |
||||
} |
||||
@ -0,0 +1,67 @@
|
||||
package persist |
||||
|
||||
import ( |
||||
"context" |
||||
"sig-pub/pkg/utils/exit" |
||||
|
||||
"github.com/jackc/pgx/v5" |
||||
"github.com/jackc/pgx/v5/pgxpool" |
||||
) |
||||
|
||||
type PGBatchWriter struct { |
||||
pool *pgxpool.Pool |
||||
} |
||||
|
||||
func NewPGBatchWriter() *PGBatchWriter { |
||||
return &PGBatchWriter{} |
||||
} |
||||
|
||||
func (w *PGBatchWriter) Init(ctx context.Context, connString string) (err error) { |
||||
w.pool, err = pgxpool.New(ctx, connString) |
||||
if err != nil { |
||||
return |
||||
} |
||||
exit.AddHook(w.pool.Close, exit.WithOrderTail()) |
||||
|
||||
return |
||||
} |
||||
|
||||
func InsertBatch[T any](w *PGBatchWriter, ctx context.Context, datas []T) (err error) { |
||||
if len(datas) == 0 { |
||||
return |
||||
} |
||||
|
||||
tableColumns := make(map[string][]string, 3) |
||||
tableValues := make(map[string][][]any, 3) |
||||
for _, data := range datas { |
||||
table, columns, valuesM, e := ReflectGormData(data) |
||||
if e != nil { |
||||
err = e |
||||
return |
||||
} |
||||
if cs, ok := tableColumns[table]; !ok { |
||||
tableColumns[table] = columns |
||||
} else { |
||||
columns = cs |
||||
} |
||||
var values []any |
||||
for _, c := range columns { |
||||
v := valuesM[c] |
||||
values = append(values, v) |
||||
} |
||||
tableValues[table] = append(tableValues[table], values) |
||||
} |
||||
tx, err := w.pool.Begin(ctx) |
||||
if err != nil { |
||||
return |
||||
} |
||||
defer tx.Rollback(ctx) |
||||
for table, values := range tableValues { |
||||
_, err = tx.CopyFrom(ctx, pgx.Identifier{table}, tableColumns[table], pgx.CopyFromRows(values)) |
||||
if err != nil { |
||||
return |
||||
} |
||||
} |
||||
err = tx.Commit(ctx) |
||||
return |
||||
} |
||||
Loading…
Reference in new issue