diff --git a/internal/trading/backtest/sig_strategy_backtester.go b/internal/trading/backtest/sig_strategy_backtester.go index c2c336a..67af764 100644 --- a/internal/trading/backtest/sig_strategy_backtester.go +++ b/internal/trading/backtest/sig_strategy_backtester.go @@ -62,6 +62,8 @@ func (b *SigStrategyBacktester) Backtest(ctx context.Context, if intervalCandlePeriods == nil { intervalCandlePeriods = types.NewIntervalState[int16]() } + // 填充参数默认值 + sig.FillDefaultInputs(sigStrategyInput, b.sigStrategy.Meta().Input) // init sig strategy if err = b.sigStrategy.Init(sigStrategyInput); err != nil { return diff --git a/internal/trading/sig/indicator_context.go b/internal/trading/sig/indicator_context.go index a56534a..98a93c1 100644 --- a/internal/trading/sig/indicator_context.go +++ b/internal/trading/sig/indicator_context.go @@ -39,6 +39,9 @@ type IndicatorContext struct { } func NewIndicatorContext(indicator indicator.IIndicator, input types.Input, indicatorStates IndicatorStates, kSeries *types.KlineSeries, indicatorsReg *indicator.IndicatorRegistry) *IndicatorContext { + // 指标参数默认值 + FillDefaultInputs(input, indicator.Meta().Input) + inputs := collect.Mapping(indicator.Meta().Input, func(in types.InputArg) string { return input.String(in.Name) }) @@ -169,3 +172,14 @@ windowLoop: } return } + +// FillDefaultInputs 填充指标参数默认值 +func FillDefaultInputs(input types.Input, args []types.InputArg) { + for _, in := range args { + if in.Default != nil { + if _, ok := input[in.Name]; !ok { + input[in.Name] = in.Default + } + } + } +} diff --git a/pkg/indicator/macd.go b/pkg/indicator/macd.go index c60180e..433c3d2 100644 --- a/pkg/indicator/macd.go +++ b/pkg/indicator/macd.go @@ -12,9 +12,9 @@ func (c *MACD) Meta() IndicatorMeta { return IndicatorMeta{ Name: "MACD", Input: []types.InputArg{ - {Name: "fast", Type: types.InputTypeUInt, Desc: "快线周期"}, // 12 - {Name: "slow", Type: types.InputTypeUInt, Desc: "慢线周期"}, // 26 - {Name: "singal", Type: types.InputTypeUInt, Desc: "信号线周期"}, // 9 + {Name: "fast", Type: types.InputTypeUInt, Desc: "快线周期", Default: 12}, // 12 + {Name: "slow", Type: types.InputTypeUInt, Desc: "慢线周期", Default: 26}, // 26 + {Name: "singal", Type: types.InputTypeUInt, Desc: "信号线周期", Default: 9}, // 9 }, State: []string{"dif", "dea"}, Plots: []Plot{ diff --git a/pkg/strategy/super_trend_macd_rsi.go b/pkg/strategy/super_trend_macd_rsi.go index 29737c1..46ffde6 100644 --- a/pkg/strategy/super_trend_macd_rsi.go +++ b/pkg/strategy/super_trend_macd_rsi.go @@ -22,12 +22,12 @@ func (s *SuperTrendMacdRSI) Meta() StrategyMeta { Name: "SuperTrendMacdRSI", Desc: "SuperTrend + MACD + RSI 量化策略", Input: []types.InputArg{ - {Name: "trendWindow", Type: types.InputTypeUInt, Desc: "SuperTrend ATR周期"}, // 10 - {Name: "trendMul", Type: types.InputTypeUFloat, Desc: "SuperTrend multipiler"}, // 3 - {Name: "rsi", Type: types.InputTypeUInt, Desc: "rsi周期"}, // 14 - {Name: "fast", Type: types.InputTypeUInt, Desc: "macd fast period"}, // 12 - {Name: "slow", Type: types.InputTypeUInt, Desc: "macd slow period"}, // 26 - {Name: "singal", Type: types.InputTypeUInt, Desc: "macd singal period"}, // 9 + {Name: "trendWindow", Type: types.InputTypeUInt, Desc: "SuperTrend ATR周期", Default: 10}, + {Name: "trendMul", Type: types.InputTypeUFloat, Desc: "SuperTrend multipiler", Default: 3}, + {Name: "rsi", Type: types.InputTypeUInt, Desc: "rsi周期", Default: 14}, + {Name: "fast", Type: types.InputTypeUInt, Desc: "macd fast period", Default: 12}, + {Name: "slow", Type: types.InputTypeUInt, Desc: "macd slow period", Default: 26}, + {Name: "singal", Type: types.InputTypeUInt, Desc: "macd singal period", Default: 9}, }, } } diff --git a/pkg/utils/security/aes.go b/pkg/utils/security/aes.go new file mode 100644 index 0000000..67aad58 --- /dev/null +++ b/pkg/utils/security/aes.go @@ -0,0 +1,90 @@ +package security + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/base64" +) + +func GenAesKey256() string { + key := genAesKey(256) + return base64.StdEncoding.EncodeToString(key) +} + +func genAesKey(keySize int) []byte { + var key = make([]byte, keySize/8) + _, err := rand.Read(key) + if err != nil { + panic(err) + } + return key +} + +// EncryptAesCBC +// src -> 要加密的原文 +// key -> 秘钥, 和加密秘钥相同, 大小为: 8byte +func EncryptAesCBC(src, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + blockSize := block.BlockSize() + // 对最后一个明文分组进行数据填充 + src = pkcs5Padding(src, blockSize) + // 3.创建一个密码分组为链接模式的,底层使用 DES 加密的 BlockMode 接口 + // 参数 iv 的长度,必须等于 b 的块尺寸 + iv := make([]byte, blockSize) + copy(iv, key) + blackMode := cipher.NewCBCEncrypter(block, iv) + // 5.加密连续的数据块 + dst := make([]byte, len(src)) + blackMode.CryptBlocks(dst, src) + return dst, nil +} + +// DecryptAesCBC +// src -> 要解密的密文 +// key -> 秘钥, 和加密秘钥相同, 大小为: 8byte +func DecryptAesCBC(src, key []byte) ([]byte, error) { + // 1. 创建并返回一个使用DES算法的cipher.Block接口 + block, err := aes.NewCipher(key) + // 2. 判断是否创建成功 + if err != nil { + return nil, err + } + blockSize := block.BlockSize() + // 3. 创建一个密码分组为链接模式的, 底层使用DES解密的BlockMode接口 + iv := make([]byte, blockSize) + copy(iv, key) + blockMode := cipher.NewCBCDecrypter(block, iv) + // 4. 解密数据 + dst := src + blockMode.CryptBlocks(src, dst) + // 5. 去掉最后一组填充的数据 + dst = pkcs5UnPadding(dst) + // 6. 返回结果 + return dst, nil +} + +// PKCS5Padding 使用pks5的方式填充 +func pkcs5Padding(ciphertext []byte, blockSize int) []byte { + // 1. 计算最后一个分组缺多少个字节 + padding := blockSize - (len(ciphertext) % blockSize) + // 2. 创建一个大小为padding的切片, 每个字节的值为padding + padText := bytes.Repeat([]byte{byte(padding)}, padding) + // 3. 将padText添加到原始数据的后边, 将最后一个分组缺少的字节数补齐 + newText := append(ciphertext, padText...) + return newText +} + +// PKCS5UnPadding 删除pks5填充的尾部数据 +func pkcs5UnPadding(origData []byte) []byte { + // 1. 计算数据的总长度 + length := len(origData) + // 2. 根据填充的字节值得到填充的次数 + number := int(origData[length-1]) + // 3. 将尾部填充的number个字节去掉 + return origData[:(length - number)] +} diff --git a/pkg/utils/security/aes_test.go b/pkg/utils/security/aes_test.go new file mode 100644 index 0000000..c972cd6 --- /dev/null +++ b/pkg/utils/security/aes_test.go @@ -0,0 +1,41 @@ +package security + +import ( + "encoding/base64" + "fmt" + "reflect" + "testing" +) + +func TestGenAesKey256(t *testing.T) { + for range 10 { + key := GenAesKey256() + fmt.Println(key) + } +} + +// 参考:https://www.yisu.com/zixun/696240.html +func TestAes(t *testing.T) { + defer func() { + if r := recover(); r != nil { + fmt.Println("recover...", r.(error).Error(), reflect.TypeOf(r)) + } + }() + + // aesKeyStr := GetAesKey256() + aesKeyStr := "VzKw8Vx+K8k1nO9fmPjhv8o+8l4vqpF+fjsbvXf0j4o=" + + fmt.Println(aesKeyStr) + key, _ := base64.StdEncoding.DecodeString(aesKeyStr) + + text := []byte("hello sig.pub") + encrypt, _ := EncryptAesCBC(text, key) + fmt.Println(base64.URLEncoding.EncodeToString(text)) + + result, err := DecryptAesCBC(encrypt, key) + if err != nil { + t.Errorf("decrypt aes cbc failed: %v", err) + return + } + fmt.Println("result:", string(result)) +}