mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-19 07:26:49 +00:00
Merge pull request #52 from shazbert/test
Added test module for WEX echange. Added Wex to testdata.
This commit is contained in:
@@ -21,7 +21,6 @@ func TestSetDefaults(t *testing.T) {
|
||||
|
||||
func TestSetup(t *testing.T) {
|
||||
t.Parallel()
|
||||
b := BTCC{}
|
||||
b.Name = "BTCC"
|
||||
cfg := config.GetConfig()
|
||||
cfg.LoadConfig("../../testdata/configtest.dat")
|
||||
@@ -55,7 +54,7 @@ func TestGetFee(t *testing.T) {
|
||||
|
||||
func TestGetTicker(t *testing.T) {
|
||||
_, err := b.GetTicker("ltccny")
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
t.Error("Test failed - GetTicker() error", err)
|
||||
}
|
||||
}
|
||||
@@ -75,12 +74,13 @@ func TestGetTradeHistory(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetOrderBook(t *testing.T) {
|
||||
b.Verbose = true
|
||||
_, err := b.GetOrderBook("ltccny", 100)
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
t.Error("Test failed - GetOrderBook() error", err)
|
||||
}
|
||||
_, err = b.GetOrderBook("ltccny", 0)
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
t.Error("Test failed - GetOrderBook() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,7 +303,7 @@ func TestGetExchangeFormatCurrencySeperator(t *testing.T) {
|
||||
}
|
||||
|
||||
expected := true
|
||||
actual := GetExchangeFormatCurrencySeperator("BTCE")
|
||||
actual := GetExchangeFormatCurrencySeperator("WEX")
|
||||
|
||||
if expected != actual {
|
||||
t.Errorf("Test failed - TestGetExchangeFormatCurrencySeperator expected %v != actual %v",
|
||||
|
||||
@@ -7,8 +7,6 @@ import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
@@ -57,50 +55,48 @@ const (
|
||||
geminiRoleFundManager = "fundmanager"
|
||||
)
|
||||
|
||||
// SessionID map guides
|
||||
var (
|
||||
sessionAPIKey map[int]string // map[sessionID]APIKEY
|
||||
sessionAPISecret map[int]string // map[sessionID]APIKEY
|
||||
sessionRole map[string]string // map[sessionID]Roles
|
||||
sessionHeartbeat map[int]bool // map[sessionID]RequiresHeartBeat
|
||||
IsSession bool
|
||||
// Session manager
|
||||
Session map[int]*Gemini
|
||||
)
|
||||
|
||||
// Gemini is the overarching type across the Gemini package, create multiple
|
||||
// instances with differing APIkeys for segregation of roles for authenticated
|
||||
// requests & sessions by appending the session function, if sandbox test is
|
||||
// needed append the sandbox function as well.
|
||||
// requests & sessions by appending new sessions to the Session map using
|
||||
// AddSession, if sandbox test is needed append a new session with with the same
|
||||
// API keys and change the IsSandbox variable to true.
|
||||
type Gemini struct {
|
||||
exchange.Base
|
||||
M sync.Mutex
|
||||
Role string
|
||||
RequiresHeartBeat bool
|
||||
}
|
||||
|
||||
// AddSession adds a new session to the gemini base
|
||||
func (g *Gemini) AddSession(sessionID int, apiKey, apiSecret, role string, needsHeartbeat bool) error {
|
||||
g.M.Lock()
|
||||
defer g.M.Unlock()
|
||||
if sessionAPIKey == nil {
|
||||
IsSession = true
|
||||
sessionAPIKey = make(map[int]string)
|
||||
sessionAPISecret = make(map[int]string)
|
||||
sessionRole = make(map[string]string)
|
||||
sessionHeartbeat = make(map[int]bool)
|
||||
func AddSession(g *Gemini, sessionID int, apiKey, apiSecret, role string, needsHeartbeat, isSandbox bool) error {
|
||||
if Session == nil {
|
||||
Session = make(map[int]*Gemini)
|
||||
}
|
||||
_, ok := sessionAPIKey[sessionID]
|
||||
|
||||
_, ok := Session[sessionID]
|
||||
if ok {
|
||||
return errors.New("sessionID already being used")
|
||||
}
|
||||
|
||||
sessionAPIKey[sessionID] = apiKey
|
||||
sessionAPISecret[sessionID] = apiSecret
|
||||
sessionRole[apiKey] = role
|
||||
sessionHeartbeat[sessionID] = needsHeartbeat
|
||||
g.APIKey = apiKey
|
||||
g.APISecret = apiSecret
|
||||
g.Role = role
|
||||
g.RequiresHeartBeat = needsHeartbeat
|
||||
g.APIUrl = geminiAPIURL
|
||||
|
||||
if isSandbox {
|
||||
g.APIUrl = geminiSandboxAPIURL
|
||||
}
|
||||
|
||||
Session[sessionID] = g
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//return session function?
|
||||
|
||||
// SetDefaults sets package defaults for gemini exchange
|
||||
func (g *Gemini) SetDefaults() {
|
||||
g.Name = "Gemini"
|
||||
@@ -140,27 +136,6 @@ func (g *Gemini) Setup(exch config.ExchangeConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
// Session is a session manager for differing APIKeys and roles, use this for all function
|
||||
// calls in this package
|
||||
func (g *Gemini) Session(sessionID int) *Gemini {
|
||||
g.M.Lock()
|
||||
defer g.M.Unlock()
|
||||
g.APIUrl = geminiAPIURL
|
||||
_, ok := sessionAPIKey[sessionID]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
g.APIKey = sessionAPIKey[sessionID]
|
||||
g.APISecret = sessionAPISecret[sessionID]
|
||||
return g
|
||||
}
|
||||
|
||||
// Sandbox diverts the apiURL to the sandbox API for testing purposes
|
||||
func (g *Gemini) Sandbox() *Gemini {
|
||||
g.APIUrl = geminiSandboxAPIURL
|
||||
return g
|
||||
}
|
||||
|
||||
// GetSymbols returns all available symbols for trading
|
||||
func (g *Gemini) GetSymbols() ([]string, error) {
|
||||
symbols := []string{}
|
||||
@@ -256,10 +231,7 @@ func (g *Gemini) GetAuctionHistory(currencyPair string, params url.Values) ([]Au
|
||||
}
|
||||
|
||||
func (g *Gemini) isCorrectSession(role string) error {
|
||||
if !IsSession {
|
||||
return errors.New("session not set")
|
||||
}
|
||||
if sessionRole[g.APIKey] != role {
|
||||
if g.Role != role {
|
||||
return errors.New("incorrect role for APIKEY cannot use this function")
|
||||
}
|
||||
return nil
|
||||
@@ -405,16 +377,10 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[st
|
||||
return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, g.Name)
|
||||
}
|
||||
|
||||
if g.Nonce.Get() == 0 {
|
||||
g.Nonce.Set(time.Now().UnixNano())
|
||||
} else {
|
||||
g.Nonce.Inc()
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
request := make(map[string]interface{})
|
||||
request["request"] = fmt.Sprintf("/v%s/%s", geminiAPIVersion, path)
|
||||
request["nonce"] = g.Nonce.Get()
|
||||
request["nonce"] = g.Nonce.GetValue(g.Name, false)
|
||||
|
||||
if params != nil {
|
||||
for key, value := range params {
|
||||
@@ -456,10 +422,5 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[st
|
||||
}
|
||||
}
|
||||
|
||||
err = common.JSONDecode([]byte(resp), &result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return common.JSONDecode([]byte(resp), &result)
|
||||
}
|
||||
|
||||
@@ -26,22 +26,23 @@ const (
|
||||
)
|
||||
|
||||
func TestAddSession(t *testing.T) {
|
||||
err := g.AddSession(1, apiKey1, apiSecret1, apiKeyRole1, true)
|
||||
err := AddSession(&g, 1, apiKey1, apiSecret1, apiKeyRole1, true, false)
|
||||
if err != nil {
|
||||
t.Error("Test failed - AddSession() error")
|
||||
}
|
||||
err = g.AddSession(1, apiKey1, apiSecret1, apiKeyRole1, true)
|
||||
err = AddSession(&g, 1, apiKey1, apiSecret1, apiKeyRole1, true, false)
|
||||
if err == nil {
|
||||
t.Error("Test failed - AddSession() error")
|
||||
}
|
||||
err = g.AddSession(2, apiKey2, apiSecret2, apiKeyRole2, false)
|
||||
err = AddSession(&g, 2, apiKey2, apiSecret2, apiKeyRole2, false, true)
|
||||
if err != nil {
|
||||
t.Error("Test failed - AddSession() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetDefaults(t *testing.T) {
|
||||
g.SetDefaults()
|
||||
Session[1].SetDefaults()
|
||||
Session[2].SetDefaults()
|
||||
}
|
||||
|
||||
func TestSetup(t *testing.T) {
|
||||
@@ -54,30 +55,21 @@ func TestSetup(t *testing.T) {
|
||||
|
||||
geminiConfig.AuthenticatedAPISupport = true
|
||||
|
||||
g.Setup(geminiConfig)
|
||||
Session[1].Setup(geminiConfig)
|
||||
Session[2].Setup(geminiConfig)
|
||||
}
|
||||
|
||||
func TestSession(t *testing.T) {
|
||||
t.Parallel()
|
||||
if g.Session(1) == nil {
|
||||
t.Error("Test Failed - Session() error")
|
||||
}
|
||||
if g.Session(1337) != nil {
|
||||
t.Error("Test Failed - Session() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSandbox(t *testing.T) {
|
||||
t.Parallel()
|
||||
g.APIUrl = geminiAPIURL
|
||||
if g.Sandbox().APIUrl != geminiSandboxAPIURL {
|
||||
t.Error("Test Failed - Sandbox() error")
|
||||
}
|
||||
}
|
||||
// func TestSandbox(t *testing.T) {
|
||||
// t.Parallel()
|
||||
// g.Sandbox(1)
|
||||
// if Management[1].URL != geminiSandboxAPIURL {
|
||||
// t.Error("Test Failed - Sandbox() error")
|
||||
// }
|
||||
// }
|
||||
|
||||
func TestGetSymbols(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := g.GetSymbols()
|
||||
_, err := Session[1].GetSymbols()
|
||||
if err != nil {
|
||||
t.Error("Test Failed - GetSymbols() error", err)
|
||||
}
|
||||
@@ -85,11 +77,11 @@ func TestGetSymbols(t *testing.T) {
|
||||
|
||||
func TestGetTicker(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := g.GetTicker("BTCUSD")
|
||||
_, err := Session[2].GetTicker("BTCUSD")
|
||||
if err != nil {
|
||||
t.Error("Test Failed - GetTicker() error", err)
|
||||
}
|
||||
_, err = g.GetTicker("bla")
|
||||
_, err = Session[1].GetTicker("bla")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetTicker() error", err)
|
||||
}
|
||||
@@ -97,7 +89,7 @@ func TestGetTicker(t *testing.T) {
|
||||
|
||||
func TestGetOrderbook(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := g.GetOrderbook("btcusd", url.Values{})
|
||||
_, err := Session[1].GetOrderbook("btcusd", url.Values{})
|
||||
if err != nil {
|
||||
t.Error("Test Failed - GetOrderbook() error", err)
|
||||
}
|
||||
@@ -105,7 +97,7 @@ func TestGetOrderbook(t *testing.T) {
|
||||
|
||||
func TestGetTrades(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := g.GetTrades("btcusd", url.Values{})
|
||||
_, err := Session[2].GetTrades("btcusd", url.Values{})
|
||||
if err != nil {
|
||||
t.Error("Test Failed - GetTrades() error", err)
|
||||
}
|
||||
@@ -113,7 +105,7 @@ func TestGetTrades(t *testing.T) {
|
||||
|
||||
func TestGetAuction(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := g.GetAuction("btcusd")
|
||||
_, err := Session[1].GetAuction("btcusd")
|
||||
if err != nil {
|
||||
t.Error("Test Failed - GetAuction() error", err)
|
||||
}
|
||||
@@ -121,7 +113,7 @@ func TestGetAuction(t *testing.T) {
|
||||
|
||||
func TestGetAuctionHistory(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := g.GetAuctionHistory("btcusd", url.Values{})
|
||||
_, err := Session[2].GetAuctionHistory("btcusd", url.Values{})
|
||||
if err != nil {
|
||||
t.Error("Test Failed - GetAuctionHistory() error", err)
|
||||
}
|
||||
@@ -129,11 +121,11 @@ func TestGetAuctionHistory(t *testing.T) {
|
||||
|
||||
func TestNewOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := g.Session(1).Sandbox().NewOrder("btcusd", 1, 4500, "buy", "exchange limit")
|
||||
_, err := Session[1].NewOrder("btcusd", 1, 4500, "buy", "exchange limit")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - NewOrder() error", err)
|
||||
}
|
||||
_, err = g.Session(2).Sandbox().NewOrder("btcusd", 1, 4500, "buy", "exchange limit")
|
||||
_, err = Session[2].NewOrder("btcusd", 1, 4500, "buy", "exchange limit")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - NewOrder() error", err)
|
||||
}
|
||||
@@ -141,7 +133,7 @@ func TestNewOrder(t *testing.T) {
|
||||
|
||||
func TestCancelOrder(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := g.Session(1).Sandbox().CancelOrder(1337)
|
||||
_, err := Session[1].CancelOrder(1337)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - CancelOrder() error", err)
|
||||
}
|
||||
@@ -149,11 +141,11 @@ func TestCancelOrder(t *testing.T) {
|
||||
|
||||
func TestCancelOrders(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := g.Session(1).Sandbox().CancelOrders(false)
|
||||
_, err := Session[1].CancelOrders(false)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - CancelOrders() error", err)
|
||||
}
|
||||
_, err = g.Session(2).Sandbox().CancelOrders(true)
|
||||
_, err = Session[2].CancelOrders(true)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - CancelOrders() error", err)
|
||||
}
|
||||
@@ -161,7 +153,7 @@ func TestCancelOrders(t *testing.T) {
|
||||
|
||||
func TestGetOrderStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := g.Session(1).Sandbox().GetOrderStatus(1337)
|
||||
_, err := Session[2].GetOrderStatus(1337)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetOrderStatus() error", err)
|
||||
}
|
||||
@@ -169,7 +161,7 @@ func TestGetOrderStatus(t *testing.T) {
|
||||
|
||||
func TestGetOrders(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := g.Session(1).Sandbox().GetOrders()
|
||||
_, err := Session[1].GetOrders()
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetOrders() error", err)
|
||||
}
|
||||
@@ -177,7 +169,7 @@ func TestGetOrders(t *testing.T) {
|
||||
|
||||
func TestGetTradeHistory(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := g.Session(1).Sandbox().GetTradeHistory("btcusd", 0)
|
||||
_, err := Session[1].GetTradeHistory("btcusd", 0)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetTradeHistory() error", err)
|
||||
}
|
||||
@@ -185,7 +177,7 @@ func TestGetTradeHistory(t *testing.T) {
|
||||
|
||||
func TestGetTradeVolume(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := g.Session(1).Sandbox().GetTradeVolume()
|
||||
_, err := Session[2].GetTradeVolume()
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetTradeVolume() error", err)
|
||||
}
|
||||
@@ -193,7 +185,7 @@ func TestGetTradeVolume(t *testing.T) {
|
||||
|
||||
func TestGetBalances(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := g.Session(1).Sandbox().GetBalances()
|
||||
_, err := Session[1].GetBalances()
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetBalances() error", err)
|
||||
}
|
||||
@@ -201,7 +193,7 @@ func TestGetBalances(t *testing.T) {
|
||||
|
||||
func TestGetDepositAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := g.Session(1).Sandbox().GetDepositAddress("LOL123", "btc")
|
||||
_, err := Session[1].GetDepositAddress("LOL123", "btc")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetDepositAddress() error", err)
|
||||
}
|
||||
@@ -209,7 +201,7 @@ func TestGetDepositAddress(t *testing.T) {
|
||||
|
||||
func TestWithdrawCrypto(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := g.Session(1).Sandbox().WithdrawCrypto("LOL123", "btc", 1)
|
||||
_, err := Session[1].WithdrawCrypto("LOL123", "btc", 1)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - WithdrawCrypto() error", err)
|
||||
}
|
||||
@@ -217,7 +209,7 @@ func TestWithdrawCrypto(t *testing.T) {
|
||||
|
||||
func TestPostHeartbeat(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := g.Session(1).Sandbox().PostHeartbeat()
|
||||
_, err := Session[2].PostHeartbeat()
|
||||
if err == nil {
|
||||
t.Error("Test Failed - PostHeartbeat() error", err)
|
||||
}
|
||||
|
||||
@@ -92,15 +92,10 @@ func (w *WEX) GetFee() float64 {
|
||||
|
||||
// GetInfo returns the WEX info
|
||||
func (w *WEX) GetInfo() (Info, error) {
|
||||
req := fmt.Sprintf("%s/%s/%s/", wexAPIPublicURL, wexAPIPublicVersion, wexInfo)
|
||||
resp := Info{}
|
||||
err := common.SendHTTPGetRequest(req, true, w.Verbose, &resp)
|
||||
req := fmt.Sprintf("%s/%s/%s/", wexAPIPublicURL, wexAPIPublicVersion, wexInfo)
|
||||
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
return resp, common.SendHTTPGetRequest(req, true, w.Verbose, &resp)
|
||||
}
|
||||
|
||||
// GetTicker returns a ticker for a specific currency
|
||||
@@ -111,12 +106,8 @@ func (w *WEX) GetTicker(symbol string) (map[string]Ticker, error) {
|
||||
|
||||
response := Response{}
|
||||
req := fmt.Sprintf("%s/%s/%s/%s", wexAPIPublicURL, wexAPIPublicVersion, wexTicker, symbol)
|
||||
err := common.SendHTTPGetRequest(req, true, w.Verbose, &response.Data)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response.Data, nil
|
||||
return response.Data, common.SendHTTPGetRequest(req, true, w.Verbose, &response.Data)
|
||||
}
|
||||
|
||||
// GetDepth returns the depth for a specific currency
|
||||
@@ -128,13 +119,8 @@ func (w *WEX) GetDepth(symbol string) (Orderbook, error) {
|
||||
response := Response{}
|
||||
req := fmt.Sprintf("%s/%s/%s/%s", wexAPIPublicURL, wexAPIPublicVersion, wexDepth, symbol)
|
||||
|
||||
err := common.SendHTTPGetRequest(req, true, w.Verbose, &response.Data)
|
||||
if err != nil {
|
||||
return Orderbook{}, err
|
||||
}
|
||||
|
||||
depth := response.Data[symbol]
|
||||
return depth, nil
|
||||
return response.Data[symbol],
|
||||
common.SendHTTPGetRequest(req, true, w.Verbose, &response.Data)
|
||||
}
|
||||
|
||||
// GetTrades returns the trades for a specific currency
|
||||
@@ -146,25 +132,16 @@ func (w *WEX) GetTrades(symbol string) ([]Trades, error) {
|
||||
response := Response{}
|
||||
req := fmt.Sprintf("%s/%s/%s/%s", wexAPIPublicURL, wexAPIPublicVersion, wexTrades, symbol)
|
||||
|
||||
err := common.SendHTTPGetRequest(req, true, w.Verbose, &response.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
trades := response.Data[symbol]
|
||||
return trades, nil
|
||||
return response.Data[symbol],
|
||||
common.SendHTTPGetRequest(req, true, w.Verbose, &response.Data)
|
||||
}
|
||||
|
||||
// GetAccountInfo returns a users account info
|
||||
func (w *WEX) GetAccountInfo() (AccountInfo, error) {
|
||||
var result AccountInfo
|
||||
err := w.SendAuthenticatedHTTPRequest(wexAccountInfo, url.Values{}, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
return result,
|
||||
w.SendAuthenticatedHTTPRequest(wexAccountInfo, url.Values{}, &result)
|
||||
}
|
||||
|
||||
// GetActiveOrders returns the active orders for a specific currency
|
||||
@@ -173,13 +150,8 @@ func (w *WEX) GetActiveOrders(pair string) (map[string]ActiveOrders, error) {
|
||||
req.Add("pair", pair)
|
||||
|
||||
var result map[string]ActiveOrders
|
||||
err := w.SendAuthenticatedHTTPRequest(wexActiveOrders, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
return result, w.SendAuthenticatedHTTPRequest(wexActiveOrders, req, &result)
|
||||
}
|
||||
|
||||
// GetOrderInfo returns the order info for a specific order ID
|
||||
@@ -188,13 +160,8 @@ func (w *WEX) GetOrderInfo(OrderID int64) (map[string]OrderInfo, error) {
|
||||
req.Add("order_id", strconv.FormatInt(OrderID, 10))
|
||||
|
||||
var result map[string]OrderInfo
|
||||
err := w.SendAuthenticatedHTTPRequest(wexOrderInfo, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
return result, w.SendAuthenticatedHTTPRequest(wexOrderInfo, req, &result)
|
||||
}
|
||||
|
||||
// CancelOrder cancels an order for a specific order ID
|
||||
@@ -221,13 +188,9 @@ func (w *WEX) Trade(pair, orderType string, amount, price float64) (int64, error
|
||||
req.Add("rate", strconv.FormatFloat(price, 'f', -1, 64))
|
||||
|
||||
var result Trade
|
||||
err := w.SendAuthenticatedHTTPRequest(wexTrade, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int64(result.OrderID), nil
|
||||
return int64(result.OrderID),
|
||||
w.SendAuthenticatedHTTPRequest(wexTrade, req, &result)
|
||||
}
|
||||
|
||||
// GetTransactionHistory returns the transaction history
|
||||
@@ -242,13 +205,9 @@ func (w *WEX) GetTransactionHistory(TIDFrom, Count, TIDEnd int64, order, since,
|
||||
req.Add("end", end)
|
||||
|
||||
var result map[string]TransHistory
|
||||
err := w.SendAuthenticatedHTTPRequest(wexTransactionHistory, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
return result,
|
||||
w.SendAuthenticatedHTTPRequest(wexTransactionHistory, req, &result)
|
||||
}
|
||||
|
||||
// GetTradeHistory returns the trade history
|
||||
@@ -264,13 +223,8 @@ func (w *WEX) GetTradeHistory(TIDFrom, Count, TIDEnd int64, order, since, end, p
|
||||
req.Add("pair", pair)
|
||||
|
||||
var result map[string]TradeHistory
|
||||
err := w.SendAuthenticatedHTTPRequest(wexTradeHistory, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
return result, w.SendAuthenticatedHTTPRequest(wexTradeHistory, req, &result)
|
||||
}
|
||||
|
||||
// WithdrawCoins withdraws coins for a specific coin
|
||||
@@ -281,12 +235,8 @@ func (w *WEX) WithdrawCoins(coin string, amount float64, address string) (Withdr
|
||||
req.Add("address", address)
|
||||
|
||||
var result WithdrawCoins
|
||||
err := w.SendAuthenticatedHTTPRequest(wexWithdrawCoin, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
return result, nil
|
||||
return result, w.SendAuthenticatedHTTPRequest(wexWithdrawCoin, req, &result)
|
||||
}
|
||||
|
||||
// CoinDepositAddress returns the deposit address for a specific currency
|
||||
@@ -295,13 +245,9 @@ func (w *WEX) CoinDepositAddress(coin string) (string, error) {
|
||||
req.Add("coinName", coin)
|
||||
|
||||
var result CoinDepositAddress
|
||||
err := w.SendAuthenticatedHTTPRequest(wexCoinDepositAddress, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return result.Address, nil
|
||||
return result.Address,
|
||||
w.SendAuthenticatedHTTPRequest(wexCoinDepositAddress, req, &result)
|
||||
}
|
||||
|
||||
// CreateCoupon creates an exchange coupon for a sepcific currency
|
||||
@@ -311,13 +257,8 @@ func (w *WEX) CreateCoupon(currency string, amount float64) (CreateCoupon, error
|
||||
req.Add("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
|
||||
var result CreateCoupon
|
||||
err := w.SendAuthenticatedHTTPRequest(wexCreateCoupon, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
return result, w.SendAuthenticatedHTTPRequest(wexCreateCoupon, req, &result)
|
||||
}
|
||||
|
||||
// RedeemCoupon redeems an exchange coupon
|
||||
@@ -326,13 +267,8 @@ func (w *WEX) RedeemCoupon(coupon string) (RedeemCoupon, error) {
|
||||
req.Add("coupon", coupon)
|
||||
|
||||
var result RedeemCoupon
|
||||
err := w.SendAuthenticatedHTTPRequest(wexRedeemCoupon, req, &result)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
return result, w.SendAuthenticatedHTTPRequest(wexRedeemCoupon, req, &result)
|
||||
}
|
||||
|
||||
// SendAuthenticatedHTTPRequest sends an authenticated HTTP request to WEX
|
||||
@@ -362,14 +298,12 @@ func (w *WEX) SendAuthenticatedHTTPRequest(method string, values url.Values, res
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
resp, err := common.SendHTTPRequest("POST", wexAPIPrivateURL, headers, strings.NewReader(encoded))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
response := Response{}
|
||||
err = common.JSONDecode([]byte(resp), &response)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -379,15 +313,9 @@ func (w *WEX) SendAuthenticatedHTTPRequest(method string, values url.Values, res
|
||||
}
|
||||
|
||||
JSONEncoded, err := common.JSONEncode(response.Return)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = common.JSONDecode(JSONEncoded, &result)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return common.JSONDecode(JSONEncoded, &result)
|
||||
}
|
||||
|
||||
144
exchanges/wex/wex_test.go
Normal file
144
exchanges/wex/wex_test.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package wex
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
)
|
||||
|
||||
var w WEX
|
||||
|
||||
// Please supply your own keys for better unit testing
|
||||
const (
|
||||
apiKey = ""
|
||||
apiSecret = ""
|
||||
)
|
||||
|
||||
func TestSetDefaults(t *testing.T) {
|
||||
w.SetDefaults()
|
||||
}
|
||||
|
||||
func TestSetup(t *testing.T) {
|
||||
wexConfig := config.GetConfig()
|
||||
wexConfig.LoadConfig("../../testdata/configtest.dat")
|
||||
conf, err := wexConfig.GetExchangeConfig("WEX")
|
||||
if err != nil {
|
||||
t.Error("Test Failed - WEX init error")
|
||||
}
|
||||
conf.APIKey = apiKey
|
||||
conf.APISecret = apiSecret
|
||||
conf.AuthenticatedAPISupport = true
|
||||
|
||||
w.Setup(conf)
|
||||
}
|
||||
|
||||
func TestGetFee(t *testing.T) {
|
||||
if w.GetFee() != 0.2 {
|
||||
t.Error("Test Failed - GetFee() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetInfo(t *testing.T) {
|
||||
_, err := w.GetInfo()
|
||||
if err != nil {
|
||||
t.Error("Test Failed - GetInfo() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTicker(t *testing.T) {
|
||||
_, err := w.GetTicker("btc_usd")
|
||||
if err != nil {
|
||||
t.Error("Test Failed - GetTicker() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDepth(t *testing.T) {
|
||||
_, err := w.GetDepth("btc_usd")
|
||||
if err != nil {
|
||||
t.Error("Test Failed - GetDepth() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTrades(t *testing.T) {
|
||||
_, err := w.GetTrades("btc_usd")
|
||||
if err != nil {
|
||||
t.Error("Test Failed - GetTrades() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAccountInfo(t *testing.T) {
|
||||
_, err := w.GetAccountInfo()
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetAccountInfo() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetActiveOrders(t *testing.T) {
|
||||
_, err := w.GetActiveOrders("")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetActiveOrders() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderInfo(t *testing.T) {
|
||||
_, err := w.GetOrderInfo(6196974)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetOrderInfo() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelOrder(t *testing.T) {
|
||||
_, err := w.CancelOrder(1337)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - CancelOrder() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrade(t *testing.T) {
|
||||
_, err := w.Trade("", "buy", 0, 0)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - Trade() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTransactionHistory(t *testing.T) {
|
||||
_, err := w.GetTransactionHistory(0, 0, 0, "", "", "")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetTransactionHistory() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTradeHistory(t *testing.T) {
|
||||
_, err := w.GetTradeHistory(0, 0, 0, "", "", "", "")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - GetTradeHistory() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithdrawCoins(t *testing.T) {
|
||||
_, err := w.WithdrawCoins("", 0, "")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - WithdrawCoins() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoinDepositAddress(t *testing.T) {
|
||||
_, err := w.CoinDepositAddress("btc")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - WithdrawCoins() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateCoupon(t *testing.T) {
|
||||
_, err := w.CreateCoupon("bla", 0)
|
||||
if err == nil {
|
||||
t.Error("Test Failed - CreateCoupon() error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRedeemCoupon(t *testing.T) {
|
||||
_, err := w.RedeemCoupon("bla")
|
||||
if err == nil {
|
||||
t.Error("Test Failed - RedeemCoupon() error", err)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,18 @@
|
||||
package wex
|
||||
|
||||
// Response is a generic struct used for exchange API request result
|
||||
type Response struct {
|
||||
Return interface{} `json:"return"`
|
||||
Success int `json:"success"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// Info holds server time and pair information
|
||||
type Info struct {
|
||||
ServerTime int64 `json:"server_time"`
|
||||
Pairs map[string]Pair `json:"pairs"`
|
||||
}
|
||||
|
||||
// Ticker stores the ticker information
|
||||
type Ticker struct {
|
||||
High float64
|
||||
@@ -28,11 +41,14 @@ type Trades struct {
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
// Response is a generic struct used for exchange API request result
|
||||
type Response struct {
|
||||
Return interface{} `json:"return"`
|
||||
Success int `json:"success"`
|
||||
Error string `json:"error"`
|
||||
// ActiveOrders stores active order information
|
||||
type ActiveOrders struct {
|
||||
Pair string `json:"pair"`
|
||||
Type string `json:"sell"`
|
||||
Amount float64 `json:"amount"`
|
||||
Rate float64 `json:"rate"`
|
||||
TimestampCreated float64 `json:"time_created"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
// Pair holds pair information
|
||||
@@ -45,12 +61,6 @@ type Pair struct {
|
||||
Fee float64 `json:"fee"`
|
||||
}
|
||||
|
||||
// Info holds server time and pair information
|
||||
type Info struct {
|
||||
ServerTime int64 `json:"server_time"`
|
||||
Pairs map[string]Pair `json:"pairs"`
|
||||
}
|
||||
|
||||
// AccountInfo stores the account information for a user
|
||||
type AccountInfo struct {
|
||||
Funds map[string]float64 `json:"funds"`
|
||||
@@ -64,16 +74,6 @@ type AccountInfo struct {
|
||||
TransactionCount int `json:"transaction_count"`
|
||||
}
|
||||
|
||||
// ActiveOrders stores active order information
|
||||
type ActiveOrders struct {
|
||||
Pair string `json:"pair"`
|
||||
Type string `json:"sell"`
|
||||
Amount float64 `json:"amount"`
|
||||
Rate float64 `json:"rate"`
|
||||
TimestampCreated float64 `json:"time_created"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
// OrderInfo stores order information
|
||||
type OrderInfo struct {
|
||||
Pair string `json:"pair"`
|
||||
|
||||
4
testdata/configtest.dat
vendored
4
testdata/configtest.dat
vendored
@@ -163,7 +163,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "BTCE",
|
||||
"Name": "WEX",
|
||||
"Enabled": true,
|
||||
"Verbose": false,
|
||||
"Websocket": false,
|
||||
@@ -436,4 +436,4 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
59
web/.angular-cli.json
Normal file
59
web/.angular-cli.json
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"project": {
|
||||
"name": "angular-electron"
|
||||
},
|
||||
"apps": [
|
||||
{
|
||||
"root": "src",
|
||||
"outDir": "dist",
|
||||
"assets": [
|
||||
"assets",
|
||||
"favicon.ico"
|
||||
],
|
||||
"index": "index.html",
|
||||
"main": "main.ts",
|
||||
"polyfills": "polyfills.ts",
|
||||
"test": "test.ts",
|
||||
"tsconfig": "tsconfig.app.json",
|
||||
"testTsconfig": "tsconfig.spec.json",
|
||||
"prefix": "app",
|
||||
"styles": [
|
||||
"styles.scss"
|
||||
],
|
||||
"scripts": [
|
||||
],
|
||||
"environmentSource": "environments/environment.ts",
|
||||
"environments": {
|
||||
"dev": "environments/environment.ts",
|
||||
"prod": "environments/environment.prod.ts"
|
||||
}
|
||||
}
|
||||
],
|
||||
"e2e": {
|
||||
"protractor": {
|
||||
"config": "./protractor.conf.js"
|
||||
}
|
||||
},
|
||||
"lint": [
|
||||
{
|
||||
"project": "src/tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"project": "src/tsconfig.spec.json"
|
||||
},
|
||||
{
|
||||
"project": "e2e/tsconfig.e2e.json"
|
||||
}
|
||||
],
|
||||
"test": {
|
||||
"karma": {
|
||||
"config": "./karma.conf.js"
|
||||
}
|
||||
},
|
||||
"defaults": {
|
||||
"styleExt": "scss",
|
||||
"component": {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"directory": "app/bower_components"
|
||||
}
|
||||
13
web/.editorconfig
Normal file
13
web/.editorconfig
Normal file
@@ -0,0 +1,13 @@
|
||||
# Editor configuration, see http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
48
web/.gitignore
vendored
48
web/.gitignore
vendored
@@ -1,7 +1,43 @@
|
||||
logs/*
|
||||
!.gitkeep
|
||||
node_modules/
|
||||
bower_components/
|
||||
tmp
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
/app-builds
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# e2e
|
||||
/e2e/*.js
|
||||
/e2e/*.map
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
.idea
|
||||
Thumbs.db
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"strict": "global",
|
||||
"globals": {
|
||||
// Angular
|
||||
"angular": false,
|
||||
|
||||
// Angular mocks
|
||||
"module": false,
|
||||
"inject": false,
|
||||
|
||||
// Jasmine
|
||||
"jasmine": false,
|
||||
"describe": false,
|
||||
"beforeEach": false,
|
||||
"afterEach": false,
|
||||
"it": false,
|
||||
"expect": false,
|
||||
|
||||
// Protractor
|
||||
"browser": false,
|
||||
"element": false,
|
||||
"by": false
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,8 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- '4.4'
|
||||
|
||||
before_script:
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- npm start > /dev/null &
|
||||
- npm run update-webdriver
|
||||
- sleep 1 # give server time to start
|
||||
|
||||
- "7"
|
||||
- "6"
|
||||
install:
|
||||
- npm install
|
||||
script:
|
||||
- node_modules/.bin/karma start karma.conf.js --no-auto-watch --single-run --reporters=dots --browsers=Firefox
|
||||
- node_modules/.bin/protractor e2e-tests/protractor.conf.js --browser=firefox
|
||||
- npm run build
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
FROM node:onbuild
|
||||
22
web/LICENSE
22
web/LICENSE
@@ -1,22 +0,0 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
194
web/LICENSE.md
Normal file
194
web/LICENSE.md
Normal file
@@ -0,0 +1,194 @@
|
||||
Apache License
|
||||
==============
|
||||
|
||||
_Version 2.0, January 2004_
|
||||
_<<http://www.apache.org/licenses/>>_
|
||||
|
||||
### Terms and Conditions for use, reproduction, and distribution
|
||||
|
||||
#### 1. Definitions
|
||||
|
||||
“License” shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
“Licensor” shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
“Legal Entity” shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, “control” means **(i)** the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or **(iii)** beneficial ownership of such entity.
|
||||
|
||||
“You” (or “Your”) shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
“Source” form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
“Object” form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
“Work” shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
“Derivative Works” shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
“Contribution” shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
“submitted” means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as “Not a Contribution.”
|
||||
|
||||
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
#### 2. Grant of Copyright License
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||
Derivative Works in Source or Object form.
|
||||
|
||||
#### 3. Grant of Patent License
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable (except as stated in this section) patent license to make, have
|
||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||
such license applies only to those patent claims licensable by such Contributor
|
||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||
submitted. If You institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||
Contribution incorporated within the Work constitutes direct or contributory
|
||||
patent infringement, then any patent licenses granted to You under this License
|
||||
for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
#### 4. Redistribution
|
||||
|
||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||
in any medium, with or without modifications, and in Source or Object form,
|
||||
provided that You meet the following conditions:
|
||||
|
||||
* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
* **(b)** You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
|
||||
all copyright, patent, trademark, and attribution notices from the Source form
|
||||
of the Work, excluding those notices that do not pertain to any part of the
|
||||
Derivative Works; and
|
||||
* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
|
||||
Derivative Works that You distribute must include a readable copy of the
|
||||
attribution notices contained within such NOTICE file, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||
following places: within a NOTICE text file distributed as part of the
|
||||
Derivative Works; within the Source form or documentation, if provided along
|
||||
with the Derivative Works; or, within a display generated by the Derivative
|
||||
Works, if and wherever such third-party notices normally appear. The contents of
|
||||
the NOTICE file are for informational purposes only and do not modify the
|
||||
License. You may add Your own attribution notices within Derivative Works that
|
||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||
provided that such additional attribution notices cannot be construed as
|
||||
modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License.
|
||||
|
||||
#### 5. Submission of Contributions
|
||||
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||
conditions of this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||
any separate license agreement you may have executed with Licensor regarding
|
||||
such Contributions.
|
||||
|
||||
#### 6. Trademarks
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks,
|
||||
service marks, or product names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
#### 7. Disclaimer of Warranty
|
||||
|
||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||
Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||
solely responsible for determining the appropriateness of using or
|
||||
redistributing the Work and assume any risks associated with Your exercise of
|
||||
permissions under this License.
|
||||
|
||||
#### 8. Limitation of Liability
|
||||
|
||||
In no event and under no legal theory, whether in tort (including negligence),
|
||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special, incidental,
|
||||
or consequential damages of any character arising as a result of this License or
|
||||
out of the use or inability to use the Work (including but not limited to
|
||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||
any and all other commercial damages or losses), even if such Contributor has
|
||||
been advised of the possibility of such damages.
|
||||
|
||||
#### 9. Accepting Warranty or Additional Liability
|
||||
|
||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||
other liability obligations and/or rights consistent with this License. However,
|
||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason of your
|
||||
accepting any such warranty or additional liability.
|
||||
|
||||
_END OF TERMS AND CONDITIONS_
|
||||
|
||||
### APPENDIX: How to apply the Apache License to your work
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate
|
||||
notice, with the fields enclosed by brackets `[]` replaced with your own
|
||||
identifying information. (Don't include the brackets!) The text should be
|
||||
enclosed in the appropriate comment syntax for the file format. We also
|
||||
recommend that a file or class name and description of purpose be included on
|
||||
the same “printed page” as the copyright notice for easier identification within
|
||||
third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,34 +1,70 @@
|
||||
## GoCryptoTrader website
|
||||
A website interface to interact with the main gocryptotrader application
|
||||
A website interface to interact with the main GoCryptoTrader application. It is developed with Angular 4 with support for Electron
|
||||
|
||||
## Current Features
|
||||
+ Basic web views
|
||||
+ Interaction between gocryptotrader and gocryptotraderweb
|
||||
|
||||
## Planned Features
|
||||
## This is still in active development
|
||||
You can track ideas, planned features and what's in progresss on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader).
|
||||
|
||||
### Prerequisites
|
||||
## Current Features
|
||||
+ It can run
|
||||
+ It can be compiled with Electron to run as an executable
|
||||
+ Websocket support to listen to GoCryptoTrader events
|
||||
+ Material design
|
||||
+ Has a semi-working Settings page
|
||||
+ Has a basic ticker dashboard
|
||||
|
||||
You can get git from [http://git-scm.com/](http://git-scm.com/).
|
||||
|
||||
You must have node.js and its package manager (npm) installed. You can get them from [http://nodejs.org/](http://nodejs.org/).
|
||||
### Install Dependencies
|
||||
|
||||
## Install dependencies with npm :
|
||||
|
||||
```
|
||||
``` bash
|
||||
npm install
|
||||
```
|
||||
|
||||
If you want to generate Angular components with Angular-cli , you **MUST** install `@angular/cli` in npm global context.
|
||||
Please follow [Angular-cli documentation](https://github.com/angular/angular-cli) if you had installed a previous version of `angular-cli`.
|
||||
|
||||
### Run the Application
|
||||
|
||||
The simplest way to start this server is:
|
||||
|
||||
```
|
||||
npm start
|
||||
``` bash
|
||||
npm install -g @angular/cli
|
||||
```
|
||||
|
||||
Now browse to the app at `http://localhost/`.
|
||||
## To build for development
|
||||
npm run web:start
|
||||
|
||||
Voila! You can use GoCryptoTrader web app in a local development environment with webpack watching!
|
||||
|
||||
|
||||
## To build for production
|
||||
|
||||
- Using development variables (environments/index.ts) : `npm run electron:dev`
|
||||
- Using production variables (environments/index.prod.ts) : `npm run electron:prod`
|
||||
|
||||
Your built files are in the /dist folder.
|
||||
|
||||
## Included Commands
|
||||
|
||||
|Command|Description|
|
||||
|--|--|
|
||||
|`npm run start:web`| Execute the app in the brower |
|
||||
|`npm run electron:linux`| Builds your application and creates an app consumable on linux system |
|
||||
|`npm run electron:windows`| On a Windows OS, builds your application and creates an app consumable in windows 32/64 bit systems |
|
||||
|`npm run electron:mac`| On a MAC OS, builds your application and generates a `.app` file of your application that can be run on Ma |
|
||||
|
||||
|
||||
## Execute E2E tests
|
||||
|
||||
You can find end-to-end tests in /e2e folder.
|
||||
|
||||
You can run tests with the command lines below :
|
||||
- **in a terminal window** -> First, start a web server on port 4200 : `npm run start:web`
|
||||
- **in another terminal window** -> Then, launch Protractor (E2E framework): `npm run e2e`
|
||||
|
||||
# Contributors
|
||||
|User|Github|Contribution|
|
||||
|--|--|--|
|
||||
|GloriousCode|https://github.com/gloriouscode |Lead front-end|
|
||||
|Maxime GRIS|https://github.com/maximegris |Angular4 + Electron Base|
|
||||
|Shazbert|https://github.com/shazbert |Initial designs|
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1
web/_config.yml
Normal file
1
web/_config.yml
Normal file
@@ -0,0 +1 @@
|
||||
theme: jekyll-theme-architect
|
||||
@@ -1,45 +0,0 @@
|
||||
/* app css stylesheet */
|
||||
|
||||
.menu {
|
||||
list-style: none;
|
||||
border-bottom: 0.1em solid black;
|
||||
margin-bottom: 2em;
|
||||
padding: 0 0 0.5em;
|
||||
}
|
||||
|
||||
.menu:before {
|
||||
content: "[";
|
||||
}
|
||||
|
||||
.menu:after {
|
||||
content: "]";
|
||||
}
|
||||
|
||||
.menu>li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.menu>li:before {
|
||||
content: "|";
|
||||
padding-right: 0.3em;
|
||||
}
|
||||
|
||||
.menu>li:nth-child(1):before {
|
||||
content: "";
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.animate-show-hide.ng-hide {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.animate-show-hide.ng-hide-add,
|
||||
.animate-show-hide.ng-hide-remove {
|
||||
transition: all linear 0.5s;
|
||||
}
|
||||
|
||||
.check-element {
|
||||
border: 1px solid black;
|
||||
opacity: 1;
|
||||
padding: 10px;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// Declare app level module which depends on views, and components
|
||||
angular.module('myApp', [
|
||||
'ngRoute',
|
||||
'ui-notification',
|
||||
'myApp.home',
|
||||
'myApp.wallets',
|
||||
'myApp.settings',
|
||||
'myApp.version',
|
||||
'myApp.buy',
|
||||
'myApp.sell',
|
||||
'myApp.enabledExchanges',
|
||||
'myApp.buyOrders',
|
||||
'myApp.sellOrders',
|
||||
'myApp.stringUtils',
|
||||
'myApp.webSocket'
|
||||
]).
|
||||
config(['$locationProvider', '$routeProvider', 'NotificationProvider', function($locationProvider, $routeProvider, NotificationProvider) {
|
||||
NotificationProvider.setOptions({
|
||||
delay: 5000,
|
||||
startTop: 60,
|
||||
startRight: 10,
|
||||
verticalSpacing: 10,
|
||||
horizontalSpacing: 20,
|
||||
positionX: 'right',
|
||||
positionY: 'top'
|
||||
});
|
||||
|
||||
$locationProvider.hashPrefix('!');
|
||||
|
||||
$routeProvider.otherwise({ redirectTo: '/' });
|
||||
}]);
|
||||
@@ -1,12 +0,0 @@
|
||||
.table-fixed-heading {
|
||||
margin-bottom:0px;
|
||||
}
|
||||
|
||||
.buy-order-table th,.buy-order-table td {
|
||||
width:25%;
|
||||
}
|
||||
|
||||
.buy-order-data {
|
||||
max-height:200px;
|
||||
overflow-y:scroll;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
<link rel="stylesheet" href="/components/buy-orders/buy-orders.css" />
|
||||
<div class="col-md-12 buy-order-table">
|
||||
<table class="table table-striped table-fixed-heading">
|
||||
<thead>
|
||||
<tr>
|
||||
<th >Price</th>
|
||||
<th >{{currencyOne}}</th>
|
||||
<th >{{currencyTwo}}</th>
|
||||
<th >Sum({{currencyTwo}})</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
<div class="buy-order-data">
|
||||
<table class="table table-striped">
|
||||
<tbody>
|
||||
<tr ng-repeat="buyOrder in buyOrders">
|
||||
<td >{{buyOrder.price}}</td>
|
||||
<td >{{buyOrder.currencyOneAmount}}</td>
|
||||
<td >{{buyOrder.currencyTwoAmount}}</td>
|
||||
<td >{{buyOrder.sum}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div>
|
||||
</div>
|
||||
@@ -1,52 +0,0 @@
|
||||
|
||||
angular.module('myApp.buyOrders',[]).component('buyorders', {
|
||||
templateUrl: '/components/buy-orders/buy-orders.html',
|
||||
controller:'BuyOrdersController',
|
||||
controller: function ($scope, $http, Notification, $rootScope) {
|
||||
$scope.currency = {};
|
||||
$scope.exchange = {};
|
||||
|
||||
$rootScope.$on('CurrencyChanged', function (event, args) {
|
||||
$scope.currency = args.Currency;
|
||||
$scope.exchange = args.Exchange;
|
||||
$scope.currencyOne = $scope.currency.FirstCurrency;
|
||||
$scope.currencyTwo = $scope.currency.SecondCurrency;
|
||||
$scope.getRecentBuyOrders();
|
||||
});
|
||||
|
||||
$scope.getRecentBuyOrders = function() {
|
||||
var exchData = {params : {exchangeName: '', currencyPair:''}};
|
||||
$http.get('/GetBuyOrdersForCurrencyPair' , exchData).success(function(data) {
|
||||
$scope.buyOrders = data;
|
||||
}).error(function() {
|
||||
$scope.buyOrders = [
|
||||
{price:12,currencyOneAmount:12,currencyTwoAmount:13,sum:1111},
|
||||
{price:13,currencyOneAmount:15,currencyTwoAmount:13,sum:11231},
|
||||
{price:14,currencyOneAmount:232,currencyTwoAmount:13,sum:4511},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<div class="col-md-12">
|
||||
<div class="form col-md-12">
|
||||
<div class="row">
|
||||
<label>Exchange: {{exchange.exchangeName}}
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Currency: </label>{{currency.CurrencyPair}}
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Lowest Ask: </label><label>{{currency.Ask}}</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Price: </label><input pattern="\d*" class="form-control" ng-model="price" type="text" placeholder="How much?" />
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Amount: </label><input pattern="\d*" ng-model="amount" class="form-control" type="text" placeholder="How much?" />
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Total: </label><label ng-show="price > 0 && amount > 0">{{price * amount}}</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<button ng-click="placeOrder()" class="form-control btn btn-success">Place Order</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
@@ -1,40 +0,0 @@
|
||||
|
||||
angular.module('myApp.buy',[]).component('buy', {
|
||||
templateUrl: '/components/buy/buy.html',
|
||||
controller:'BuyController',
|
||||
controller: function ($scope, $http, Notification, $rootScope) {
|
||||
$scope.currency = {};
|
||||
$scope.exchange = {};
|
||||
|
||||
$rootScope.$on('CurrencyChanged', function (event, args) {
|
||||
$scope.currency = args.Currency;
|
||||
$scope.exchange = args.Exchange;
|
||||
console.log($scope.currency);
|
||||
$scope.GetLatestDataFromExchangeCurrency();
|
||||
$scope.price = $scope.currency.Ask;
|
||||
});
|
||||
|
||||
$scope.GetLatestDataFromExchangeCurrency = function () {
|
||||
$http.get('/GetLatestDataFromExchangeCurrency?exhange=' + $scope.exchange.exchangeName + '¤cy='+ $scope.currency.CurrencyPair).success(function (data) {
|
||||
$scope.currency.Last = data.Last;
|
||||
$scope.currency.Volume = data.Volume;
|
||||
$scope.currency.Ask = data.Ask;
|
||||
$scope.price = $scope.currency.Ask;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.placeOrder = function () {
|
||||
var obj = {};
|
||||
obj.ExchangeName = $scope.exchange.exchangeName;
|
||||
obj.Currency = $scope.currency;
|
||||
obj.Price = $scope.price;
|
||||
obj.Amount = $scope.amount;
|
||||
$http.post('/Command/PlaceBuyOrder', obj).success(function (response) {
|
||||
Notification.success("Successfully placed order");
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
<h4>All enabled currencies</h4>
|
||||
<div class="panel-group" id="accordion">
|
||||
<div class="panel panel-default" ng-repeat="exchange in exchanges">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<a href="javascript:;" data-toggle="collapse" data-parent="#accordion" data-target="#{{exchange.exchangeName | removeSpaces}}">
|
||||
{{exchange.exchangeName}}
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="{{exchange.exchangeName | removeSpaces}}" class="panel-collapse collapse" ng-class='{in:$first}'>
|
||||
<div class="panel-body">
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th>Currency</th>
|
||||
<th>Last</th>
|
||||
<th>Volume</th>
|
||||
</tr>
|
||||
<tr ng-repeat="value in exchange.exchangeValues">
|
||||
<td><a href="" ng-click="reloadDashboardWithExchangeCurrency(exchange,value)">{{value.CurrencyPair}}</a></td>
|
||||
<td>{{value.Last | number: 6}}</td>
|
||||
<td>{{value.Volume | number: 2}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,32 +0,0 @@
|
||||
angular.module('myApp.enabledExchanges', []).component('enabledexchanges', {
|
||||
templateUrl: '/components/enabled-exchanges/enabled-exchanges.html',
|
||||
controller: 'EnabledExchangesController',
|
||||
controller: function($scope, $http, Notification, $rootScope) {
|
||||
$scope.selected = {};
|
||||
$scope.getDashboardData = function() {
|
||||
$http({
|
||||
method: 'GET',
|
||||
url: '/data/all-enabled-currencies'
|
||||
}).
|
||||
success(function(data, status, headers, config) {
|
||||
$scope.exchanges = data.data;
|
||||
$scope.reloadDashboardWithExchangeCurrency($scope.exchanges[0], $scope.exchanges[0].exchangeValues[0]);
|
||||
Notification.info("Retrieved latest data");
|
||||
}).
|
||||
error(function(data, status, headers, config) {
|
||||
console.log('error');
|
||||
});
|
||||
};
|
||||
|
||||
$scope.reloadDashboardWithExchangeCurrency = function(exchange, value) {
|
||||
$scope.selected.Exchange = exchange;
|
||||
$scope.selected.Currency = value;
|
||||
$rootScope.$emit('CurrencyChanged', $scope.selected);
|
||||
|
||||
};
|
||||
|
||||
|
||||
$scope.getDashboardData();
|
||||
|
||||
}
|
||||
});
|
||||
@@ -1,9 +0,0 @@
|
||||
angular.module('myApp.stringUtils', [])
|
||||
.filter('removeSpaces', [function() {
|
||||
return function(string) {
|
||||
if (!angular.isString(string)) {
|
||||
return string;
|
||||
}
|
||||
return string.replace(/[\s]/g, '');
|
||||
};
|
||||
}]);
|
||||
@@ -1,21 +0,0 @@
|
||||
angular.module('myApp.webSocket', ['ngWebSocket'])
|
||||
.factory('webSocket', function($websocket) {
|
||||
// Open a WebSocket connection
|
||||
var dataStream = $websocket('ws://localhost:9050/');
|
||||
|
||||
var collection = [];
|
||||
|
||||
dataStream.onMessage(function(message) {
|
||||
collection.push(JSON.parse(message.data));
|
||||
});
|
||||
|
||||
var methods = {
|
||||
collection: collection,
|
||||
get: function() {
|
||||
dataStream.send(JSON.stringify({ action: 'get' }));
|
||||
}
|
||||
};
|
||||
|
||||
return methods;
|
||||
|
||||
})
|
||||
@@ -1,12 +0,0 @@
|
||||
.table-fixed-heading {
|
||||
margin-bottom:0px;
|
||||
}
|
||||
|
||||
.buy-order-table th,.buy-order-table td {
|
||||
width:25%;
|
||||
}
|
||||
|
||||
.buy-order-data {
|
||||
max-height:200px;
|
||||
overflow-y:scroll;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
<link rel="stylesheet" href="/components/sell-orders/sell-orders.css" />
|
||||
<div class="col-md-12 buy-order-table">
|
||||
<table class="table table-striped table-fixed-heading">
|
||||
<thead>
|
||||
<tr>
|
||||
<th >Price</th>
|
||||
<th >{{currencyOne}}</th>
|
||||
<th >{{currencyTwo}}</th>
|
||||
<th >Sum({{currencyTwo}})</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
<div class="buy-order-data">
|
||||
<table class="table table-striped">
|
||||
<tbody>
|
||||
<tr ng-repeat="sellOrder in sellOrders">
|
||||
<td >{{sellOrder.price}}</td>
|
||||
<td >{{sellOrder.currencyOneAmount}}</td>
|
||||
<td >{{sellOrder.currencyTwoAmount}}</td>
|
||||
<td >{{sellOrder.sum}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div>
|
||||
</div>
|
||||
@@ -1,52 +0,0 @@
|
||||
|
||||
angular.module('myApp.sellOrders',[]).component('sellorders', {
|
||||
templateUrl: '/components/sell-orders/sell-orders.html',
|
||||
controller:'SellOrdersController',
|
||||
controller: function ($scope, $http, Notification, $rootScope) {
|
||||
$scope.currency = {};
|
||||
$scope.exchange = {};
|
||||
|
||||
$rootScope.$on('CurrencyChanged', function (event, args) {
|
||||
$scope.currency = args.Currency;
|
||||
$scope.exchange = args.Exchange;
|
||||
$scope.currencyOne = $scope.currency.FirstCurrency;
|
||||
$scope.currencyTwo = $scope.currency.SecondCurrency;
|
||||
$scope.getRecentSellOrders();
|
||||
});
|
||||
|
||||
$scope.getRecentSellOrders = function() {
|
||||
var exchData = {params : {exchangeName: '', currencyPair:''}};
|
||||
$http.get('/GetSellOrdersForCurrencyPair' , exchData).success(function(data) {
|
||||
$scope.sellOrders = data;
|
||||
}).error(function() {
|
||||
$scope.sellOrders = [
|
||||
{price:456,currencyOneAmount:12,currencyTwoAmount:13,sum:1111},
|
||||
{price:234,currencyOneAmount:15,currencyTwoAmount:13,sum:11231},
|
||||
{price:12344,currencyOneAmount:232,currencyTwoAmount:13,sum:4511},
|
||||
{price:15467,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:6717,currencyOneAmount:2452,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:34522,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
{price:17,currencyOneAmount:22,currencyTwoAmount:13,sum:11212311},
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<div class="col-md-12">
|
||||
<div class="form col-md-12">
|
||||
<div class="row">
|
||||
<label>Exchange: {{exchange.exchangeName}}
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Currency: </label>{{currency.CurrencyPair}}
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Bid: </label><label>{{currency.Bid}}</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Price: </label><input pattern="\d*" class="form-control" ng-model="price" type="text" placeholder="How much?" />
|
||||
</div>
|
||||
<div class="row">
|
||||
<label>Amount: </label><input pattern="\d*" ng-model="amount" class="form-control" type="text" placeholder="How much?" />
|
||||
</div>
|
||||
<div class="row" >
|
||||
<label>Total: </label><label ng-show="price > 0 && amount > 0">{{price * amount}}</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<button ng-click="placeOrder()" class="form-control btn btn-success">Place Order</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
@@ -1,40 +0,0 @@
|
||||
|
||||
angular.module('myApp.sell',[]).component('sell', {
|
||||
templateUrl: '/components/sell/sell.html',
|
||||
controller:'SellController',
|
||||
controller: function ($scope, $http, Notification, $rootScope) {
|
||||
$scope.currency = {};
|
||||
$scope.exchange = {};
|
||||
|
||||
$rootScope.$on('CurrencyChanged', function (event, args) {
|
||||
$scope.currency = args.Currency;
|
||||
$scope.exchange = args.Exchange;
|
||||
console.log($scope.currency);
|
||||
$scope.GetLatestDataFromExchangeCurrency();
|
||||
$scope.price = $scope.currency.Bid;
|
||||
});
|
||||
|
||||
$scope.GetLatestDataFromExchangeCurrency = function () {
|
||||
$http.get('/GetLatestDataFromExchangeCurrency?exhange=' + $scope.exchange.exchangeName + '¤cy='+ $scope.currency.CurrencyPair).success(function (data) {
|
||||
$scope.currency.Last = data.Last;
|
||||
$scope.currency.Volume = data.Volume;
|
||||
$scope.currency.Bid = data.Bid;
|
||||
$scope.price = $scope.currency.Bid;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.placeOrder = function () {
|
||||
var obj = {};
|
||||
obj.ExchangeName = $scope.exchange.exchangeName;
|
||||
obj.Currency = $scope.currency;
|
||||
obj.Price = $scope.price;
|
||||
obj.Amount = $scope.amount;
|
||||
$http.post('/Command/PlaceSellOrder', obj).success(function (response) {
|
||||
Notification.success("Successfully placed order");
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('myApp.version.interpolate-filter', [])
|
||||
|
||||
.filter('interpolate', ['version', function(version) {
|
||||
return function(text) {
|
||||
return String(text).replace(/\%VERSION\%/mg, version);
|
||||
};
|
||||
}]);
|
||||
@@ -1,15 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('myApp.version module', function() {
|
||||
beforeEach(module('myApp.version'));
|
||||
|
||||
describe('interpolate filter', function() {
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('version', 'TEST_VER');
|
||||
}));
|
||||
|
||||
it('should replace VERSION', inject(function(interpolateFilter) {
|
||||
expect(interpolateFilter('before %VERSION% after')).toEqual('before TEST_VER after');
|
||||
}));
|
||||
});
|
||||
});
|
||||
@@ -1,9 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('myApp.version.version-directive', [])
|
||||
|
||||
.directive('appVersion', ['version', function(version) {
|
||||
return function(scope, elm, attrs) {
|
||||
elm.text(version);
|
||||
};
|
||||
}]);
|
||||
@@ -1,17 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('myApp.version module', function() {
|
||||
beforeEach(module('myApp.version'));
|
||||
|
||||
describe('app-version directive', function() {
|
||||
it('should print current version', function() {
|
||||
module(function($provide) {
|
||||
$provide.value('version', 'TEST_VER');
|
||||
});
|
||||
inject(function($compile, $rootScope) {
|
||||
var element = $compile('<span app-version></span>')($rootScope);
|
||||
expect(element.text()).toEqual('TEST_VER');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,8 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('myApp.version', [
|
||||
'myApp.version.interpolate-filter',
|
||||
'myApp.version.version-directive'
|
||||
])
|
||||
|
||||
.value('version', '0.1');
|
||||
@@ -1,11 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('myApp.version module', function() {
|
||||
beforeEach(module('myApp.version'));
|
||||
|
||||
describe('version service', function() {
|
||||
it('should return current version', inject(function(version) {
|
||||
expect(version).toEqual('0.1');
|
||||
}));
|
||||
});
|
||||
});
|
||||
11
web/app/darktheme.min.css
vendored
11
web/app/darktheme.min.css
vendored
File diff suppressed because one or more lines are too long
@@ -1,58 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="bower_components/html5-boilerplate/css/normalize.css">
|
||||
<link rel="stylesheet" href="bower_components/html5-boilerplate/css/main.css">
|
||||
<style>
|
||||
[ng-cloak] {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<script src="bower_components/html5-boilerplate/js/vendor/modernizr-2.6.2.min.js"></script>
|
||||
<script>
|
||||
// include angular loader, which allows the files to load in any order
|
||||
//@@NG_LOADER_START@@
|
||||
// You need to run `npm run update-index-async` to inject the angular async code here
|
||||
//@@NG_LOADER_END@@
|
||||
|
||||
// include a third-party async loader library
|
||||
/*!
|
||||
* $script.js v1.3
|
||||
* https://github.com/ded/script.js
|
||||
* Copyright: @ded & @fat - Dustin Diaz, Jacob Thornton 2011
|
||||
* Follow our software http://twitter.com/dedfat
|
||||
* License: MIT
|
||||
*/
|
||||
!function(a,b,c){function t(a,c){var e=b.createElement("script"),f=j;e.onload=e.onerror=e[o]=function(){e[m]&&!/^c|loade/.test(e[m])||f||(e.onload=e[o]=null,f=1,c())},e.async=1,e.src=a,d.insertBefore(e,d.firstChild)}function q(a,b){p(a,function(a){return!b(a)})}var d=b.getElementsByTagName("head")[0],e={},f={},g={},h={},i="string",j=!1,k="push",l="DOMContentLoaded",m="readyState",n="addEventListener",o="onreadystatechange",p=function(a,b){for(var c=0,d=a.length;c<d;++c)if(!b(a[c]))return j;return 1};!b[m]&&b[n]&&(b[n](l,function r(){b.removeEventListener(l,r,j),b[m]="complete"},j),b[m]="loading");var s=function(a,b,d){function o(){if(!--m){e[l]=1,j&&j();for(var a in g)p(a.split("|"),n)&&!q(g[a],n)&&(g[a]=[])}}function n(a){return a.call?a():e[a]}a=a[k]?a:[a];var i=b&&b.call,j=i?b:d,l=i?a.join(""):b,m=a.length;c(function(){q(a,function(a){h[a]?(l&&(f[l]=1),o()):(h[a]=1,l&&(f[l]=1),t(s.path?s.path+a+".js":a,o))})},0);return s};s.get=t,s.ready=function(a,b,c){a=a[k]?a:[a];var d=[];!q(a,function(a){e[a]||d[k](a)})&&p(a,function(a){return e[a]})?b():!function(a){g[a]=g[a]||[],g[a][k](b),c&&c(d)}(a.join("|"));return s};var u=a.$script;s.noConflict=function(){a.$script=u;return this},typeof module!="undefined"&&module.exports?module.exports=s:a.$script=s}(this,document,setTimeout)
|
||||
|
||||
// load all of the dependencies asynchronously.
|
||||
$script([
|
||||
'bower_components/angular/angular.js',
|
||||
'bower_components/angular-route/angular-route.js',
|
||||
'app.js',
|
||||
'view1/view1.js',
|
||||
'view2/view2.js',
|
||||
'components/version/version.js',
|
||||
'components/version/version-directive.js',
|
||||
'components/version/interpolate-filter.js'
|
||||
], function() {
|
||||
// when all is done, execute bootstrap angular application
|
||||
angular.bootstrap(document, ['myApp']);
|
||||
});
|
||||
</script>
|
||||
<title>My AngularJS App</title>
|
||||
<link rel="stylesheet" href="app.css">
|
||||
</head>
|
||||
<body ng-cloak>
|
||||
<ul class="menu">
|
||||
<li><a href="#!/view1">view1</a></li>
|
||||
<li><a href="#!/view2">view2</a></li>
|
||||
</ul>
|
||||
|
||||
<div ng-view></div>
|
||||
|
||||
<div>Angular seed app: v<span app-version></span></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,80 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!--[if lt IE 7]> <html lang="en" ng-app="myApp" class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||
<!--[if IE 7]> <html lang="en" ng-app="myApp" class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||
<!--[if IE 8]> <html lang="en" ng-app="myApp" class="no-js lt-ie9"> <![endif]-->
|
||||
<!--[if gt IE 8]><!-->
|
||||
<html lang="en" ng-app="myApp" class="no-js">
|
||||
<!--<![endif]-->
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>GoCrypto Trader</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="/bower_components/html5-boilerplate/dist/css/normalize.css">
|
||||
<link rel="stylesheet" href="/bower_components/html5-boilerplate/dist/css/main.css">
|
||||
<link rel="stylesheet" href="/bower_components/angular-ui-notification/dist/angular-ui-notification.min.css">
|
||||
<link rel="stylesheet" href="/app.css">
|
||||
<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="/darktheme.min.css">
|
||||
|
||||
<script src="/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
|
||||
<script src="/bower_components/jquery/dist/jquery.min.js"></script>
|
||||
<script src="/bower_components/html5-boilerplate/dist/js/vendor/modernizr-2.8.3.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar navbar-default navbar-fixed-top navbar" role="navigation">
|
||||
<div class="container-fluid">
|
||||
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="/">
|
||||
GoCrypto Trader
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="#!/">Dashboard</a></li>
|
||||
<li><a href="#!/wallets">Wallets</a></li>
|
||||
</ul>
|
||||
<ul class="nav navbar-nav pull-right">
|
||||
<li class=""><a href="#!/settings">Settings</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
<!--[if lt IE 7]>
|
||||
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
|
||||
<![endif]-->
|
||||
|
||||
<div ng-view style="padding-top:60px;"></div>
|
||||
|
||||
|
||||
<p class="text-center text-muted">Copyright 2016 GoCrypto Trader</p>
|
||||
<!-- In production use:
|
||||
<script src="//ajax.googleapis.com/ajax/libs/angularjs/x.x.x/angular.min.js"></script>
|
||||
-->
|
||||
<script src="/bower_components/angular/angular.js"></script>
|
||||
<script src="/bower_components/angular-ui-notification/dist/angular-ui-notification.min.js"></script>
|
||||
<script src="/bower_components/angular-route/angular-route.js"></script>
|
||||
<script src="/bower_components/angular-websocket/dist/angular-websocket.js"></script>
|
||||
<script src="/app.js"></script>
|
||||
<script src="/views/settings/settings.js"></script>
|
||||
<script src="/views/home/home.js"></script>
|
||||
<script src="/views/wallets/wallets.js"></script>
|
||||
<script src="/components/buy/buy.js"></script>
|
||||
<script src="/components/sell/sell.js"></script>
|
||||
<script src="/components/enabled-exchanges/enabled-exchanges.js"></script>
|
||||
<script src="/components/version/version.js"></script>
|
||||
<script src="/components/buy-orders/buy-orders.js"></script>
|
||||
<script src="/components/sell-orders/sell-orders.js"></script>
|
||||
<script src="/components/version/version-directive.js"></script>
|
||||
<script src="/components/version/interpolate-filter.js"></script>
|
||||
<script src="/components/helpers/stringUtils.js"></script>
|
||||
<script src="/components/helpers/webSocket.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,73 +0,0 @@
|
||||
<div class="col-md-12">
|
||||
<div ng-show="loaded">
|
||||
<div class="col-md-12">
|
||||
<h2>{{exchange.exchangeName}} Exchange</h2>
|
||||
<h4>{{currency.CurrencyPair}}</h4>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="col-md-12">
|
||||
<!-- This is to get a sense of scale, I don't intend on any hardcoded heights!' -->
|
||||
<div class="col-md-6" style=" height: 300px;">
|
||||
<h3>Sell orders</h3>
|
||||
<!--plceholder-->
|
||||
<sellorders></sellorders>
|
||||
|
||||
</div>
|
||||
<div class="col-md-6" style=" height: 300px;">
|
||||
<h3>Buy orders</h3>
|
||||
<buyorders></buyorders>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12" style=" height: 300px;">
|
||||
<h3>Market depth chart</h3>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h4>Buy</h4>
|
||||
<buy></buy>
|
||||
</div>
|
||||
<div class="col-md-4" style=" height: 300px;">
|
||||
<h4>Sell</h4>
|
||||
<sell></sell>
|
||||
</div>
|
||||
<div class="col-md-4" style=" height: 300px;">
|
||||
<h4>Wallet quick look</h4>
|
||||
See current holding of selected currency
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="col-md-6" style=" height: 400px;">
|
||||
<h3>Trade History</h3>
|
||||
</div>
|
||||
<div class="col-md-6" style=" height: 400px;">
|
||||
<h3>My Open Orders</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<enabledexchanges></enabledexchanges>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="!loaded && !loadFailed" class="col-md-12 text-center">
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="120" height="120" fill="#40bfc0">
|
||||
<path opacity=".25" d="M16 0 A16 16 0 0 0 16 32 A16 16 0 0 0 16 0 M16 4 A12 12 0 0 1 16 28 A12 12 0 0 1 16 4"/>
|
||||
<path d="M16 0 A16 16 0 0 1 32 16 L28 16 A12 12 0 0 0 16 4z">
|
||||
<animateTransform attributeName="transform" type="rotate" from="0 16 16" to="360 16 16" dur="0.8s" repeatCount="indefinite" />
|
||||
</path>
|
||||
</svg>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
<div ng-show="loadFailed">
|
||||
<br />
|
||||
<br />
|
||||
<h1 class="text-center">D:</h1>
|
||||
<br />
|
||||
<h4 class="text-center">Something went wrong! Make sure Gocryptotrader is running. If things still aren't cool, open a ticket <a href="https://github.com/thrasher-/gocryptotrader/issues" target="blank">here</a></h4>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,28 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('myApp.home', ['ngRoute'])
|
||||
|
||||
.config(['$routeProvider', function($routeProvider) {
|
||||
$routeProvider.when('/', {
|
||||
templateUrl: '/views/home/home.html',
|
||||
controller: 'HomeController'
|
||||
});
|
||||
}])
|
||||
|
||||
.controller('HomeController', function($scope, $rootScope, $timeout, webSocket) {
|
||||
$scope.loaded = false;
|
||||
|
||||
$rootScope.$on('CurrencyChanged', function(event, args) {
|
||||
$scope.currency = args.Currency;
|
||||
$scope.exchange = args.Exchange;
|
||||
$scope.loaded = true;
|
||||
});
|
||||
|
||||
$timeout(function() {
|
||||
if ($scope.currency) {
|
||||
$scope.loaded = true;
|
||||
} else {
|
||||
$scope.loadFailed = true;
|
||||
}
|
||||
}, 10000);
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('myApp.view1 module', function() {
|
||||
|
||||
beforeEach(module('myApp.view1'));
|
||||
|
||||
describe('view1 controller', function(){
|
||||
|
||||
it('should ....', inject(function($controller) {
|
||||
//spec body
|
||||
var view1Ctrl = $controller('View1Ctrl');
|
||||
expect(view1Ctrl).toBeDefined();
|
||||
}));
|
||||
|
||||
});
|
||||
});
|
||||
@@ -1,60 +0,0 @@
|
||||
<h2>Settings</h2>
|
||||
<div class="col-md-12">
|
||||
<div class="col-md-10 container " >
|
||||
<h4>Exchange config</h4>
|
||||
<div ng-repeat="exchange in config.Exchanges">
|
||||
<div class="row">
|
||||
<h3>{{exchange.Name}}</h3>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<label class="input-form">Enabled:</label>
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
<input type="checkbox" class="input-form" ng-model="exchange.Enabled"/>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="exchange.Enabled">
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<label class="input-form">API Key:</label>
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
<input type="text" class="input-form" ng-model="exchange.APIKey"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<label class="input-form">API Secret:</label>
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
<input type="text" class="input-form" ng-model="exchange.APISecret"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<label class="input-form">Client ID:</label>
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
<input type="text" class="input-form" ng-model="exchange.ClientID"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<label class="input-form">Currencies to check:</label>
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
<div class="col-md-2" ng-repeat="availableCurrency in exchange.AvailablePairsSplit">
|
||||
<div class="col-md-8">{{availableCurrency}}</div>
|
||||
<div class="col-md-4"><input type="checkbox" class="input-form" ng-click="toggleCurrencyToEnabledCurrencies(availableCurrency, exchange)" ng-checked="exchange.EnabledPairsSplit.indexOf(availableCurrency) > -1"/></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-success btn-lg" ng-click="saveAllSettings()">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button class="btn btn-success btn-lg" style="float:right;position:fixed;top:120px;right:60px;" ng-click="saveAllSettings()">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,80 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('myApp.settings', ['ngRoute'])
|
||||
.config(['$routeProvider', function($routeProvider) {
|
||||
$routeProvider.when('/settings', {
|
||||
templateUrl: '/views/settings/settings.html',
|
||||
controller: 'SettingsController'
|
||||
});
|
||||
}])
|
||||
|
||||
.controller('SettingsController', function ($scope, $http, Notification) {
|
||||
$scope.getconfigData = function() {
|
||||
$http({
|
||||
method: 'GET',
|
||||
url: '/config/all'
|
||||
}).
|
||||
success(function (data, status, headers, config) {
|
||||
for(var i=0; i<data.Exchanges.length;i++) {
|
||||
data.Exchanges[i].AvailablePairsSplit = data.Exchanges[i].AvailablePairs.split(",");
|
||||
data.Exchanges[i].EnabledPairsSplit = data.Exchanges[i].EnabledPairs.split(",");
|
||||
}
|
||||
$scope.config = data;
|
||||
Notification.info('Settings loaded');
|
||||
}).
|
||||
error(function (data, status, headers, config) {
|
||||
console.log('error');
|
||||
});
|
||||
};
|
||||
|
||||
$scope.getconfigData();
|
||||
|
||||
$scope.toggleCurrencyToEnabledCurrencies = function(currency, exchange) {
|
||||
for(var i=0; i<$scope.config.Exchanges.length;i++) {
|
||||
if($scope.config.Exchanges[i].Name == exchange.Name) {
|
||||
if(exchange.EnabledPairsSplit.indexOf(currency) > -1) {
|
||||
$scope.config.Exchanges[i].EnabledPairsSplit.splice($scope.config.Exchanges[i].EnabledPairsSplit.indexOf(currency),1);
|
||||
//I feel there's a better way to do this, but for right now, whatever
|
||||
$scope.config.Exchanges[i].EnabledPairs = $scope.config.Exchanges[i].EnabledPairs.replace(currency,"");
|
||||
$scope.config.Exchanges[i].EnabledPairs = $scope.config.Exchanges[i].EnabledPairs.replace(",,","");
|
||||
$scope.config.Exchanges[i].EnabledPairs = $scope.config.Exchanges[i].EnabledPairs.replace(/,\s*$/, "");
|
||||
|
||||
} else {
|
||||
$scope.config.Exchanges[i].EnabledPairsSplit.push(currency);
|
||||
$scope.config.Exchanges[i].EnabledPairs = $scope.config.Exchanges[i].EnabledPairs + "," + currency;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$scope.saveAllSettings = function() {
|
||||
$scope.postObject = jQuery.extend(true, {}, $scope.config);
|
||||
//Purge any unnecessary post data
|
||||
delete $scope.postObject.Webserver;
|
||||
for(var i=0; i<$scope.postObject.Exchanges.length;i++) {
|
||||
delete $scope.postObject.Exchanges[i].AvailablePairsSplit;
|
||||
delete $scope.postObject.Exchanges[i].AvailablePairs;
|
||||
delete $scope.postObject.Exchanges[i].BaseCurrencies;
|
||||
delete $scope.postObject.Exchanges[i].EnabledPairsSplit;
|
||||
}
|
||||
|
||||
//Send to be saved
|
||||
$http({
|
||||
method: 'POST',
|
||||
url: '/config/all/save',
|
||||
data: $scope.postObject
|
||||
}).
|
||||
success(function (data) {
|
||||
Notification.success('Saved settings');
|
||||
for(var i=0; i<data.Exchanges.length;i++) {
|
||||
data.Exchanges[i].AvailablePairsSplit = data.Exchanges[i].AvailablePairs.split(",");
|
||||
data.Exchanges[i].EnabledPairsSplit = data.Exchanges[i].EnabledPairs.split(",");
|
||||
}
|
||||
$scope.config = data;
|
||||
Notification.info('Settings loaded');
|
||||
}).
|
||||
error(function (data) {
|
||||
Notification.error('Save failed');
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('myApp.view1 module', function() {
|
||||
|
||||
beforeEach(module('myApp.view1'));
|
||||
|
||||
describe('view1 controller', function(){
|
||||
|
||||
it('should ....', inject(function($controller) {
|
||||
//spec body
|
||||
var view1Ctrl = $controller('View1Ctrl');
|
||||
expect(view1Ctrl).toBeDefined();
|
||||
}));
|
||||
|
||||
});
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
<h2>Wallets</h2>
|
||||
<h3>All your currency, all in one place</h3>
|
||||
<div ng-repeat="wallet in wallets">
|
||||
<h4>{{wallet.ExchangeName}}</h4>
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th>Currency Name</th>
|
||||
<th>Total</th>
|
||||
<th>Hold</th>
|
||||
</tr>
|
||||
<tr ng-repeat="value in wallet.Currencies">
|
||||
<td>{{value.CurrencyName}}</td>
|
||||
<td>{{value.TotalValue}}</td>
|
||||
<td>{{value.Hold}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
@@ -1,29 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
angular.module('myApp.wallets', ['ngRoute'])
|
||||
|
||||
.config(['$routeProvider', function($routeProvider) {
|
||||
$routeProvider.when('/wallets', {
|
||||
templateUrl: '/views/wallets/wallets.html',
|
||||
controller: 'WalletsController'
|
||||
});
|
||||
}])
|
||||
|
||||
.controller('WalletsController', function ($scope, $http, Notification) {
|
||||
$scope.getDashboardData = function() {
|
||||
$http({
|
||||
method: 'GET',
|
||||
url: '/data/all-enabled-exchange-account-info'
|
||||
}).
|
||||
success(function (data, status, headers, config) {
|
||||
$scope.wallets = data.data;
|
||||
Notification.info("Got your wallet!");
|
||||
}).
|
||||
error(function (data, status, headers, config) {
|
||||
console.log('error');
|
||||
});
|
||||
};
|
||||
|
||||
$scope.getDashboardData();
|
||||
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
describe('myApp.view1 module', function() {
|
||||
|
||||
beforeEach(module('myApp.view1'));
|
||||
|
||||
describe('view1 controller', function(){
|
||||
|
||||
it('should ....', inject(function($controller) {
|
||||
//spec body
|
||||
var view1Ctrl = $controller('View1Ctrl');
|
||||
expect(view1Ctrl).toBeDefined();
|
||||
}));
|
||||
|
||||
});
|
||||
});
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"name": "angular-seed",
|
||||
"description": "A starter project for AngularJS",
|
||||
"version": "0.0.0",
|
||||
"homepage": "https://github.com/angular/angular-seed",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"angular": "~1.5.0",
|
||||
"angular-route": "~1.5.0",
|
||||
"angular-loader": "~1.5.0",
|
||||
"angular-mocks": "~1.5.0",
|
||||
"html5-boilerplate": "^5.3.0",
|
||||
"angular-ui-notification":"latest",
|
||||
"angular-websocket":"2.0.0",
|
||||
"bootstrap":"3.3.7"
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
//jshint strict: false
|
||||
exports.config = {
|
||||
|
||||
allScriptsTimeout: 11000,
|
||||
|
||||
specs: [
|
||||
'*.js'
|
||||
],
|
||||
|
||||
capabilities: {
|
||||
'browserName': 'chrome'
|
||||
},
|
||||
|
||||
baseUrl: 'http://localhost:8000/',
|
||||
|
||||
framework: 'jasmine',
|
||||
|
||||
jasmineNodeOpts: {
|
||||
defaultTimeoutInterval: 30000
|
||||
}
|
||||
|
||||
};
|
||||
@@ -1,42 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/* https://github.com/angular/protractor/blob/master/docs/toc.md */
|
||||
|
||||
describe('my app', function() {
|
||||
|
||||
|
||||
it('should automatically redirect to /view1 when location hash/fragment is empty', function() {
|
||||
browser.get('index.html');
|
||||
expect(browser.getLocationAbsUrl()).toMatch("/view1");
|
||||
});
|
||||
|
||||
|
||||
describe('view1', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
browser.get('index.html#!/view1');
|
||||
});
|
||||
|
||||
|
||||
it('should render view1 when user navigates to /view1', function() {
|
||||
expect(element.all(by.css('[ng-view] p')).first().getText()).
|
||||
toMatch(/partial for view 1/);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('view2', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
browser.get('index.html#!/view2');
|
||||
});
|
||||
|
||||
|
||||
it('should render view2 when user navigates to /view2', function() {
|
||||
expect(element.all(by.css('[ng-view] p')).first().getText()).
|
||||
toMatch(/partial for view 2/);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
14
web/e2e/app.e2e-spec.ts
Normal file
14
web/e2e/app.e2e-spec.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { AngularElectronPage } from './app.po';
|
||||
import { browser, element, by } from 'protractor';
|
||||
|
||||
describe('angular-electron App', () => {
|
||||
let page: AngularElectronPage;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new AngularElectronPage();
|
||||
});
|
||||
|
||||
it('should display message saying App works !', () => {
|
||||
expect(element(by.css('app-home h1')).getText()).toMatch('App works !');
|
||||
});
|
||||
});
|
||||
8
web/e2e/app.po.ts
Normal file
8
web/e2e/app.po.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { browser, element, by } from 'protractor';
|
||||
|
||||
/* tslint:disable */
|
||||
export class AngularElectronPage {
|
||||
navigateTo(route: string) {
|
||||
return browser.get(route);
|
||||
}
|
||||
}
|
||||
12
web/e2e/tsconfig.e2e.json
Normal file
12
web/e2e/tsconfig.e2e.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/e2e",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"types":[
|
||||
"jasmine",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,44 @@
|
||||
//jshint strict: false
|
||||
module.exports = function(config) {
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/0.13/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
|
||||
basePath: './app',
|
||||
|
||||
files: [
|
||||
'bower_components/angular/angular.js',
|
||||
'bower_components/angular-route/angular-route.js',
|
||||
'bower_components/angular-mocks/angular-mocks.js',
|
||||
'components/**/*.js',
|
||||
'view*/**/*.js'
|
||||
],
|
||||
|
||||
autoWatch: true,
|
||||
|
||||
frameworks: ['jasmine'],
|
||||
|
||||
browsers: ['Chrome'],
|
||||
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular/cli'],
|
||||
plugins: [
|
||||
'karma-chrome-launcher',
|
||||
'karma-firefox-launcher',
|
||||
'karma-jasmine',
|
||||
'karma-junit-reporter'
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular/cli/plugins/karma')
|
||||
],
|
||||
|
||||
junitReporter: {
|
||||
outputFile: 'test_out/unit.xml',
|
||||
suite: 'unit'
|
||||
}
|
||||
|
||||
client:{
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
files: [
|
||||
{ pattern: './src/test.ts', watched: false }
|
||||
],
|
||||
preprocessors: {
|
||||
'./src/test.ts': ['@angular/cli']
|
||||
},
|
||||
mime: {
|
||||
'text/x-typescript': ['ts','tsx']
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
reports: [ 'html', 'lcovonly' ],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
angularCli: {
|
||||
environment: 'dev'
|
||||
},
|
||||
reporters: config.angularCli && config.angularCli.codeCoverage
|
||||
? ['progress', 'coverage-istanbul']
|
||||
: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false
|
||||
});
|
||||
};
|
||||
|
||||
BIN
web/logo-angular.jpg
Normal file
BIN
web/logo-angular.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
BIN
web/logo-electron.jpg
Normal file
BIN
web/logo-electron.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.8 KiB |
71
web/main.ts
Normal file
71
web/main.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { app, BrowserWindow, screen } from 'electron';
|
||||
import * as path from 'path';
|
||||
|
||||
|
||||
let win, serve;
|
||||
const args = process.argv.slice(1);
|
||||
serve = args.some(val => val === '--serve');
|
||||
|
||||
if (serve) {
|
||||
require('electron-reload')(__dirname, {
|
||||
});
|
||||
}
|
||||
|
||||
function createWindow() {
|
||||
|
||||
const electronScreen = screen;
|
||||
const size = electronScreen.getPrimaryDisplay().workAreaSize;
|
||||
|
||||
// Create the browser window.
|
||||
win = new BrowserWindow({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: size.width,
|
||||
height: size.height
|
||||
});
|
||||
|
||||
// and load the index.html of the app.
|
||||
win.loadURL('file://' + __dirname + '/index.html');
|
||||
|
||||
// Open the DevTools.
|
||||
if (serve) {
|
||||
win.webContents.openDevTools();
|
||||
}
|
||||
|
||||
// Emitted when the window is closed.
|
||||
win.on('closed', () => {
|
||||
// Dereference the window object, usually you would store window
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
win = null;
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on('ready', createWindow);
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', () => {
|
||||
// On OS X it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
}
|
||||
});
|
||||
|
||||
app.on('activate', () => {
|
||||
// On OS X it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (win === null) {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
// Catch Error
|
||||
// throw e;
|
||||
}
|
||||
9930
web/package-lock.json
generated
Normal file
9930
web/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
60
web/package.js
Normal file
60
web/package.js
Normal file
@@ -0,0 +1,60 @@
|
||||
"use strict";
|
||||
|
||||
var packager = require('electron-packager');
|
||||
const pkg = require('./package.json');
|
||||
const argv = require('minimist')(process.argv.slice(1));
|
||||
|
||||
const appName = argv.name || pkg.name;
|
||||
const buildVersion = pkg.version || '1.0';
|
||||
const shouldUseAsar = argv.asar || false;
|
||||
const shouldBuildAll = argv.all || false;
|
||||
const arch = argv.arch || 'all';
|
||||
const platform = argv.platform || 'darwin';
|
||||
|
||||
const DEFAULT_OPTS = {
|
||||
dir: './dist',
|
||||
name: appName,
|
||||
asar: shouldUseAsar,
|
||||
buildVersion: buildVersion
|
||||
};
|
||||
|
||||
|
||||
pack(platform, arch, function done(err, appPath) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
} else {
|
||||
console.log('Application packaged successfuly!', appPath);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
function pack(plat, arch, cb) {
|
||||
// there is no darwin ia32 electron
|
||||
if (plat === 'darwin' && arch === 'ia32') return;
|
||||
|
||||
let icon = 'src/favicon';
|
||||
|
||||
if (icon) {
|
||||
DEFAULT_OPTS.icon = icon + (() => {
|
||||
let extension = '.png';
|
||||
if (plat === 'darwin') {
|
||||
extension = '.icns';
|
||||
} else if (plat === 'win32') {
|
||||
extension = '.ico';
|
||||
}
|
||||
return extension;
|
||||
})();
|
||||
}
|
||||
|
||||
const opts = Object.assign({}, DEFAULT_OPTS, {
|
||||
platform: plat,
|
||||
arch,
|
||||
prune: true,
|
||||
overwrite: true,
|
||||
all: shouldBuildAll,
|
||||
out: `app-builds`
|
||||
});
|
||||
|
||||
console.log(opts)
|
||||
packager(opts, cb);
|
||||
}
|
||||
151
web/package.json
151
web/package.json
@@ -1,38 +1,117 @@
|
||||
{
|
||||
"name": "angular-seed",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"description": "A starter project for AngularJS",
|
||||
"repository": "https://github.com/angular/angular-seed",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"bower": "^1.7.7",
|
||||
"http-server": "^0.9.0",
|
||||
"jasmine-core": "^2.4.1",
|
||||
"karma": "^0.13.22",
|
||||
"karma-chrome-launcher": "^0.2.3",
|
||||
"karma-firefox-launcher": "^0.1.7",
|
||||
"karma-jasmine": "^0.3.8",
|
||||
"karma-junit-reporter": "^0.4.1",
|
||||
"protractor": "^3.2.2",
|
||||
"express": "latest",
|
||||
"requestify": "latest",
|
||||
"body-parser": "latest"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "bower install",
|
||||
"prestart": "npm install",
|
||||
"start": "node server.js",
|
||||
"pretest": "npm install",
|
||||
"test": "karma start karma.conf.js",
|
||||
"test-single-run": "karma start karma.conf.js --single-run",
|
||||
"preupdate-webdriver": "npm install",
|
||||
"update-webdriver": "webdriver-manager update",
|
||||
"preprotractor": "npm run update-webdriver",
|
||||
"protractor": "protractor e2e-tests/protractor.conf.js",
|
||||
"update-index-async": "node -e \"var fs=require('fs'),indexFile='app/index-async.html',loaderFile='app/bower_components/angular-loader/angular-loader.min.js',loaderText=fs.readFileSync(loaderFile,'utf-8').split(/sourceMappingURL=angular-loader.min.js.map/).join('sourceMappingURL=bower_components/angular-loader/angular-loader.min.js.map'),indexText=fs.readFileSync(indexFile,'utf-8').split(/\\/\\/@@NG_LOADER_START@@[\\s\\S]*\\/\\/@@NG_LOADER_END@@/).join('//@@NG_LOADER_START@@\\n'+loaderText+' //@@NG_LOADER_END@@');fs.writeFileSync(indexFile,indexText);\""
|
||||
},
|
||||
"dependencies": {
|
||||
"request": "^2.74.0"
|
||||
}
|
||||
"name": "gocryptotrader-web",
|
||||
"version": "0.1.0",
|
||||
"description": "Front-end interface for GoCryptoTrader",
|
||||
"homepage": "https://github.com/thrasher-/gocryptotrader",
|
||||
"author": {
|
||||
"name": "Scott",
|
||||
"email": "scott@gloriousedge.com",
|
||||
"github": "https://github.com/gloriousCode/",
|
||||
"website": "https://www.gloriousedge.com"
|
||||
},
|
||||
"contributers": [
|
||||
{
|
||||
"name": "Maxime GRIS",
|
||||
"email": "maxime.gris@gmail.com",
|
||||
"github": "https://github.com/maximegris/"
|
||||
}
|
||||
],
|
||||
"keywords": [
|
||||
"angular",
|
||||
"electron",
|
||||
"typescript",
|
||||
"sass",
|
||||
"bitcoin",
|
||||
"exchange"
|
||||
],
|
||||
"main": "main.js",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"lint": "ng lint",
|
||||
"start": "webpack --watch",
|
||||
"start:web": "webpack-dev-server --content-base . --port 4200 --inline",
|
||||
"build:electron:main": "tsc main.ts --outDir dist && copyfiles package.json dist && cd dist && npm install --prod && cd ..",
|
||||
"build": "webpack --display-error-details && npm run build:electron:main",
|
||||
"build:prod": "cross-env NODE_ENV=production npm run build",
|
||||
"electron:serve": "npm run build:electron:main && electron ./dist --serve",
|
||||
"electron:test": "electron ./dist",
|
||||
"electron:dev": "npm run build && electron ./dist",
|
||||
"electron:prod": "npm run build:prod && electron ./dist",
|
||||
"electron:linux": "npm run build:prod && node package.js --asar --platform=linux --arch=x64",
|
||||
"electron:windows": "npm run build:prod && node package.js --asar --platform=win32 --arch=ia32",
|
||||
"electron:mac": "npm run build:prod && node package.js --asar --platform=darwin --arch=x64",
|
||||
"test": "karma start ./karma.conf.js",
|
||||
"pree2e": "webdriver-manager update --standalone false --gecko false --quiet && npm run build",
|
||||
"e2e": "protractor ./protractor.conf.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "^4.3.4",
|
||||
"@angular/cdk": "^2.0.0-beta.10",
|
||||
"@angular/common": "4.3.0",
|
||||
"@angular/compiler": "4.3.0",
|
||||
"@angular/core": "4.3.4",
|
||||
"@angular/forms": "4.3.0",
|
||||
"@angular/http": "4.3.0",
|
||||
"@angular/material": "^2.0.0-beta.10",
|
||||
"@angular/platform-browser": "4.3.0",
|
||||
"@angular/platform-browser-dynamic": "4.3.0",
|
||||
"@angular/router": "4.3.0",
|
||||
"core-js": "2.4.1",
|
||||
"enhanced-resolve": "3.3.0",
|
||||
"rxjs": "^5.4.3",
|
||||
"zone.js": "0.8.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/cli": "1.2.1",
|
||||
"@angular/compiler-cli": "4.3.0",
|
||||
"@types/bluebird": "3.5.8",
|
||||
"@types/core-js": "0.9.36",
|
||||
"@types/jasmine": "2.5.53",
|
||||
"@types/node": "7.0.7",
|
||||
"autoprefixer": "7.1.1",
|
||||
"codelyzer": "3.1.1",
|
||||
"copyfiles": "1.2.0",
|
||||
"cross-env": "5.0.1",
|
||||
"css-loader": "0.28.4",
|
||||
"cssnano": "3.10.0",
|
||||
"electron": "1.6.11",
|
||||
"electron-packager": "8.7.2",
|
||||
"electron-reload": "1.2.1",
|
||||
"exports-loader": "0.6.4",
|
||||
"extract-zip": "=1.6.5",
|
||||
"file-loader": "0.11.2",
|
||||
"html-loader": "0.4.5",
|
||||
"istanbul-instrumenter-loader": "2.0.0",
|
||||
"jasmine-core": "2.6.4",
|
||||
"jasmine-spec-reporter": "4.1.1",
|
||||
"json-loader": "0.5.4",
|
||||
"karma": "1.7.0",
|
||||
"karma-chrome-launcher": "2.2.0",
|
||||
"karma-cli": "1.0.1",
|
||||
"karma-coverage-istanbul-reporter": "0.2.0",
|
||||
"karma-jasmine": "1.1.0",
|
||||
"karma-jasmine-html-reporter": "0.2.2",
|
||||
"karma-sourcemap-loader": "0.3.7",
|
||||
"less-loader": "4.0.4",
|
||||
"minimist": "1.2.0",
|
||||
"mkdirp": "0.5.1",
|
||||
"postcss-loader": "1.3.3",
|
||||
"postcss-url": "7.0.0",
|
||||
"protractor": "5.1.2",
|
||||
"raw-loader": "0.5.1",
|
||||
"sass-loader": "6.0.6",
|
||||
"script-loader": "0.7.0",
|
||||
"source-map-loader": "0.2.1",
|
||||
"style-loader": "0.18.2",
|
||||
"stylus-loader": "3.0.1",
|
||||
"ts-node": "3.1.0",
|
||||
"tslint": "5.4.3",
|
||||
"typescript": "2.4.1",
|
||||
"url-loader": "0.5.9",
|
||||
"webdriver-manager": "12.0.6",
|
||||
"webpack": "3.3.0",
|
||||
"webpack-dev-server": "2.5.0"
|
||||
},
|
||||
"license": "SEE LICENSE IN LICENSE.md"
|
||||
}
|
||||
|
||||
36
web/protractor.conf.js
Normal file
36
web/protractor.conf.js
Normal file
@@ -0,0 +1,36 @@
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||
|
||||
exports.config = {
|
||||
allScriptsTimeout: 25000,
|
||||
getPageTimeout: 15000,
|
||||
delayBrowserTimeInSeconds: 0,
|
||||
specs: [
|
||||
'./e2e/**/*.e2e-spec.ts'
|
||||
],
|
||||
capabilities: {
|
||||
'browserName': 'chrome',
|
||||
chromeOptions: {
|
||||
binary: './node_modules/electron/dist/electron.exe',
|
||||
args: ['--test-type=webdriver', 'app=dist/main.js']
|
||||
}
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
framework: 'jasmine2',
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
defaultTimeoutInterval: 30000,
|
||||
print: function () { }
|
||||
},
|
||||
beforeLaunch: function () {
|
||||
require('ts-node').register({
|
||||
project: 'e2e/tsconfig.e2e.json'
|
||||
});
|
||||
},
|
||||
onPrepare() {
|
||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||
}
|
||||
};
|
||||
@@ -1,63 +0,0 @@
|
||||
var express = require('express')
|
||||
, app = express();
|
||||
var requestify = require('requestify');
|
||||
var bodyParser = require('body-parser')
|
||||
|
||||
var request = require('request');
|
||||
var path = __dirname + '/app/';
|
||||
|
||||
app.use("/bower_components", express.static(path + '/bower_components'));
|
||||
app.use( bodyParser.json() );
|
||||
|
||||
|
||||
app.get("/",function(req,res){
|
||||
res.sendFile(path + "index.html");
|
||||
});
|
||||
|
||||
app.use("/", express.static(path + '/'));
|
||||
|
||||
app.get('/data/all-enabled-currencies', function (req, res) {
|
||||
request({
|
||||
url :'http://localhost:9050/exchanges/enabled/latest/all'
|
||||
},function(err, resp, body){
|
||||
res.send(body);
|
||||
})
|
||||
});
|
||||
|
||||
app.get('/data/all-enabled-exchange-account-info', function (req, res) {
|
||||
request({
|
||||
url :'http://localhost:9050/exchanges/enabled/accounts/all'
|
||||
},function(err, resp, body){
|
||||
res.send(body);
|
||||
})
|
||||
});
|
||||
|
||||
app.get('/config/all', function (req, res) {
|
||||
request({
|
||||
url :'http://localhost:9050/config/all'
|
||||
},function(err, resp, body){
|
||||
res.send(body);
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// Posts
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
app.post('/config/all/save', function(req, res) {
|
||||
requestify.post('http://localhost:9050/config/all/save', {
|
||||
Data: req.body
|
||||
})
|
||||
.then(function(response) {
|
||||
console.log(response);
|
||||
res.send(response.body);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
var port = process.env.GCT_WEB_PORT || 80;
|
||||
app.listen(port, function(){
|
||||
console.log(`GoCyptoTrader website running! Enter http://localhost:${port}/ into browser`);
|
||||
});
|
||||
33
web/src/app/app-routing.module.ts
Normal file
33
web/src/app/app-routing.module.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { HomeComponent } from './pages/home/home.component';
|
||||
import { SettingsComponent } from './pages/settings/settings.component';
|
||||
import { AboutComponent } from './pages/about/about.component';
|
||||
import { DashboardComponent } from './pages/dashboard/dashboard.component';
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: DashboardComponent
|
||||
},
|
||||
{
|
||||
path:'about',
|
||||
component: AboutComponent
|
||||
},
|
||||
{
|
||||
path:'dashboard',
|
||||
component: DashboardComponent
|
||||
},
|
||||
{
|
||||
path: 'settings',
|
||||
component: SettingsComponent
|
||||
}
|
||||
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes, {useHash: true})],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
9
web/src/app/app.component.html
Normal file
9
web/src/app/app.component.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<app-navbar></app-navbar>
|
||||
|
||||
<div class="sidebar"></div>
|
||||
|
||||
<div class="main">
|
||||
<div class="main-content">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
24
web/src/app/app.component.spec.ts
Normal file
24
web/src/app/app.component.spec.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { TestBed, async } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
import { ElectronService } from 'app/providers/electron.service';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
providers : [
|
||||
ElectronService
|
||||
],
|
||||
imports: [RouterTestingModule]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
it('should create the app', async(() => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
}));
|
||||
});
|
||||
22
web/src/app/app.component.ts
Normal file
22
web/src/app/app.component.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ElectronService } from './providers/electron.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss'],
|
||||
})
|
||||
export class AppComponent {
|
||||
constructor(public electronService: ElectronService) {
|
||||
|
||||
if (electronService.isElectron()) {
|
||||
console.log('Mode electron');
|
||||
// Check if electron is correctly injected (see externals in webpack.config.js)
|
||||
console.log('c', electronService.ipcRenderer);
|
||||
// Check if nodeJs childProcess is correctly injected (see externals in webpack.config.js)
|
||||
console.log('c', electronService.childProcess);
|
||||
} else {
|
||||
console.log('Mode web');
|
||||
}
|
||||
}
|
||||
}
|
||||
77
web/src/app/app.module.ts
Normal file
77
web/src/app/app.module.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import 'zone.js/dist/zone-mix';
|
||||
import 'reflect-metadata';
|
||||
import 'polyfills';
|
||||
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { HttpModule } from '@angular/http';
|
||||
import { NgModule, Injectable } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
import {
|
||||
MdButtonModule,
|
||||
MdCardModule,
|
||||
MdMenuModule,
|
||||
MdToolbarModule,
|
||||
MdIconModule,
|
||||
MdFormFieldModule,
|
||||
MdInputModule,
|
||||
MdCheckboxModule,
|
||||
MdGridListModule,
|
||||
MdProgressSpinnerModule,
|
||||
} from '@angular/material';
|
||||
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { HomeComponent } from './pages/home/home.component';
|
||||
import { AboutComponent } from './pages/about/about.component';
|
||||
import { SettingsComponent } from './pages/settings/settings.component';
|
||||
import { DashboardComponent } from './pages/dashboard/dashboard.component';
|
||||
|
||||
import { NavbarComponent } from './shared/navbar/navbar.component';
|
||||
import { ExchangeCurrencyTickerComponent } from './shared/exchange-currency-ticker/exchange-currency-ticker.component';
|
||||
import { AllEnabledCurrencyTickersComponent } from './shared/all-enabled-currency-tickers/all-enabled-currency-tickers.component';
|
||||
//services
|
||||
import { WebsocketService } from './services/websocket/websocket.service';
|
||||
import { WebsocketHandlerService } from './services/websocket-handler/websocket-handler.service';
|
||||
import { ElectronService } from './providers/electron.service';
|
||||
//Routing
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
|
||||
import * as Rx from 'rxjs/Rx';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
HomeComponent,
|
||||
AboutComponent,
|
||||
NavbarComponent,
|
||||
SettingsComponent,
|
||||
DashboardComponent,
|
||||
ExchangeCurrencyTickerComponent,
|
||||
AllEnabledCurrencyTickersComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
HttpModule,
|
||||
AppRoutingModule,
|
||||
BrowserAnimationsModule,
|
||||
MdButtonModule,
|
||||
MdMenuModule,
|
||||
MdCardModule,
|
||||
MdToolbarModule,
|
||||
MdIconModule,
|
||||
MdFormFieldModule,
|
||||
MdInputModule,
|
||||
MdCheckboxModule,
|
||||
MdGridListModule,
|
||||
MdProgressSpinnerModule,
|
||||
],
|
||||
providers: [ElectronService,WebsocketService,WebsocketHandlerService],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule {
|
||||
|
||||
}
|
||||
3
web/src/app/pages/about/about.component.html
Normal file
3
web/src/app/pages/about/about.component.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<p>
|
||||
about works!
|
||||
</p>
|
||||
0
web/src/app/pages/about/about.component.scss
Normal file
0
web/src/app/pages/about/about.component.scss
Normal file
25
web/src/app/pages/about/about.component.spec.ts
Normal file
25
web/src/app/pages/about/about.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AboutComponent } from './about.component';
|
||||
|
||||
describe('AboutComponent', () => {
|
||||
let component: AboutComponent;
|
||||
let fixture: ComponentFixture<AboutComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ AboutComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AboutComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
15
web/src/app/pages/about/about.component.ts
Normal file
15
web/src/app/pages/about/about.component.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-about',
|
||||
templateUrl: './about.component.html',
|
||||
styleUrls: ['./about.component.scss']
|
||||
})
|
||||
export class AboutComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
1
web/src/app/pages/dashboard/dashboard.component.html
Normal file
1
web/src/app/pages/dashboard/dashboard.component.html
Normal file
@@ -0,0 +1 @@
|
||||
<app-all-enabled-currency-tickers></app-all-enabled-currency-tickers>
|
||||
25
web/src/app/pages/dashboard/dashboard.component.spec.ts
Normal file
25
web/src/app/pages/dashboard/dashboard.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DashboardComponent } from './dashboard.component';
|
||||
|
||||
describe('DashboardComponent', () => {
|
||||
let component: DashboardComponent;
|
||||
let fixture: ComponentFixture<DashboardComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ DashboardComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DashboardComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
18
web/src/app/pages/dashboard/dashboard.component.ts
Normal file
18
web/src/app/pages/dashboard/dashboard.component.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
templateUrl: './dashboard.component.html',
|
||||
styleUrls: ['./dashboard.component.scss'],
|
||||
})
|
||||
export class DashboardComponent implements OnInit {
|
||||
constructor()
|
||||
{
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
2
web/src/app/pages/home/home.component.html
Normal file
2
web/src/app/pages/home/home.component.html
Normal file
@@ -0,0 +1,2 @@
|
||||
<div class="container">
|
||||
</div>
|
||||
0
web/src/app/pages/home/home.component.scss
Normal file
0
web/src/app/pages/home/home.component.scss
Normal file
38
web/src/app/pages/home/home.component.spec.ts
Normal file
38
web/src/app/pages/home/home.component.spec.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HomeComponent } from './home.component';
|
||||
|
||||
describe('HomeComponent', () => {
|
||||
let component: HomeComponent;
|
||||
let fixture: ComponentFixture<HomeComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ HomeComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(HomeComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'App works !'`, async(() => {
|
||||
fixture = TestBed.createComponent(HomeComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app.title).toEqual('App works !');
|
||||
}));
|
||||
|
||||
it('should render title in a h1 tag', async(() => {
|
||||
fixture = TestBed.createComponent(HomeComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
expect(compiled.querySelector('h1').textContent).toContain('App works !');
|
||||
}));
|
||||
});
|
||||
17
web/src/app/pages/home/home.component.ts
Normal file
17
web/src/app/pages/home/home.component.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
templateUrl: './home.component.html',
|
||||
styleUrls: ['./home.component.scss'],
|
||||
|
||||
})
|
||||
export class HomeComponent implements OnInit {
|
||||
title = `App works !`;
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
93
web/src/app/pages/settings/settings.component.html
Normal file
93
web/src/app/pages/settings/settings.component.html
Normal file
@@ -0,0 +1,93 @@
|
||||
<div class="loading-spinner" *ngIf="settings === null">
|
||||
<md-progress-spinner mode="indeterminate"></md-progress-spinner>
|
||||
</div>
|
||||
<div *ngIf="settings !== null">
|
||||
<button (click)="saveSettings()" md-fab color="accent" class="md-fab md-fab-bottom-right">Save</button>
|
||||
<form *ngIf="settings.SMSGlobal != null">
|
||||
<md-card class="exchange-card">
|
||||
<md-card-header>
|
||||
<md-card-title>SMS Global Settings</md-card-title>
|
||||
</md-card-header>
|
||||
<md-card-content>
|
||||
<table cellspacing="0">
|
||||
<tr>
|
||||
<td>
|
||||
<md-checkbox name="smsEnabled" [(ngModel)]="settings.SMSGlobal.Enabled">Enabled</md-checkbox>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<md-grid-list cols="2" rowHeight="3:1">
|
||||
<md-grid-tile>
|
||||
<md-form-field>
|
||||
<input mdInput name="smsUsername" [(ngModel)]="settings.SMSGlobal.Username" [disabled]="!settings?.SMSGlobal.Enabled" placeholder="Username">
|
||||
</md-form-field>
|
||||
</md-grid-tile>
|
||||
<md-grid-tile>
|
||||
<md-form-field>
|
||||
<input mdInput name="smsPassword" [(ngModel)]="settings.SMSGlobal.Password" [disabled]="!settings?.SMSGlobal.Enabled" placeholder="Password">
|
||||
</md-form-field>
|
||||
</md-grid-tile>
|
||||
</md-grid-list>
|
||||
<md-grid-list cols="3" rowHeight="2:1" *ngFor="let contact of settings.SMSGlobal?.Contacts">
|
||||
<md-grid-tile>
|
||||
<md-checkbox name="contactEnabled" [disabled]="!settings?.SMSGlobal.Enabled" [(ngModel)]="contact.Enabled">Enabled</md-checkbox>
|
||||
</md-grid-tile>
|
||||
<md-grid-tile>
|
||||
<md-form-field>
|
||||
<input mdInput name="contactUsername" [(ngModel)]="contact.Name" [disabled]="!settings.SMSGlobal.Enabled || !contact.Enabled" placeholder="Contact Name">
|
||||
</md-form-field>
|
||||
</md-grid-tile>
|
||||
<md-grid-tile>
|
||||
<md-form-field>
|
||||
<input mdInput name="contactPassword" [(ngModel)]="contact.Number" [disabled]="!settings.SMSGlobal.Enabled || !contact.Enabled" placeholder="Contact Number">
|
||||
</md-form-field>
|
||||
</md-grid-tile>
|
||||
</md-grid-list>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
</form>
|
||||
|
||||
<form *ngFor="let exchange of settings?.Exchanges">
|
||||
<md-card class="exchange-card">
|
||||
<md-card-header>
|
||||
<md-card-title>{{exchange.Name}} Exchange Settings</md-card-title>
|
||||
</md-card-header>
|
||||
<md-card-content>
|
||||
<table cellspacing="0">
|
||||
<tr>
|
||||
<td>
|
||||
<md-checkbox name="exchangeEnabled" [(ngModel)]="exchange.Enabled">Enabled</md-checkbox>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<md-grid-list cols="3" rowHeight="2:1">
|
||||
<md-grid-tile>
|
||||
<md-form-field>
|
||||
<input mdInput name="apiKey" [(ngModel)]="exchange.APIKey" [disabled]="!exchange.Enabled" placeholder="Exchange API Key">
|
||||
</md-form-field>
|
||||
</md-grid-tile>
|
||||
<md-grid-tile>
|
||||
<md-form-field>
|
||||
<input mdInput name="apiSecretKey" [(ngModel)]="exchange.APISecret" [disabled]="!exchange.Enabled" placeholder="Exchange API Secret Key">
|
||||
</md-form-field>
|
||||
</md-grid-tile>
|
||||
<md-grid-tile>
|
||||
<md-form-field>
|
||||
<input mdInput name="apiClientId" [(ngModel)]="exchange.ClientID" [disabled]="!exchange.Enabled" placeholder="Exchange API ClientID (optional)">
|
||||
</md-form-field>
|
||||
</md-grid-tile>
|
||||
</md-grid-list>
|
||||
|
||||
<label>Enabled Currencies</label>
|
||||
|
||||
<md-grid-list cols="6" rowHeight="2:1">
|
||||
<md-grid-tile *ngFor="let currency of exchange.AvailablePairs.split(',')">
|
||||
<md-checkbox name="availableCurrency" [disabled]="true || !exchange.Enabled">{{currency}}</md-checkbox>
|
||||
</md-grid-tile>
|
||||
</md-grid-list>
|
||||
</md-card-content>
|
||||
</md-card>
|
||||
</form>
|
||||
|
||||
|
||||
</div>
|
||||
20
web/src/app/pages/settings/settings.component.scss
Normal file
20
web/src/app/pages/settings/settings.component.scss
Normal file
@@ -0,0 +1,20 @@
|
||||
.example-form {
|
||||
min-width: 150px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.example-full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.exchange-card {
|
||||
margin-bottom: 20px;
|
||||
width: 1000px;
|
||||
}
|
||||
|
||||
.md-fab {
|
||||
margin: 0;
|
||||
position: fixed;
|
||||
bottom: 5%;
|
||||
right: 2%;
|
||||
}
|
||||
25
web/src/app/pages/settings/settings.component.spec.ts
Normal file
25
web/src/app/pages/settings/settings.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SettingsComponent } from './settings.component';
|
||||
|
||||
describe('SettingsComponent', () => {
|
||||
let component: SettingsComponent;
|
||||
let fixture: ComponentFixture<SettingsComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ SettingsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SettingsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
145
web/src/app/pages/settings/settings.component.ts
Normal file
145
web/src/app/pages/settings/settings.component.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { WebsocketHandlerService } from './../../services/websocket-handler/websocket-handler.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings',
|
||||
templateUrl: './settings.component.html',
|
||||
styleUrls: ['./settings.component.scss']
|
||||
})
|
||||
|
||||
|
||||
export class SettingsComponent implements OnInit {
|
||||
public settings: Config = null;
|
||||
private ws: WebsocketHandlerService;
|
||||
private failCount = 0;
|
||||
private timer: any;
|
||||
|
||||
private getSettingsMessage = {
|
||||
Event: 'GetConfig',
|
||||
data: null,
|
||||
};
|
||||
|
||||
constructor(private websocketHandler: WebsocketHandlerService) {
|
||||
this.ws = websocketHandler;
|
||||
this.ws.messages.subscribe(msg => {
|
||||
|
||||
if (msg.Event === 'GetConfig') {
|
||||
this.settings = <Config>msg.data;
|
||||
} else if (msg.Event === 'SaveConfig') {
|
||||
// something!
|
||||
}
|
||||
});
|
||||
}
|
||||
ngOnInit() {
|
||||
this.getSettings();
|
||||
}
|
||||
|
||||
private getSettings(): void {
|
||||
this.ws.messages.next(this.getSettingsMessage);
|
||||
this.resendMessageIfPageRefreshed();
|
||||
}
|
||||
|
||||
|
||||
private saveSettings(): void {
|
||||
//Send the message
|
||||
var settingsSave = {
|
||||
Event: 'SaveConfig',
|
||||
data: this.settings,
|
||||
}
|
||||
this.ws.messages.next(settingsSave);
|
||||
}
|
||||
|
||||
//there has to be a better way
|
||||
private resendMessageIfPageRefreshed(): void {
|
||||
if (this.failCount <= 10) {
|
||||
setTimeout(() => {
|
||||
if (this.settings === null) {
|
||||
//console.log(this.failCount);
|
||||
//console.log('Settings hasnt been set. Trying again');
|
||||
this.failCount++;
|
||||
this.getSettings();
|
||||
}
|
||||
}, 1000);
|
||||
} else {
|
||||
// something has gone wrong
|
||||
console.log('Could not load settings. Check if GocryptoTrader server is running, otherwise open a ticket');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export interface CurrencyPairFormat {
|
||||
Uppercase: boolean;
|
||||
Delimiter: string;
|
||||
}
|
||||
|
||||
export interface PortfolioAddresses {
|
||||
Addresses?: any;
|
||||
}
|
||||
|
||||
export interface Contact {
|
||||
Name: string;
|
||||
Number: string;
|
||||
Enabled: boolean;
|
||||
}
|
||||
|
||||
export interface SMSGlobal {
|
||||
Enabled: boolean;
|
||||
Username: string;
|
||||
Password: string;
|
||||
Contacts: Contact[];
|
||||
}
|
||||
|
||||
export interface Webserver {
|
||||
Enabled: boolean;
|
||||
AdminUsername: string;
|
||||
AdminPassword: string;
|
||||
ListenAddress: string;
|
||||
WebsocketConnectionLimit: number;
|
||||
WebsocketAllowInsecureOrigin: boolean;
|
||||
}
|
||||
|
||||
export interface ConfigCurrencyPairFormat {
|
||||
Uppercase: boolean;
|
||||
Index: string;
|
||||
Delimiter: string;
|
||||
}
|
||||
|
||||
export interface RequestCurrencyPairFormat {
|
||||
Uppercase: boolean;
|
||||
Index: string;
|
||||
Delimiter: string;
|
||||
Separator: string;
|
||||
}
|
||||
|
||||
export interface Exchange {
|
||||
Name: string;
|
||||
Enabled: boolean;
|
||||
Verbose: boolean;
|
||||
Websocket: boolean;
|
||||
RESTPollingDelay: number;
|
||||
AuthenticatedAPISupport: boolean;
|
||||
APIKey: string;
|
||||
APISecret: string;
|
||||
AvailablePairs: string;
|
||||
EnabledPairs: string;
|
||||
BaseCurrencies: string;
|
||||
AssetTypes: string;
|
||||
ConfigCurrencyPairFormat: ConfigCurrencyPairFormat;
|
||||
RequestCurrencyPairFormat: RequestCurrencyPairFormat;
|
||||
ClientID: string;
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
Name: string;
|
||||
EncryptConfig?: number;
|
||||
Cryptocurrencies: string;
|
||||
CurrencyExchangeProvider: string;
|
||||
CurrencyPairFormat: CurrencyPairFormat;
|
||||
PortfolioAddresses: PortfolioAddresses;
|
||||
SMSGlobal: SMSGlobal;
|
||||
Webserver: Webserver;
|
||||
Exchanges: Exchange[];
|
||||
}
|
||||
|
||||
|
||||
26
web/src/app/providers/electron.service.ts
Normal file
26
web/src/app/providers/electron.service.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
// If you import a module but never use any of the imported values other than as TypeScript types,
|
||||
// the resulting javascript file will look as if you never imported the module at all.
|
||||
import { ipcRenderer } from 'electron';
|
||||
import * as childProcess from 'child_process';
|
||||
|
||||
@Injectable()
|
||||
export class ElectronService {
|
||||
|
||||
ipcRenderer: typeof ipcRenderer;
|
||||
childProcess: typeof childProcess;
|
||||
|
||||
constructor() {
|
||||
// Conditional imports
|
||||
if (this.isElectron()) {
|
||||
this.ipcRenderer = window.require('electron').ipcRenderer;
|
||||
this.childProcess = window.require('child_process');
|
||||
}
|
||||
}
|
||||
|
||||
isElectron = () => {
|
||||
return window && window.process && window.process.type;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { TestBed, inject } from '@angular/core/testing';
|
||||
|
||||
import { WebsocketHandlerService } from './websocket-handler.service';
|
||||
|
||||
describe('WebsocketHandlerService', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [WebsocketHandlerService]
|
||||
});
|
||||
});
|
||||
|
||||
it('should be created', inject([WebsocketHandlerService], (service: WebsocketHandlerService) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable, Subject } from 'rxjs/Rx';
|
||||
import { WebsocketService } from './../../services/websocket/websocket.service';
|
||||
|
||||
const WEBSOCKET_URL = 'ws://localhost:9050/ws';
|
||||
|
||||
export interface Message {
|
||||
Event: string,
|
||||
data:any,
|
||||
Exchange:string,
|
||||
AssetType:string
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class WebsocketHandlerService {
|
||||
public messages: Subject<any>;
|
||||
|
||||
private authenticateMessage = {
|
||||
Event:'auth',
|
||||
data:{"username":"admin","password":"e7cf3ef4f17c3999a94f2c6f612e8a888e5b1026878e4e19398b23bd38ec221a"},
|
||||
}
|
||||
|
||||
public authenticate() {
|
||||
this.messages.next(this.authenticateMessage);
|
||||
}
|
||||
|
||||
constructor(wsService: WebsocketService) {
|
||||
this.messages = <Subject<Message>>wsService
|
||||
.connect(WEBSOCKET_URL)
|
||||
.map((response: MessageEvent): Message => {
|
||||
|
||||
let data = JSON.parse(response.data);
|
||||
// variables aren't consistent yet. Here's a hack!
|
||||
var dataData = data.Data === undefined ? data.data : data.Data;
|
||||
var eventEvent = data.Event === undefined ? data.event : data.Event;
|
||||
return {
|
||||
Event: eventEvent,
|
||||
data: dataData,
|
||||
Exchange: data.exchange,
|
||||
AssetType: data.assetType
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
15
web/src/app/services/websocket/websocket.service.spec.ts
Normal file
15
web/src/app/services/websocket/websocket.service.spec.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { TestBed, inject } from '@angular/core/testing';
|
||||
|
||||
import { WebsocketService } from './websocket.service';
|
||||
|
||||
describe('WebsocketService', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [WebsocketService]
|
||||
});
|
||||
});
|
||||
|
||||
it('should be created', inject([WebsocketService], (service: WebsocketService) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
});
|
||||
45
web/src/app/services/websocket/websocket.service.ts
Normal file
45
web/src/app/services/websocket/websocket.service.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import * as Rx from 'rxjs/Rx';
|
||||
|
||||
@Injectable()
|
||||
export class WebsocketService {
|
||||
constructor() { }
|
||||
|
||||
private subject: Rx.Subject<MessageEvent>;
|
||||
|
||||
public connect(url): Rx.Subject<MessageEvent> {
|
||||
if (!this.subject) {
|
||||
this.subject = this.create(url);
|
||||
}
|
||||
return this.subject;
|
||||
}
|
||||
|
||||
private authenticateMessage = {
|
||||
Event:'auth',
|
||||
data:{"username":"admin","password":"e7cf3ef4f17c3999a94f2c6f612e8a888e5b1026878e4e19398b23bd38ec221a"},
|
||||
}
|
||||
|
||||
private isAuth = false;
|
||||
|
||||
private create(url): Rx.Subject<MessageEvent> {
|
||||
let ws = new WebSocket(url);
|
||||
|
||||
let observable = Rx.Observable.create(
|
||||
(obs: Rx.Observer<MessageEvent>) => {
|
||||
ws.onmessage = obs.next.bind(obs);
|
||||
ws.onerror = obs.error.bind(obs);
|
||||
ws.onclose = obs.complete.bind(obs);
|
||||
return ws.close.bind(ws);
|
||||
})
|
||||
let observer = {
|
||||
next: (data: any) => {
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify(this.authenticateMessage));
|
||||
|
||||
ws.send(JSON.stringify(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
return Rx.Subject.create(observer, observable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<div class="loading-spinner" *ngIf="tickerCards === null || tickerCards.length === 0">
|
||||
<md-progress-spinner mode="indeterminate"></md-progress-spinner>
|
||||
</div>
|
||||
<md-grid-list cols="4" rowHeight="4:3" >
|
||||
<md-grid-tile *ngFor="let ticker of tickerCards">
|
||||
<app-exchange-currency-ticker [ticker]="ticker" ></app-exchange-currency-ticker>
|
||||
</md-grid-tile>
|
||||
</md-grid-list>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AllEnabledCurrencyTickersComponent } from './all-enabled-currency-tickers.component';
|
||||
|
||||
describe('AllEnabledCurrencyTickersComponent', () => {
|
||||
let component: AllEnabledCurrencyTickersComponent;
|
||||
let fixture: ComponentFixture<AllEnabledCurrencyTickersComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ AllEnabledCurrencyTickersComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AllEnabledCurrencyTickersComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,77 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { WebsocketHandlerService } from './../../services/websocket-handler/websocket-handler.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-all-enabled-currency-tickers',
|
||||
templateUrl: './all-enabled-currency-tickers.component.html',
|
||||
styleUrls: ['./all-enabled-currency-tickers.component.scss']
|
||||
})
|
||||
export class AllEnabledCurrencyTickersComponent implements OnInit {
|
||||
private ws: WebsocketHandlerService;
|
||||
allCurrencies:ExchangeCurrency[];
|
||||
tickerCards: TickerUpdate[];
|
||||
|
||||
constructor(private websocketHandler: WebsocketHandlerService) {
|
||||
this.ws = websocketHandler;
|
||||
this.allCurrencies = <ExchangeCurrency[]>[];
|
||||
this.tickerCards = <TickerUpdate[]>[];
|
||||
this.ws.messages.subscribe(msg => {
|
||||
if (msg.Event === 'ticker_update') {
|
||||
var modal = <ExchangeCurrency>{};
|
||||
modal.currencyPair = msg.data.CurrencyPair;
|
||||
modal.exchangeName = msg.Exchange;
|
||||
var found = false;
|
||||
|
||||
for(var i = 0; i< this.allCurrencies.length; i++) {
|
||||
if(this.allCurrencies[i].currencyPair === msg.data.CurrencyPair &&
|
||||
this.allCurrencies[i].exchangeName === msg.Exchange) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
//time to add
|
||||
var ticker = <TickerUpdate>msg.data;
|
||||
ticker.Exchange = msg.Exchange;
|
||||
this.tickerCards.push(ticker);
|
||||
this.allCurrencies.push(modal);
|
||||
} else {
|
||||
//time to replace
|
||||
for(var j = 0; j< this.tickerCards.length; j++) {
|
||||
if(this.tickerCards[j].Exchange === msg.Exchange
|
||||
&& this.tickerCards[j].CurrencyPair === msg.data.CurrencyPair) {
|
||||
var ticker = <TickerUpdate>msg.data;
|
||||
this.tickerCards[j] = ticker;
|
||||
this.tickerCards[j].Exchange = msg.Exchange;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
ngOnInit() { }
|
||||
}
|
||||
|
||||
export interface ExchangeCurrency {
|
||||
currencyPair: string;
|
||||
exchangeName:string;
|
||||
}
|
||||
|
||||
export interface CurrencyPair {
|
||||
delimiter: string;
|
||||
first_currency: string;
|
||||
second_currency: string;
|
||||
}
|
||||
|
||||
export interface TickerUpdate {
|
||||
Pair: CurrencyPair;
|
||||
CurrencyPair: string;
|
||||
Last: number;
|
||||
High: number;
|
||||
Low: number;
|
||||
Bid: number;
|
||||
Ask: number;
|
||||
Volume: number;
|
||||
PriceATH: number;
|
||||
Exchange:string;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<md-card class="exchange-card one-time-animation" >
|
||||
<md-card-header>
|
||||
<md-card-title>{{ticker?.Exchange}} {{ticker?.CurrencyPair}} Ticker update</md-card-title>
|
||||
</md-card-header>
|
||||
<md-card-content>
|
||||
<md-grid-list cols="3" rowHeight="16:9" >
|
||||
<md-grid-tile>
|
||||
<p>Last: {{ticker?.Last | number:'1.0-1'}}</p>
|
||||
</md-grid-tile>
|
||||
<md-grid-tile>
|
||||
<p> Low: {{ticker?.Low | number:'1.0-1'}}</p>
|
||||
</md-grid-tile>
|
||||
<md-grid-tile>
|
||||
<p> High: {{ticker?.High | number:'1.0-1'}}</p>
|
||||
</md-grid-tile>
|
||||
|
||||
<md-grid-tile>
|
||||
<p> Bid: {{ticker?.Bid | number:'1.0-1'}}</p>
|
||||
</md-grid-tile>
|
||||
<md-grid-tile>
|
||||
<p> Ask: {{ticker?.Ask | number:'1.0-1'}}</p>
|
||||
</md-grid-tile>
|
||||
<md-grid-tile>
|
||||
<p>Volume: {{ticker?.Volume | number:'1.0-1'}}</p>
|
||||
</md-grid-tile>
|
||||
</md-grid-list>
|
||||
</md-card-content>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user