From 0eb204036e15a51a8f0562be936e895168a3adf5 Mon Sep 17 00:00:00 2001 From: Alessandro Sanino Date: Sat, 10 Nov 2018 15:10:01 +0100 Subject: [PATCH 1/2] Adding Logo to readme and enhancing style --- README.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9c8e7a2..6916f64 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@ -# golang-crypto-trading-bot - -[![Go Report Card](https://goreportcard.com/badge/github.com/saniales/golang-crypto-trading-bot)](https://goreportcard.com/report/github.com/saniales/golang-crypto-trading-bot) -[![GoDoc](https://godoc.org/github.com/saniales/golang-crypto-trading-bot?status.svg)](https://godoc.org/github.com/saniales/golang-crypto-trading-bot) -[![Travis CI](https://img.shields.io/travis/saniales/golang-crypto-trading-bot.svg)]((https://travis-ci.org/saniales/golang-crypto-trading-bot)) -[![GitHub release](https://img.shields.io/github/release/saniales/golang-crypto-trading-bot.svg)](https://github.com/saniales/golang-crypto-trading-bot/releases) -[![license](https://img.shields.io/github/license/saniales/golang-crypto-trading-bot.svg?maxAge=2592000)](https://github.com/saniales/golang-crypto-trading-bot/LICENSE) +

+

+ Build Status + Godoc reference + Last Release + License + Goreportcard +

+ +# Golang Crypto Trading Bot A golang implementation of a console-based trading bot for cryptocurrency exchanges. From 130d7429a4f6cc623bd06c5b64a3c0e26da09f71 Mon Sep 17 00:00:00 2001 From: Mario Arranz Date: Mon, 26 Nov 2018 14:58:26 +0000 Subject: [PATCH 2/2] new way to start with a new strategy (#81) Added examples refactoring --- README.md | 2 +- examples/interval.go | 137 ++++++++++++++++++++++++++++ examples/package-info.go | 17 ++++ examples/slack_integration.go | 64 ------------- examples/telegram_integration.go | 53 ----------- examples/{watch.go => websocket.go} | 34 ++++--- strategies/interval.go | 1 + strategies/websocket.go | 42 ++------- 8 files changed, 179 insertions(+), 171 deletions(-) create mode 100644 examples/interval.go create mode 100644 examples/package-info.go delete mode 100644 examples/slack_integration.go delete mode 100644 examples/telegram_integration.go rename examples/{watch.go => websocket.go} (77%) diff --git a/README.md b/README.md index 6916f64..a7651a6 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ If you need to, you can create a strategy and bind it to the bot: import bot "github.com/saniales/golang-crypto-trading-bot/cmd" func main() { - bot.AddCustomStrategy(myStrategy) + bot.AddCustomStrategy(examples.MyStrategy) bot.Execute() } ``` diff --git a/examples/interval.go b/examples/interval.go new file mode 100644 index 0000000..8d5b872 --- /dev/null +++ b/examples/interval.go @@ -0,0 +1,137 @@ +// Copyright © 2017 Alessandro Sanino +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package examples + +import ( + "fmt" + "log" + "time" + + "github.com/nlopes/slack" + "github.com/saniales/golang-crypto-trading-bot/environment" + "github.com/saniales/golang-crypto-trading-bot/exchanges" + "github.com/saniales/golang-crypto-trading-bot/strategies" + "github.com/shomali11/slacker" + "github.com/sirupsen/logrus" + tb "gopkg.in/tucnak/telebot.v2" +) + +// Watch5Sec prints out the info of the market every 5 seconds. +var Watch5Sec = strategies.IntervalStrategy{ + Model: strategies.StrategyModel{ + Name: "Watch5Sec", + Setup: func(wrappers []exchanges.ExchangeWrapper, markets []*environment.Market) error { + fmt.Println("Watch5Sec starting") + return nil + }, + OnUpdate: func(wrappers []exchanges.ExchangeWrapper, markets []*environment.Market) error { + _, err := wrappers[0].GetMarketSummary(markets[0]) + if err != nil { + return err + } + logrus.Info(markets) + logrus.Info(wrappers) + return nil + }, + OnError: func(err error) { + fmt.Println(err) + }, + TearDown: func(wrappers []exchanges.ExchangeWrapper, markets []*environment.Market) error { + fmt.Println("Watch5Sec exited") + return nil + }, + }, + Interval: time.Second * 5, +} + +var slackBot *slacker.Slacker + +// SlackIntegrationExample send messages to Slack as a strategy. +// RTM not supported (and usually not requested when trading, this is an automated slackBot). +var SlackIntegrationExample = strategies.IntervalStrategy{ + Model: strategies.StrategyModel{ + Name: "SlackIntegrationExample", + Setup: func([]exchanges.ExchangeWrapper, []*environment.Market) error { + // connect slack token + slackBot = slacker.NewClient("YOUR-TOKEN-HERE") + slackBot.Init(func() { + log.Println("Slack BOT Connected") + }) + slackBot.Err(func(err string) { + log.Println("Error during slack slackBot connection: ", err) + }) + go func() { + err := slackBot.Listen() + if err != nil { + log.Fatal(err) + } + }() + return nil + }, + OnUpdate: func([]exchanges.ExchangeWrapper, []*environment.Market) error { + //if updates has requirements + _, _, err := slackBot.Client.PostMessage("DESIRED-CHANNEL", "OMG something happening!!!!!", slack.PostMessageParameters{}) + return err + }, + OnError: func(err error) { + logrus.Errorf("I Got an error %s", err) + }, + }, + Interval: time.Second * 10, +} + +var telegramBot *tb.Bot + +// TelegramIntegrationExample send messages to Telegram as a strategy. +var TelegramIntegrationExample = strategies.IntervalStrategy{ + Model: strategies.StrategyModel{ + Name: "TelegramIntegrationExample", + Setup: func([]exchanges.ExchangeWrapper, []*environment.Market) error { + telegramBot, err := tb.NewBot(tb.Settings{ + Token: "YOUR-TELEGRAM-TOKEN", + Poller: &tb.LongPoller{Timeout: 10 * time.Second}, + }) + + if err != nil { + return err + } + + telegramBot.Start() + return nil + }, + OnUpdate: func([]exchanges.ExchangeWrapper, []*environment.Market) error { + telegramBot.Send(&tb.User{ + Username: "YOUR-USERNAME-GROUP-OR-USER", + }, "OMG SOMETHING HAPPENING!!!!!", tb.SendOptions{}) + + /* + // Optionally it can have options + telegramBot.Send(tb.User{ + Username: "YOUR-JOINED-GROUP-USERNAME", + }, "OMG SOMETHING HAPPENING!!!!!", tb.SendOptions{}) + */ + return nil + }, + OnError: func(err error) { + logrus.Errorf("I Got an error %s", err) + telegramBot.Stop() + }, + TearDown: func([]exchanges.ExchangeWrapper, []*environment.Market) error { + telegramBot.Stop() + return nil + }, + }, +} diff --git a/examples/package-info.go b/examples/package-info.go new file mode 100644 index 0000000..f357957 --- /dev/null +++ b/examples/package-info.go @@ -0,0 +1,17 @@ +// Copyright © 2017 Alessandro Sanino +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Package examples contains different implementations of the strategies. +package examples diff --git a/examples/slack_integration.go b/examples/slack_integration.go deleted file mode 100644 index c3086e4..0000000 --- a/examples/slack_integration.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright © 2017 Alessandro Sanino -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -package strategies - -import ( - "log" - "time" - - "github.com/nlopes/slack" - "github.com/saniales/golang-crypto-trading-bot/environment" - "github.com/saniales/golang-crypto-trading-bot/exchanges" - "github.com/saniales/golang-crypto-trading-bot/strategies" - "github.com/shomali11/slacker" - "github.com/sirupsen/logrus" -) - -var slackBot *slacker.Slacker - -// The following slack integration allows to send messages as a strategy. -// RTM not supported (and usually not requested when trading, this is an automated slackBot). -var slackIntegrationExample = strategies.IntervalStrategy{ - Model: strategies.StrategyModel{ - Name: "slackIntegrationExample", - Setup: func([]exchanges.ExchangeWrapper, []*environment.Market) error { - // connect slack token - slackBot = slacker.NewClient("YOUR-TOKEN-HERE") - slackBot.Init(func() { - log.Println("Slack BOT Connected") - }) - slackBot.Err(func(err string) { - log.Println("Error during slack slackBot connection: ", err) - }) - go func() { - err := slackBot.Listen() - if err != nil { - log.Fatal(err) - } - }() - return nil - }, - OnUpdate: func([]exchanges.ExchangeWrapper, []*environment.Market) error { - //if updates has requirements - _, _, err := slackBot.Client.PostMessage("DESIRED-CHANNEL", "OMG something happening!!!!!", slack.PostMessageParameters{}) - return err - }, - OnError: func(err error) { - logrus.Errorf("I Got an error %s", err) - }, - }, - Interval: time.Second * 10, -} diff --git a/examples/telegram_integration.go b/examples/telegram_integration.go deleted file mode 100644 index a7b301a..0000000 --- a/examples/telegram_integration.go +++ /dev/null @@ -1,53 +0,0 @@ -package strategies - -import ( - "time" - - "github.com/saniales/golang-crypto-trading-bot/environment" - "github.com/saniales/golang-crypto-trading-bot/exchanges" - "github.com/saniales/golang-crypto-trading-bot/strategies" - "github.com/sirupsen/logrus" - tb "gopkg.in/tucnak/telebot.v2" -) - -var telegramBot *tb.Bot - -var telegramIntegrationExample = strategies.IntervalStrategy{ - Model: strategies.StrategyModel{ - Name: "telegramIntegrationExample", - Setup: func([]exchanges.ExchangeWrapper, []*environment.Market) error { - telegramBot, err := tb.NewBot(tb.Settings{ - Token: "TOKEN_HERE", - Poller: &tb.LongPoller{Timeout: 10 * time.Second}, - }) - - if err != nil { - return err - } - - telegramBot.Start() - return nil - }, - OnUpdate: func([]exchanges.ExchangeWrapper, []*environment.Market) error { - telegramBot.Send(&tb.User{ - Username: "YOUR-USERNAME-GROUP-OR-USER", - }, "OMG SOMETHING HAPPENING!!!!!", tb.SendOptions{}) - - /* - // Optionally it can have options - telegramBot.Send(tb.User{ - Username: "YOUR-JOINED-GROUP-USERNAME", - }, "OMG SOMETHING HAPPENING!!!!!", tb.SendOptions{}) - */ - return nil - }, - OnError: func(err error) { - logrus.Errorf("I Got an error %s", err) - telegramBot.Stop() - }, - TearDown: func([]exchanges.ExchangeWrapper, []*environment.Market) error { - telegramBot.Stop() - return nil - }, - }, -} diff --git a/examples/watch.go b/examples/websocket.go similarity index 77% rename from examples/watch.go rename to examples/websocket.go index 359f936..8552a25 100644 --- a/examples/watch.go +++ b/examples/websocket.go @@ -13,40 +13,38 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package strategies +package examples import ( - "fmt" - "time" - "github.com/saniales/golang-crypto-trading-bot/environment" "github.com/saniales/golang-crypto-trading-bot/exchanges" "github.com/saniales/golang-crypto-trading-bot/strategies" + "github.com/sirupsen/logrus" ) -// Watch5Min prints out the info of the market every 5 minutes. -var Watch5Min = strategies.IntervalStrategy{ +// Websocket strategy +var Websocket = strategies.WebsocketStrategy{ Model: strategies.StrategyModel{ - Name: "Watch5Min", + Name: "Websocket", Setup: func(wrappers []exchanges.ExchangeWrapper, markets []*environment.Market) error { - fmt.Println("Watch5Min starting") - return nil - }, - OnUpdate: func(wrappers []exchanges.ExchangeWrapper, markets []*environment.Market) error { - _, err := wrappers[0].GetMarketSummary(markets[0]) - if err != nil { + for _, wrapper := range wrappers { + err := wrapper.FeedConnect(markets) + if err == exchanges.ErrWebsocketNotSupported || err == nil { + continue + } return err } - fmt.Println(markets) return nil }, - OnError: func(err error) { - fmt.Println(err) + OnUpdate: func(wrappers []exchanges.ExchangeWrapper, markets []*environment.Market) error { + // do something + return nil }, TearDown: func(wrappers []exchanges.ExchangeWrapper, markets []*environment.Market) error { - fmt.Println("Watch5Min exited") return nil }, + OnError: func(err error) { + logrus.Error(err) + }, }, - Interval: time.Minute * 5, } diff --git a/strategies/interval.go b/strategies/interval.go index bf8629f..e4762a0 100644 --- a/strategies/interval.go +++ b/strategies/interval.go @@ -54,6 +54,7 @@ func (is IntervalStrategy) Apply(wrappers []exchanges.ExchangeWrapper, markets [ is.Model.OnError(err) } } + if !hasUpdateFunc { _err := errors.New("OnUpdate func cannot be empty") if hasErrorFunc { diff --git a/strategies/websocket.go b/strategies/websocket.go index f656e21..af05e91 100644 --- a/strategies/websocket.go +++ b/strategies/websocket.go @@ -3,8 +3,6 @@ package strategies import ( "errors" - "github.com/sirupsen/logrus" - "github.com/saniales/golang-crypto-trading-bot/environment" "github.com/saniales/golang-crypto-trading-bot/exchanges" ) @@ -35,6 +33,13 @@ func (wss WebsocketStrategy) Apply(wrappers []exchanges.ExchangeWrapper, markets hasUpdateFunc := wss.Model.OnUpdate != nil hasErrorFunc := wss.Model.OnError != nil + if hasSetupFunc { + err = wss.Model.Setup(wrappers, markets) + if err != nil && hasErrorFunc { + wss.Model.OnError(err) + } + } + // update is handled by the developer externally, here we just checked for existence. if !hasUpdateFunc { _err := errors.New("OnUpdate func cannot be empty") @@ -45,13 +50,6 @@ func (wss WebsocketStrategy) Apply(wrappers []exchanges.ExchangeWrapper, markets } } - if hasSetupFunc { - err = wss.Model.Setup(wrappers, markets) - if err != nil && hasErrorFunc { - wss.Model.OnError(err) - } - } - if hasTearDownFunc { err = wss.Model.TearDown(wrappers, markets) if err != nil && hasErrorFunc { @@ -59,29 +57,3 @@ func (wss WebsocketStrategy) Apply(wrappers []exchanges.ExchangeWrapper, markets } } } - -var example = WebsocketStrategy{ - Model: StrategyModel{ - Name: "example", - Setup: func(wrappers []exchanges.ExchangeWrapper, markets []*environment.Market) error { - for _, wrapper := range wrappers { - err := wrapper.FeedConnect(markets) - if err == exchanges.ErrWebsocketNotSupported || err == nil { - continue - } - return err - } - return nil - }, - OnUpdate: func(wrappers []exchanges.ExchangeWrapper, markets []*environment.Market) error { - // do something - return nil - }, - TearDown: func(wrappers []exchanges.ExchangeWrapper, markets []*environment.Market) error { - return nil - }, - OnError: func(err error) { - logrus.Error(err) - }, - }, -}