mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-14 07:26:47 +00:00
* modernise: Run new gopls modernise tool against codebase
* Address shazbert's nits
* apichecker, gctcli: Simplify HTML scraping functions and improve depth limit handling
* refactor: Create minSyncInterval const and update order book limit handling for binance and binanceUS
* refactor: Various slice usage improvements and rename TODO
* tranches: Revert deleteByID changes due to performance decrease
Shazbert was a F1 driver in a past lifetime 🏎️
* tranches: Simply retrieve copy
Thanks to shazbert
* documentation: Sort contributors list by contributions
* tranches: Remove deadcode in deleteByID
218 lines
5.4 KiB
Go
218 lines
5.4 KiB
Go
package engine
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"slices"
|
|
|
|
"github.com/gofrs/uuid"
|
|
gctcommon "github.com/thrasher-corp/gocryptotrader/common"
|
|
)
|
|
|
|
var (
|
|
errTaskNotFound = errors.New("task not found")
|
|
errTaskAlreadyMonitored = errors.New("task already monitored")
|
|
errAlreadyRan = errors.New("task already ran")
|
|
errTaskHasNotRan = errors.New("task hasn't ran yet")
|
|
errTaskIsRunning = errors.New("task is already running")
|
|
errCannotClear = errors.New("cannot clear task")
|
|
)
|
|
|
|
// NewTaskManager creates a run manager to allow the backtester to manage multiple strategies
|
|
func NewTaskManager() *TaskManager {
|
|
return &TaskManager{}
|
|
}
|
|
|
|
// AddTask adds a run to the manager
|
|
func (r *TaskManager) AddTask(b *BackTest) error {
|
|
if r == nil {
|
|
return fmt.Errorf("%w TaskManager", gctcommon.ErrNilPointer)
|
|
}
|
|
if b == nil {
|
|
return fmt.Errorf("%w BackTest", gctcommon.ErrNilPointer)
|
|
}
|
|
r.m.Lock()
|
|
defer r.m.Unlock()
|
|
for i := range r.tasks {
|
|
if r.tasks[i].Equal(b) {
|
|
return fmt.Errorf("%w %s %s", errTaskAlreadyMonitored, b.MetaData.ID, b.MetaData.Strategy)
|
|
}
|
|
}
|
|
|
|
err := b.SetupMetaData()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.tasks = append(r.tasks, b)
|
|
return nil
|
|
}
|
|
|
|
// List details all strategy tasks
|
|
func (r *TaskManager) List() ([]*TaskSummary, error) {
|
|
if r == nil {
|
|
return nil, fmt.Errorf("%w TaskManager", gctcommon.ErrNilPointer)
|
|
}
|
|
r.m.Lock()
|
|
defer r.m.Unlock()
|
|
resp := make([]*TaskSummary, len(r.tasks))
|
|
for i := range r.tasks {
|
|
sum, err := r.tasks[i].GenerateSummary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp[i] = sum
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
// GetSummary returns details about a completed strategy task
|
|
func (r *TaskManager) GetSummary(id uuid.UUID) (*TaskSummary, error) {
|
|
if r == nil {
|
|
return nil, fmt.Errorf("%w TaskManager", gctcommon.ErrNilPointer)
|
|
}
|
|
r.m.Lock()
|
|
defer r.m.Unlock()
|
|
for i := range r.tasks {
|
|
if !r.tasks[i].MatchesID(id) {
|
|
continue
|
|
}
|
|
return r.tasks[i].GenerateSummary()
|
|
}
|
|
return nil, fmt.Errorf("%s %w", id, errTaskNotFound)
|
|
}
|
|
|
|
// StopTask stops a strategy task if enabled, this will run CloseAllPositions
|
|
func (r *TaskManager) StopTask(id uuid.UUID) error {
|
|
if r == nil {
|
|
return fmt.Errorf("%w TaskManager", gctcommon.ErrNilPointer)
|
|
}
|
|
r.m.Lock()
|
|
defer r.m.Unlock()
|
|
for i := range r.tasks {
|
|
switch {
|
|
case !r.tasks[i].MatchesID(id):
|
|
continue
|
|
case r.tasks[i].IsRunning():
|
|
return r.tasks[i].Stop()
|
|
case r.tasks[i].HasRan():
|
|
return fmt.Errorf("%w %v", errAlreadyRan, id)
|
|
default:
|
|
return fmt.Errorf("%w %v", errTaskHasNotRan, id)
|
|
}
|
|
}
|
|
return fmt.Errorf("%s %w", id, errTaskNotFound)
|
|
}
|
|
|
|
// StopAllTasks stops all running strategies
|
|
func (r *TaskManager) StopAllTasks() ([]*TaskSummary, error) {
|
|
if r == nil {
|
|
return nil, fmt.Errorf("%w TaskManager", gctcommon.ErrNilPointer)
|
|
}
|
|
r.m.Lock()
|
|
defer r.m.Unlock()
|
|
resp := make([]*TaskSummary, 0, len(r.tasks))
|
|
for i := range r.tasks {
|
|
if !r.tasks[i].IsRunning() {
|
|
continue
|
|
}
|
|
err := r.tasks[i].Stop()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sum, err := r.tasks[i].GenerateSummary()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
resp = append(resp, sum)
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
// StartTask executes a strategy if found
|
|
func (r *TaskManager) StartTask(id uuid.UUID) error {
|
|
if r == nil {
|
|
return fmt.Errorf("%w TaskManager", gctcommon.ErrNilPointer)
|
|
}
|
|
r.m.Lock()
|
|
defer r.m.Unlock()
|
|
for i := range r.tasks {
|
|
switch {
|
|
case !r.tasks[i].MatchesID(id):
|
|
continue
|
|
case r.tasks[i].IsRunning():
|
|
return fmt.Errorf("%w %v", errTaskIsRunning, id)
|
|
case r.tasks[i].HasRan():
|
|
return fmt.Errorf("%w %v", errAlreadyRan, id)
|
|
default:
|
|
return r.tasks[i].ExecuteStrategy(false)
|
|
}
|
|
}
|
|
return fmt.Errorf("%s %w", id, errTaskNotFound)
|
|
}
|
|
|
|
// StartAllTasks executes all strategies
|
|
func (r *TaskManager) StartAllTasks() ([]uuid.UUID, error) {
|
|
if r == nil {
|
|
return nil, fmt.Errorf("%w TaskManager", gctcommon.ErrNilPointer)
|
|
}
|
|
r.m.Lock()
|
|
defer r.m.Unlock()
|
|
executedRuns := make([]uuid.UUID, 0, len(r.tasks))
|
|
for i := range r.tasks {
|
|
if r.tasks[i].HasRan() {
|
|
continue
|
|
}
|
|
executedRuns = append(executedRuns, r.tasks[i].MetaData.ID)
|
|
err := r.tasks[i].ExecuteStrategy(false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return executedRuns, nil
|
|
}
|
|
|
|
// ClearTask removes a run from memory, but only if it is not running
|
|
func (r *TaskManager) ClearTask(id uuid.UUID) error {
|
|
if r == nil {
|
|
return fmt.Errorf("%w TaskManager", gctcommon.ErrNilPointer)
|
|
}
|
|
r.m.Lock()
|
|
defer r.m.Unlock()
|
|
for i := range r.tasks {
|
|
if !r.tasks[i].MatchesID(id) {
|
|
continue
|
|
}
|
|
if r.tasks[i].IsRunning() {
|
|
return fmt.Errorf("%w %v, currently running. Stop it first", errCannotClear, r.tasks[i].MetaData.ID)
|
|
}
|
|
r.tasks = slices.Delete(r.tasks, i, i+1)
|
|
return nil
|
|
}
|
|
return fmt.Errorf("%s %w", id, errTaskNotFound)
|
|
}
|
|
|
|
// ClearAllTasks removes all tasks from memory, but only if they are not running
|
|
func (r *TaskManager) ClearAllTasks() (clearedRuns, remainingRuns []*TaskSummary, err error) {
|
|
if r == nil {
|
|
return nil, nil, fmt.Errorf("%w TaskManager", gctcommon.ErrNilPointer)
|
|
}
|
|
r.m.Lock()
|
|
defer r.m.Unlock()
|
|
for i := 0; i < len(r.tasks); i++ {
|
|
var run *TaskSummary
|
|
run, err = r.tasks[i].GenerateSummary()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
if r.tasks[i].IsRunning() {
|
|
remainingRuns = append(remainingRuns, run)
|
|
} else {
|
|
clearedRuns = append(clearedRuns, run)
|
|
r.tasks = slices.Delete(r.tasks, i, i+1)
|
|
i--
|
|
}
|
|
}
|
|
return clearedRuns, remainingRuns, nil
|
|
}
|