build/ci: Update Go to v1.24, golangci-lint to v1.64.6 and fix issues (#1804)

* build/ci: Update Go to v1.24, golangci-lint to v1.64.5 and fix issues

* Address shazbert's nitters

* linter/config: Fix new linter issue and use versionSize const

* Address gk's nitters and fix additional linter issue after rebase

* Address glorious nits

* staticcheck: Fix additional linter issues after upgrading to Go 1.24.1 and golangci-lint v1.64.6

Also addresses nits

* Improve testing, assertify usage and use common.ErrParsingWSField

* TestCreateNewStrategy: Replace must > should wording
This commit is contained in:
Adrian Gallagher
2025-03-10 16:33:55 +11:00
committed by GitHub
parent c086e281cf
commit d64d56f77c
114 changed files with 5080 additions and 9355 deletions

View File

@@ -1,15 +1,16 @@
package main
import (
"errors"
"fmt"
"path/filepath"
"strconv"
"time"
"github.com/thrasher-corp/gocryptotrader/backtester/btrpc"
"github.com/thrasher-corp/gocryptotrader/backtester/config"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/urfave/cli/v2"
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/timestamppb"
)
@@ -49,10 +50,10 @@ var executeStrategyFromFileCommand = &cli.Command{
Aliases: []string{"e"},
Usage: fmt.Sprintf("override the strategy file's end time using your local time. eg '%v'", time.Now().Truncate(time.Hour).Format(time.DateTime)),
},
&cli.Uint64Flag{
&cli.DurationFlag{
Name: "intervaloverride",
Aliases: []string{"i"},
Usage: "override the strategy file's candle interval, in seconds. eg 60 = 1 minute",
Usage: "override the strategy file's candle interval in the format of a time duration. eg '1m' for 1 minute",
},
},
}
@@ -118,16 +119,18 @@ func executeStrategyFromFile(c *cli.Context) error {
}
}
var intervalOverride uint64
var intervalOverride time.Duration
if c.IsSet("intervaloverride") {
intervalOverride = c.Uint64("intervaloverride")
intervalOverride = c.Duration("intervaloverride")
} else if c.Args().Get(5) != "" {
intervalOverride, err = strconv.ParseUint(c.Args().Get(5), 10, 64)
intervalOverride, err = time.ParseDuration(c.Args().Get(5))
if err != nil {
return err
}
}
overrideDuration := time.Duration(intervalOverride) * time.Second
if intervalOverride < 0 {
return errors.New("interval override duration cannot be less than 0")
}
client := btrpc.NewBacktesterServiceClient(conn)
result, err := client.ExecuteStrategyFromFile(
@@ -138,7 +141,7 @@ func executeStrategyFromFile(c *cli.Context) error {
DoNotStore: dns,
StartTimeOverride: timestamppb.New(s),
EndTimeOverride: timestamppb.New(e),
IntervalOverride: uint64(overrideDuration),
IntervalOverride: durationpb.New(intervalOverride),
},
)
@@ -503,7 +506,7 @@ func executeStrategyFromConfig(c *cli.Context) error {
}
dataSettings := &btrpc.DataSettings{
Interval: uint64(defaultConfig.DataSettings.Interval.Duration().Nanoseconds()),
Interval: durationpb.New(defaultConfig.DataSettings.Interval.Duration()),
Datatype: defaultConfig.DataSettings.DataType,
}
if defaultConfig.DataSettings.APIData != nil {
@@ -546,7 +549,7 @@ func executeStrategyFromConfig(c *cli.Context) error {
if defaultConfig.DataSettings.DatabaseData != nil {
dbConnectionDetails := &btrpc.DatabaseConnectionDetails{
Host: defaultConfig.DataSettings.DatabaseData.Config.Host,
Port: uint32(defaultConfig.DataSettings.DatabaseData.Config.Port),
Port: defaultConfig.DataSettings.DatabaseData.Config.Port,
Password: defaultConfig.DataSettings.DatabaseData.Config.Password,
Database: defaultConfig.DataSettings.DatabaseData.Config.Database,
SslMode: defaultConfig.DataSettings.DatabaseData.Config.SSLMode,

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@ syntax = "proto3";
package btrpc;
import "google/api/annotations.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";
option go_package = "github.com/thrasher-corp/gocryptotrader/backtester/btrpc";
@@ -151,7 +152,7 @@ message ExchangeCredentials {
}
message DataSettings {
uint64 interval = 1;
google.protobuf.Duration interval = 1;
string datatype = 2;
ApiData api_data = 3;
DatabaseData database_data = 4;
@@ -205,7 +206,7 @@ message ExecuteStrategyFromFileRequest {
bool do_not_store = 3;
google.protobuf.Timestamp start_time_override = 4;
google.protobuf.Timestamp end_time_override = 5;
uint64 interval_override = 6;
google.protobuf.Duration interval_override = 6;
}
message ExecuteStrategyResponse {

View File

@@ -138,8 +138,7 @@
"name": "config.dataSettings.interval",
"in": "query",
"required": false,
"type": "string",
"format": "uint64"
"type": "string"
},
{
"name": "config.dataSettings.datatype",
@@ -426,8 +425,7 @@
"name": "intervalOverride",
"in": "query",
"required": false,
"type": "string",
"format": "uint64"
"type": "string"
}
],
"tags": [
@@ -723,8 +721,7 @@
"type": "object",
"properties": {
"interval": {
"type": "string",
"format": "uint64"
"type": "string"
},
"datatype": {
"type": "string"

View File

@@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc-gen-go-grpc v1.4.0
// - protoc (unknown)
// source: btrpc.proto
@@ -15,8 +15,8 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// Requires gRPC-Go v1.62.0 or later.
const _ = grpc.SupportPackageIsVersion8
const (
BacktesterService_ExecuteStrategyFromFile_FullMethodName = "/btrpc.BacktesterService/ExecuteStrategyFromFile"
@@ -54,8 +54,9 @@ func NewBacktesterServiceClient(cc grpc.ClientConnInterface) BacktesterServiceCl
}
func (c *backtesterServiceClient) ExecuteStrategyFromFile(ctx context.Context, in *ExecuteStrategyFromFileRequest, opts ...grpc.CallOption) (*ExecuteStrategyResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ExecuteStrategyResponse)
err := c.cc.Invoke(ctx, BacktesterService_ExecuteStrategyFromFile_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, BacktesterService_ExecuteStrategyFromFile_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -63,8 +64,9 @@ func (c *backtesterServiceClient) ExecuteStrategyFromFile(ctx context.Context, i
}
func (c *backtesterServiceClient) ExecuteStrategyFromConfig(ctx context.Context, in *ExecuteStrategyFromConfigRequest, opts ...grpc.CallOption) (*ExecuteStrategyResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ExecuteStrategyResponse)
err := c.cc.Invoke(ctx, BacktesterService_ExecuteStrategyFromConfig_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, BacktesterService_ExecuteStrategyFromConfig_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -72,8 +74,9 @@ func (c *backtesterServiceClient) ExecuteStrategyFromConfig(ctx context.Context,
}
func (c *backtesterServiceClient) ListAllTasks(ctx context.Context, in *ListAllTasksRequest, opts ...grpc.CallOption) (*ListAllTasksResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListAllTasksResponse)
err := c.cc.Invoke(ctx, BacktesterService_ListAllTasks_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, BacktesterService_ListAllTasks_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -81,8 +84,9 @@ func (c *backtesterServiceClient) ListAllTasks(ctx context.Context, in *ListAllT
}
func (c *backtesterServiceClient) StartTask(ctx context.Context, in *StartTaskRequest, opts ...grpc.CallOption) (*StartTaskResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StartTaskResponse)
err := c.cc.Invoke(ctx, BacktesterService_StartTask_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, BacktesterService_StartTask_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -90,8 +94,9 @@ func (c *backtesterServiceClient) StartTask(ctx context.Context, in *StartTaskRe
}
func (c *backtesterServiceClient) StartAllTasks(ctx context.Context, in *StartAllTasksRequest, opts ...grpc.CallOption) (*StartAllTasksResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StartAllTasksResponse)
err := c.cc.Invoke(ctx, BacktesterService_StartAllTasks_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, BacktesterService_StartAllTasks_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -99,8 +104,9 @@ func (c *backtesterServiceClient) StartAllTasks(ctx context.Context, in *StartAl
}
func (c *backtesterServiceClient) StopTask(ctx context.Context, in *StopTaskRequest, opts ...grpc.CallOption) (*StopTaskResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StopTaskResponse)
err := c.cc.Invoke(ctx, BacktesterService_StopTask_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, BacktesterService_StopTask_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -108,8 +114,9 @@ func (c *backtesterServiceClient) StopTask(ctx context.Context, in *StopTaskRequ
}
func (c *backtesterServiceClient) StopAllTasks(ctx context.Context, in *StopAllTasksRequest, opts ...grpc.CallOption) (*StopAllTasksResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StopAllTasksResponse)
err := c.cc.Invoke(ctx, BacktesterService_StopAllTasks_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, BacktesterService_StopAllTasks_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -117,8 +124,9 @@ func (c *backtesterServiceClient) StopAllTasks(ctx context.Context, in *StopAllT
}
func (c *backtesterServiceClient) ClearTask(ctx context.Context, in *ClearTaskRequest, opts ...grpc.CallOption) (*ClearTaskResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ClearTaskResponse)
err := c.cc.Invoke(ctx, BacktesterService_ClearTask_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, BacktesterService_ClearTask_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -126,8 +134,9 @@ func (c *backtesterServiceClient) ClearTask(ctx context.Context, in *ClearTaskRe
}
func (c *backtesterServiceClient) ClearAllTasks(ctx context.Context, in *ClearAllTasksRequest, opts ...grpc.CallOption) (*ClearAllTasksResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ClearAllTasksResponse)
err := c.cc.Invoke(ctx, BacktesterService_ClearAllTasks_FullMethodName, in, out, opts...)
err := c.cc.Invoke(ctx, BacktesterService_ClearAllTasks_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}

View File

@@ -4,8 +4,15 @@ deps:
- remote: buf.build
owner: googleapis
repository: googleapis
commit: 62f35d8aed1149c291d606d958a7ce32
commit: 546238c53f7340c6a2a6099fb863bc1b
digest: shake256:8d75c12f391e392b24c076d05117b47aeddb090add99c70247a8f4389b906a65f61a933c68e54ed8b73a050b967b6b712ba194348b67c3ab3ee26cc2cb25852c
- remote: buf.build
owner: grpc-ecosystem
repository: grpc-gateway
commit: bc28b723cd774c32b6fbc77621518765
commit: 4c5ba75caaf84e928b7137ae5c18c26a
digest: shake256:e174ad9408f3e608f6157907153ffec8d310783ee354f821f57178ffbeeb8faa6bb70b41b61099c1783c82fe16210ebd1279bc9c9ee6da5cffba9f0e675b8b99
- remote: buf.build
owner: protocolbuffers
repository: wellknowntypes
commit: d4f14e5e0a9c40889c90d373c74e95eb
digest: shake256:c9824714afd6cc432c2e1fafa20df47c87a8a0aca9e27192cd5732619453997af1721c2eac5c0fbbe7b29a741af5b8d7ba4ee89c85903e782d9c725d7b9436b5

View File

@@ -14,4 +14,5 @@ breaking:
- FILE
deps:
- buf.build/googleapis/googleapis
- buf.build/protocolbuffers/wellknowntypes
- buf.build/grpc-ecosystem/grpc-gateway

View File

@@ -429,12 +429,12 @@ func parseDatabase(reader *bufio.Reader, cfg *config.Config) error {
input = quickParse(reader)
var port uint64
if input != "" {
port, err = strconv.ParseUint(input, 10, 16)
port, err = strconv.ParseUint(input, 10, 32)
if err != nil {
return err
}
}
cfg.DataSettings.DatabaseData.Config.Port = uint16(port)
cfg.DataSettings.DatabaseData.Config.Port = uint32(port) //nolint:gosec // No overflow risk
err = database.DB.SetConfig(&cfg.DataSettings.DatabaseData.Config)
if err != nil {
return fmt.Errorf("database failed to set config: %w", err)

View File

@@ -130,7 +130,7 @@ candleLoop:
d.Item.RemoveDuplicates()
d.Item.SortCandlesByTimestamp(false)
if d.RangeHolder != nil {
d.RangeHolder, err = gctkline.CalculateCandleDateRanges(d.Item.Candles[0].Time, d.Item.Candles[len(d.Item.Candles)-1].Time.Add(d.Item.Interval.Duration()), d.Item.Interval, uint32(d.RangeHolder.Limit))
d.RangeHolder, err = gctkline.CalculateCandleDateRanges(d.Item.Candles[0].Time, d.Item.Candles[len(d.Item.Candles)-1].Time.Add(d.Item.Interval.Duration()), d.Item.Interval, d.RangeHolder.Limit)
if err != nil {
return err
}

View File

@@ -211,11 +211,11 @@ func (s *GRPCServer) ExecuteStrategyFromFile(_ context.Context, request *btrpc.E
return nil, err
}
if io64 := int64(request.IntervalOverride); io64 > 0 {
if io64 < gctkline.FifteenSecond.Duration().Nanoseconds() {
return nil, fmt.Errorf("%w, interval must be >= 15 seconds, received '%v'", gctkline.ErrInvalidInterval, time.Duration(request.IntervalOverride))
if io := request.IntervalOverride.AsDuration(); io > 0 {
if io < gctkline.FifteenSecond.Duration() {
return nil, fmt.Errorf("%w, interval must be >= 15 seconds, received '%v'", gctkline.ErrInvalidInterval, io)
}
cfg.DataSettings.Interval = gctkline.Interval(request.IntervalOverride)
cfg.DataSettings.Interval = gctkline.Interval(io)
}
if startTime := request.StartTimeOverride.AsTime(); startTime.Unix() != 0 && !startTime.IsZero() {
@@ -528,7 +528,7 @@ func (s *GRPCServer) ExecuteStrategyFromConfig(_ context.Context, request *btrpc
Driver: request.Config.DataSettings.DatabaseData.Config.Driver,
ConnectionDetails: drivers.ConnectionDetails{
Host: request.Config.DataSettings.DatabaseData.Config.Config.Host,
Port: uint16(request.Config.DataSettings.DatabaseData.Config.Config.Port),
Port: request.Config.DataSettings.DatabaseData.Config.Config.Port,
Username: request.Config.DataSettings.DatabaseData.Config.Config.UserName,
Password: request.Config.DataSettings.DatabaseData.Config.Config.Password,
Database: request.Config.DataSettings.DatabaseData.Config.Config.Database,
@@ -591,7 +591,7 @@ func (s *GRPCServer) ExecuteStrategyFromConfig(_ context.Context, request *btrpc
},
CurrencySettings: configSettings,
DataSettings: config.DataSettings{
Interval: gctkline.Interval(request.Config.DataSettings.Interval),
Interval: gctkline.Interval(request.Config.DataSettings.Interval.AsDuration()),
DataType: request.Config.DataSettings.Datatype,
APIData: apiData,
DatabaseData: dbData,

View File

@@ -17,6 +17,7 @@ import (
"github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/binancecashandcarry"
gctcommon "github.com/thrasher-corp/gocryptotrader/common"
gctkline "github.com/thrasher-corp/gocryptotrader/exchanges/kline"
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/timestamppb"
)
@@ -80,7 +81,7 @@ func TestExecuteStrategyFromFile(t *testing.T) {
StrategyFilePath: dcaConfigPath,
StartTimeOverride: timestamppb.New(time.Now().Add(-time.Minute)),
EndTimeOverride: timestamppb.New(time.Now()),
IntervalOverride: 1,
IntervalOverride: durationpb.New(time.Duration(1)),
})
if !errors.Is(err, gctkline.ErrInvalidInterval) {
t.Errorf("received '%v' expecting '%v'", err, gctkline.ErrInvalidInterval)
@@ -90,7 +91,7 @@ func TestExecuteStrategyFromFile(t *testing.T) {
StrategyFilePath: dcaConfigPath,
StartTimeOverride: timestamppb.New(time.Now().Add(-time.Hour * 6).Truncate(time.Hour)),
EndTimeOverride: timestamppb.New(time.Now().Add(-time.Hour * 2).Truncate(time.Hour)),
IntervalOverride: uint64(time.Hour.Nanoseconds()),
IntervalOverride: durationpb.New(time.Hour),
})
if !errors.Is(err, nil) {
t.Errorf("received '%v' expecting '%v'", err, nil)
@@ -218,7 +219,7 @@ func TestExecuteStrategyFromConfig(t *testing.T) {
}
dataSettings := &btrpc.DataSettings{
Interval: uint64(defaultConfig.DataSettings.Interval.Duration().Nanoseconds()),
Interval: durationpb.New(defaultConfig.DataSettings.Interval.Duration()),
Datatype: defaultConfig.DataSettings.DataType,
}
if defaultConfig.DataSettings.APIData != nil {
@@ -256,7 +257,7 @@ func TestExecuteStrategyFromConfig(t *testing.T) {
if defaultConfig.DataSettings.DatabaseData != nil {
dbConnectionDetails := &btrpc.DatabaseConnectionDetails{
Host: defaultConfig.DataSettings.DatabaseData.Config.Host,
Port: uint32(defaultConfig.DataSettings.DatabaseData.Config.Port),
Port: defaultConfig.DataSettings.DatabaseData.Config.Port,
Password: defaultConfig.DataSettings.DatabaseData.Config.Password,
Database: defaultConfig.DataSettings.DatabaseData.Config.Database,
SslMode: defaultConfig.DataSettings.DatabaseData.Config.SSLMode,

View File

@@ -820,19 +820,12 @@ func (bt *BackTest) loadData(cfg *config.Config, exch gctexchange.IBotExchange,
cfg.DataSettings.APIData.EndDate = cfg.DataSettings.APIData.EndDate.Add(cfg.DataSettings.Interval.Duration())
}
var limit int64
limit, err = b.Features.Enabled.Kline.GetIntervalResultLimit(cfg.DataSettings.Interval)
limit, err := b.Features.Enabled.Kline.GetIntervalResultLimit(cfg.DataSettings.Interval)
if err != nil {
return nil, err
}
resp, err = loadAPIData(
cfg,
exch,
fPair,
a,
uint32(limit),
dataType)
resp, err = loadAPIData(cfg, exch, fPair, a, limit, dataType)
if err != nil {
return resp, err
}
@@ -894,7 +887,7 @@ func loadDatabaseData(cfg *config.Config, name string, fPair currency.Pair, a as
isUSDTrackingPair)
}
func loadAPIData(cfg *config.Config, exch gctexchange.IBotExchange, fPair currency.Pair, a asset.Item, resultLimit uint32, dataType int64) (*kline.DataFromKline, error) {
func loadAPIData(cfg *config.Config, exch gctexchange.IBotExchange, fPair currency.Pair, a asset.Item, resultLimit uint64, dataType int64) (*kline.DataFromKline, error) {
if cfg.DataSettings.Interval <= 0 {
return nil, errIntervalUnset
}

View File

@@ -31,39 +31,29 @@ func LoadStrategyByName(name string, useSimultaneousProcessing bool) (Handler, e
func createNewStrategy(name string, useSimultaneousProcessing bool, h Handler) (Handler, error) {
if h == nil {
return nil, fmt.Errorf("cannot load %v supported strategies contains %w", name, common.ErrNilPointer)
return nil, fmt.Errorf("cannot load strategy %q: %w", name, common.ErrNilPointer)
}
if !strings.EqualFold(name, h.Name()) {
return nil, nil
}
// create new instance so strategy is not shared across all tasks
strategyValue := reflect.ValueOf(h)
if strategyValue.IsNil() {
return nil, fmt.Errorf("cannot load %v supported strategies element is a %w", name, common.ErrNilPointer)
if strategyValue.Kind() != reflect.Ptr || strategyValue.IsNil() {
return nil, fmt.Errorf("cannot load strategy %q: handler must be a non-nil pointer, got %T", name, h)
}
strategyElement := strategyValue.Elem()
if !strategyElement.IsValid() {
return nil, fmt.Errorf("cannot load %v strategy element is invalid %w", name, common.ErrTypeAssertFailure)
}
strategyType := strategyElement.Type()
if strategyType == nil {
return nil, fmt.Errorf("cannot load %v strategy type is a %w", name, common.ErrNilPointer)
}
newStrategy := reflect.New(strategyType)
if newStrategy.IsNil() {
return nil, fmt.Errorf("cannot load %v new instance of strategy is a %w", name, common.ErrNilPointer)
}
strategyInterface := newStrategy.Interface()
if strategyInterface == nil {
return nil, fmt.Errorf("cannot load %v new instance of strategy is not an interface. %w", name, common.ErrTypeAssertFailure)
}
strategy, ok := strategyInterface.(Handler)
// create new instance so strategy is not shared across all tasks
strategy, ok := reflect.New(strategyValue.Elem().Type()).Interface().(Handler)
if !ok {
return nil, fmt.Errorf("cannot load %v new instance of strategy is not a Handler interface. %w", name, common.ErrTypeAssertFailure)
return nil, fmt.Errorf("cannot load strategy %q: type %T doesn't implement Handler interface: %w",
name, strategy, common.ErrTypeAssertFailure)
}
if useSimultaneousProcessing && !strategy.SupportsSimultaneousProcessing() {
return nil, base.ErrSimultaneousProcessingNotSupported
}
strategy.SetSimultaneousProcessing(useSimultaneousProcessing)
return strategy, nil
}

View File

@@ -4,6 +4,7 @@ import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/thrasher-corp/gocryptotrader/backtester/data"
"github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/portfolio"
"github.com/thrasher-corp/gocryptotrader/backtester/eventhandlers/strategies/base"
@@ -82,35 +83,33 @@ func TestCreateNewStrategy(t *testing.T) {
t.Parallel()
// invalid Handler
resp, err := createNewStrategy(dollarcostaverage.Name, false, nil)
if !errors.Is(err, common.ErrNilPointer) {
t.Errorf("received '%v' expected '%v'", err, common.ErrNilPointer)
}
if resp != nil {
t.Errorf("received '%v' expected '%v'", resp, nil)
}
_, err := createNewStrategy(dollarcostaverage.Name, false, nil)
assert.ErrorIs(t, err, common.ErrNilPointer)
// mismatched name
resp, err = createNewStrategy(dollarcostaverage.Name, false, &customStrategy{})
if !errors.Is(err, nil) {
t.Errorf("received '%v' expected '%v'", err, nil)
}
if resp != nil {
t.Errorf("received '%v' expected '%v'", resp, nil)
}
resp, err := createNewStrategy(dollarcostaverage.Name, false, &customStrategy{})
assert.NoError(t, err, "createNewStrategy should not error")
assert.Nil(t, resp)
// nil Handler
var h Handler = (*customStrategy)(nil)
_, err = createNewStrategy("custom-strategy", false, h)
assert.ErrorContains(t, err, "must be a non-nil pointer")
// valid
h := new(dollarcostaverage.Strategy)
resp, err = createNewStrategy(dollarcostaverage.Name, false, h)
if !errors.Is(err, nil) {
t.Errorf("received '%v' expected '%v'", err, nil)
}
if resp == nil {
t.Errorf("received '%v' expected '%v'", resp, h)
}
h = new(dollarcostaverage.Strategy)
resp, err = createNewStrategy(dollarcostaverage.Name, true, h)
assert.NoError(t, err, "createNewStrategy should not error")
assert.NotSame(t, h, resp, "createNewStrategy should return a new pointer")
// simultaneous processing desired but not supported
h = &customStrategy{allowSimultaneousProcessing: false}
_, err = createNewStrategy("custom-strategy", true, h)
assert.ErrorIs(t, err, base.ErrSimultaneousProcessingNotSupported)
}
type customStrategy struct {
allowSimultaneousProcessing bool
base.Strategy
}
@@ -121,7 +120,7 @@ func (s *customStrategy) Description() string {
return "this is a demonstration of loading strategies via custom plugins"
}
func (s *customStrategy) SupportsSimultaneousProcessing() bool {
return true
return s.allowSimultaneousProcessing
}
func (s *customStrategy) OnSignal(d data.Handler, _ funding.IFundingTransferer, _ portfolio.Handler) (signal.Event, error) {
return s.createSignal(d)