mirror of
https://github.com/lukewilson2002/autotrader.git
synced 2025-06-15 00:13:51 +00:00
111 lines
2.6 KiB
Go
111 lines
2.6 KiB
Go
package autotrader
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/go-co-op/gocron"
|
|
df "github.com/rocketlaunchr/dataframe-go"
|
|
)
|
|
|
|
// Trader acts as the primary interface to the broker and strategy. To the strategy, it provides all the information
|
|
// about the current state of the market and the portfolio. To the broker, it provides the orders to be executed and
|
|
// requests for the current state of the portfolio.
|
|
type Trader struct {
|
|
Broker Broker
|
|
Strategy Strategy
|
|
Symbol string
|
|
Frequency string
|
|
CandlesToKeep int
|
|
Log *log.Logger
|
|
|
|
data *df.DataFrame
|
|
sched *gocron.Scheduler
|
|
idx int
|
|
}
|
|
|
|
func (t *Trader) Data() *df.DataFrame {
|
|
return t.data
|
|
}
|
|
|
|
// Run starts the trader. This is a blocking call.
|
|
func (t *Trader) Run() {
|
|
t.sched = gocron.NewScheduler(time.UTC)
|
|
capitalizedFreq := strings.ToUpper(t.Frequency)
|
|
if strings.HasPrefix(capitalizedFreq, "S") {
|
|
seconds, err := strconv.Atoi(t.Frequency[1:])
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
t.sched.Every(seconds).Seconds()
|
|
} else if strings.HasPrefix(capitalizedFreq, "M") {
|
|
minutes, err := strconv.Atoi(t.Frequency[1:])
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
t.sched.Every(minutes).Minutes()
|
|
} else if strings.HasPrefix(capitalizedFreq, "H") {
|
|
hours, err := strconv.Atoi(t.Frequency[1:])
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
t.sched.Every(hours).Hours()
|
|
} else {
|
|
switch capitalizedFreq {
|
|
case "D":
|
|
t.sched.Every(1).Day()
|
|
case "W":
|
|
t.sched.Every(1).Day()
|
|
case "M":
|
|
t.sched.Every(1).Day()
|
|
default:
|
|
panic(fmt.Sprintf("invalid frequency: %s", t.Frequency))
|
|
}
|
|
}
|
|
t.sched.Do(t.Tick) // Set the function to be run when the interval repeats.
|
|
t.sched.StartBlocking()
|
|
}
|
|
|
|
// Tick updates the current state of the market and runs the strategy.
|
|
func (t *Trader) Tick() {
|
|
if t.idx == 0 {
|
|
t.Strategy.Init(t)
|
|
}
|
|
t.fetchData()
|
|
t.Strategy.Next(t)
|
|
t.Log.Println("Tick")
|
|
}
|
|
|
|
func (t *Trader) fetchData() {
|
|
var err error
|
|
t.data, err = t.Broker.Candles(t.Symbol, t.Frequency, t.CandlesToKeep)
|
|
if err != nil {
|
|
panic(err) // TODO: implement safe shutdown procedure
|
|
}
|
|
}
|
|
|
|
type TraderConfig struct {
|
|
Broker Broker
|
|
Strategy Strategy
|
|
Symbol string
|
|
Frequency string
|
|
CandlesToKeep int
|
|
}
|
|
|
|
// NewTrader initializes a new Trader which can be used for live trading or backtesting.
|
|
func NewTrader(config TraderConfig) *Trader {
|
|
logger := log.New(os.Stdout, "autotrader: ", log.LstdFlags|log.Lshortfile)
|
|
return &Trader{
|
|
Broker: config.Broker,
|
|
Strategy: config.Strategy,
|
|
Symbol: config.Symbol,
|
|
Frequency: config.Frequency,
|
|
CandlesToKeep: config.CandlesToKeep,
|
|
Log: logger,
|
|
}
|
|
}
|