New communications package

Support for Slack, SMSGlobal, SMTP and Telegram

Supersedes: https://github.com/thrasher-/gocryptotrader/pull/126
This commit is contained in:
Ryan O'Hara-Reid
2018-05-21 17:08:44 +10:00
committed by Adrian Gallagher
parent d34fc9aae8
commit 9d0616d8cf
26 changed files with 2748 additions and 868 deletions

166
communications/base/base.go Normal file
View File

@@ -0,0 +1,166 @@
package base
import (
"fmt"
"sync"
"time"
"github.com/thrasher-/gocryptotrader/common"
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
)
//global vars contain staged update data that will be sent to the communication
// mediums
var (
TickerStaged map[string]map[string]map[string]ticker.Price
OrderbookStaged map[string]map[string]map[string]Orderbook
PortfolioStaged Portfolio
SettingsStaged Settings
ServiceStarted time.Time
m sync.Mutex
)
// Orderbook holds the minimal orderbook details to be sent to a communication
// medium
type Orderbook struct {
CurrencyPair string
AssetType string
TotalAsks float64
TotalBids float64
LastUpdated string
}
// Ticker holds the minimal orderbook details to be sent to a communication
// medium
type Ticker struct {
CurrencyPair string
LastUpdated string
}
// Portfolio holds the minimal portfolio details to be sent to a communication
// medium
type Portfolio struct {
ProfitLoss string
}
// Settings holds the minimal setting details to be sent to a communication
// medium
type Settings struct {
EnabledExchanges string
EnabledCommunications string
}
// Base enforces standard variables across communication packages
type Base struct {
Name string
Enabled bool
Verbose bool
Connected bool
}
// Event is a generalise event type
type Event struct {
Type string
GainLoss string
TradeDetails string
}
// IsEnabled returns if the comms package has been enabled in the configuration
func (b *Base) IsEnabled() bool {
return b.Enabled
}
// IsConnected returns if the package is connected to a server and/or ready to
// send
func (b *Base) IsConnected() bool {
return b.Connected
}
// GetName returns a package name
func (b *Base) GetName() string {
return b.Name
}
// GetTicker returns staged ticker data
func (b *Base) GetTicker(exchangeName string) string {
m.Lock()
defer m.Unlock()
tickerPrice, ok := TickerStaged[exchangeName]
if !ok {
return ""
}
var tickerPrices []ticker.Price
for _, x := range tickerPrice {
for _, y := range x {
tickerPrices = append(tickerPrices, y)
}
}
var packagedTickers []string
for i := range tickerPrices {
packagedTickers = append(packagedTickers, fmt.Sprintf(
"Currency Pair: %s Ask: %f, Bid: %f High: %f Last: %f Low: %f ATH: %f Volume: %f",
tickerPrices[i].CurrencyPair,
tickerPrices[i].Ask,
tickerPrices[i].Bid,
tickerPrices[i].High,
tickerPrices[i].Last,
tickerPrices[i].Low,
tickerPrices[i].PriceATH,
tickerPrices[i].Volume))
}
return common.JoinStrings(packagedTickers, "\n")
}
// GetOrderbook returns staged orderbook data
func (b *Base) GetOrderbook(exchangeName string) string {
m.Lock()
defer m.Unlock()
orderbook, ok := OrderbookStaged[exchangeName]
if !ok {
return ""
}
var orderbooks []Orderbook
for _, x := range orderbook {
for _, y := range x {
orderbooks = append(orderbooks, y)
}
}
var packagedOrderbooks []string
for i := range orderbooks {
packagedOrderbooks = append(packagedOrderbooks, fmt.Sprintf(
"Currency Pair: %s AssetType: %s, LastUpdated: %s TotalAsks: %f TotalBids: %f",
orderbooks[i].CurrencyPair,
orderbooks[i].AssetType,
orderbooks[i].LastUpdated,
orderbooks[i].TotalAsks,
orderbooks[i].TotalBids))
}
return common.JoinStrings(packagedOrderbooks, "\n")
}
// GetPortfolio returns staged portfolio info
func (b *Base) GetPortfolio() string {
m.Lock()
defer m.Unlock()
return fmt.Sprintf("%v", PortfolioStaged)
}
// GetSettings returns stage setting info
func (b *Base) GetSettings() string {
m.Lock()
defer m.Unlock()
return fmt.Sprintf("%v", SettingsStaged)
}
// GetStatus returns status data
func (b *Base) GetStatus() string {
return `
GoCryptoTrader Service: Online
Service Started: ` + ServiceStarted.String()
}

View File

@@ -0,0 +1,108 @@
package base
import (
"log"
"time"
"github.com/thrasher-/gocryptotrader/config"
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
)
// IComm is the main interface array across the communication packages
type IComm []ICommunicate
// ICommunicate enforces standard functions across communication packages
type ICommunicate interface {
Setup(config config.CommunicationsConfig)
Connect() error
PushEvent(Event) error
IsEnabled() bool
IsConnected() bool
GetName() string
}
// Setup sets up communication variables and intiates a connection to the
// communication mediums
func (c IComm) Setup() {
TickerStaged = make(map[string]map[string]map[string]ticker.Price)
OrderbookStaged = make(map[string]map[string]map[string]Orderbook)
ServiceStarted = time.Now()
for i := range c {
if c[i].IsEnabled() && !c[i].IsConnected() {
err := c[i].Connect()
if err != nil {
log.Printf("Communications: %s failed to connect. Err: %s", c[i].GetName(), err)
}
}
}
}
// PushEvent pushes triggered events to all enabled communication links
func (c IComm) PushEvent(event Event) {
for i := range c {
if c[i].IsEnabled() && c[i].IsConnected() {
err := c[i].PushEvent(event)
if err != nil {
log.Printf("Communications error - PushEvent() in package %s with %v",
c[i].GetName(), event)
}
}
}
}
// GetEnabledCommunicationMediums prints out enabled and connected communication
// packages
func (c IComm) GetEnabledCommunicationMediums() {
var count int
for i := range c {
if c[i].IsEnabled() && c[i].IsConnected() {
log.Printf("Communications: Medium %s is enabled.", c[i].GetName())
count++
}
}
if count == 0 {
log.Println("Communications: No communication mediums are enabled.")
}
}
// StageTickerData stages updated ticker data for the communications package
func (c IComm) StageTickerData(exchangeName, assetType string, tickerPrice ticker.Price) {
m.Lock()
defer m.Unlock()
if _, ok := TickerStaged[exchangeName]; !ok {
TickerStaged[exchangeName] = make(map[string]map[string]ticker.Price)
}
if _, ok := TickerStaged[exchangeName][assetType]; !ok {
TickerStaged[exchangeName][assetType] = make(map[string]ticker.Price)
}
TickerStaged[exchangeName][assetType][tickerPrice.CurrencyPair] = tickerPrice
}
// StageOrderbookData stages updated orderbook data for the communications
// package
func (c IComm) StageOrderbookData(exchangeName, assetType string, orderbook orderbook.Base) {
m.Lock()
defer m.Unlock()
if _, ok := OrderbookStaged[exchangeName]; !ok {
OrderbookStaged[exchangeName] = make(map[string]map[string]Orderbook)
}
if _, ok := OrderbookStaged[exchangeName][assetType]; !ok {
OrderbookStaged[exchangeName][assetType] = make(map[string]Orderbook)
}
_, totalAsks := orderbook.CalculateTotalAsks()
_, totalBids := orderbook.CalculateTotalBids()
OrderbookStaged[exchangeName][assetType][orderbook.CurrencyPair] = Orderbook{
CurrencyPair: orderbook.CurrencyPair,
TotalAsks: totalAsks,
TotalBids: totalBids,
LastUpdated: orderbook.LastUpdated.String()}
}

View File

@@ -0,0 +1,84 @@
package base
import (
"testing"
)
var (
b Base
i IComm
)
func TestStart(t *testing.T) {
b = Base{
Name: "test",
Enabled: true,
Verbose: true,
Connected: true,
}
}
func TestIsEnabled(t *testing.T) {
if !b.IsEnabled() {
t.Error("test failed - base IsEnabled() error")
}
}
func TestIsConnected(t *testing.T) {
if !b.IsConnected() {
t.Error("test failed - base IsConnected() error")
}
}
func TestGetName(t *testing.T) {
if b.GetName() != "test" {
t.Error("test failed - base GetName() error")
}
}
func TestGetTicker(t *testing.T) {
v := b.GetTicker("ANX")
if v != "" {
t.Error("test failed - base GetTicker() error")
}
}
func TestGetOrderbook(t *testing.T) {
v := b.GetOrderbook("ANX")
if v != "" {
t.Error("test failed - base GetOrderbook() error")
}
}
func TestGetPortfolio(t *testing.T) {
v := b.GetPortfolio()
if v != "{}" {
t.Error("test failed - base GetPortfolio() error")
}
}
func TestGetSettings(t *testing.T) {
v := b.GetSettings()
if v != "{ }" {
t.Error("test failed - base GetSettings() error")
}
}
func TestGetStatus(t *testing.T) {
v := b.GetStatus()
if v == "" {
t.Error("test failed - base GetStatus() error")
}
}
func TestSetup(t *testing.T) {
i.Setup()
}
func TestPushEvent(t *testing.T) {
i.PushEvent(Event{})
}
func TestGetEnabledCommunicationMediums(t *testing.T) {
i.GetEnabledCommunicationMediums()
}