Engine improvements

Add back events tests
Fill out SMTP comms handler
Add getcommunicationrelayers gRPC command
This commit is contained in:
Adrian Gallagher
2019-06-14 18:00:42 +10:00
parent 6b2cfe7905
commit b901c4b670
14 changed files with 978 additions and 741 deletions

View File

@@ -183,6 +183,32 @@ func getRPCEndpoints(_ *cli.Context) error {
return nil
}
var getCommunicationRelayersCommand = cli.Command{
Name: "getcommsrelayers",
Usage: "gets GoCryptoTrader communication relayers",
Action: getCommunicationRelayers,
}
func getCommunicationRelayers(_ *cli.Context) error {
conn, err := setupClient()
if err != nil {
return err
}
defer conn.Close()
client := gctrpc.NewGoCryptoTraderClient(conn)
result, err := client.GetCommunicationRelayers(context.Background(),
&gctrpc.GetCommunicationRelayersRequest{},
)
if err != nil {
return err
}
jsonOutput(result)
return nil
}
var getExchangesCommand = cli.Command{
Name: "getexchanges",
Usage: "gets a list of enabled or available exchanges",

View File

@@ -89,6 +89,7 @@ func main() {
enableSubsystemCommand,
disableSubsystemCommand,
getRPCEndpointsCommand,
getCommunicationRelayersCommand,
getExchangesCommand,
enableExchangeCommand,
disableExchangeCommand,

View File

@@ -24,6 +24,12 @@ type Event struct {
Message string
}
// CommsStatus stores the status of a comms relayer
type CommsStatus struct {
Enabled bool `json:"enabled"`
Connected bool `json:"connected"`
}
// IsEnabled returns if the comms package has been enabled in the configuration
func (b *Base) IsEnabled() bool {
return b.Enabled

View File

@@ -30,7 +30,9 @@ func (c IComm) Setup() {
err := c[i].Connect()
if err != nil {
log.Errorf("Communications: %s failed to connect. Err: %s", c[i].GetName(), err)
continue
}
log.Debugf("Communications: %v is enabled and online.", c[i].GetName())
}
}
}
@@ -48,6 +50,18 @@ func (c IComm) PushEvent(event Event) {
}
}
// GetStatus returns the status of the comms relayers
func (c IComm) GetStatus() map[string]CommsStatus {
result := make(map[string]CommsStatus)
for x := range c {
result[c[x].GetName()] = CommsStatus{
Enabled: c[x].IsEnabled(),
Connected: c[x].IsConnected(),
}
}
return result
}
// GetEnabledCommunicationMediums prints out enabled and connected communication
// packages
// (#debug output only)

View File

@@ -6,9 +6,9 @@ import (
"net/smtp"
"strings"
"github.com/thrasher-/gocryptotrader/common"
"github.com/thrasher-/gocryptotrader/communications/base"
"github.com/thrasher-/gocryptotrader/config"
log "github.com/thrasher-/gocryptotrader/logger"
)
const (
@@ -23,6 +23,7 @@ type SMTPservice struct {
Port string
AccountName string
AccountPassword string
From string
RecipientList string
}
@@ -36,7 +37,9 @@ func (s *SMTPservice) Setup(cfg *config.CommunicationsConfig) {
s.Port = cfg.SMTPConfig.Port
s.AccountName = cfg.SMTPConfig.AccountName
s.AccountPassword = cfg.SMTPConfig.AccountPassword
s.From = cfg.SMTPConfig.From
s.RecipientList = cfg.SMTPConfig.RecipientList
log.Debugf("SMTP: Setup - From: %v. To: %s. Server: %s.", s.From, s.RecipientList, s.Host)
}
// IsConnected returns whether or not the connection is connected
@@ -51,36 +54,34 @@ func (s *SMTPservice) Connect() error {
}
// PushEvent sends an event to supplied recipient list via SMTP
func (s *SMTPservice) PushEvent(base.Event) error {
return common.ErrNotYetImplemented
func (s *SMTPservice) PushEvent(e base.Event) error {
return s.Send(e.Type, e.Message)
}
// Send sends an email template to the recipient list via your SMTP host when
// an internal event is triggered by GoCryptoTrader
func (s *SMTPservice) Send(subject, alert string) error {
if subject == "" || alert == "" {
func (s *SMTPservice) Send(subject, msg string) error {
if subject == "" || msg == "" {
return errors.New("STMPservice Send() please add subject and alert")
}
list := strings.Split(s.RecipientList, ",")
log.Debugf("SMTP: Sending email to %v. Subject: %s Message: %s [From: %s]", s.RecipientList,
subject, msg, s.From)
messageToSend := fmt.Sprintf(
msgSMTP,
s.RecipientList,
subject,
mime,
msg)
for i := range list {
messageToSend := fmt.Sprintf(
msgSMTP,
list[i],
subject,
mime,
alert)
err := smtp.SendMail(
s.Host+":"+s.Port,
smtp.PlainAuth("", s.AccountName, s.AccountPassword, s.Host),
s.AccountName,
[]string{list[i]},
[]byte(messageToSend))
if err != nil {
return err
}
err := smtp.SendMail(
s.Host+":"+s.Port,
smtp.PlainAuth("", s.AccountName, s.AccountPassword, s.Host),
s.From,
strings.Split(s.RecipientList, ","),
[]byte(messageToSend))
if err != nil {
return err
}
return nil
}

View File

@@ -251,6 +251,7 @@ type SMTPConfig struct {
Port string `json:"port"`
AccountName string `json:"accountName"`
AccountPassword string `json:"accountPassword"`
From string `json:"from"`
RecipientList string `json:"recipientList"`
}

View File

@@ -47,6 +47,13 @@ func (c *commsManager) Start() (err error) {
return nil
}
func (c *commsManager) GetStatus() (map[string]base.CommsStatus, error) {
if !c.Started() {
return nil, errors.New("communications manager not started")
}
return c.comms.GetStatus(), nil
}
func (c *commsManager) Stop() error {
if atomic.LoadInt32(&c.started) == 0 {
return errors.New("communications manager not started")

View File

@@ -143,14 +143,12 @@ func (e *Event) ExecuteAction() bool {
// String turns the structure event into a string
func (e *Event) String() string {
return fmt.Sprintf(
"If the %s%s [%s] %s on %s meets the following %v then %s.", e.Pair.Base.String(),
e.Pair.Quote.String(), e.Asset, e.Item, e.Exchange, e.Condition, e.Action,
"If the %s [%s] %s on %s meets the following %v then %s.", e.Pair.String(),
strings.ToUpper(e.Asset.String()), e.Item, e.Exchange, e.Condition, e.Action,
)
}
func (e *Event) processTicker() bool {
targetPrice := e.Condition.Price
t, err := ticker.GetTicker(e.Exchange, e.Pair, e.Asset)
if err != nil {
if Bot.Settings.Verbose {
@@ -159,16 +157,13 @@ func (e *Event) processTicker() bool {
return false
}
lastPrice := t.Last
if lastPrice == 0 {
if t.Last == 0 {
if Bot.Settings.Verbose {
log.Debugln("Events: ticker last price is 0")
}
return false
}
return e.processCondition(lastPrice, targetPrice)
return e.processCondition(t.Last, e.Condition.Price)
}
func (e *Event) processCondition(actual, threshold float64) bool {
@@ -259,14 +254,14 @@ func IsValidEvent(exchange, item string, condition EventConditionParams, action
}
if item == ItemPrice {
if condition.Price == 0 {
if condition.Price <= 0 {
return errInvalidCondition
}
}
if item == ItemOrderbook {
if condition.OrderbookAmount == 0 {
return errInvalidAction
if condition.OrderbookAmount <= 0 {
return errInvalidCondition
}
}
@@ -276,10 +271,6 @@ func IsValidEvent(exchange, item string, condition EventConditionParams, action
if a[0] != ActionSMSNotify {
return errInvalidAction
}
if a[1] != "ALL" {
Bot.CommsManager.PushEvent(base.Event{Type: a[1]})
}
} else if action != ActionConsolePrint && action != ActionTest {
return errInvalidAction
}
@@ -302,10 +293,12 @@ func EventManger() {
}
success := event.CheckEventCondition()
if success {
log.Debugf(
"Events: ID: %d triggered on %s successfully.\n", event.ID,
event.Exchange,
msg := fmt.Sprintf(
"Events: ID: %d triggered on %s successfully [%v]\n", event.ID,
event.Exchange, event.String(),
)
log.Info(msg)
Bot.CommsManager.PushEvent(base.Event{Type: "event", Message: msg})
event.Executed = true
}
}

View File

@@ -1,369 +1,358 @@
package engine
//
// import (
// "testing"
//
// "github.com/thrasher-/gocryptotrader/config"
// "github.com/thrasher-/gocryptotrader/currency/pair"
// "github.com/thrasher-/gocryptotrader/exchanges/ticker"
// "github.com/thrasher-/gocryptotrader/smsglobal"
// )
//
// var (
// loaded = false
// )
//
// func testSetup(t *testing.T) {
// if !loaded {
// cfg := config.GetConfig()
// err := cfg.LoadConfig("")
// if err != nil {
// t.Fatalf("Test failed. Failed to load config %s", err)
// }
// smsglobal.New(cfg.SMS.Username, cfg.SMS.Password, cfg.Name, cfg.SMS.Contacts)
// loaded = true
// }
// }
//
// func TestAddEvent(t *testing.T) {
// testSetup(t)
//
// pair := currency.NewPairFromStrings("BTC", "USD")
// eventID, err := AddEvent("ANX", "price", ">,==", pair, assets.AssetTypeSpot, actionTest)
// if err != nil && eventID != 0 {
// t.Errorf("Test Failed. AddEvent: Error, %s", err)
// }
// eventID, err = AddEvent("ANXX", "price", ">,==", pair, assets.AssetTypeSpot, actionTest)
// if err == nil && eventID == 0 {
// t.Error("Test Failed. AddEvent: Error, error not captured in Exchange")
// }
// eventID, err = AddEvent("ANX", "prices", ">,==", pair, assets.AssetTypeSpot, actionTest)
// if err == nil && eventID == 0 {
// t.Error("Test Failed. AddEvent: Error, error not captured in Item")
// }
// eventID, err = AddEvent("ANX", "price", "3===D", pair, assets.AssetTypeSpot, actionTest)
// if err == nil && eventID == 0 {
// t.Error("Test Failed. AddEvent: Error, error not captured in Condition")
// }
// eventID, err = AddEvent("ANX", "price", ">,==", pair, assets.AssetTypeSpot, "console_prints")
// if err == nil && eventID == 0 {
// t.Error("Test Failed. AddEvent: Error, error not captured in Action")
// }
//
// if !RemoveEvent(eventID) {
// t.Error("Test Failed. RemoveEvent: Error, error removing event")
// }
// }
//
// func TestRemoveEvent(t *testing.T) {
// testSetup(t)
//
// pair := currency.NewPairFromStrings("BTC", "USD")
// eventID, err := AddEvent("ANX", "price", ">,==", pair, assets.AssetTypeSpot, actionTest)
// if err != nil && eventID != 0 {
// t.Errorf("Test Failed. RemoveEvent: Error, %s", err)
// }
// if !RemoveEvent(eventID) {
// t.Error("Test Failed. RemoveEvent: Error, error removing event")
// }
// if RemoveEvent(1234) {
// t.Error("Test Failed. RemoveEvent: Error, error removing event")
// }
// }
//
// func TestGetEventCounter(t *testing.T) {
// testSetup(t)
//
// pair := currency.NewPairFromStrings("BTC", "USD")
// one, err := AddEvent("ANX", "price", ">,==", pair, assets.AssetTypeSpot, actionTest)
// if err != nil {
// t.Errorf("Test Failed. GetEventCounter: Error, %s", err)
// }
// two, err := AddEvent("ANX", "price", ">,==", pair, assets.AssetTypeSpot, actionTest)
// if err != nil {
// t.Errorf("Test Failed. GetEventCounter: Error, %s", err)
// }
// three, err := AddEvent("ANX", "price", ">,==", pair, assets.AssetTypeSpot, actionTest)
// if err != nil {
// t.Errorf("Test Failed. GetEventCounter: Error, %s", err)
// }
//
// Events[three-1].Executed = true
//
// total, _ := GetEventCounter()
// if total <= 0 {
// t.Errorf("Test Failed. GetEventCounter: Total = %d", total)
// }
// if !RemoveEvent(one) {
// t.Error("Test Failed. GetEventCounter: Error, error removing event")
// }
// if !RemoveEvent(two) {
// t.Error("Test Failed. GetEventCounter: Error, error removing event")
// }
// if !RemoveEvent(three) {
// t.Error("Test Failed. GetEventCounter: Error, error removing event")
// }
//
// total2, _ := GetEventCounter()
// if total2 != 0 {
// t.Errorf("Test Failed. GetEventCounter: Total = %d", total2)
// }
// }
//
// func TestExecuteAction(t *testing.T) {
// testSetup(t)
//
// pair := currency.NewPairFromStrings("BTC", "USD")
// one, err := AddEvent("ANX", "price", ">,==", pair, assets.AssetTypeSpot, actionTest)
// if err != nil {
// t.Fatalf("Test Failed. ExecuteAction: Error, %s", err)
// }
// isExecuted := Events[one].ExecuteAction()
// if !isExecuted {
// t.Error("Test Failed. ExecuteAction: Error, error removing event")
// }
// if !RemoveEvent(one) {
// t.Error("Test Failed. ExecuteAction: Error, error removing event")
// }
//
// action := actionSMSNotify + "," + "ALL"
// one, err = AddEvent("ANX", "price", ">,==", pair, assets.AssetTypeSpot, action)
// if err != nil {
// t.Fatalf("Test Failed. ExecuteAction: Error, %s", err)
// }
//
// isExecuted = Events[one].ExecuteAction()
// if !isExecuted {
// t.Error("Test Failed. ExecuteAction: Error, error removing event")
// }
// if !RemoveEvent(one) {
// t.Error("Test Failed. ExecuteAction: Error, error removing event")
// }
//
// action = actionSMSNotify + "," + "StyleGherkin"
// one, err = AddEvent("ANX", "price", ">,==", pair, assets.AssetTypeSpot, action)
// if err != nil {
// t.Fatalf("Test Failed. ExecuteAction: Error, %s", err)
// }
//
// isExecuted = Events[one].ExecuteAction()
// if !isExecuted {
// t.Error("Test Failed. ExecuteAction: Error, error removing event")
// }
// if !RemoveEvent(one) {
// t.Error("Test Failed. ExecuteAction: Error, error removing event")
// }
// // More tests when ExecuteAction is expanded
// }
//
// func TestEventToString(t *testing.T) {
// testSetup(t)
//
// pair := currency.NewPairFromStrings("BTC", "USD")
// one, err := AddEvent("ANX", "price", ">,==", pair, assets.AssetTypeSpot, actionTest)
// if err != nil {
// t.Errorf("Test Failed. EventToString: Error, %s", err)
// }
//
// eventString := Events[one].String()
// if eventString != "If the BTCUSD [SPOT] price on ANX is > == then ACTION_TEST." {
// t.Error("Test Failed. EventToString: Error, incorrect return string")
// }
//
// if !RemoveEvent(one) {
// t.Error("Test Failed. EventToString: Error, error removing event")
// }
// }
//
// func TestCheckCondition(t *testing.T) {
// testSetup(t)
//
// // Test invalid currency pair
// newPair := currency.NewPairFromStrings("A", "B")
// one, err := AddEvent("ANX", "price", ">=,10", newPair, assets.AssetTypeSpot, actionTest)
// if err != nil {
// t.Errorf("Test Failed. CheckCondition: Error, %s", err)
// }
// conditionBool := Events[one].CheckCondition()
// if conditionBool {
// t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
// }
//
// // Test last price == 0
// var tickerNew ticker.Price
// tickerNew.Last = 0
// newPair = currency.NewPairFromStrings("BTC", "USD")
// ticker.ProcessTicker("ANX", newPair, tickerNew, exchange.AssetTypeSpot)
// Events[one].Pair = newPair
// conditionBool = Events[one].CheckCondition()
// if conditionBool {
// t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
// }
//
// // Test last pricce > 0 and conditional logic
// tickerNew.Last = 11
// ticker.ProcessTicker("ANX", newPair, tickerNew, exchange.AssetTypeSpot)
// Events[one].Condition = ">,10"
// conditionBool = Events[one].CheckCondition()
// if !conditionBool {
// t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
// }
//
// // Test last price >= 10
// Events[one].Condition = ">=,10"
// conditionBool = Events[one].CheckCondition()
// if !conditionBool {
// t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
// }
//
// // Test last price <= 10
// Events[one].Condition = "<,100"
// conditionBool = Events[one].CheckCondition()
// if !conditionBool {
// t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
// }
//
// // Test last price <= 10
// Events[one].Condition = "<=,100"
// conditionBool = Events[one].CheckCondition()
// if !conditionBool {
// t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
// }
//
// Events[one].Condition = "==,11"
// conditionBool = Events[one].CheckCondition()
// if !conditionBool {
// t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
// }
//
// Events[one].Condition = "^,11"
// conditionBool = Events[one].CheckCondition()
// if conditionBool {
// t.Error("Test Failed. CheckCondition: Error, wrong conditional.")
// }
//
// if !RemoveEvent(one) {
// t.Error("Test Failed. CheckCondition: Error, error removing event")
// }
// }
//
// func TestIsValidEvent(t *testing.T) {
// testSetup(t)
//
// err := IsValidEvent("ANX", "price", ">,==", actionTest)
// if err != nil {
// t.Errorf("Test Failed. IsValidEvent: %s", err)
// }
// err = IsValidEvent("ANX", "price", ">,", actionTest)
// if err == nil {
// t.Errorf("Test Failed. IsValidEvent: %s", err)
// }
// err = IsValidEvent("ANX", "Testy", ">,==", actionTest)
// if err == nil {
// t.Errorf("Test Failed. IsValidEvent: %s", err)
// }
// err = IsValidEvent("Testys", "price", ">,==", actionTest)
// if err == nil {
// t.Errorf("Test Failed. IsValidEvent: %s", err)
// }
//
// action := "blah,blah"
// err = IsValidEvent("ANX", "price", ">=,10", action)
// if err == nil {
// t.Errorf("Test Failed. IsValidEvent: %s", err)
// }
//
// action = "SMS,blah"
// err = IsValidEvent("ANX", "price", ">=,10", action)
// if err == nil {
// t.Errorf("Test Failed. IsValidEvent: %s", err)
// }
//
// //Function tests need to appended to this function when more actions are
// //implemented
// }
//
// func TestCheckEvents(t *testing.T) {
// testSetup(t)
//
// pair := currency.NewPairFromStrings("BTC", "USD")
// _, err := AddEvent("ANX", "price", ">=,10", pair, assets.AssetTypeSpot, actionTest)
// if err != nil {
// t.Fatal("Test failed. TestChcheckEvents add event")
// }
//
// go CheckEvents()
// }
//
// func TestIsValidExchange(t *testing.T) {
// testSetup(t)
//
// boolean := IsValidExchange("ANX")
// if !boolean {
// t.Error("Test Failed. IsValidExchange: Error, incorrect Exchange")
// }
// boolean = IsValidExchange("OBTUSE")
// if boolean {
// t.Error("Test Failed. IsValidExchange: Error, incorrect return")
// }
// }
//
// func TestIsValidCondition(t *testing.T) {
// testSetup(t)
//
// boolean := IsValidCondition(">")
// if !boolean {
// t.Error("Test Failed. IsValidCondition: Error, incorrect Condition")
// }
// boolean = IsValidCondition(">=")
// if !boolean {
// t.Error("Test Failed. IsValidCondition: Error, incorrect Condition")
// }
// boolean = IsValidCondition("<")
// if !boolean {
// t.Error("Test Failed. IsValidCondition: Error, incorrect Condition")
// }
// boolean = IsValidCondition("<=")
// if !boolean {
// t.Error("Test Failed. IsValidCondition: Error, incorrect Condition")
// }
// boolean = IsValidCondition("==")
// if !boolean {
// t.Error("Test Failed. IsValidCondition: Error, incorrect Condition")
// }
// boolean = IsValidCondition("**********")
// if boolean {
// t.Error("Test Failed. IsValidCondition: Error, incorrect return")
// }
// }
//
// func TestIsValidAction(t *testing.T) {
// testSetup(t)
//
// boolean := IsValidAction("sms")
// if !boolean {
// t.Error("Test Failed. IsValidAction: Error, incorrect Action")
// }
// boolean = IsValidAction(actionTest)
// if !boolean {
// t.Error("Test Failed. IsValidAction: Error, incorrect Action")
// }
// boolean = IsValidAction("randomstring")
// if boolean {
// t.Error("Test Failed. IsValidAction: Error, incorrect return")
// }
// }
//
// func TestIsValidItem(t *testing.T) {
// testSetup(t)
//
// boolean := IsValidItem("price")
// if !boolean {
// t.Error("Test Failed. IsValidItem: Error, incorrect Item")
// }
// boolean = IsValidItem("obtuse")
// if boolean {
// t.Error("Test Failed. IsValidItem: Error, incorrect return")
// }
// }
import (
"testing"
"github.com/thrasher-/gocryptotrader/currency"
"github.com/thrasher-/gocryptotrader/exchanges/assets"
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
)
const (
testExchange = "Bitstamp"
)
var (
configLoaded = false
)
func addValidEvent() (int64, error) {
return Add(testExchange,
ItemPrice,
EventConditionParams{Condition: ConditionGreaterThan, Price: 1},
currency.NewPair(currency.BTC, currency.USD),
assets.AssetTypeSpot,
"SMS,test")
}
func TestAdd(t *testing.T) {
if !configLoaded {
loadConfig(t)
}
_, err := Add("", "", EventConditionParams{}, currency.Pair{}, "", "")
if err == nil {
t.Error("should err on invalid params")
}
_, err = addValidEvent()
if err != nil {
t.Error("unexpected result", err)
}
_, err = addValidEvent()
if err != nil {
t.Error("unexpected result", err)
}
if len(Events) != 2 {
t.Error("2 events should be stored")
}
}
func TestRemove(t *testing.T) {
if !configLoaded {
loadConfig(t)
}
id, err := addValidEvent()
if err != nil {
t.Error("unexpected result", err)
}
if s := Remove(id); !s {
t.Error("unexpected result")
}
if s := Remove(id); s {
t.Error("unexpected result")
}
}
func TestGetEventCounter(t *testing.T) {
if !configLoaded {
loadConfig(t)
}
_, err := addValidEvent()
if err != nil {
t.Error("unexpected result", err)
}
n, e := GetEventCounter()
if n == 0 || e > 0 {
t.Error("unexpected result")
}
Events[0].Executed = true
n, e = GetEventCounter()
if n == 0 || e == 0 {
t.Error("unexpected result")
}
}
func TestExecuteAction(t *testing.T) {
t.Parallel()
if Bot == nil {
Bot = new(Engine)
}
var e Event
if r := e.ExecuteAction(); !r {
t.Error("unexpected result")
}
e.Action = "SMS,test"
if r := e.ExecuteAction(); !r {
t.Error("unexpected result")
}
e.Action = "SMS,ALL"
if r := e.ExecuteAction(); !r {
t.Error("unexpected result")
}
}
func TestString(t *testing.T) {
t.Parallel()
e := Event{
Exchange: testExchange,
Item: ItemPrice,
Condition: EventConditionParams{
Condition: ConditionGreaterThan,
Price: 1,
},
Pair: currency.NewPair(currency.BTC, currency.USD),
Asset: assets.AssetTypeSpot,
Action: "SMS,ALL",
}
if r := e.String(); r != "If the BTCUSD [SPOT] PRICE on Bitstamp meets the following {> 1 false false 0} then SMS,ALL." {
t.Error("unexpected result")
}
}
func TestProcessTicker(t *testing.T) {
if Bot == nil {
Bot = new(Engine)
}
Bot.Settings.Verbose = true
e := Event{
Exchange: testExchange,
Pair: currency.NewPair(currency.BTC, currency.USD),
Asset: assets.AssetTypeSpot,
Condition: EventConditionParams{
Condition: ConditionGreaterThan,
Price: 1,
},
}
// this will throw an err with an unpopulated ticker
ticker.Tickers = nil
if r := e.processTicker(); r {
t.Error("unexpected result")
}
// now populate it with a 0 entry
tick := ticker.Price{
Pair: currency.NewPair(currency.BTC, currency.USD),
Last: 0,
}
if err := ticker.ProcessTicker(e.Exchange, &tick, e.Asset); err != nil {
t.Fatal("unexpected result:", err)
}
if r := e.processTicker(); r {
t.Error("unexpected result")
}
// now populate it with a number > 0
tick.Last = 1337
if err := ticker.ProcessTicker(e.Exchange, &tick, e.Asset); err != nil {
t.Fatal("unexpected result:", err)
}
if r := e.processTicker(); !r {
t.Error("unexpected result")
}
}
func TestProcessCondition(t *testing.T) {
t.Parallel()
var e Event
tester := []struct {
Condition string
Actual float64
Threshold float64
ExpectedResult bool
}{
{ConditionGreaterThan, 1, 2, false},
{ConditionGreaterThan, 2, 1, true},
{ConditionGreaterThanOrEqual, 1, 2, false},
{ConditionGreaterThanOrEqual, 2, 1, true},
{ConditionIsEqual, 1, 1, true},
{ConditionIsEqual, 1, 2, false},
{ConditionLessThan, 1, 2, true},
{ConditionLessThan, 2, 1, false},
{ConditionLessThanOrEqual, 1, 2, true},
{ConditionLessThanOrEqual, 2, 1, false},
}
for x := range tester {
e.Condition.Condition = tester[x].Condition
if r := e.processCondition(tester[x].Actual, tester[x].Threshold); r != tester[x].ExpectedResult {
t.Error("unexpected result")
}
}
}
func TestProcessOrderbook(t *testing.T) {
if Bot == nil {
Bot = new(Engine)
}
Bot.Settings.Verbose = true
e := Event{
Exchange: testExchange,
Pair: currency.NewPair(currency.BTC, currency.USD),
Asset: assets.AssetTypeSpot,
Condition: EventConditionParams{
Condition: ConditionGreaterThan,
CheckBidsAndAsks: true,
OrderbookAmount: 100,
},
}
// this will throw an err with an unpopulated orderbook
orderbook.Orderbooks = nil
if r := e.processOrderbook(); r {
t.Error("unexpected result")
}
// now populate it with a 0 entry
o := orderbook.Base{
Pair: currency.NewPair(currency.BTC, currency.USD),
Bids: []orderbook.Item{{Amount: 24, Price: 23}},
Asks: []orderbook.Item{{Amount: 24, Price: 23}},
ExchangeName: e.Exchange,
AssetType: e.Asset,
}
if err := o.Process(); err != nil {
t.Fatal("unexpected result:", err)
}
if r := e.processOrderbook(); !r {
t.Error("unexpected result")
}
}
func TestCheckEventCondition(t *testing.T) {
t.Parallel()
if Bot == nil {
Bot = new(Engine)
}
Bot.Settings.Verbose = true
e := Event{
Item: ItemPrice,
}
if r := e.CheckEventCondition(); r {
t.Error("unexpected result")
}
e.Item = ItemOrderbook
if r := e.CheckEventCondition(); r {
t.Error("unexpected result")
}
}
func TestIsValidEvent(t *testing.T) {
if !configLoaded {
loadConfig(t)
}
// invalid exchange name
if err := IsValidEvent("meow", "", EventConditionParams{}, ""); err != errExchangeDisabled {
t.Error("unexpected result:", err)
}
// invalid item
if err := IsValidEvent(testExchange, "", EventConditionParams{}, ""); err != errInvalidItem {
t.Error("unexpected result:", err)
}
// invalid condition
if err := IsValidEvent(testExchange, ItemPrice, EventConditionParams{}, ""); err != errInvalidCondition {
t.Error("unexpected result:", err)
}
// valid condition but empty price which will still throw an errInvalidCondition
c := EventConditionParams{
Condition: ConditionGreaterThan,
}
if err := IsValidEvent(testExchange, ItemPrice, c, ""); err != errInvalidCondition {
t.Error("unexpected result:", err)
}
// valid condition but empty orderbook amount will still still throw an errInvalidCondition
if err := IsValidEvent(testExchange, ItemOrderbook, c, ""); err != errInvalidCondition {
t.Error("unexpected result:", err)
}
// test action splitting, but invalid
c.OrderbookAmount = 1337
if err := IsValidEvent(testExchange, ItemOrderbook, c, "a,meow"); err != errInvalidAction {
t.Error("unexpected result:", err)
}
// check for invalid action without splitting
if err := IsValidEvent(testExchange, ItemOrderbook, c, "hi"); err != errInvalidAction {
t.Error("unexpected result:", err)
}
// valid event
if err := IsValidEvent(testExchange, ItemOrderbook, c, "SMS,test"); err != nil {
t.Error("unexpected result:", err)
}
}
func TestIsValidExchange(t *testing.T) {
t.Parallel()
if s := IsValidExchange("invalidexchangerino"); s {
t.Error("unexpected result")
}
loadConfig(t)
if s := IsValidExchange(testExchange); !s {
t.Error("unexpected result")
}
}
func TestIsValidCondition(t *testing.T) {
t.Parallel()
if s := IsValidCondition("invalidconditionerino"); s {
t.Error("unexpected result")
}
if s := IsValidCondition(ConditionGreaterThan); !s {
t.Error("unexpected result")
}
}
func TestIsValidAction(t *testing.T) {
t.Parallel()
if s := IsValidAction("invalidactionerino"); s {
t.Error("unexpected result")
}
if s := IsValidAction(ActionSMSNotify); !s {
t.Error("unexpected result")
}
}
func TestIsValidItem(t *testing.T) {
t.Parallel()
if s := IsValidItem("invaliditemerino"); s {
t.Error("unexpected result")
}
if s := IsValidItem(ItemPrice); !s {
t.Error("unexpected result")
}
}

View File

@@ -195,6 +195,24 @@ func (s *RPCServer) GetRPCEndpoints(ctx context.Context, r *gctrpc.GetRPCEndpoin
return &resp, nil
}
// GetCommunicationRelayers returns the status of the engines communication relayers
func (s *RPCServer) GetCommunicationRelayers(ctx context.Context, r *gctrpc.GetCommunicationRelayersRequest) (*gctrpc.GetCommunicationRelayersResponse, error) {
relayers, err := Bot.CommsManager.GetStatus()
if err != nil {
return nil, err
}
var resp gctrpc.GetCommunicationRelayersResponse
resp.CommunicationRelayers = make(map[string]*gctrpc.CommunicationRelayer)
for k, v := range relayers {
resp.CommunicationRelayers[k] = &gctrpc.CommunicationRelayer{
Enabled: v.Enabled,
Connected: v.Connected,
}
}
return &resp, nil
}
// GetExchanges returns a list of exchanges
// Param is whether or not you wish to list enabled exchanges
func (s *RPCServer) GetExchanges(ctx context.Context, r *gctrpc.GetExchangesRequest) (*gctrpc.GetExchangesResponse, error) {

File diff suppressed because it is too large Load Diff

View File

@@ -89,6 +89,15 @@ func request_GoCryptoTrader_GetRPCEndpoints_0(ctx context.Context, marshaler run
}
func request_GoCryptoTrader_GetCommunicationRelayers_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GetCommunicationRelayersRequest
var metadata runtime.ServerMetadata
msg, err := client.GetCommunicationRelayers(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
var (
filter_GoCryptoTrader_GetExchanges_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
@@ -665,6 +674,26 @@ func RegisterGoCryptoTraderHandlerClient(ctx context.Context, mux *runtime.Serve
})
mux.Handle("GET", pattern_GoCryptoTrader_GetCommunicationRelayers_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_GoCryptoTrader_GetCommunicationRelayers_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_GoCryptoTrader_GetCommunicationRelayers_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_GoCryptoTrader_GetExchanges_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@@ -1279,6 +1308,8 @@ var (
pattern_GoCryptoTrader_GetRPCEndpoints_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getrpcendpoints"}, ""))
pattern_GoCryptoTrader_GetCommunicationRelayers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getcommunicationrelayers"}, ""))
pattern_GoCryptoTrader_GetExchanges_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getexchanges"}, ""))
pattern_GoCryptoTrader_DisableExchange_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "disableexchange"}, ""))
@@ -1351,6 +1382,8 @@ var (
forward_GoCryptoTrader_GetRPCEndpoints_0 = runtime.ForwardResponseMessage
forward_GoCryptoTrader_GetCommunicationRelayers_0 = runtime.ForwardResponseMessage
forward_GoCryptoTrader_GetExchanges_0 = runtime.ForwardResponseMessage
forward_GoCryptoTrader_DisableExchange_0 = runtime.ForwardResponseMessage

View File

@@ -16,9 +16,16 @@ message GetInfoResponse {
map<string, RPCEndpoint> rpc_endpoints = 7;
}
// TO-DO comms APIs
message GetCommunicationRelayersRequest {}
message GetCommunicationRelayersResponse {}
message CommunicationRelayer {
bool enabled = 1;
bool connected = 2;
}
message GetCommunicationRelayersResponse {
map<string, CommunicationRelayer> communication_relayers = 1;
}
message GenericSubsystemRequest {
string subsystem = 1;
@@ -456,6 +463,12 @@ service GoCryptoTrader {
};
}
rpc GetCommunicationRelayers (GetCommunicationRelayersRequest) returns (GetCommunicationRelayersResponse) {
option (google.api.http) = {
get: "/v1/getcommunicationrelayers"
};
}
rpc GetExchanges (GetExchangesRequest) returns (GetExchangesResponse) {
option (google.api.http) = {
get: "/v1/getexchanges"

View File

@@ -243,6 +243,22 @@
]
}
},
"/v1/getcommunicationrelayers": {
"get": {
"operationId": "GetCommunicationRelayers",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/gctrpcGetCommunicationRelayersResponse"
}
}
},
"tags": [
"GoCryptoTrader"
]
}
},
"/v1/getconfig": {
"get": {
"operationId": "GetConfig",
@@ -960,6 +976,19 @@
}
}
},
"gctrpcCommunicationRelayer": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"format": "boolean"
},
"connected": {
"type": "boolean",
"format": "boolean"
}
}
},
"gctrpcConditionParams": {
"type": "object",
"properties": {
@@ -1075,6 +1104,17 @@
}
}
},
"gctrpcGetCommunicationRelayersResponse": {
"type": "object",
"properties": {
"communication_relayers": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/gctrpcCommunicationRelayer"
}
}
}
},
"gctrpcGetConfigResponse": {
"type": "object",
"properties": {