mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
* Makefile: add new recipes and linter features * expand linter coverage and fix issues * Update makefile * address PR nitterinos
228 lines
6.5 KiB
Go
228 lines
6.5 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"runtime"
|
|
"strconv"
|
|
"syscall"
|
|
|
|
"github.com/thrasher-/gocryptotrader/common"
|
|
"github.com/thrasher-/gocryptotrader/communications"
|
|
"github.com/thrasher-/gocryptotrader/config"
|
|
"github.com/thrasher-/gocryptotrader/currency"
|
|
"github.com/thrasher-/gocryptotrader/currency/forexprovider"
|
|
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
|
log "github.com/thrasher-/gocryptotrader/logger"
|
|
"github.com/thrasher-/gocryptotrader/portfolio"
|
|
)
|
|
|
|
// Bot contains configuration, portfolio, exchange & ticker data and is the
|
|
// overarching type across this code base.
|
|
type Bot struct {
|
|
config *config.Config
|
|
portfolio *portfolio.Base
|
|
exchanges []exchange.IBotExchange
|
|
comms *communications.Communications
|
|
shutdown chan bool
|
|
dryRun bool
|
|
configFile string
|
|
dataDir string
|
|
}
|
|
|
|
const banner = `
|
|
______ ______ __ ______ __
|
|
/ ____/____ / ____/_____ __ __ ____ / /_ ____ /_ __/_____ ______ ____/ /___ _____
|
|
/ / __ / __ \ / / / ___// / / // __ \ / __// __ \ / / / ___// __ // __ // _ \ / ___/
|
|
/ /_/ // /_/ // /___ / / / /_/ // /_/ // /_ / /_/ // / / / / /_/ // /_/ // __// /
|
|
\____/ \____/ \____//_/ \__, // .___/ \__/ \____//_/ /_/ \__,_/ \__,_/ \___//_/
|
|
/____//_/
|
|
`
|
|
|
|
var bot Bot
|
|
|
|
func main() {
|
|
bot.shutdown = make(chan bool)
|
|
HandleInterrupt()
|
|
|
|
defaultPath, err := config.GetFilePath("")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
//Handle flags
|
|
flag.StringVar(&bot.configFile, "config", defaultPath, "config file to load")
|
|
flag.StringVar(&bot.dataDir, "datadir", common.GetDefaultDataDir(runtime.GOOS), "default data directory for GoCryptoTrader files")
|
|
dryrun := flag.Bool("dryrun", false, "dry runs bot, doesn't save config file")
|
|
version := flag.Bool("version", false, "retrieves current GoCryptoTrader version")
|
|
verbosity := flag.Bool("verbose", false, "increases logging verbosity for GoCryptoTrader")
|
|
|
|
flag.Parse()
|
|
|
|
if *version {
|
|
fmt.Printf(BuildVersion(true))
|
|
os.Exit(0)
|
|
}
|
|
|
|
if *dryrun {
|
|
bot.dryRun = true
|
|
}
|
|
|
|
fmt.Println(banner)
|
|
fmt.Println(BuildVersion(false))
|
|
|
|
bot.config = &config.Cfg
|
|
log.Debugf("Loading config file %s..\n", bot.configFile)
|
|
err = bot.config.LoadConfig(bot.configFile)
|
|
if err != nil {
|
|
log.Fatalf("Failed to load config. Err: %s", err)
|
|
}
|
|
|
|
err = common.CheckDir(bot.dataDir, true)
|
|
if err != nil {
|
|
log.Fatalf("Failed to open/create data directory: %s. Err: %s", bot.dataDir, err)
|
|
}
|
|
log.Debugf("Using data directory: %s.\n", bot.dataDir)
|
|
|
|
err = bot.config.CheckLoggerConfig()
|
|
if err != nil {
|
|
log.Errorf("Failed to configure logger reason: %s", err)
|
|
}
|
|
|
|
err = log.SetupLogger()
|
|
if err != nil {
|
|
log.Errorf("Failed to setup logger reason: %s", err)
|
|
}
|
|
|
|
AdjustGoMaxProcs()
|
|
log.Debugf("Bot '%s' started.\n", bot.config.Name)
|
|
log.Debugf("Bot dry run mode: %v.\n", common.IsEnabled(bot.dryRun))
|
|
|
|
log.Debugf("Available Exchanges: %d. Enabled Exchanges: %d.\n",
|
|
len(bot.config.Exchanges),
|
|
bot.config.CountEnabledExchanges())
|
|
|
|
common.HTTPClient = common.NewHTTPClientWithTimeout(bot.config.GlobalHTTPTimeout)
|
|
log.Debugf("Global HTTP request timeout: %v.\n", common.HTTPClient.Timeout)
|
|
|
|
SetupExchanges()
|
|
if len(bot.exchanges) == 0 {
|
|
log.Fatalf("No exchanges were able to be loaded. Exiting")
|
|
}
|
|
|
|
log.Debugf("Starting communication mediums..")
|
|
bot.comms = communications.NewComm(bot.config.GetCommunicationsConfig())
|
|
bot.comms.GetEnabledCommunicationMediums()
|
|
|
|
log.Debugf("Fiat display currency: %s.", bot.config.Currency.FiatDisplayCurrency)
|
|
currency.BaseCurrency = bot.config.Currency.FiatDisplayCurrency
|
|
currency.FXProviders = forexprovider.StartFXService(bot.config.GetCurrencyConfig().ForexProviders)
|
|
log.Debugf("Primary forex conversion provider: %s.\n", bot.config.GetPrimaryForexProvider())
|
|
err = bot.config.RetrieveConfigCurrencyPairs(true)
|
|
if err != nil {
|
|
log.Fatalf("Failed to retrieve config currency pairs. Error: %s", err)
|
|
}
|
|
log.Debugf("Successfully retrieved config currencies.")
|
|
log.Debugf("Fetching currency data from forex provider..")
|
|
err = currency.SeedCurrencyData(common.JoinStrings(currency.FiatCurrencies, ","))
|
|
if err != nil {
|
|
log.Fatalf("Unable to fetch forex data. Error: %s", err)
|
|
}
|
|
|
|
bot.portfolio = &portfolio.Portfolio
|
|
bot.portfolio.SeedPortfolio(bot.config.Portfolio)
|
|
SeedExchangeAccountInfo(GetAllEnabledExchangeAccountInfo().Data)
|
|
|
|
if bot.config.Webserver.Enabled {
|
|
listenAddr := bot.config.Webserver.ListenAddress
|
|
log.Debugf(
|
|
"HTTP Webserver support enabled. Listen URL: http://%s:%d/\n",
|
|
common.ExtractHost(listenAddr), common.ExtractPort(listenAddr),
|
|
)
|
|
|
|
router := NewRouter()
|
|
go func() {
|
|
err = http.ListenAndServe(listenAddr, router)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}()
|
|
|
|
log.Debugln("HTTP Webserver started successfully.")
|
|
log.Debugln("Starting websocket handler.")
|
|
StartWebsocketHandler()
|
|
} else {
|
|
log.Debugln("HTTP RESTful Webserver support disabled.")
|
|
}
|
|
|
|
go portfolio.StartPortfolioWatcher()
|
|
|
|
go TickerUpdaterRoutine()
|
|
go OrderbookUpdaterRoutine()
|
|
go WebsocketRoutine(*verbosity)
|
|
|
|
<-bot.shutdown
|
|
Shutdown()
|
|
}
|
|
|
|
// AdjustGoMaxProcs adjusts the maximum processes that the CPU can handle.
|
|
func AdjustGoMaxProcs() {
|
|
log.Debugln("Adjusting bot runtime performance..")
|
|
maxProcsEnv := os.Getenv("GOMAXPROCS")
|
|
maxProcs := runtime.NumCPU()
|
|
log.Debugln("Number of CPU's detected:", maxProcs)
|
|
|
|
if maxProcsEnv != "" {
|
|
log.Debugln("GOMAXPROCS env =", maxProcsEnv)
|
|
env, err := strconv.Atoi(maxProcsEnv)
|
|
if err != nil {
|
|
log.Debugf("Unable to convert GOMAXPROCS to int, using %d", maxProcs)
|
|
} else {
|
|
maxProcs = env
|
|
}
|
|
}
|
|
if i := runtime.GOMAXPROCS(maxProcs); i != maxProcs {
|
|
log.Error("Go Max Procs were not set correctly.")
|
|
}
|
|
log.Debugln("Set GOMAXPROCS to:", maxProcs)
|
|
}
|
|
|
|
// HandleInterrupt monitors and captures the SIGTERM in a new goroutine then
|
|
// shuts down bot
|
|
func HandleInterrupt() {
|
|
c := make(chan os.Signal, 1)
|
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
|
go func() {
|
|
sig := <-c
|
|
log.Debugf("Captured %v, shutdown requested.", sig)
|
|
bot.shutdown <- true
|
|
}()
|
|
}
|
|
|
|
// Shutdown correctly shuts down bot saving configuration files
|
|
func Shutdown() {
|
|
log.Debugln("Bot shutting down..")
|
|
|
|
if len(portfolio.Portfolio.Addresses) != 0 {
|
|
bot.config.Portfolio = portfolio.Portfolio
|
|
}
|
|
|
|
if !bot.dryRun {
|
|
err := bot.config.SaveConfig(bot.configFile)
|
|
|
|
if err != nil {
|
|
log.Warn("Unable to save config.")
|
|
} else {
|
|
log.Debugln("Config file saved successfully.")
|
|
}
|
|
}
|
|
|
|
log.Debugln("Exiting.")
|
|
|
|
log.CloseLogFile()
|
|
os.Exit(0)
|
|
}
|