mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-14 07:26:47 +00:00
Engine improvements
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
run:
|
||||
deadline: 30s
|
||||
deadline: 40s
|
||||
issues-exit-code: 1
|
||||
tests: true
|
||||
skip-dirs:
|
||||
|
||||
@@ -116,7 +116,15 @@ func testWrappers(e exchange.IBotExchange) []string {
|
||||
funcs = append(funcs, "GetFundingHistory")
|
||||
}
|
||||
|
||||
_, err = e.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1000000, 10000000000, "meow")
|
||||
s := &exchange.OrderSubmission{
|
||||
Pair: p,
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Amount: 1000000,
|
||||
Price: 10000000000,
|
||||
ClientID: "meow",
|
||||
}
|
||||
_, err = e.SubmitOrder(s)
|
||||
if err == common.ErrNotYetImplemented {
|
||||
funcs = append(funcs, "SubmitOrder")
|
||||
}
|
||||
|
||||
@@ -171,6 +171,53 @@ func disableExchange(c *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var getExchangeOTPCommand = cli.Command{
|
||||
Name: "getexchangeotp",
|
||||
Usage: "gets a specific exchanges otp code",
|
||||
ArgsUsage: "<exchange>",
|
||||
Action: getExchangeOTPCode,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "exchange",
|
||||
Usage: "the exchange to get the otp code for",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func getExchangeOTPCode(c *cli.Context) error {
|
||||
if c.NArg() == 0 && c.NumFlags() == 0 {
|
||||
cli.ShowCommandHelp(c, "getexchangeotp")
|
||||
return nil
|
||||
}
|
||||
|
||||
conn, err := setupClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
var exchangeName string
|
||||
if c.IsSet("exchange") {
|
||||
exchangeName = c.String("exchange")
|
||||
} else {
|
||||
exchangeName = c.Args().First()
|
||||
}
|
||||
|
||||
client := gctrpc.NewGoCryptoTraderClient(conn)
|
||||
result, err := client.GetExchangeOTPCode(context.Background(),
|
||||
&gctrpc.GenericExchangeNameRequest{
|
||||
Exchange: exchangeName,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonOutput(result)
|
||||
return nil
|
||||
}
|
||||
|
||||
var getExchangeInfoCommand = cli.Command{
|
||||
Name: "getexchangeinfo",
|
||||
Usage: "gets a specific exchanges info",
|
||||
|
||||
@@ -81,6 +81,7 @@ func main() {
|
||||
getExchangesCommand,
|
||||
enableExchangeCommand,
|
||||
disableExchangeCommand,
|
||||
getExchangeOTPCommand,
|
||||
getExchangeInfoCommand,
|
||||
getTickerCommand,
|
||||
getTickersCommand,
|
||||
|
||||
@@ -1,56 +1,15 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/assets"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// global vars contain staged update data that will be sent to the communication
|
||||
// mediums
|
||||
var (
|
||||
TickerStaged map[string]map[assets.AssetType]map[string]ticker.Price
|
||||
OrderbookStaged map[string]map[assets.AssetType]map[string]Orderbook
|
||||
PortfolioStaged Portfolio
|
||||
SettingsStaged Settings
|
||||
ServiceStarted time.Time
|
||||
m sync.Mutex
|
||||
ServiceStarted time.Time
|
||||
)
|
||||
|
||||
// Orderbook holds the minimal orderbook details to be sent to a communication
|
||||
// medium
|
||||
type Orderbook struct {
|
||||
CurrencyPair string
|
||||
AssetType string
|
||||
TotalAsks float64
|
||||
TotalBids float64
|
||||
LastUpdated string
|
||||
}
|
||||
|
||||
// Ticker holds the minimal orderbook details to be sent to a communication
|
||||
// medium
|
||||
type Ticker struct {
|
||||
CurrencyPair string
|
||||
LastUpdated string
|
||||
}
|
||||
|
||||
// Portfolio holds the minimal portfolio details to be sent to a communication
|
||||
// medium
|
||||
type Portfolio struct {
|
||||
ProfitLoss string
|
||||
}
|
||||
|
||||
// Settings holds the minimal setting details to be sent to a communication
|
||||
// medium
|
||||
type Settings struct {
|
||||
EnabledExchanges string
|
||||
EnabledCommunications string
|
||||
}
|
||||
|
||||
// Base enforces standard variables across communication packages
|
||||
type Base struct {
|
||||
Name string
|
||||
@@ -61,9 +20,8 @@ type Base struct {
|
||||
|
||||
// Event is a generalise event type
|
||||
type Event struct {
|
||||
Type string
|
||||
GainLoss string
|
||||
TradeDetails string
|
||||
Type string
|
||||
Message string
|
||||
}
|
||||
|
||||
// IsEnabled returns if the comms package has been enabled in the configuration
|
||||
@@ -82,83 +40,6 @@ func (b *Base) GetName() string {
|
||||
return b.Name
|
||||
}
|
||||
|
||||
// GetTicker returns staged ticker data
|
||||
func (b *Base) GetTicker(exchangeName string) string {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
tickerPrice, ok := TickerStaged[exchangeName]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
var tickerPrices []ticker.Price
|
||||
for x := range tickerPrice {
|
||||
for y := range tickerPrice[x] {
|
||||
tickerPrices = append(tickerPrices, tickerPrice[x][y])
|
||||
}
|
||||
}
|
||||
|
||||
var packagedTickers []string
|
||||
for i := range tickerPrices {
|
||||
packagedTickers = append(packagedTickers, fmt.Sprintf(
|
||||
"Currency Pair: %s Ask: %f, Bid: %f High: %f Last: %f Low: %f ATH: %f Volume: %f",
|
||||
tickerPrices[i].Pair,
|
||||
tickerPrices[i].Ask,
|
||||
tickerPrices[i].Bid,
|
||||
tickerPrices[i].High,
|
||||
tickerPrices[i].Last,
|
||||
tickerPrices[i].Low,
|
||||
tickerPrices[i].PriceATH,
|
||||
tickerPrices[i].Volume))
|
||||
}
|
||||
return strings.Join(packagedTickers, "\n")
|
||||
}
|
||||
|
||||
// GetOrderbook returns staged orderbook data
|
||||
func (b *Base) GetOrderbook(exchangeName string) string {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
orderbook, ok := OrderbookStaged[exchangeName]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
var orderbooks []Orderbook
|
||||
for _, x := range orderbook {
|
||||
for _, y := range x {
|
||||
orderbooks = append(orderbooks, y)
|
||||
}
|
||||
}
|
||||
|
||||
var packagedOrderbooks []string
|
||||
for i := range orderbooks {
|
||||
packagedOrderbooks = append(packagedOrderbooks, fmt.Sprintf(
|
||||
"Currency Pair: %s AssetType: %s, LastUpdated: %s TotalAsks: %f TotalBids: %f",
|
||||
orderbooks[i].CurrencyPair,
|
||||
orderbooks[i].AssetType,
|
||||
orderbooks[i].LastUpdated,
|
||||
orderbooks[i].TotalAsks,
|
||||
orderbooks[i].TotalBids))
|
||||
}
|
||||
return strings.Join(packagedOrderbooks, "\n")
|
||||
}
|
||||
|
||||
// GetPortfolio returns staged portfolio info
|
||||
func (b *Base) GetPortfolio() string {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
return fmt.Sprintf("%v", PortfolioStaged)
|
||||
}
|
||||
|
||||
// GetSettings returns stage setting info
|
||||
func (b *Base) GetSettings() string {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
return fmt.Sprintf("%v", SettingsStaged)
|
||||
}
|
||||
|
||||
// GetStatus returns status data
|
||||
func (b *Base) GetStatus() string {
|
||||
return `
|
||||
|
||||
@@ -4,9 +4,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/assets"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
log "github.com/thrasher-/gocryptotrader/logger"
|
||||
)
|
||||
|
||||
@@ -26,10 +23,7 @@ type ICommunicate interface {
|
||||
// Setup sets up communication variables and intiates a connection to the
|
||||
// communication mediums
|
||||
func (c IComm) Setup() {
|
||||
TickerStaged = make(map[string]map[assets.AssetType]map[string]ticker.Price)
|
||||
OrderbookStaged = make(map[string]map[assets.AssetType]map[string]Orderbook)
|
||||
ServiceStarted = time.Now()
|
||||
|
||||
for i := range c {
|
||||
if c[i].IsEnabled() && !c[i].IsConnected() {
|
||||
err := c[i].Connect()
|
||||
@@ -46,8 +40,8 @@ func (c IComm) PushEvent(event Event) {
|
||||
if c[i].IsEnabled() && c[i].IsConnected() {
|
||||
err := c[i].PushEvent(event)
|
||||
if err != nil {
|
||||
log.Errorf("Communications error - PushEvent() in package %s with %v",
|
||||
c[i].GetName(), event)
|
||||
log.Errorf("Communications error - PushEvent() in package %s with %v. Err %s",
|
||||
c[i].GetName(), event, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,42 +62,3 @@ func (c IComm) GetEnabledCommunicationMediums() {
|
||||
log.Warnf("Communications: No communication mediums are enabled.")
|
||||
}
|
||||
}
|
||||
|
||||
// StageTickerData stages updated ticker data for the communications package
|
||||
func (c IComm) StageTickerData(exchangeName string, assetType assets.AssetType, tickerPrice *ticker.Price) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
if _, ok := TickerStaged[exchangeName]; !ok {
|
||||
TickerStaged[exchangeName] = make(map[assets.AssetType]map[string]ticker.Price)
|
||||
}
|
||||
|
||||
if _, ok := TickerStaged[exchangeName][assetType]; !ok {
|
||||
TickerStaged[exchangeName][assetType] = make(map[string]ticker.Price)
|
||||
}
|
||||
|
||||
TickerStaged[exchangeName][assetType][tickerPrice.Pair.String()] = *tickerPrice
|
||||
}
|
||||
|
||||
// StageOrderbookData stages updated orderbook data for the communications
|
||||
// package
|
||||
func (c IComm) StageOrderbookData(exchangeName string, assetType assets.AssetType, ob *orderbook.Base) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
if _, ok := OrderbookStaged[exchangeName]; !ok {
|
||||
OrderbookStaged[exchangeName] = make(map[assets.AssetType]map[string]Orderbook)
|
||||
}
|
||||
|
||||
if _, ok := OrderbookStaged[exchangeName][assetType]; !ok {
|
||||
OrderbookStaged[exchangeName][assetType] = make(map[string]Orderbook)
|
||||
}
|
||||
|
||||
_, totalAsks := ob.TotalAsksAmount()
|
||||
_, totalBids := ob.TotalBidsAmount()
|
||||
|
||||
OrderbookStaged[exchangeName][assetType][ob.Pair.String()] = Orderbook{
|
||||
CurrencyPair: ob.Pair.String(),
|
||||
TotalAsks: totalAsks,
|
||||
TotalBids: totalBids}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,10 @@ package base
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
var (
|
||||
b Base
|
||||
i IComm
|
||||
)
|
||||
|
||||
func TestStart(t *testing.T) {
|
||||
@@ -39,41 +35,6 @@ func TestGetName(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTicker(t *testing.T) {
|
||||
v := b.GetTicker("ANX")
|
||||
if v != "" {
|
||||
t.Error("test failed - base GetTicker() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderbook(t *testing.T) {
|
||||
v := b.GetOrderbook("ANX")
|
||||
if v != "" {
|
||||
t.Error("test failed - base GetOrderbook() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPortfolio(t *testing.T) {
|
||||
v := b.GetPortfolio()
|
||||
if v != "{}" {
|
||||
t.Error("test failed - base GetPortfolio() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetSettings(t *testing.T) {
|
||||
v := b.GetSettings()
|
||||
if v != "{ }" {
|
||||
t.Error("test failed - base GetSettings() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetStatus(t *testing.T) {
|
||||
v := b.GetStatus()
|
||||
if v == "" {
|
||||
t.Error("test failed - base GetStatus() error")
|
||||
}
|
||||
}
|
||||
|
||||
type CommunicationProvider struct {
|
||||
ICommunicate
|
||||
|
||||
@@ -166,58 +127,3 @@ func TestPushEvent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStageTickerData(t *testing.T) {
|
||||
_, ok := TickerStaged["bitstamp"]["someAsset"]["BTCUSD"]
|
||||
if ok {
|
||||
t.Fatalf("key should not exists")
|
||||
}
|
||||
|
||||
price := ticker.Price{}
|
||||
var i IComm
|
||||
i.Setup()
|
||||
|
||||
i.StageTickerData("bitstamp", "someAsset", &price)
|
||||
|
||||
_, ok = TickerStaged["bitstamp"]["someAsset"][price.Pair.String()]
|
||||
if !ok {
|
||||
t.Fatalf("key should exists")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrderbookData(t *testing.T) {
|
||||
_, ok := OrderbookStaged["bitstamp"]["someAsset"]["someOrderbook"]
|
||||
if ok {
|
||||
t.Fatal("key should not exists")
|
||||
}
|
||||
|
||||
ob := orderbook.Base{
|
||||
Asks: []orderbook.Item{
|
||||
{Amount: 1, Price: 2, ID: 3},
|
||||
{Amount: 4, Price: 5, ID: 6},
|
||||
},
|
||||
}
|
||||
var i IComm
|
||||
i.Setup()
|
||||
|
||||
i.StageOrderbookData("bitstamp", "someAsset", &ob)
|
||||
|
||||
orderbook, ok := OrderbookStaged["bitstamp"]["someAsset"][ob.Pair.String()]
|
||||
if !ok {
|
||||
t.Fatal("key should exists")
|
||||
}
|
||||
|
||||
if ob.Pair.String() != orderbook.CurrencyPair {
|
||||
t.Fatal("currency missmatched")
|
||||
}
|
||||
|
||||
_, totalAsks := ob.TotalAsksAmount()
|
||||
if totalAsks != orderbook.TotalAsks {
|
||||
t.Fatal("total asks missmatched")
|
||||
}
|
||||
|
||||
_, totalBids := ob.TotalBidsAmount()
|
||||
if totalBids != orderbook.TotalBids {
|
||||
t.Fatal("total bids missmatched")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,21 +24,13 @@ import (
|
||||
const (
|
||||
SlackURL = "https://slack.com/api/rtm.start"
|
||||
|
||||
cmdStatus = "!status"
|
||||
cmdHelp = "!help"
|
||||
cmdSettings = "!settings"
|
||||
cmdTicker = "!ticker"
|
||||
cmdPortfolio = "!portfolio"
|
||||
cmdOrderbook = "!orderbook"
|
||||
cmdStatus = "!status"
|
||||
cmdHelp = "!help"
|
||||
|
||||
getHelp = `GoCryptoTrader SlackBot, thank you for using this service!
|
||||
Current commands are:
|
||||
!status - Displays current working status of bot
|
||||
!help - Displays help text
|
||||
!settings - Displays current settings
|
||||
!ticker - Displays recent ANX ticker
|
||||
!portfolio - Displays portfolio data
|
||||
!orderbook - Displays current ANX orderbook`
|
||||
!help - Displays help text`
|
||||
)
|
||||
|
||||
// Slack starts a websocket connection and uses https://api.slack.com/rtm real
|
||||
@@ -58,6 +50,11 @@ type Slack struct {
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// IsConnected returns whether or not the connection is connected
|
||||
func (s *Slack) IsConnected() bool {
|
||||
return s.Connected
|
||||
}
|
||||
|
||||
// Setup takes in a slack configuration, sets bots target channel and
|
||||
// sets verification token to access workspace
|
||||
func (s *Slack) Setup(cfg *config.CommunicationsConfig) {
|
||||
@@ -70,12 +67,22 @@ func (s *Slack) Setup(cfg *config.CommunicationsConfig) {
|
||||
|
||||
// Connect connects to the service
|
||||
func (s *Slack) Connect() error {
|
||||
return s.NewConnection()
|
||||
err := s.NewConnection()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Connected = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// PushEvent pushes an event to either a slack channel or specific client
|
||||
func (s *Slack) PushEvent(base.Event) error {
|
||||
return common.ErrNotYetImplemented
|
||||
func (s *Slack) PushEvent(event base.Event) error {
|
||||
if s.Connected {
|
||||
return s.WebsocketSend("message",
|
||||
fmt.Sprintf("event: %s %s", event.Type, event.Message))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BuildURL returns an appended token string with the SlackURL
|
||||
@@ -155,19 +162,22 @@ func (s *Slack) NewConnection() error {
|
||||
}
|
||||
|
||||
if s.Verbose {
|
||||
log.Debugf("%s [%s] connected to %s [%s] \nWebsocket URL: %s.\n",
|
||||
log.Debugf("Slack: %s [%s] connected to %s [%s] \nWebsocket URL: %s.\n",
|
||||
s.Details.Self.Name,
|
||||
s.Details.Self.ID,
|
||||
s.Details.Team.Domain,
|
||||
s.Details.Team.ID,
|
||||
s.Details.URL)
|
||||
log.Debugf("Slack channels: %s", s.GetChannelsString())
|
||||
log.Debugf("Slack: Public channels: %s", s.GetChannelsString())
|
||||
}
|
||||
|
||||
s.TargetChannelID, err = s.GetIDByName(s.TargetChannel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Slack: Target channel ID: %v [#%v]", s.TargetChannelID,
|
||||
s.TargetChannel)
|
||||
return s.WebsocketConnect()
|
||||
}
|
||||
return errors.New("slack.go NewConnection() Already Connected")
|
||||
@@ -202,7 +212,6 @@ func (s *Slack) WebsocketReader() {
|
||||
}
|
||||
|
||||
var data WebsocketResponse
|
||||
|
||||
err = common.JSONDecode(resp, &data)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
@@ -240,7 +249,7 @@ func (s *Slack) WebsocketReader() {
|
||||
|
||||
case "pong":
|
||||
if s.Verbose {
|
||||
log.Debugf("Pong received from server")
|
||||
log.Debugf("Slack: Pong received from server")
|
||||
}
|
||||
default:
|
||||
log.Debugf(string(resp))
|
||||
@@ -255,7 +264,7 @@ func (s *Slack) handlePresenceChange(resp []byte) error {
|
||||
return err
|
||||
}
|
||||
if s.Verbose {
|
||||
log.Debugf("Presence change. User %s [%s] changed status to %s\n",
|
||||
log.Debugf("Slack: Presence change. User %s [%s] changed status to %s\n",
|
||||
s.GetUsernameByID(pres.User),
|
||||
pres.User, pres.Presence)
|
||||
}
|
||||
@@ -272,7 +281,7 @@ func (s *Slack) handleMessageResponse(resp []byte, data WebsocketResponse) error
|
||||
return err
|
||||
}
|
||||
if s.Verbose {
|
||||
log.Debugf("Msg received by %s [%s] with text: %s\n",
|
||||
log.Debugf("Slack: Message received by %s [%s] with text: %s\n",
|
||||
s.GetUsernameByID(msg.User),
|
||||
msg.User, msg.Text)
|
||||
}
|
||||
@@ -304,7 +313,7 @@ func (s *Slack) handleErrorResponse(data WebsocketResponse) error {
|
||||
|
||||
func (s *Slack) handleHelloResponse() {
|
||||
if s.Verbose {
|
||||
log.Debugln("Websocket connected successfully.")
|
||||
log.Debugln("Slack: Websocket connected successfully.")
|
||||
}
|
||||
s.Connected = true
|
||||
go s.WebsocketKeepAlive()
|
||||
@@ -321,7 +330,7 @@ func (s *Slack) handleReconnectResponse(resp []byte) error {
|
||||
}
|
||||
s.ReconnectURL = recURL.URL
|
||||
if s.Verbose {
|
||||
log.Debugf("Reconnect URL set to %s\n", s.ReconnectURL)
|
||||
log.Debugf("Slack: Reconnect URL set to %s\n", s.ReconnectURL)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -334,7 +343,7 @@ func (s *Slack) WebsocketKeepAlive() {
|
||||
for {
|
||||
<-ticker.C
|
||||
if err := s.WebsocketSend("ping", ""); err != nil {
|
||||
log.Debugf("slack WebsocketKeepAlive() error %s", err)
|
||||
log.Debugf("Slack: WebsocketKeepAlive() error %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -353,6 +362,11 @@ func (s *Slack) WebsocketSend(eventType, text string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s.Verbose {
|
||||
log.Debugf("Slack: Sending websocket message: %s", string(data))
|
||||
}
|
||||
|
||||
if s.WebsocketConn == nil {
|
||||
return errors.New("websocket not connected")
|
||||
}
|
||||
@@ -362,7 +376,7 @@ func (s *Slack) WebsocketSend(eventType, text string) error {
|
||||
// HandleMessage handles incoming messages and/or commands from slack
|
||||
func (s *Slack) HandleMessage(msg *Message) error {
|
||||
if msg == nil {
|
||||
return errors.New("msg is nil")
|
||||
return errors.New("slack msg is nil")
|
||||
}
|
||||
|
||||
msg.Text = strings.ToLower(msg.Text)
|
||||
@@ -373,18 +387,6 @@ func (s *Slack) HandleMessage(msg *Message) error {
|
||||
case strings.Contains(msg.Text, cmdHelp):
|
||||
return s.WebsocketSend("message", getHelp)
|
||||
|
||||
case strings.Contains(msg.Text, cmdTicker):
|
||||
return s.WebsocketSend("message", s.GetTicker("ANX"))
|
||||
|
||||
case strings.Contains(msg.Text, cmdOrderbook):
|
||||
return s.WebsocketSend("message", s.GetOrderbook("ANX"))
|
||||
|
||||
case strings.Contains(msg.Text, cmdSettings):
|
||||
return s.WebsocketSend("message", s.GetSettings())
|
||||
|
||||
case strings.Contains(msg.Text, cmdPortfolio):
|
||||
return s.WebsocketSend("message", s.GetPortfolio())
|
||||
|
||||
default:
|
||||
return s.WebsocketSend("message", "GoCryptoTrader SlackBot - Command Unknown!")
|
||||
}
|
||||
|
||||
@@ -343,7 +343,6 @@ func TestWebsocketSend(t *testing.T) {
|
||||
|
||||
func TestHandleMessage(t *testing.T) {
|
||||
msg := &Message{}
|
||||
|
||||
err := s.HandleMessage(msg)
|
||||
if err == nil {
|
||||
t.Error("test failed - slack HandleMessage(), Sent message through nil websocket")
|
||||
@@ -358,24 +357,4 @@ func TestHandleMessage(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Error("test failed - slack HandleMessage(), Sent message through nil websocket")
|
||||
}
|
||||
msg.Text = cmdTicker
|
||||
err = s.HandleMessage(msg)
|
||||
if err == nil {
|
||||
t.Error("test failed - slack HandleMessage(), Sent message through nil websocket")
|
||||
}
|
||||
msg.Text = cmdOrderbook
|
||||
err = s.HandleMessage(msg)
|
||||
if err == nil {
|
||||
t.Error("test failed - slack HandleMessage(), Sent message through nil websocket")
|
||||
}
|
||||
msg.Text = cmdSettings
|
||||
err = s.HandleMessage(msg)
|
||||
if err == nil {
|
||||
t.Error("test failed - slack HandleMessage(), Sent message through nil websocket")
|
||||
}
|
||||
msg.Text = cmdPortfolio
|
||||
err = s.HandleMessage(msg)
|
||||
if err == nil {
|
||||
t.Error("test failed - slack HandleMessage(), Sent message through nil websocket")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/communications/base"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
log "github.com/thrasher-/gocryptotrader/logger"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -39,6 +40,7 @@ func (s *SMSGlobal) Setup(cfg *config.CommunicationsConfig) {
|
||||
s.Verbose = cfg.SMSGlobalConfig.Verbose
|
||||
s.Username = cfg.SMSGlobalConfig.Username
|
||||
s.Password = cfg.SMSGlobalConfig.Password
|
||||
s.SendFrom = cfg.SMSGlobalConfig.From
|
||||
|
||||
var contacts []Contact
|
||||
for x := range cfg.SMSGlobalConfig.Contacts {
|
||||
@@ -49,10 +51,19 @@ func (s *SMSGlobal) Setup(cfg *config.CommunicationsConfig) {
|
||||
Enabled: cfg.SMSGlobalConfig.Contacts[x].Enabled,
|
||||
},
|
||||
)
|
||||
log.Debugf("SMSGlobal: SMS Contact: %s. Number: %s. Enabled: %v",
|
||||
cfg.SMSGlobalConfig.Contacts[x].Name,
|
||||
cfg.SMSGlobalConfig.Contacts[x].Number,
|
||||
cfg.SMSGlobalConfig.Contacts[x].Enabled)
|
||||
}
|
||||
s.Contacts = contacts
|
||||
}
|
||||
|
||||
// IsConnected returns whether or not the connection is connected
|
||||
func (s *SMSGlobal) IsConnected() bool {
|
||||
return s.Connected
|
||||
}
|
||||
|
||||
// Connect connects to the service
|
||||
func (s *SMSGlobal) Connect() error {
|
||||
s.Connected = true
|
||||
@@ -60,8 +71,8 @@ func (s *SMSGlobal) Connect() error {
|
||||
}
|
||||
|
||||
// PushEvent pushes an event to a contact list via SMS
|
||||
func (s *SMSGlobal) PushEvent(base.Event) error {
|
||||
return common.ErrNotYetImplemented
|
||||
func (s *SMSGlobal) PushEvent(event base.Event) error {
|
||||
return s.SendMessageToAll(event.Message)
|
||||
}
|
||||
|
||||
// GetEnabledContacts returns how many SMS contacts are enabled in the
|
||||
@@ -139,6 +150,10 @@ func (s *SMSGlobal) RemoveContact(contact Contact) error {
|
||||
func (s *SMSGlobal) SendMessageToAll(message string) error {
|
||||
for x := range s.Contacts {
|
||||
if s.Contacts[x].Enabled {
|
||||
if s.Verbose {
|
||||
log.Debugf("SMSGlobal: Sending SMS to %s. Number: %s. Message: %s [From: %s]",
|
||||
s.Contacts[x].Name, s.Contacts[x].Number, message, s.SendFrom)
|
||||
}
|
||||
err := s.SendMessage(s.Contacts[x].Number, message)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -25,7 +25,7 @@ func TestConnect(t *testing.T) {
|
||||
|
||||
func TestPushEvent(t *testing.T) {
|
||||
err := s.PushEvent(base.Event{})
|
||||
if err == nil {
|
||||
if err != nil {
|
||||
t.Error("test failed - SMSGlobal PushEvent() error")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,11 @@ func (s *SMTPservice) Setup(cfg *config.CommunicationsConfig) {
|
||||
s.RecipientList = cfg.SMTPConfig.RecipientList
|
||||
}
|
||||
|
||||
// IsConnected returns whether or not the connection is connected
|
||||
func (s *SMTPservice) IsConnected() bool {
|
||||
return s.Connected
|
||||
}
|
||||
|
||||
// Connect connects to service
|
||||
func (s *SMTPservice) Connect() error {
|
||||
s.Connected = true
|
||||
|
||||
@@ -23,23 +23,17 @@ const (
|
||||
methodGetUpdates = "getUpdates"
|
||||
methodSendMessage = "sendMessage"
|
||||
|
||||
cmdStart = "/start"
|
||||
cmdStatus = "/status"
|
||||
cmdHelp = "/help"
|
||||
cmdSettings = "/settings"
|
||||
cmdTicker = "/ticker"
|
||||
cmdPortfolio = "/portfolio"
|
||||
cmdOrders = "/orderbooks"
|
||||
cmdStart = "/start"
|
||||
cmdStatus = "/status"
|
||||
cmdHelp = "/help"
|
||||
cmdSettings = "/settings"
|
||||
|
||||
cmdHelpReply = `GoCryptoTrader TelegramBot, thank you for using this service!
|
||||
Current commands are:
|
||||
/start - Will authenticate your ID
|
||||
/status - Displays the status of the bot
|
||||
/help - Displays current command list
|
||||
/settings - Displays current bot settings
|
||||
/ticker - Displays current ANX ticker data
|
||||
/portfolio - Displays your current portfolio
|
||||
/orderbooks - Displays current orderbooks for ANX`
|
||||
/settings - Displays current bot settings`
|
||||
|
||||
talkRoot = "GoCryptoTrader bot"
|
||||
)
|
||||
@@ -52,6 +46,9 @@ type Telegram struct {
|
||||
AuthorisedClients []int64
|
||||
}
|
||||
|
||||
// IsConnected returns whether or not the connection is connected
|
||||
func (t *Telegram) IsConnected() bool { return t.Connected }
|
||||
|
||||
// Setup takes in a Telegram configuration and sets verification token
|
||||
func (t *Telegram) Setup(cfg *config.CommunicationsConfig) {
|
||||
t.Name = cfg.TelegramConfig.Name
|
||||
@@ -73,8 +70,8 @@ func (t *Telegram) Connect() error {
|
||||
// PushEvent sends an event to a supplied recipient list via telegram
|
||||
func (t *Telegram) PushEvent(event base.Event) error {
|
||||
for i := range t.AuthorisedClients {
|
||||
err := t.SendMessage(fmt.Sprintf("Type: %s Details: %s GainOrLoss: %s",
|
||||
event.Type, event.TradeDetails, event.GainLoss), t.AuthorisedClients[i])
|
||||
err := t.SendMessage(fmt.Sprintf("Type: %s Message: %s",
|
||||
event.Type, event.Message), t.AuthorisedClients[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -146,21 +143,9 @@ func (t *Telegram) HandleMessages(text string, chatID int64) error {
|
||||
case strings.Contains(text, cmdStart):
|
||||
return t.SendMessage(fmt.Sprintf("%s: START COMMANDS HERE", talkRoot), chatID)
|
||||
|
||||
case strings.Contains(text, cmdOrders):
|
||||
return t.SendMessage(fmt.Sprintf("%s: %s", talkRoot, t.GetOrderbook("ANX")), chatID)
|
||||
|
||||
case strings.Contains(text, cmdStatus):
|
||||
return t.SendMessage(fmt.Sprintf("%s: %s", talkRoot, t.GetStatus()), chatID)
|
||||
|
||||
case strings.Contains(text, cmdTicker):
|
||||
return t.SendMessage(fmt.Sprintf("%s: %s", talkRoot, t.GetTicker("ANX")), chatID)
|
||||
|
||||
case strings.Contains(text, cmdSettings):
|
||||
return t.SendMessage(fmt.Sprintf("%s: %s", talkRoot, t.GetSettings()), chatID)
|
||||
|
||||
case strings.Contains(text, cmdPortfolio):
|
||||
return t.SendMessage(fmt.Sprintf("%s: %s", talkRoot, t.GetPortfolio()), chatID)
|
||||
|
||||
default:
|
||||
return t.SendMessage(fmt.Sprintf("command %s not recognized", text), chatID)
|
||||
}
|
||||
|
||||
@@ -58,31 +58,16 @@ func TestHandleMessages(t *testing.T) {
|
||||
t.Errorf("test failed - telegram HandleMessages() error, expected 'Not found' got '%s'",
|
||||
err)
|
||||
}
|
||||
err = T.HandleMessages(cmdOrders, chatID)
|
||||
if err.Error() != testErrNotFound {
|
||||
t.Errorf("test failed - telegram HandleMessages() error, expected 'Not found' got '%s'",
|
||||
err)
|
||||
}
|
||||
err = T.HandleMessages(cmdStatus, chatID)
|
||||
if err.Error() != testErrNotFound {
|
||||
t.Errorf("test failed - telegram HandleMessages() error, expected 'Not found' got '%s'",
|
||||
err)
|
||||
}
|
||||
err = T.HandleMessages(cmdTicker, chatID)
|
||||
if err.Error() != testErrNotFound {
|
||||
t.Errorf("test failed - telegram HandleMessages() error, expected 'Not found' got '%s'",
|
||||
err)
|
||||
}
|
||||
err = T.HandleMessages(cmdSettings, chatID)
|
||||
if err.Error() != testErrNotFound {
|
||||
t.Errorf("test failed - telegram HandleMessages() error, expected 'Not found' got '%s'",
|
||||
err)
|
||||
}
|
||||
err = T.HandleMessages(cmdPortfolio, chatID)
|
||||
if err.Error() != testErrNotFound {
|
||||
t.Errorf("test failed - telegram HandleMessages() error, expected 'Not found' got '%s'",
|
||||
err)
|
||||
}
|
||||
err = T.HandleMessages("Not a command", chatID)
|
||||
if err.Error() != testErrNotFound {
|
||||
t.Errorf("test failed - telegram HandleMessages() error, expected 'Not found' got '%s'",
|
||||
|
||||
@@ -299,6 +299,7 @@ func (c *Config) CheckCommunicationsConfig() {
|
||||
} else {
|
||||
c.Communications.SMSGlobalConfig = SMSGlobalConfig{
|
||||
Name: "SMSGlobal",
|
||||
From: c.Name,
|
||||
Username: "main",
|
||||
Password: "test",
|
||||
|
||||
@@ -328,6 +329,10 @@ func (c *Config) CheckCommunicationsConfig() {
|
||||
}
|
||||
|
||||
} else {
|
||||
if c.Communications.SMSGlobalConfig.From == "" {
|
||||
c.Communications.SMSGlobalConfig.From = c.Name
|
||||
}
|
||||
|
||||
if c.SMS != nil {
|
||||
// flush old SMS config
|
||||
c.SMS = nil
|
||||
@@ -931,7 +936,7 @@ func (c *Config) CheckExchangeConfigValues() error {
|
||||
c.Exchanges[i].Enabled = false
|
||||
continue
|
||||
}
|
||||
if c.Exchanges[i].API.AuthenticatedSupport {
|
||||
if c.Exchanges[i].API.AuthenticatedSupport && c.Exchanges[i].API.CredentialsValidator != nil {
|
||||
if c.Exchanges[i].API.CredentialsValidator.RequiresKey && (c.Exchanges[i].API.Credentials.Key == "" || c.Exchanges[i].API.Credentials.Key == DefaultAPIKey) {
|
||||
c.Exchanges[i].API.AuthenticatedSupport = false
|
||||
}
|
||||
|
||||
@@ -222,6 +222,7 @@ type SMSContact struct {
|
||||
// messaging and broadcast used by SMSGlobal
|
||||
type SMSGlobalConfig struct {
|
||||
Name string `json:"name"`
|
||||
From string `json:"from"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Verbose bool `json:"verbose"`
|
||||
Username string `json:"username"`
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -40,6 +41,7 @@ type Engine struct {
|
||||
Settings Settings
|
||||
CryptocurrencyDepositAddresses map[string]map[string]string
|
||||
Uptime time.Time
|
||||
ServicesWG sync.WaitGroup
|
||||
}
|
||||
|
||||
// Vars for engine
|
||||
@@ -436,6 +438,8 @@ func (e *Engine) Stop() {
|
||||
log.Debugln("Config file saved successfully.")
|
||||
}
|
||||
}
|
||||
// Wait for services to gracefully shutdown
|
||||
e.ServicesWG.Wait()
|
||||
log.Debugln("Exiting.")
|
||||
log.CloseLogFile()
|
||||
os.Exit(0)
|
||||
|
||||
@@ -139,7 +139,10 @@ func (e *Event) ExecuteAction() bool {
|
||||
if action[0] == ActionSMSNotify {
|
||||
message := fmt.Sprintf("Event triggered: %s", e.String())
|
||||
if action[1] == "ALL" {
|
||||
comms.PushEvent(base.Event{TradeDetails: message})
|
||||
comms.PushEvent(base.Event{
|
||||
Type: "event",
|
||||
Message: message,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -7,13 +7,16 @@ import (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pquerna/otp/totp"
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
@@ -26,6 +29,21 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/utils"
|
||||
)
|
||||
|
||||
// GetOTPByExchange returns a OTP code for the desired exchange
|
||||
// if it exists
|
||||
func GetOTPByExchange(exchName string) (string, error) {
|
||||
for x := range Bot.Config.Exchanges {
|
||||
if !strings.EqualFold(Bot.Config.Exchanges[x].Name, exchName) {
|
||||
continue
|
||||
}
|
||||
|
||||
if otpSecret := Bot.Config.Exchanges[x].API.Credentials.OTPSecret; otpSecret != "" {
|
||||
return totp.GenerateCode(otpSecret, time.Now())
|
||||
}
|
||||
}
|
||||
return "", errors.New("exchange does not have a otpsecret stored")
|
||||
}
|
||||
|
||||
// GetAuthAPISupportedExchanges returns a list of auth api enabled exchanges
|
||||
func GetAuthAPISupportedExchanges() []string {
|
||||
var exchanges []string
|
||||
|
||||
165
engine/orders.go
165
engine/orders.go
@@ -2,10 +2,12 @@ package engine
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/communications/base"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
log "github.com/thrasher-/gocryptotrader/logger"
|
||||
)
|
||||
@@ -16,9 +18,10 @@ var (
|
||||
ErrOrdersAlreadyExists = errors.New("order already exists")
|
||||
)
|
||||
|
||||
type orderStore struct {
|
||||
m sync.Mutex
|
||||
Orders map[string][]exchange.OrderDetail
|
||||
func (o *orderStore) Get() map[string][]exchange.OrderDetail {
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
return o.Orders
|
||||
}
|
||||
|
||||
func (o *orderStore) exists(order *exchange.OrderDetail) bool {
|
||||
@@ -50,13 +53,6 @@ func (o *orderStore) Add(order *exchange.OrderDetail) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type orderManager struct {
|
||||
started int32
|
||||
stopped int32
|
||||
shutdown chan struct{}
|
||||
orderStore orderStore
|
||||
}
|
||||
|
||||
func (o *orderManager) Started() bool {
|
||||
return atomic.LoadInt32(&o.started) == 1
|
||||
}
|
||||
@@ -67,6 +63,9 @@ func (o *orderManager) Start() error {
|
||||
}
|
||||
|
||||
log.Debugln("Order manager starting...")
|
||||
|
||||
// test param
|
||||
o.cfg.CancelOrdersOnShutdown = true
|
||||
o.shutdown = make(chan struct{})
|
||||
o.orderStore.Orders = make(map[string][]exchange.OrderDetail)
|
||||
go o.run()
|
||||
@@ -82,17 +81,59 @@ func (o *orderManager) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *orderManager) gracefulShutdown() {
|
||||
if o.cfg.CancelOrdersOnShutdown {
|
||||
log.Debug("Order manager: Cancelling any open orders...")
|
||||
orders := o.orderStore.Get()
|
||||
if orders == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range orders {
|
||||
log.Debugf("Order manager: Cancelling order(s) for exchange %s.", k)
|
||||
for y := range v {
|
||||
log.Debugf("order manager: Cancelling order ID %v [%v]",
|
||||
v[y].ID, v[y])
|
||||
err := o.Cancel(k, &exchange.OrderCancellation{
|
||||
OrderID: v[y].ID,
|
||||
})
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("Order manager: Exchange %s unable to cancel order ID=%v. Err: %s",
|
||||
k, v[y].ID, err)
|
||||
log.Debugln(msg)
|
||||
Bot.CommsRelayer.PushEvent(base.Event{
|
||||
Type: "order",
|
||||
Message: msg,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("Order manager: Exchange %s order ID=%v cancelled.",
|
||||
k, v[y].ID)
|
||||
log.Debugln(msg)
|
||||
Bot.CommsRelayer.PushEvent(base.Event{
|
||||
Type: "order",
|
||||
Message: msg,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *orderManager) run() {
|
||||
log.Debugln("Order manager started.")
|
||||
tick := time.NewTicker(OrderManagerDelay)
|
||||
Bot.ServicesWG.Add(1)
|
||||
defer func() {
|
||||
log.Debugf("Order manager shutdown.")
|
||||
tick.Stop()
|
||||
Bot.ServicesWG.Done()
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-o.shutdown:
|
||||
o.gracefulShutdown()
|
||||
return
|
||||
case <-tick.C:
|
||||
o.processOrders()
|
||||
@@ -100,9 +141,99 @@ func (o *orderManager) run() {
|
||||
}
|
||||
}
|
||||
|
||||
func (o *orderManager) Cancel() {}
|
||||
func (o *orderManager) CancelAllOrders() {}
|
||||
|
||||
func (o *orderManager) Place() {}
|
||||
func (o *orderManager) Cancel(exchName string, order *exchange.OrderCancellation) error {
|
||||
if exchName == "" {
|
||||
return errors.New("order exchange name is empty")
|
||||
}
|
||||
|
||||
if order == nil {
|
||||
return errors.New("order cancel param is nil")
|
||||
}
|
||||
|
||||
if order.OrderID == "" {
|
||||
return errors.New("order id is empty")
|
||||
}
|
||||
|
||||
exch := GetExchangeByName(exchName)
|
||||
if exch == nil {
|
||||
return errors.New("unable to get exchange by name")
|
||||
}
|
||||
|
||||
if order.AssetType.String() != "" && !exch.GetAssetTypes().Contains(order.AssetType) {
|
||||
return errors.New("order asset type not supported by exchange")
|
||||
}
|
||||
|
||||
return exch.CancelOrder(order)
|
||||
}
|
||||
|
||||
func (o *orderManager) Submit(exchName string, order *exchange.OrderSubmission) (*orderSubmitResponse, error) {
|
||||
if exchName == "" {
|
||||
return nil, errors.New("order exchange name must be specified")
|
||||
}
|
||||
|
||||
if order == nil {
|
||||
return nil, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if err := order.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if o.cfg.EnforceLimitConfig {
|
||||
if !o.cfg.AllowMarketOrders && order.OrderType == exchange.MarketOrderType {
|
||||
return nil, errors.New("order market type is not allowed")
|
||||
}
|
||||
|
||||
if o.cfg.LimitAmount > 0 && order.Amount > o.cfg.LimitAmount {
|
||||
return nil, errors.New("order limit exceeds allowed limit")
|
||||
}
|
||||
|
||||
if len(o.cfg.AllowedExchanges) > 0 &&
|
||||
!common.StringDataCompareInsensitive(o.cfg.AllowedExchanges, exchName) {
|
||||
return nil, errors.New("order exchange not found in allowed list")
|
||||
}
|
||||
|
||||
if len(o.cfg.AllowedPairs) > 0 && !o.cfg.AllowedPairs.Contains(order.Pair, true) {
|
||||
return nil, errors.New("order pair not found in allowed list")
|
||||
}
|
||||
}
|
||||
|
||||
exch := GetExchangeByName(exchName)
|
||||
if exch == nil {
|
||||
return nil, errors.New("unable to get exchange by name")
|
||||
}
|
||||
|
||||
id, err := common.GetV4UUID()
|
||||
if err != nil {
|
||||
log.Warnf("Order manager: Unable to generate UUID. Err: %s", err)
|
||||
}
|
||||
|
||||
result, err := exch.SubmitOrder(order)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.IsOrderPlaced {
|
||||
return nil, errors.New("order unable to be placed")
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("Order manager: Exchange %s submitted order ID=%v [Ours: %v] pair=%v price=%v amount=%v side=%v type=%v.",
|
||||
exchName, result.OrderID, id.String(), order.Pair, order.Price, order.Amount, order.OrderSide, order.OrderType)
|
||||
log.Debugln(msg)
|
||||
Bot.CommsRelayer.PushEvent(base.Event{
|
||||
Type: "order",
|
||||
Message: msg,
|
||||
})
|
||||
|
||||
return &orderSubmitResponse{
|
||||
SubmitOrderResponse: exchange.SubmitOrderResponse{
|
||||
OrderID: result.OrderID,
|
||||
},
|
||||
OurOrderID: id.String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (o *orderManager) processOrders() {
|
||||
authExchanges := GetAuthAPISupportedExchanges()
|
||||
@@ -123,8 +254,14 @@ func (o *orderManager) processOrders() {
|
||||
order := &result[x]
|
||||
result := o.orderStore.Add(order)
|
||||
if result != ErrOrdersAlreadyExists {
|
||||
log.Debugf("Order manager: Exchange %s added order ID=%v pair=%v price=%v amount=%v side=%v type=%v.",
|
||||
msg := fmt.Sprintf("Order manager: Exchange %s added order ID=%v pair=%v price=%v amount=%v side=%v type=%v.",
|
||||
order.Exchange, order.ID, order.CurrencyPair, order.Price, order.Amount, order.OrderSide, order.OrderType)
|
||||
log.Debug(msg)
|
||||
Bot.CommsRelayer.PushEvent(base.Event{
|
||||
Type: "order",
|
||||
Message: msg,
|
||||
})
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
36
engine/orders_types.go
Normal file
36
engine/orders_types.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
)
|
||||
|
||||
type orderManagerConfig struct {
|
||||
EnforceLimitConfig bool
|
||||
AllowMarketOrders bool
|
||||
CancelOrdersOnShutdown bool
|
||||
LimitAmount float64
|
||||
AllowedPairs currency.Pairs
|
||||
AllowedExchanges []string
|
||||
OrderSubmissionRetries int64
|
||||
}
|
||||
|
||||
type orderStore struct {
|
||||
m sync.Mutex
|
||||
Orders map[string][]exchange.OrderDetail
|
||||
}
|
||||
|
||||
type orderManager struct {
|
||||
started int32
|
||||
stopped int32
|
||||
shutdown chan struct{}
|
||||
orderStore orderStore
|
||||
cfg orderManagerConfig
|
||||
}
|
||||
|
||||
type orderSubmitResponse struct {
|
||||
exchange.SubmitOrderResponse
|
||||
OurOrderID string
|
||||
}
|
||||
@@ -48,10 +48,12 @@ func (p *portfolioManager) Stop() error {
|
||||
|
||||
func (p *portfolioManager) run() {
|
||||
log.Debugln("Portfolio manager started.")
|
||||
Bot.ServicesWG.Add(1)
|
||||
tick := time.NewTicker(PortfolioSleepDelay)
|
||||
defer func() {
|
||||
log.Debugf("Portfolio manager shutdown.")
|
||||
tick.Stop()
|
||||
Bot.ServicesWG.Done()
|
||||
}()
|
||||
|
||||
for {
|
||||
|
||||
@@ -214,7 +214,6 @@ func TickerUpdaterRoutine() {
|
||||
}
|
||||
printTickerSummary(&result, c, assetType, exchangeName, err)
|
||||
if err == nil {
|
||||
Bot.CommsRelayer.StageTickerData(exchangeName, assetType, &result)
|
||||
if Bot.Config.RemoteControl.WebsocketRPC.Enabled {
|
||||
relayWebsocketEvent(result, "ticker_update", assetType.String(), exchangeName)
|
||||
}
|
||||
@@ -261,7 +260,6 @@ func OrderbookUpdaterRoutine() {
|
||||
result, err := exch.UpdateOrderbook(c, assetType)
|
||||
printOrderbookSummary(&result, c, assetType, exchangeName, err)
|
||||
if err == nil {
|
||||
Bot.CommsRelayer.StageOrderbookData(exchangeName, assetType, &result)
|
||||
if Bot.Config.RemoteControl.WebsocketRPC.Enabled {
|
||||
relayWebsocketEvent(result, "orderbook_update", assetType.String(), exchangeName)
|
||||
}
|
||||
@@ -324,7 +322,7 @@ var wg sync.WaitGroup
|
||||
func Websocketshutdown(ws *exchange.Websocket) error {
|
||||
err := ws.Shutdown() // shutdown routines on the exchange
|
||||
if err != nil {
|
||||
log.Errorf("routines.go error - failed to shutodwn %s", err)
|
||||
log.Errorf("routines.go error - failed to shutdown %s", err)
|
||||
}
|
||||
|
||||
timer := time.NewTimer(5 * time.Second)
|
||||
@@ -426,7 +424,10 @@ func WebsocketDataHandler(ws *exchange.Websocket) {
|
||||
Low: d.LowPrice,
|
||||
Volume: d.Quantity,
|
||||
}
|
||||
Bot.ExchangeCurrencyPairManager.update(ws.GetName(), d.Pair, d.AssetType, SyncItemTicker, nil)
|
||||
if Bot.Settings.EnableExchangeSyncManager && Bot.ExchangeCurrencyPairManager != nil {
|
||||
Bot.ExchangeCurrencyPairManager.update(ws.GetName(),
|
||||
d.Pair, d.AssetType, SyncItemTicker, nil)
|
||||
}
|
||||
ticker.ProcessTicker(ws.GetName(), &tickerNew, d.AssetType)
|
||||
printTickerSummary(&tickerNew, tickerNew.Pair, d.AssetType, ws.GetName(), nil)
|
||||
case exchange.KlineData:
|
||||
@@ -437,7 +438,11 @@ func WebsocketDataHandler(ws *exchange.Websocket) {
|
||||
case exchange.WebsocketOrderbookUpdate:
|
||||
// Orderbook data
|
||||
result := data.(exchange.WebsocketOrderbookUpdate)
|
||||
Bot.ExchangeCurrencyPairManager.update(ws.GetName(), result.Pair, result.Asset, SyncItemOrderbook, nil)
|
||||
if Bot.Settings.EnableExchangeSyncManager && Bot.ExchangeCurrencyPairManager != nil {
|
||||
Bot.ExchangeCurrencyPairManager.update(ws.GetName(),
|
||||
result.Pair, result.Asset, SyncItemOrderbook, nil)
|
||||
}
|
||||
// TO-DO: printOrderbookSummary
|
||||
//nolint:gocritic log.Infof("Websocket %s %s orderbook updated", ws.GetName(), result.Pair.Pair().String())
|
||||
default:
|
||||
if Bot.Settings.Verbose {
|
||||
|
||||
@@ -176,6 +176,12 @@ func (s *RPCServer) EnableExchange(ctx context.Context, r *gctrpc.GenericExchang
|
||||
return &gctrpc.GenericExchangeNameResponse{}, err
|
||||
}
|
||||
|
||||
// GetExchangeOTPCode retrieves an exchanges OTP code
|
||||
func (s *RPCServer) GetExchangeOTPCode(ctx context.Context, r *gctrpc.GenericExchangeNameRequest) (*gctrpc.GetExchangeOTPReponse, error) {
|
||||
result, err := GetOTPByExchange(r.Exchange)
|
||||
return &gctrpc.GetExchangeOTPReponse{OtpCode: result}, err
|
||||
}
|
||||
|
||||
// GetExchangeInfo gets info for a specific exchange
|
||||
func (s *RPCServer) GetExchangeInfo(ctx context.Context, r *gctrpc.GenericExchangeNameRequest) (*gctrpc.GetExchangeInfoResponse, error) {
|
||||
exchCfg, err := Bot.Config.GetExchangeConfig(r.Exchange)
|
||||
@@ -580,9 +586,15 @@ func (s *RPCServer) SubmitOrder(ctx context.Context, r *gctrpc.SubmitOrderReques
|
||||
}
|
||||
|
||||
p := currency.NewPairFromStrings(r.Pair.Base, r.Pair.Quote)
|
||||
result, err := exch.SubmitOrder(p, exchange.OrderSide(r.Side),
|
||||
exchange.OrderType(r.OrderType), r.Amount, r.Price, r.ClientId)
|
||||
|
||||
submission := &exchange.OrderSubmission{
|
||||
Pair: p,
|
||||
OrderSide: exchange.OrderSide(r.Side),
|
||||
OrderType: exchange.OrderType(r.OrderType),
|
||||
Amount: r.Amount,
|
||||
Price: r.Price,
|
||||
ClientID: r.ClientId,
|
||||
}
|
||||
result, err := exch.SubmitOrder(submission)
|
||||
return &gctrpc.SubmitOrderResponse{
|
||||
OrderId: result.OrderID,
|
||||
OrderPlaced: result.IsOrderPlaced,
|
||||
|
||||
@@ -197,7 +197,7 @@ func (e *ExchangeCurrencyPairSyncer) update(exchangeName string, p currency.Pair
|
||||
return
|
||||
}
|
||||
default:
|
||||
log.Warnf("ExchangeCurrencyPairSyncer: unkown sync item %v", syncType)
|
||||
log.Warnf("ExchangeCurrencyPairSyncer: unknown sync item %v", syncType)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -267,10 +267,6 @@ func (e *ExchangeCurrencyPairSyncer) worker() {
|
||||
continue
|
||||
}
|
||||
|
||||
if Bot.Exchanges[x].GetName() == "BTCC" {
|
||||
continue
|
||||
}
|
||||
|
||||
exchangeName := Bot.Exchanges[x].GetName()
|
||||
assetTypes := Bot.Exchanges[x].GetAssetTypes()
|
||||
supportsREST := Bot.Exchanges[x].SupportsREST()
|
||||
@@ -456,17 +452,13 @@ func (e *ExchangeCurrencyPairSyncer) Start() {
|
||||
continue
|
||||
}
|
||||
|
||||
if Bot.Exchanges[x].GetName() == "BTCC" {
|
||||
continue
|
||||
}
|
||||
|
||||
exchangeName := Bot.Exchanges[x].GetName()
|
||||
supportsWebsocket := Bot.Exchanges[x].SupportsWebsocket()
|
||||
assetTypes := Bot.Exchanges[x].GetAssetTypes()
|
||||
supportsREST := Bot.Exchanges[x].SupportsREST()
|
||||
|
||||
if !supportsREST && !supportsWebsocket {
|
||||
log.Warnf("Loaded exchange %s does not support REST or Websocket", exchangeName)
|
||||
log.Warnf("Loaded exchange %s does not support REST or Websocket.", exchangeName)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -536,12 +536,21 @@ func TestSubmitOrder(t *testing.T) {
|
||||
if areTestAPIKeysSet(a) && !canManipulateRealOrders {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
var p = currency.Pair{
|
||||
Delimiter: "_",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USD,
|
||||
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Delimiter: "_",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USD,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := a.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId")
|
||||
|
||||
response, err := a.SubmitOrder(orderSubmission)
|
||||
if !areTestAPIKeysSet(a) && err == nil {
|
||||
t.Error("Expecting an error when no keys are set")
|
||||
}
|
||||
|
||||
@@ -183,20 +183,25 @@ func (a *Alphapoint) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (a *Alphapoint) GetExchangeHistory(p currency.Pair, assetType assets.AssetType) ([]exchange.TradeHistory, error) {
|
||||
var resp []exchange.TradeHistory
|
||||
|
||||
return resp, common.ErrNotYetImplemented
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order and returns a true value when
|
||||
// successfully submitted
|
||||
func (a *Alphapoint) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
|
||||
func (a *Alphapoint) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
response, err := a.CreateOrder(p.String(),
|
||||
side.ToString(),
|
||||
orderType.ToString(),
|
||||
amount, price)
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
response, err := a.CreateOrder(order.Pair.String(),
|
||||
order.OrderSide.ToString(),
|
||||
order.OrderSide.ToString(),
|
||||
order.Amount, order.Price)
|
||||
|
||||
if response > 0 {
|
||||
submitOrderResponse.OrderID = fmt.Sprintf("%v", response)
|
||||
|
||||
@@ -259,13 +259,20 @@ func TestSubmitOrder(t *testing.T) {
|
||||
if areTestAPIKeysSet() && !canManipulateRealOrders {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
var p = currency.Pair{
|
||||
Delimiter: "_",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USD,
|
||||
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Delimiter: "_",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USD,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := a.SubmitOrder(p,
|
||||
exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId")
|
||||
response, err := a.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -339,26 +339,33 @@ func (a *ANX) GetExchangeHistory(p currency.Pair, assetType assets.AssetType) ([
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (a *ANX) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
|
||||
func (a *ANX) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
var isBuying bool
|
||||
var limitPriceInSettlementCurrency float64
|
||||
|
||||
if side == exchange.BuyOrderSide {
|
||||
if order.OrderSide == exchange.BuyOrderSide {
|
||||
isBuying = true
|
||||
}
|
||||
|
||||
if orderType == exchange.LimitOrderType {
|
||||
limitPriceInSettlementCurrency = price
|
||||
if order.OrderType == exchange.LimitOrderType {
|
||||
limitPriceInSettlementCurrency = order.Price
|
||||
}
|
||||
|
||||
response, err := a.NewOrder(orderType.ToString(),
|
||||
response, err := a.NewOrder(order.OrderType.ToString(),
|
||||
isBuying,
|
||||
p.Base.String(),
|
||||
amount,
|
||||
p.Quote.String(),
|
||||
amount,
|
||||
order.Pair.Base.String(),
|
||||
order.Amount,
|
||||
order.Pair.Quote.String(),
|
||||
order.Amount,
|
||||
limitPriceInSettlementCurrency,
|
||||
false,
|
||||
"",
|
||||
|
||||
@@ -402,13 +402,20 @@ func TestSubmitOrder(t *testing.T) {
|
||||
b.SetDefaults()
|
||||
TestSetup(t)
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "",
|
||||
Base: currency.LTC,
|
||||
Quote: currency.BTC,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Delimiter: "_",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USD,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := b.SubmitOrder(p, exchange.BuyOrderSide,
|
||||
exchange.LimitOrderType, 1, 1, "clientId")
|
||||
|
||||
response, err := b.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -312,18 +312,25 @@ func (b *Binance) GetExchangeHistory(p currency.Pair, assetType assets.AssetType
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (b *Binance) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
|
||||
func (b *Binance) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
var sideType string
|
||||
if side == exchange.BuyOrderSide {
|
||||
if order.OrderSide == exchange.BuyOrderSide {
|
||||
sideType = exchange.BuyOrderSide.ToString()
|
||||
} else {
|
||||
sideType = exchange.SellOrderSide.ToString()
|
||||
}
|
||||
|
||||
var requestParamsOrderType RequestParamsOrderType
|
||||
switch orderType {
|
||||
switch order.OrderType {
|
||||
case exchange.MarketOrderType:
|
||||
requestParamsOrderType = BinanceRequestParamsOrderMarket
|
||||
case exchange.LimitOrderType:
|
||||
@@ -334,10 +341,10 @@ func (b *Binance) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderTyp
|
||||
}
|
||||
|
||||
var orderRequest = NewOrderRequest{
|
||||
Symbol: p.Base.String() + p.Quote.String(),
|
||||
Symbol: order.Pair.Base.String() + order.Pair.Quote.String(),
|
||||
Side: sideType,
|
||||
Price: price,
|
||||
Quantity: amount,
|
||||
Price: order.Price,
|
||||
Quantity: order.Amount,
|
||||
TradeType: requestParamsOrderType,
|
||||
TimeInForce: BinanceRequestParamsTimeGTC,
|
||||
}
|
||||
|
||||
@@ -764,12 +764,19 @@ func TestSubmitOrder(t *testing.T) {
|
||||
if areTestAPIKeysSet() && !canManipulateRealOrders {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
var p = currency.Pair{
|
||||
Delimiter: "",
|
||||
Base: currency.LTC,
|
||||
Quote: currency.BTC,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Delimiter: "_",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USD,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId")
|
||||
response, err := b.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -295,19 +295,26 @@ func (b *Bitfinex) GetExchangeHistory(p currency.Pair, assetType assets.AssetTyp
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (b *Bitfinex) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
|
||||
func (b *Bitfinex) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
var isBuying bool
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if side == exchange.BuyOrderSide {
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
var isBuying bool
|
||||
if order.OrderSide == exchange.BuyOrderSide {
|
||||
isBuying = true
|
||||
}
|
||||
|
||||
response, err := b.NewOrder(p.String(),
|
||||
amount,
|
||||
price,
|
||||
response, err := b.NewOrder(order.Pair.String(),
|
||||
order.Amount,
|
||||
order.Price,
|
||||
isBuying,
|
||||
orderType.ToString(),
|
||||
order.OrderType.ToString(),
|
||||
false)
|
||||
|
||||
if response.OrderID > 0 {
|
||||
|
||||
@@ -303,12 +303,18 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "",
|
||||
Base: currency.LTC,
|
||||
Quote: currency.BTC,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Base: currency.BTC,
|
||||
Quote: currency.LTC,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
_, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId")
|
||||
_, err := b.SubmitOrder(orderSubmission)
|
||||
if err != common.ErrNotYetImplemented {
|
||||
t.Errorf("Expected 'Not Yet Implemented', received %v", err)
|
||||
}
|
||||
|
||||
@@ -277,10 +277,8 @@ func (b *Bitflyer) GetExchangeHistory(p currency.Pair, assetType assets.AssetTyp
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (b *Bitflyer) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
|
||||
return submitOrderResponse, common.ErrNotYetImplemented
|
||||
func (b *Bitflyer) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
return exchange.SubmitOrderResponse{}, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
// ModifyOrder will allow of changing orderbook placement and limit to
|
||||
|
||||
@@ -345,12 +345,18 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.LTC,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Base: currency.BTC,
|
||||
Quote: currency.LTC,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId")
|
||||
response, err := b.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -272,28 +272,34 @@ func (b *Bithumb) GetExchangeHistory(p currency.Pair, assetType assets.AssetType
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
// TODO: Fill this out to support limit orders
|
||||
func (b *Bithumb) SubmitOrder(p currency.Pair, side exchange.OrderSide, _ exchange.OrderType, amount, _ float64, _ string) (exchange.SubmitOrderResponse, error) {
|
||||
func (b *Bithumb) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
var err error
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
var orderID string
|
||||
if side == exchange.BuyOrderSide {
|
||||
var err error
|
||||
if order.OrderSide == exchange.BuyOrderSide {
|
||||
var result MarketBuy
|
||||
result, err = b.MarketBuyOrder(p.Base.String(), amount)
|
||||
result, err = b.MarketBuyOrder(order.Pair.Base.String(), order.Amount)
|
||||
orderID = result.OrderID
|
||||
} else if side == exchange.SellOrderSide {
|
||||
} else if order.OrderSide == exchange.SellOrderSide {
|
||||
var result MarketSell
|
||||
result, err = b.MarketSellOrder(p.Base.String(), amount)
|
||||
result, err = b.MarketSellOrder(order.Pair.Base.String(), order.Amount)
|
||||
orderID = result.OrderID
|
||||
}
|
||||
|
||||
if orderID != "" {
|
||||
submitOrderResponse.OrderID = fmt.Sprintf("%v", orderID)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
submitOrderResponse.IsOrderPlaced = true
|
||||
}
|
||||
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
|
||||
@@ -516,12 +516,18 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "",
|
||||
Base: currency.XBT,
|
||||
Quote: currency.USD,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Base: currency.XBT,
|
||||
Quote: currency.USD,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId")
|
||||
response, err := b.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -343,23 +343,30 @@ func (b *Bitmex) GetExchangeHistory(p currency.Pair, assetType assets.AssetType)
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (b *Bitmex) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
|
||||
func (b *Bitmex) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if math.Mod(amount, 1) != 0 {
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
if math.Mod(order.Amount, 1) != 0 {
|
||||
return submitOrderResponse,
|
||||
errors.New("contract amount can not have decimals")
|
||||
errors.New("order contract amount can not have decimals")
|
||||
}
|
||||
|
||||
var orderNewParams = OrderNewParams{
|
||||
OrdType: side.ToString(),
|
||||
Symbol: p.String(),
|
||||
OrderQty: amount,
|
||||
Side: side.ToString(),
|
||||
OrdType: order.OrderSide.ToString(),
|
||||
Symbol: order.Pair.String(),
|
||||
OrderQty: order.Amount,
|
||||
Side: order.OrderSide.ToString(),
|
||||
}
|
||||
|
||||
if orderType == exchange.LimitOrderType {
|
||||
orderNewParams.Price = price
|
||||
if order.OrderType == exchange.LimitOrderType {
|
||||
orderNewParams.Price = order.Price
|
||||
}
|
||||
|
||||
response, err := b.CreateOrder(&orderNewParams)
|
||||
|
||||
@@ -417,13 +417,18 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USD,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USD,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := b.SubmitOrder(p,
|
||||
exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId")
|
||||
response, err := b.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -308,11 +308,20 @@ func (b *Bitstamp) GetExchangeHistory(p currency.Pair, assetType assets.AssetTyp
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (b *Bitstamp) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
|
||||
func (b *Bitstamp) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
buy := side == exchange.BuyOrderSide
|
||||
market := orderType == exchange.MarketOrderType
|
||||
response, err := b.PlaceOrder(p.String(), price, amount, buy, market)
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
buy := order.OrderSide == exchange.BuyOrderSide
|
||||
market := order.OrderType == exchange.MarketOrderType
|
||||
response, err := b.PlaceOrder(order.Pair.String(), order.Price, order.Amount,
|
||||
buy, market)
|
||||
|
||||
if response.ID > 0 {
|
||||
submitOrderResponse.OrderID = fmt.Sprintf("%v", response.ID)
|
||||
|
||||
@@ -373,12 +373,19 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "-",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.LTC,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Delimiter: "-",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.LTC,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId")
|
||||
response, err := b.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -288,20 +288,30 @@ func (b *Bittrex) GetExchangeHistory(p currency.Pair, assetType assets.AssetType
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (b *Bittrex) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
|
||||
func (b *Bittrex) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
buy := side == exchange.BuyOrderSide
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
buy := order.OrderSide == exchange.BuyOrderSide
|
||||
var response UUID
|
||||
var err error
|
||||
|
||||
if orderType != exchange.LimitOrderType {
|
||||
return submitOrderResponse, errors.New("not supported on exchange")
|
||||
if order.OrderType != exchange.LimitOrderType {
|
||||
return submitOrderResponse, errors.New("limit order not supported on exchange")
|
||||
}
|
||||
|
||||
if buy {
|
||||
response, err = b.PlaceBuyLimit(p.String(), amount, price)
|
||||
response, err = b.PlaceBuyLimit(order.Pair.String(), order.Amount,
|
||||
order.Price)
|
||||
} else {
|
||||
response, err = b.PlaceSellLimit(p.String(), amount, price)
|
||||
response, err = b.PlaceSellLimit(order.Pair.String(), order.Amount,
|
||||
order.Price)
|
||||
}
|
||||
|
||||
if response.Result.ID != "" {
|
||||
|
||||
@@ -332,12 +332,19 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "-",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.LTC,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Delimiter: "-",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.LTC,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := b.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 1, "clientId")
|
||||
response, err := b.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -273,15 +273,30 @@ func (b *BTCMarkets) GetExchangeHistory(p currency.Pair, assetType assets.AssetT
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (b *BTCMarkets) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (exchange.SubmitOrderResponse, error) {
|
||||
func (b *BTCMarkets) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
response, err := b.NewOrder(p.Base.Upper().String(),
|
||||
p.Quote.Upper().String(),
|
||||
price,
|
||||
amount,
|
||||
side.ToString(),
|
||||
orderType.ToString(),
|
||||
clientID)
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
if strings.EqualFold(order.OrderSide.ToString(), exchange.SellOrderSide.ToString()) {
|
||||
order.OrderSide = exchange.AskOrderSide
|
||||
}
|
||||
if strings.EqualFold(order.OrderSide.ToString(), exchange.BuyOrderSide.ToString()) {
|
||||
order.OrderSide = exchange.BuyOrderSide
|
||||
}
|
||||
|
||||
response, err := b.NewOrder(order.Pair.Base.Upper().String(),
|
||||
order.Pair.Quote.Upper().String(),
|
||||
order.Price,
|
||||
order.Amount,
|
||||
order.OrderSide.ToString(),
|
||||
order.OrderType.ToString(),
|
||||
order.ClientID)
|
||||
|
||||
if response > 0 {
|
||||
submitOrderResponse.OrderID = fmt.Sprintf("%v", response)
|
||||
|
||||
@@ -242,12 +242,18 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USD,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USD,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := b.SubmitOrder(p, exchange.SellOrderSide, exchange.LimitOrderType, 0.01, 1000000, "clientId")
|
||||
response, err := b.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -260,11 +260,19 @@ func (b *BTSE) GetExchangeHistory(p currency.Pair, assetType assets.AssetType) (
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (b *BTSE) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (exchange.SubmitOrderResponse, error) {
|
||||
func (b *BTSE) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var resp exchange.SubmitOrderResponse
|
||||
r, err := b.CreateOrder(amount, price, side.ToString(),
|
||||
orderType.ToString(), b.FormatExchangeCurrency(p,
|
||||
assets.AssetTypeSpot).String(), "GTC", clientID)
|
||||
if order == nil {
|
||||
return resp, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if err := order.Validate(); err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
r, err := b.CreateOrder(order.Amount, order.Price, order.OrderSide.ToString(),
|
||||
order.OrderType.ToString(), b.FormatExchangeCurrency(order.Pair,
|
||||
assets.AssetTypeSpot).String(), "GTC", order.ClientID)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
@@ -470,13 +470,19 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "-",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.LTC,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Delimiter: "-",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USD,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := c.SubmitOrder(p, exchange.BuyOrderSide,
|
||||
exchange.LimitOrderType, 1, 1, "clientId")
|
||||
response, err := c.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -285,27 +285,34 @@ func (c *CoinbasePro) GetExchangeHistory(p currency.Pair, assetType assets.Asset
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (c *CoinbasePro) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
|
||||
func (c *CoinbasePro) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
var response string
|
||||
var err error
|
||||
|
||||
switch orderType {
|
||||
switch order.OrderType {
|
||||
case exchange.MarketOrderType:
|
||||
response, err = c.PlaceMarginOrder("",
|
||||
amount,
|
||||
amount,
|
||||
side.ToString(),
|
||||
p.String(),
|
||||
response, err = c.PlaceMarketOrder("",
|
||||
order.Amount,
|
||||
order.Amount,
|
||||
order.OrderSide.ToString(),
|
||||
order.Pair.String(),
|
||||
"")
|
||||
case exchange.LimitOrderType:
|
||||
response, err = c.PlaceLimitOrder("",
|
||||
price,
|
||||
amount,
|
||||
side.ToString(),
|
||||
order.Price,
|
||||
order.Amount,
|
||||
order.OrderSide.ToString(),
|
||||
"",
|
||||
"",
|
||||
p.String(),
|
||||
order.Pair.String(),
|
||||
"",
|
||||
false)
|
||||
default:
|
||||
|
||||
@@ -246,12 +246,18 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USD,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USD,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := c.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, "1234234")
|
||||
response, err := c.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package coinut
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -328,33 +327,41 @@ func (c *COINUT) GetExchangeHistory(p currency.Pair, assetType assets.AssetType)
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (c *COINUT) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (exchange.SubmitOrderResponse, error) {
|
||||
func (c *COINUT) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
var err error
|
||||
var APIresponse interface{}
|
||||
isBuyOrder := side == exchange.BuyOrderSide
|
||||
clientIDInt, err := strconv.ParseUint(clientID, 0, 32)
|
||||
clientIDUint := uint32(clientIDInt)
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
var APIresponse interface{}
|
||||
isBuyOrder := order.OrderSide == exchange.BuyOrderSide
|
||||
clientIDInt, err := strconv.ParseUint(order.ClientID, 0, 32)
|
||||
if err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
clientIDUint := uint32(clientIDInt)
|
||||
|
||||
// Need to get the ID of the currency sent
|
||||
instruments, err := c.GetInstruments()
|
||||
if err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
currencyArray := instruments.Instruments[p.String()]
|
||||
currencyArray := instruments.Instruments[order.Pair.String()]
|
||||
currencyID := currencyArray[0].InstID
|
||||
|
||||
switch orderType {
|
||||
switch order.OrderType {
|
||||
case exchange.LimitOrderType:
|
||||
APIresponse, err = c.NewOrder(currencyID, amount, price, isBuyOrder, clientIDUint)
|
||||
APIresponse, err = c.NewOrder(currencyID, order.Amount, order.Price,
|
||||
isBuyOrder, clientIDUint)
|
||||
case exchange.MarketOrderType:
|
||||
APIresponse, err = c.NewOrder(currencyID, amount, 0, isBuyOrder, clientIDUint)
|
||||
default:
|
||||
return submitOrderResponse, errors.New("unsupported order type")
|
||||
APIresponse, err = c.NewOrder(currencyID, order.Amount, 0, isBuyOrder,
|
||||
clientIDUint)
|
||||
}
|
||||
|
||||
switch apiResp := APIresponse.(type) {
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
package exchange
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/currency"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/assets"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/request"
|
||||
)
|
||||
|
||||
@@ -51,12 +47,6 @@ const (
|
||||
Contact
|
||||
)
|
||||
|
||||
// SubmitOrderResponse is what is returned after submitting an order to an exchange
|
||||
type SubmitOrderResponse struct {
|
||||
IsOrderPlaced bool
|
||||
OrderID string
|
||||
}
|
||||
|
||||
// FeeBuilder is the type which holds all parameters required to calculate a fee
|
||||
// for an exchange
|
||||
type FeeBuilder struct {
|
||||
@@ -119,80 +109,6 @@ const (
|
||||
UnknownWithdrawalTypeText string = "UNKNOWN"
|
||||
)
|
||||
|
||||
// ModifyOrder is a an order modifyer
|
||||
// ModifyOrder is a an order modifyer
|
||||
type ModifyOrder struct {
|
||||
OrderID string
|
||||
OrderType
|
||||
OrderSide
|
||||
Price float64
|
||||
Amount float64
|
||||
LimitPriceUpper float64
|
||||
LimitPriceLower float64
|
||||
CurrencyPair currency.Pair
|
||||
|
||||
ImmediateOrCancel bool
|
||||
HiddenOrder bool
|
||||
FillOrKill bool
|
||||
PostOnly bool
|
||||
}
|
||||
|
||||
// ModifyOrderResponse is an order modifying return type
|
||||
type ModifyOrderResponse struct {
|
||||
OrderID string
|
||||
}
|
||||
|
||||
// CancelAllOrdersResponse returns the status from attempting to cancel all orders on an exchagne
|
||||
type CancelAllOrdersResponse struct {
|
||||
OrderStatus map[string]string
|
||||
}
|
||||
|
||||
// OrderType enforces a standard for Ordertypes across the code base
|
||||
type OrderType string
|
||||
|
||||
// OrderType ...types
|
||||
const (
|
||||
AnyOrderType OrderType = "ANY"
|
||||
LimitOrderType OrderType = "LIMIT"
|
||||
MarketOrderType OrderType = "MARKET"
|
||||
ImmediateOrCancelOrderType OrderType = "IMMEDIATE_OR_CANCEL"
|
||||
StopOrderType OrderType = "STOP"
|
||||
TrailingStopOrderType OrderType = "TRAILINGSTOP"
|
||||
UnknownOrderType OrderType = "UNKNOWN"
|
||||
)
|
||||
|
||||
// ToLower changes the ordertype to lower case
|
||||
func (o OrderType) ToLower() OrderType {
|
||||
return OrderType(strings.ToLower(string(o)))
|
||||
}
|
||||
|
||||
// ToString changes the ordertype to the exchange standard and returns a string
|
||||
func (o OrderType) ToString() string {
|
||||
return fmt.Sprintf("%v", o)
|
||||
}
|
||||
|
||||
// OrderSide enforces a standard for OrderSides across the code base
|
||||
type OrderSide string
|
||||
|
||||
// OrderSide types
|
||||
const (
|
||||
AnyOrderSide OrderSide = "ANY"
|
||||
BuyOrderSide OrderSide = "BUY"
|
||||
SellOrderSide OrderSide = "SELL"
|
||||
BidOrderSide OrderSide = "BID"
|
||||
AskOrderSide OrderSide = "ASK"
|
||||
)
|
||||
|
||||
// ToLower changes the ordertype to lower case
|
||||
func (o OrderSide) ToLower() OrderSide {
|
||||
return OrderSide(strings.ToLower(string(o)))
|
||||
}
|
||||
|
||||
// ToString changes the ordertype to the exchange standard and returns a string
|
||||
func (o OrderSide) ToString() string {
|
||||
return fmt.Sprintf("%v", o)
|
||||
}
|
||||
|
||||
// AccountInfo is a Generic type to hold each exchange's holdings in
|
||||
// all enabled currencies
|
||||
type AccountInfo struct {
|
||||
@@ -225,34 +141,6 @@ type TradeHistory struct {
|
||||
Description string
|
||||
}
|
||||
|
||||
// OrderDetail holds order detail data
|
||||
type OrderDetail struct {
|
||||
Exchange string
|
||||
AccountID string
|
||||
ID string
|
||||
CurrencyPair currency.Pair
|
||||
OrderSide OrderSide
|
||||
OrderType OrderType
|
||||
OrderDate time.Time
|
||||
Status string
|
||||
Price float64
|
||||
Amount float64
|
||||
ExecutedAmount float64
|
||||
RemainingAmount float64
|
||||
Fee float64
|
||||
Trades []TradeHistory
|
||||
}
|
||||
|
||||
// OrderCancellation type required when requesting to cancel an order
|
||||
type OrderCancellation struct {
|
||||
AccountID string
|
||||
OrderID string
|
||||
CurrencyPair currency.Pair
|
||||
AssetType assets.AssetType
|
||||
WalletAddress string
|
||||
Side OrderSide
|
||||
}
|
||||
|
||||
// FundHistory holds exchange funding history data
|
||||
type FundHistory struct {
|
||||
ExchangeName string
|
||||
@@ -402,227 +290,6 @@ type API struct {
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrdersRequest used for GetOrderHistory and GetOpenOrders wrapper functions
|
||||
type GetOrdersRequest struct {
|
||||
OrderType OrderType
|
||||
OrderSide OrderSide
|
||||
StartTicks time.Time
|
||||
EndTicks time.Time
|
||||
// Currencies Empty array = all currencies. Some endpoints only support singular currency enquiries
|
||||
Currencies []currency.Pair
|
||||
}
|
||||
|
||||
// OrderStatus defines order status types
|
||||
type OrderStatus string
|
||||
|
||||
// All OrderStatus types
|
||||
const (
|
||||
AnyOrderStatus OrderStatus = "ANY"
|
||||
NewOrderStatus OrderStatus = "NEW"
|
||||
ActiveOrderStatus OrderStatus = "ACTIVE"
|
||||
PartiallyFilledOrderStatus OrderStatus = "PARTIALLY_FILLED"
|
||||
FilledOrderStatus OrderStatus = "FILLED"
|
||||
CancelledOrderStatus OrderStatus = "CANCELED"
|
||||
PendingCancelOrderStatus OrderStatus = "PENDING_CANCEL"
|
||||
RejectedOrderStatus OrderStatus = "REJECTED"
|
||||
ExpiredOrderStatus OrderStatus = "EXPIRED"
|
||||
HiddenOrderStatus OrderStatus = "HIDDEN"
|
||||
UnknownOrderStatus OrderStatus = "UNKNOWN"
|
||||
)
|
||||
|
||||
// FilterOrdersBySide removes any OrderDetails that don't match the orderStatus provided
|
||||
func FilterOrdersBySide(orders *[]OrderDetail, orderSide OrderSide) {
|
||||
if orderSide == "" || orderSide == AnyOrderSide {
|
||||
return
|
||||
}
|
||||
|
||||
var filteredOrders []OrderDetail
|
||||
for i := range *orders {
|
||||
if strings.EqualFold(string((*orders)[i].OrderSide), string(orderSide)) {
|
||||
filteredOrders = append(filteredOrders, (*orders)[i])
|
||||
}
|
||||
}
|
||||
|
||||
*orders = filteredOrders
|
||||
}
|
||||
|
||||
// FilterOrdersByType removes any OrderDetails that don't match the orderType provided
|
||||
func FilterOrdersByType(orders *[]OrderDetail, orderType OrderType) {
|
||||
if orderType == "" || orderType == AnyOrderType {
|
||||
return
|
||||
}
|
||||
|
||||
var filteredOrders []OrderDetail
|
||||
for i := range *orders {
|
||||
if strings.EqualFold(string((*orders)[i].OrderType), string(orderType)) {
|
||||
filteredOrders = append(filteredOrders, (*orders)[i])
|
||||
}
|
||||
}
|
||||
|
||||
*orders = filteredOrders
|
||||
}
|
||||
|
||||
// FilterOrdersByTickRange removes any OrderDetails outside of the tick range
|
||||
func FilterOrdersByTickRange(orders *[]OrderDetail, startTicks, endTicks time.Time) {
|
||||
if startTicks.IsZero() || endTicks.IsZero() ||
|
||||
startTicks.Unix() == 0 || endTicks.Unix() == 0 || endTicks.Before(startTicks) {
|
||||
return
|
||||
}
|
||||
|
||||
var filteredOrders []OrderDetail
|
||||
for i := range *orders {
|
||||
if (*orders)[i].OrderDate.Unix() >= startTicks.Unix() && (*orders)[i].OrderDate.Unix() <= endTicks.Unix() {
|
||||
filteredOrders = append(filteredOrders, (*orders)[i])
|
||||
}
|
||||
}
|
||||
|
||||
*orders = filteredOrders
|
||||
}
|
||||
|
||||
// FilterOrdersByCurrencies removes any OrderDetails that do not match the provided currency list
|
||||
// It is forgiving in that the provided currencies can match quote or base currencies
|
||||
func FilterOrdersByCurrencies(orders *[]OrderDetail, currencies []currency.Pair) {
|
||||
if len(currencies) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var filteredOrders []OrderDetail
|
||||
for i := range *orders {
|
||||
matchFound := false
|
||||
for _, c := range currencies {
|
||||
if !matchFound && (*orders)[i].CurrencyPair.EqualIncludeReciprocal(c) {
|
||||
matchFound = true
|
||||
}
|
||||
}
|
||||
|
||||
if matchFound {
|
||||
filteredOrders = append(filteredOrders, (*orders)[i])
|
||||
}
|
||||
}
|
||||
|
||||
*orders = filteredOrders
|
||||
}
|
||||
|
||||
// ByPrice used for sorting orders by price
|
||||
type ByPrice []OrderDetail
|
||||
|
||||
func (b ByPrice) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (b ByPrice) Less(i, j int) bool {
|
||||
return b[i].Price < b[j].Price
|
||||
}
|
||||
|
||||
func (b ByPrice) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
// SortOrdersByPrice the caller function to sort orders
|
||||
func SortOrdersByPrice(orders *[]OrderDetail, reverse bool) {
|
||||
if reverse {
|
||||
sort.Sort(sort.Reverse(ByPrice(*orders)))
|
||||
} else {
|
||||
sort.Sort(ByPrice(*orders))
|
||||
}
|
||||
}
|
||||
|
||||
// ByOrderType used for sorting orders by order type
|
||||
type ByOrderType []OrderDetail
|
||||
|
||||
func (b ByOrderType) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (b ByOrderType) Less(i, j int) bool {
|
||||
return b[i].OrderType.ToString() < b[j].OrderType.ToString()
|
||||
}
|
||||
|
||||
func (b ByOrderType) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
// SortOrdersByType the caller function to sort orders
|
||||
func SortOrdersByType(orders *[]OrderDetail, reverse bool) {
|
||||
if reverse {
|
||||
sort.Sort(sort.Reverse(ByOrderType(*orders)))
|
||||
} else {
|
||||
sort.Sort(ByOrderType(*orders))
|
||||
}
|
||||
}
|
||||
|
||||
// ByCurrency used for sorting orders by order currency
|
||||
type ByCurrency []OrderDetail
|
||||
|
||||
func (b ByCurrency) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (b ByCurrency) Less(i, j int) bool {
|
||||
return b[i].CurrencyPair.String() < b[j].CurrencyPair.String()
|
||||
}
|
||||
|
||||
func (b ByCurrency) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
// SortOrdersByCurrency the caller function to sort orders
|
||||
func SortOrdersByCurrency(orders *[]OrderDetail, reverse bool) {
|
||||
if reverse {
|
||||
sort.Sort(sort.Reverse(ByCurrency(*orders)))
|
||||
} else {
|
||||
sort.Sort(ByCurrency(*orders))
|
||||
}
|
||||
}
|
||||
|
||||
// ByDate used for sorting orders by order date
|
||||
type ByDate []OrderDetail
|
||||
|
||||
func (b ByDate) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (b ByDate) Less(i, j int) bool {
|
||||
return b[i].OrderDate.Unix() < b[j].OrderDate.Unix()
|
||||
}
|
||||
|
||||
func (b ByDate) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
// SortOrdersByDate the caller function to sort orders
|
||||
func SortOrdersByDate(orders *[]OrderDetail, reverse bool) {
|
||||
if reverse {
|
||||
sort.Sort(sort.Reverse(ByDate(*orders)))
|
||||
} else {
|
||||
sort.Sort(ByDate(*orders))
|
||||
}
|
||||
}
|
||||
|
||||
// ByOrderSide used for sorting orders by order side (buy sell)
|
||||
type ByOrderSide []OrderDetail
|
||||
|
||||
func (b ByOrderSide) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (b ByOrderSide) Less(i, j int) bool {
|
||||
return b[i].OrderSide.ToString() < b[j].OrderSide.ToString()
|
||||
}
|
||||
|
||||
func (b ByOrderSide) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
// SortOrdersBySide the caller function to sort orders
|
||||
func SortOrdersBySide(orders *[]OrderDetail, reverse bool) {
|
||||
if reverse {
|
||||
sort.Sort(sort.Reverse(ByOrderSide(*orders)))
|
||||
} else {
|
||||
sort.Sort(ByOrderSide(*orders))
|
||||
}
|
||||
}
|
||||
|
||||
// Base stores the individual exchange information
|
||||
type Base struct {
|
||||
Name string
|
||||
|
||||
@@ -306,13 +306,19 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "_",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USD,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Delimiter: "_",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USD,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := e.SubmitOrder(p, exchange.BuyOrderSide,
|
||||
exchange.LimitOrderType, 1, 10, "1234234")
|
||||
response, err := e.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -298,24 +298,29 @@ func (e *EXMO) GetExchangeHistory(p currency.Pair, assetType assets.AssetType) (
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (e *EXMO) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
|
||||
func (e *EXMO) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
var oT string
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
switch orderType {
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
var oT string
|
||||
switch order.OrderType {
|
||||
case exchange.LimitOrderType:
|
||||
return submitOrderResponse, errors.New("unsupported order type")
|
||||
case exchange.MarketOrderType:
|
||||
oT = "market_buy"
|
||||
if side == exchange.SellOrderSide {
|
||||
if order.OrderSide == exchange.SellOrderSide {
|
||||
oT = "market_sell"
|
||||
}
|
||||
default:
|
||||
return submitOrderResponse, errors.New("unsupported order type")
|
||||
}
|
||||
|
||||
response, err := e.CreateOrder(p.String(), oT, price, amount)
|
||||
|
||||
response, err := e.CreateOrder(order.Pair.String(), oT, order.Price,
|
||||
order.Amount)
|
||||
if response > 0 {
|
||||
submitOrderResponse.OrderID = fmt.Sprintf("%v", response)
|
||||
}
|
||||
|
||||
@@ -312,13 +312,19 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "_",
|
||||
Base: currency.LTC,
|
||||
Quote: currency.BTC,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Delimiter: "_",
|
||||
Base: currency.LTC,
|
||||
Quote: currency.BTC,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := g.SubmitOrder(p, exchange.BuyOrderSide,
|
||||
exchange.LimitOrderType, 1, 10, "1234234")
|
||||
response, err := g.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -318,25 +318,31 @@ func (g *Gateio) GetExchangeHistory(p currency.Pair, assetType assets.AssetType)
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
// TODO: support multiple order types (IOC)
|
||||
func (g *Gateio) SubmitOrder(p currency.Pair, side exchange.OrderSide, _ exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
|
||||
func (g *Gateio) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
var orderTypeFormat string
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if side == exchange.BuyOrderSide {
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
var orderTypeFormat string
|
||||
if order.OrderSide == exchange.BuyOrderSide {
|
||||
orderTypeFormat = exchange.BuyOrderSide.ToLower().ToString()
|
||||
} else {
|
||||
orderTypeFormat = exchange.SellOrderSide.ToLower().ToString()
|
||||
}
|
||||
|
||||
var spotNewOrderRequestParams = SpotNewOrderRequestParams{
|
||||
Amount: amount,
|
||||
Price: price,
|
||||
Symbol: p.String(),
|
||||
Amount: order.Amount,
|
||||
Price: order.Price,
|
||||
Symbol: order.Pair.String(),
|
||||
Type: orderTypeFormat,
|
||||
}
|
||||
|
||||
response, err := g.SpotNewOrder(spotNewOrderRequestParams)
|
||||
|
||||
if response.OrderNumber > 0 {
|
||||
submitOrderResponse.OrderID = fmt.Sprintf("%v", response.OrderNumber)
|
||||
}
|
||||
|
||||
@@ -409,13 +409,19 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "_",
|
||||
Base: currency.LTC,
|
||||
Quote: currency.BTC,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Delimiter: "_",
|
||||
Base: currency.LTC,
|
||||
Quote: currency.BTC,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := Session[1].SubmitOrder(p, exchange.BuyOrderSide,
|
||||
exchange.LimitOrderType, 1, 10, "1234234")
|
||||
response, err := Session[1].SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -266,22 +266,27 @@ func (g *Gemini) GetExchangeHistory(p currency.Pair, assetType assets.AssetType)
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (g *Gemini) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
|
||||
func (g *Gemini) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
response, err := g.NewOrder(p.String(),
|
||||
amount,
|
||||
price,
|
||||
side.ToString(),
|
||||
orderType.ToString())
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
response, err := g.NewOrder(order.Pair.String(),
|
||||
order.Amount,
|
||||
order.Price,
|
||||
order.OrderSide.ToString(),
|
||||
order.OrderType.ToString())
|
||||
if response > 0 {
|
||||
submitOrderResponse.OrderID = fmt.Sprintf("%v", response)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
submitOrderResponse.IsOrderPlaced = true
|
||||
}
|
||||
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
|
||||
@@ -232,13 +232,18 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "",
|
||||
Base: currency.DGD,
|
||||
Quote: currency.BTC,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Base: currency.DGD,
|
||||
Quote: currency.BTC,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := h.SubmitOrder(p, exchange.BuyOrderSide,
|
||||
exchange.LimitOrderType, 1, 10, "1234234")
|
||||
response, err := h.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -291,22 +291,27 @@ func (h *HitBTC) GetExchangeHistory(p currency.Pair, assetType assets.AssetType)
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (h *HitBTC) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
|
||||
func (h *HitBTC) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
response, err := h.PlaceOrder(p.String(),
|
||||
price,
|
||||
amount,
|
||||
strings.ToLower(orderType.ToString()),
|
||||
strings.ToLower(side.ToString()))
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
response, err := h.PlaceOrder(order.Pair.String(),
|
||||
order.Price,
|
||||
order.Amount,
|
||||
strings.ToLower(order.OrderType.ToString()),
|
||||
strings.ToLower(order.OrderSide.ToString()))
|
||||
if response.OrderNumber > 0 {
|
||||
submitOrderResponse.OrderID = fmt.Sprintf("%v", response.OrderNumber)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
submitOrderResponse.IsOrderPlaced = true
|
||||
}
|
||||
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
|
||||
@@ -432,18 +432,23 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USDT,
|
||||
}
|
||||
|
||||
accounts, err := h.GetAccounts()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get accounts. Err: %s", err)
|
||||
}
|
||||
|
||||
response, err := h.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, strconv.FormatInt(accounts[0].ID, 10))
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USDT,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: strconv.FormatInt(accounts[0].ID, 10),
|
||||
}
|
||||
response, err := h.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
}
|
||||
|
||||
@@ -379,34 +379,40 @@ func (h *HUOBI) GetExchangeHistory(p currency.Pair, assetType assets.AssetType)
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (h *HUOBI) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (exchange.SubmitOrderResponse, error) {
|
||||
func (h *HUOBI) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
accountID, err := strconv.ParseInt(clientID, 10, 64)
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
accountID, err := strconv.ParseInt(order.ClientID, 10, 64)
|
||||
if err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
var formattedType SpotNewOrderRequestParamsType
|
||||
var params = SpotNewOrderRequestParams{
|
||||
Amount: amount,
|
||||
Amount: order.Amount,
|
||||
Source: "api",
|
||||
Symbol: strings.ToLower(p.String()),
|
||||
Symbol: strings.ToLower(order.Pair.String()),
|
||||
AccountID: int(accountID),
|
||||
}
|
||||
|
||||
switch {
|
||||
case side == exchange.BuyOrderSide && orderType == exchange.MarketOrderType:
|
||||
case order.OrderSide == exchange.BuyOrderSide && order.OrderType == exchange.MarketOrderType:
|
||||
formattedType = SpotNewOrderRequestTypeBuyMarket
|
||||
case side == exchange.SellOrderSide && orderType == exchange.MarketOrderType:
|
||||
case order.OrderSide == exchange.SellOrderSide && order.OrderType == exchange.MarketOrderType:
|
||||
formattedType = SpotNewOrderRequestTypeSellMarket
|
||||
case side == exchange.BuyOrderSide && orderType == exchange.LimitOrderType:
|
||||
case order.OrderSide == exchange.BuyOrderSide && order.OrderType == exchange.LimitOrderType:
|
||||
formattedType = SpotNewOrderRequestTypeBuyLimit
|
||||
params.Price = price
|
||||
case side == exchange.SellOrderSide && orderType == exchange.LimitOrderType:
|
||||
params.Price = order.Price
|
||||
case order.OrderSide == exchange.SellOrderSide && order.OrderType == exchange.LimitOrderType:
|
||||
formattedType = SpotNewOrderRequestTypeSellLimit
|
||||
params.Price = price
|
||||
default:
|
||||
return submitOrderResponse, errors.New("unsupported order type")
|
||||
params.Price = order.Price
|
||||
}
|
||||
|
||||
params.Type = formattedType
|
||||
@@ -414,11 +420,9 @@ func (h *HUOBI) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType
|
||||
if response > 0 {
|
||||
submitOrderResponse.OrderID = fmt.Sprintf("%v", response)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
submitOrderResponse.IsOrderPlaced = true
|
||||
}
|
||||
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
|
||||
@@ -466,18 +466,23 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USDT,
|
||||
}
|
||||
|
||||
accounts, err := h.GetAccounts()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get accounts. Err: %s", err)
|
||||
}
|
||||
|
||||
response, err := h.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, strconv.FormatInt(accounts[0].ID, 10))
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USDT,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: strconv.FormatInt(accounts[0].ID, 10),
|
||||
}
|
||||
response, err := h.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
}
|
||||
|
||||
@@ -340,48 +340,50 @@ func (h *HUOBIHADAX) GetExchangeHistory(p currency.Pair, assetType assets.AssetT
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (h *HUOBIHADAX) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (exchange.SubmitOrderResponse, error) {
|
||||
func (h *HUOBIHADAX) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
accountID, err := strconv.ParseInt(clientID, 0, 64)
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
accountID, err := strconv.ParseInt(order.ClientID, 10, 64)
|
||||
if err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
var formattedType SpotNewOrderRequestParamsType
|
||||
var params = SpotNewOrderRequestParams{
|
||||
Amount: amount,
|
||||
Amount: order.Amount,
|
||||
Source: "api",
|
||||
Symbol: strings.ToLower(p.String()),
|
||||
Symbol: strings.ToLower(order.Pair.String()),
|
||||
AccountID: int(accountID),
|
||||
}
|
||||
|
||||
switch {
|
||||
case side == exchange.BuyOrderSide && orderType == exchange.MarketOrderType:
|
||||
case order.OrderSide == exchange.BuyOrderSide && order.OrderType == exchange.MarketOrderType:
|
||||
formattedType = SpotNewOrderRequestTypeBuyMarket
|
||||
case side == exchange.SellOrderSide && orderType == exchange.MarketOrderType:
|
||||
case order.OrderSide == exchange.SellOrderSide && order.OrderType == exchange.MarketOrderType:
|
||||
formattedType = SpotNewOrderRequestTypeSellMarket
|
||||
case side == exchange.BuyOrderSide && orderType == exchange.LimitOrderType:
|
||||
case order.OrderSide == exchange.BuyOrderSide && order.OrderType == exchange.LimitOrderType:
|
||||
formattedType = SpotNewOrderRequestTypeBuyLimit
|
||||
params.Price = price
|
||||
case side == exchange.SellOrderSide && orderType == exchange.LimitOrderType:
|
||||
params.Price = order.Price
|
||||
case order.OrderSide == exchange.SellOrderSide && order.OrderType == exchange.LimitOrderType:
|
||||
formattedType = SpotNewOrderRequestTypeSellLimit
|
||||
params.Price = price
|
||||
default:
|
||||
return submitOrderResponse, errors.New("unsupported order type")
|
||||
params.Price = order.Price
|
||||
}
|
||||
|
||||
params.Type = formattedType
|
||||
|
||||
response, err := h.SpotNewOrder(params)
|
||||
|
||||
if response > 0 {
|
||||
submitOrderResponse.OrderID = fmt.Sprintf("%v", response)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
submitOrderResponse.IsOrderPlaced = true
|
||||
}
|
||||
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ type IBotExchange interface {
|
||||
FormatWithdrawPermissions() string
|
||||
SupportsWithdrawPermissions(permissions uint32) bool
|
||||
GetFundingHistory() ([]FundHistory, error)
|
||||
SubmitOrder(p currency.Pair, side OrderSide, orderType OrderType, amount, price float64, clientID string) (SubmitOrderResponse, error)
|
||||
SubmitOrder(order *OrderSubmission) (SubmitOrderResponse, error)
|
||||
ModifyOrder(action *ModifyOrder) (string, error)
|
||||
CancelOrder(order *OrderCancellation) error
|
||||
CancelAllOrders(orders *OrderCancellation) (CancelAllOrdersResponse, error)
|
||||
|
||||
@@ -303,12 +303,18 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USDT,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USD,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := i.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, "hi")
|
||||
response, err := i.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -273,10 +273,17 @@ func (i *ItBit) GetExchangeHistory(p currency.Pair, assetType assets.AssetType)
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (i *ItBit) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
|
||||
func (i *ItBit) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
var wallet string
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
var wallet string
|
||||
wallets, err := i.GetWallets(url.Values{})
|
||||
if err != nil {
|
||||
return submitOrderResponse, err
|
||||
@@ -285,8 +292,8 @@ func (i *ItBit) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType
|
||||
// Determine what wallet ID to use if there is any actual available currency to make the trade!
|
||||
for _, i := range wallets {
|
||||
for j := range i.Balances {
|
||||
if i.Balances[j].Currency == p.Base.String() &&
|
||||
i.Balances[j].AvailableBalance >= amount {
|
||||
if i.Balances[j].Currency == order.Pair.Base.String() &&
|
||||
i.Balances[j].AvailableBalance >= order.Amount {
|
||||
wallet = i.ID
|
||||
}
|
||||
}
|
||||
@@ -295,23 +302,21 @@ func (i *ItBit) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType
|
||||
if wallet == "" {
|
||||
return submitOrderResponse,
|
||||
fmt.Errorf("no wallet found with currency: %s with amount >= %v",
|
||||
p.Base,
|
||||
amount)
|
||||
order.Pair.Base,
|
||||
order.Amount)
|
||||
}
|
||||
|
||||
response, err := i.PlaceOrder(wallet,
|
||||
side.ToString(),
|
||||
orderType.ToString(),
|
||||
p.Base.String(),
|
||||
amount,
|
||||
price,
|
||||
p.String(),
|
||||
order.OrderSide.ToString(),
|
||||
order.OrderType.ToString(),
|
||||
order.Pair.Base.String(),
|
||||
order.Amount,
|
||||
order.Price,
|
||||
order.Pair.String(),
|
||||
"")
|
||||
|
||||
if response.ID != "" {
|
||||
submitOrderResponse.OrderID = response.ID
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
submitOrderResponse.IsOrderPlaced = true
|
||||
}
|
||||
|
||||
@@ -422,12 +422,18 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "",
|
||||
Base: currency.XBT,
|
||||
Quote: currency.CAD,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Base: currency.XBT,
|
||||
Quote: currency.USD,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := k.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, "hi")
|
||||
response, err := k.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -318,27 +318,31 @@ func (k *Kraken) GetExchangeHistory(p currency.Pair, assetType assets.AssetType)
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (k *Kraken) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
|
||||
func (k *Kraken) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
var args = AddOrderOptions{}
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
response, err := k.AddOrder(p.String(),
|
||||
side.ToString(),
|
||||
orderType.ToString(),
|
||||
amount,
|
||||
price,
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
var args = AddOrderOptions{}
|
||||
response, err := k.AddOrder(order.Pair.String(),
|
||||
order.OrderSide.ToString(),
|
||||
order.OrderType.ToString(),
|
||||
order.Amount,
|
||||
order.Price,
|
||||
0,
|
||||
0,
|
||||
&args)
|
||||
|
||||
if len(response.TransactionIds) > 0 {
|
||||
submitOrderResponse.OrderID = strings.Join(response.TransactionIds, ", ")
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
submitOrderResponse.IsOrderPlaced = true
|
||||
}
|
||||
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
|
||||
@@ -297,12 +297,18 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.EUR,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Base: currency.BTC,
|
||||
Quote: currency.EUR,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := l.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, "hi")
|
||||
response, err := l.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -266,19 +266,25 @@ func (l *LakeBTC) GetExchangeHistory(p currency.Pair, assetType assets.AssetType
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (l *LakeBTC) SubmitOrder(p currency.Pair, side exchange.OrderSide, _ exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
|
||||
func (l *LakeBTC) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
isBuyOrder := side == exchange.BuyOrderSide
|
||||
response, err := l.Trade(isBuyOrder, amount, price, p.Lower().String())
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
isBuyOrder := order.OrderSide == exchange.BuyOrderSide
|
||||
response, err := l.Trade(isBuyOrder, order.Amount, order.Price,
|
||||
order.Pair.Lower().String())
|
||||
if response.ID > 0 {
|
||||
submitOrderResponse.OrderID = fmt.Sprintf("%v", response.ID)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
submitOrderResponse.IsOrderPlaced = true
|
||||
}
|
||||
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
|
||||
@@ -256,12 +256,18 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var p = currency.Pair{
|
||||
Delimiter: "",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.EUR,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Base: currency.BTC,
|
||||
Quote: currency.EUR,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := l.SubmitOrder(p, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, "hi")
|
||||
response, err := l.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -255,8 +255,16 @@ func (l *LocalBitcoins) GetExchangeHistory(p currency.Pair, assetType assets.Ass
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (l *LocalBitcoins) SubmitOrder(p currency.Pair, side exchange.OrderSide, _ exchange.OrderType, amount, _ float64, _ string) (exchange.SubmitOrderResponse, error) {
|
||||
func (l *LocalBitcoins) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
// These are placeholder details
|
||||
// TODO store a user's localbitcoin details to use here
|
||||
var params = AdCreate{
|
||||
@@ -266,17 +274,17 @@ func (l *LocalBitcoins) SubmitOrder(p currency.Pair, side exchange.OrderSide, _
|
||||
City: "City",
|
||||
Location: "Location",
|
||||
CountryCode: "US",
|
||||
Currency: p.Quote.String(),
|
||||
Currency: order.Pair.Quote.String(),
|
||||
AccountInfo: "-",
|
||||
BankName: "Bank",
|
||||
MSG: side.ToString(),
|
||||
MSG: order.OrderSide.ToString(),
|
||||
SMSVerficationRequired: true,
|
||||
TrackMaxAmount: true,
|
||||
RequireTrustedByAdvertiser: true,
|
||||
RequireIdentification: true,
|
||||
OnlineProvider: "",
|
||||
TradeType: "",
|
||||
MinAmount: int(math.Round(amount)),
|
||||
MinAmount: int(math.Round(order.Amount)),
|
||||
}
|
||||
|
||||
// Does not return any orderID, so create the add, then get the order
|
||||
|
||||
@@ -1045,13 +1045,18 @@ func TestFormatWithdrawPermissions(t *testing.T) {
|
||||
// TestSubmitOrder Wrapper test
|
||||
func TestSubmitOrder(t *testing.T) {
|
||||
TestSetRealOrderDefaults(t)
|
||||
var p = currency.Pair{
|
||||
Delimiter: "",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.EUR,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USD,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := o.SubmitOrder(p, exchange.BuyOrderSide,
|
||||
exchange.LimitOrderType, 1, 10, "hi")
|
||||
response, err := o.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -1815,13 +1815,18 @@ func TestFormatWithdrawPermissions(t *testing.T) {
|
||||
func TestSubmitOrder(t *testing.T) {
|
||||
TestSetRealOrderDefaults(t)
|
||||
t.Parallel()
|
||||
var p = currency.Pair{
|
||||
Delimiter: "",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USDT,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USDT,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := o.SubmitOrder(p, exchange.BuyOrderSide,
|
||||
exchange.LimitOrderType, 1, 10, "hi")
|
||||
response, err := o.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -206,25 +206,34 @@ func (o *OKGroup) GetExchangeHistory(p currency.Pair, assetType assets.AssetType
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (o *OKGroup) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (resp exchange.SubmitOrderResponse, err error) {
|
||||
request := PlaceSpotOrderRequest{
|
||||
ClientOID: clientID,
|
||||
InstrumentID: o.FormatExchangeCurrency(p, assets.AssetTypeSpot).String(),
|
||||
Side: strings.ToLower(side.ToString()),
|
||||
Type: strings.ToLower(orderType.ToString()),
|
||||
Size: strconv.FormatFloat(amount, 'f', -1, 64),
|
||||
func (o *OKGroup) SubmitOrder(order *exchange.OrderSubmission) (resp exchange.SubmitOrderResponse, err error) {
|
||||
if order == nil {
|
||||
return resp, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
if orderType == exchange.LimitOrderType {
|
||||
request.Price = strconv.FormatFloat(price, 'f', -1, 64)
|
||||
|
||||
err = order.Validate()
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
request := PlaceSpotOrderRequest{
|
||||
ClientOID: order.ClientID,
|
||||
InstrumentID: o.FormatExchangeCurrency(order.Pair, assets.AssetTypeSpot).String(),
|
||||
Side: strings.ToLower(order.OrderSide.ToString()),
|
||||
Type: strings.ToLower(order.OrderType.ToString()),
|
||||
Size: strconv.FormatFloat(order.Amount, 'f', -1, 64),
|
||||
}
|
||||
if order.OrderType == exchange.LimitOrderType {
|
||||
request.Price = strconv.FormatFloat(order.Price, 'f', -1, 64)
|
||||
}
|
||||
|
||||
orderResponse, err := o.PlaceSpotOrder(&request)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp.IsOrderPlaced = orderResponse.Result
|
||||
resp.OrderID = orderResponse.OrderID
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
380
exchanges/order_types.go
Normal file
380
exchanges/order_types.go
Normal file
@@ -0,0 +1,380 @@
|
||||
package exchange
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/currency"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/assets"
|
||||
)
|
||||
|
||||
// vars related to orders
|
||||
var (
|
||||
ErrOrderSubmissionIsNil = errors.New("order submission is nil")
|
||||
)
|
||||
|
||||
// OrderSubmission contains the order submission data
|
||||
type OrderSubmission struct {
|
||||
Pair currency.Pair
|
||||
OrderSide OrderSide
|
||||
OrderType OrderType
|
||||
Price float64
|
||||
Amount float64
|
||||
ClientID string
|
||||
}
|
||||
|
||||
// Validate checks the supplied data and returns whether or not its valid
|
||||
func (o *OrderSubmission) Validate() error {
|
||||
if o.Pair.IsEmpty() {
|
||||
return errors.New("order pair is empty")
|
||||
}
|
||||
|
||||
if o.OrderSide != BuyOrderSide && o.OrderSide != SellOrderSide ||
|
||||
o.OrderSide != BidOrderSide && o.OrderSide != AskOrderSide {
|
||||
return errors.New("order side is invalid")
|
||||
}
|
||||
|
||||
if o.OrderType != MarketOrderType && o.OrderType != LimitOrderType {
|
||||
return errors.New("order type is invalid")
|
||||
}
|
||||
|
||||
if o.Amount <= 0 {
|
||||
return errors.New("order amount is invalid")
|
||||
}
|
||||
|
||||
if o.OrderType == LimitOrderType && o.Price <= 0 {
|
||||
return errors.New("order price must be set if limit order type is desired")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SubmitOrderResponse is what is returned after submitting an order to an exchange
|
||||
type SubmitOrderResponse struct {
|
||||
IsOrderPlaced bool
|
||||
OrderID string
|
||||
}
|
||||
|
||||
// ModifyOrder is a an order modifyer
|
||||
type ModifyOrder struct {
|
||||
OrderID string
|
||||
OrderType
|
||||
OrderSide
|
||||
Price float64
|
||||
Amount float64
|
||||
LimitPriceUpper float64
|
||||
LimitPriceLower float64
|
||||
CurrencyPair currency.Pair
|
||||
ImmediateOrCancel bool
|
||||
HiddenOrder bool
|
||||
FillOrKill bool
|
||||
PostOnly bool
|
||||
}
|
||||
|
||||
// ModifyOrderResponse is an order modifying return type
|
||||
type ModifyOrderResponse struct {
|
||||
OrderID string
|
||||
}
|
||||
|
||||
// CancelAllOrdersResponse returns the status from attempting to cancel all orders on an exchagne
|
||||
type CancelAllOrdersResponse struct {
|
||||
OrderStatus map[string]string
|
||||
}
|
||||
|
||||
// OrderType enforces a standard for Ordertypes across the code base
|
||||
type OrderType string
|
||||
|
||||
// OrderType ...types
|
||||
const (
|
||||
AnyOrderType OrderType = "ANY"
|
||||
LimitOrderType OrderType = "LIMIT"
|
||||
MarketOrderType OrderType = "MARKET"
|
||||
ImmediateOrCancelOrderType OrderType = "IMMEDIATE_OR_CANCEL"
|
||||
StopOrderType OrderType = "STOP"
|
||||
TrailingStopOrderType OrderType = "TRAILINGSTOP"
|
||||
UnknownOrderType OrderType = "UNKNOWN"
|
||||
)
|
||||
|
||||
// ToLower changes the ordertype to lower case
|
||||
func (o OrderType) ToLower() OrderType {
|
||||
return OrderType(strings.ToLower(string(o)))
|
||||
}
|
||||
|
||||
// ToString changes the ordertype to the exchange standard and returns a string
|
||||
func (o OrderType) ToString() string {
|
||||
return fmt.Sprintf("%v", o)
|
||||
}
|
||||
|
||||
// OrderSide enforces a standard for OrderSides across the code base
|
||||
type OrderSide string
|
||||
|
||||
// OrderSide types
|
||||
const (
|
||||
AnyOrderSide OrderSide = "ANY"
|
||||
BuyOrderSide OrderSide = "BUY"
|
||||
SellOrderSide OrderSide = "SELL"
|
||||
BidOrderSide OrderSide = "BID"
|
||||
AskOrderSide OrderSide = "ASK"
|
||||
)
|
||||
|
||||
// ToLower changes the ordertype to lower case
|
||||
func (o OrderSide) ToLower() OrderSide {
|
||||
return OrderSide(strings.ToLower(string(o)))
|
||||
}
|
||||
|
||||
// ToString changes the ordertype to the exchange standard and returns a string
|
||||
func (o OrderSide) ToString() string {
|
||||
return fmt.Sprintf("%v", o)
|
||||
}
|
||||
|
||||
// OrderDetail holds order detail data
|
||||
type OrderDetail struct {
|
||||
Exchange string
|
||||
AccountID string
|
||||
ID string
|
||||
CurrencyPair currency.Pair
|
||||
OrderSide OrderSide
|
||||
OrderType OrderType
|
||||
OrderDate time.Time
|
||||
Status string
|
||||
Price float64
|
||||
Amount float64
|
||||
ExecutedAmount float64
|
||||
RemainingAmount float64
|
||||
Fee float64
|
||||
Trades []TradeHistory
|
||||
}
|
||||
|
||||
// OrderCancellation type required when requesting to cancel an order
|
||||
type OrderCancellation struct {
|
||||
AccountID string
|
||||
OrderID string
|
||||
CurrencyPair currency.Pair
|
||||
AssetType assets.AssetType
|
||||
WalletAddress string
|
||||
Side OrderSide
|
||||
}
|
||||
|
||||
// GetOrdersRequest used for GetOrderHistory and GetOpenOrders wrapper functions
|
||||
type GetOrdersRequest struct {
|
||||
OrderType OrderType
|
||||
OrderSide OrderSide
|
||||
StartTicks time.Time
|
||||
EndTicks time.Time
|
||||
// Currencies Empty array = all currencies. Some endpoints only support singular currency enquiries
|
||||
Currencies []currency.Pair
|
||||
}
|
||||
|
||||
// OrderStatus defines order status types
|
||||
type OrderStatus string
|
||||
|
||||
// All OrderStatus types
|
||||
const (
|
||||
AnyOrderStatus OrderStatus = "ANY"
|
||||
NewOrderStatus OrderStatus = "NEW"
|
||||
ActiveOrderStatus OrderStatus = "ACTIVE"
|
||||
PartiallyFilledOrderStatus OrderStatus = "PARTIALLY_FILLED"
|
||||
FilledOrderStatus OrderStatus = "FILLED"
|
||||
CancelledOrderStatus OrderStatus = "CANCELED"
|
||||
PendingCancelOrderStatus OrderStatus = "PENDING_CANCEL"
|
||||
RejectedOrderStatus OrderStatus = "REJECTED"
|
||||
ExpiredOrderStatus OrderStatus = "EXPIRED"
|
||||
HiddenOrderStatus OrderStatus = "HIDDEN"
|
||||
UnknownOrderStatus OrderStatus = "UNKNOWN"
|
||||
)
|
||||
|
||||
// FilterOrdersBySide removes any OrderDetails that don't match the orderStatus provided
|
||||
func FilterOrdersBySide(orders *[]OrderDetail, orderSide OrderSide) {
|
||||
if orderSide == "" || orderSide == AnyOrderSide {
|
||||
return
|
||||
}
|
||||
|
||||
var filteredOrders []OrderDetail
|
||||
for i := range *orders {
|
||||
if strings.EqualFold(string((*orders)[i].OrderSide), string(orderSide)) {
|
||||
filteredOrders = append(filteredOrders, (*orders)[i])
|
||||
}
|
||||
}
|
||||
|
||||
*orders = filteredOrders
|
||||
}
|
||||
|
||||
// FilterOrdersByType removes any OrderDetails that don't match the orderType provided
|
||||
func FilterOrdersByType(orders *[]OrderDetail, orderType OrderType) {
|
||||
if orderType == "" || orderType == AnyOrderType {
|
||||
return
|
||||
}
|
||||
|
||||
var filteredOrders []OrderDetail
|
||||
for i := range *orders {
|
||||
if strings.EqualFold(string((*orders)[i].OrderType), string(orderType)) {
|
||||
filteredOrders = append(filteredOrders, (*orders)[i])
|
||||
}
|
||||
}
|
||||
|
||||
*orders = filteredOrders
|
||||
}
|
||||
|
||||
// FilterOrdersByTickRange removes any OrderDetails outside of the tick range
|
||||
func FilterOrdersByTickRange(orders *[]OrderDetail, startTicks, endTicks time.Time) {
|
||||
if startTicks.IsZero() || endTicks.IsZero() ||
|
||||
startTicks.Unix() == 0 || endTicks.Unix() == 0 || endTicks.Before(startTicks) {
|
||||
return
|
||||
}
|
||||
|
||||
var filteredOrders []OrderDetail
|
||||
for i := range *orders {
|
||||
if (*orders)[i].OrderDate.Unix() >= startTicks.Unix() && (*orders)[i].OrderDate.Unix() <= endTicks.Unix() {
|
||||
filteredOrders = append(filteredOrders, (*orders)[i])
|
||||
}
|
||||
}
|
||||
|
||||
*orders = filteredOrders
|
||||
}
|
||||
|
||||
// FilterOrdersByCurrencies removes any OrderDetails that do not match the provided currency list
|
||||
// It is forgiving in that the provided currencies can match quote or base currencies
|
||||
func FilterOrdersByCurrencies(orders *[]OrderDetail, currencies []currency.Pair) {
|
||||
if len(currencies) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var filteredOrders []OrderDetail
|
||||
for i := range *orders {
|
||||
matchFound := false
|
||||
for _, c := range currencies {
|
||||
if !matchFound && (*orders)[i].CurrencyPair.EqualIncludeReciprocal(c) {
|
||||
matchFound = true
|
||||
}
|
||||
}
|
||||
|
||||
if matchFound {
|
||||
filteredOrders = append(filteredOrders, (*orders)[i])
|
||||
}
|
||||
}
|
||||
|
||||
*orders = filteredOrders
|
||||
}
|
||||
|
||||
// ByPrice used for sorting orders by price
|
||||
type ByPrice []OrderDetail
|
||||
|
||||
func (b ByPrice) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (b ByPrice) Less(i, j int) bool {
|
||||
return b[i].Price < b[j].Price
|
||||
}
|
||||
|
||||
func (b ByPrice) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
// SortOrdersByPrice the caller function to sort orders
|
||||
func SortOrdersByPrice(orders *[]OrderDetail, reverse bool) {
|
||||
if reverse {
|
||||
sort.Sort(sort.Reverse(ByPrice(*orders)))
|
||||
} else {
|
||||
sort.Sort(ByPrice(*orders))
|
||||
}
|
||||
}
|
||||
|
||||
// ByOrderType used for sorting orders by order type
|
||||
type ByOrderType []OrderDetail
|
||||
|
||||
func (b ByOrderType) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (b ByOrderType) Less(i, j int) bool {
|
||||
return b[i].OrderType.ToString() < b[j].OrderType.ToString()
|
||||
}
|
||||
|
||||
func (b ByOrderType) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
// SortOrdersByType the caller function to sort orders
|
||||
func SortOrdersByType(orders *[]OrderDetail, reverse bool) {
|
||||
if reverse {
|
||||
sort.Sort(sort.Reverse(ByOrderType(*orders)))
|
||||
} else {
|
||||
sort.Sort(ByOrderType(*orders))
|
||||
}
|
||||
}
|
||||
|
||||
// ByCurrency used for sorting orders by order currency
|
||||
type ByCurrency []OrderDetail
|
||||
|
||||
func (b ByCurrency) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (b ByCurrency) Less(i, j int) bool {
|
||||
return b[i].CurrencyPair.String() < b[j].CurrencyPair.String()
|
||||
}
|
||||
|
||||
func (b ByCurrency) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
// SortOrdersByCurrency the caller function to sort orders
|
||||
func SortOrdersByCurrency(orders *[]OrderDetail, reverse bool) {
|
||||
if reverse {
|
||||
sort.Sort(sort.Reverse(ByCurrency(*orders)))
|
||||
} else {
|
||||
sort.Sort(ByCurrency(*orders))
|
||||
}
|
||||
}
|
||||
|
||||
// ByDate used for sorting orders by order date
|
||||
type ByDate []OrderDetail
|
||||
|
||||
func (b ByDate) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (b ByDate) Less(i, j int) bool {
|
||||
return b[i].OrderDate.Unix() < b[j].OrderDate.Unix()
|
||||
}
|
||||
|
||||
func (b ByDate) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
// SortOrdersByDate the caller function to sort orders
|
||||
func SortOrdersByDate(orders *[]OrderDetail, reverse bool) {
|
||||
if reverse {
|
||||
sort.Sort(sort.Reverse(ByDate(*orders)))
|
||||
} else {
|
||||
sort.Sort(ByDate(*orders))
|
||||
}
|
||||
}
|
||||
|
||||
// ByOrderSide used for sorting orders by order side (buy sell)
|
||||
type ByOrderSide []OrderDetail
|
||||
|
||||
func (b ByOrderSide) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (b ByOrderSide) Less(i, j int) bool {
|
||||
return b[i].OrderSide.ToString() < b[j].OrderSide.ToString()
|
||||
}
|
||||
|
||||
func (b ByOrderSide) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
// SortOrdersBySide the caller function to sort orders
|
||||
func SortOrdersBySide(orders *[]OrderDetail, reverse bool) {
|
||||
if reverse {
|
||||
sort.Sort(sort.Reverse(ByOrderSide(*orders)))
|
||||
} else {
|
||||
sort.Sort(ByOrderSide(*orders))
|
||||
}
|
||||
}
|
||||
@@ -253,19 +253,19 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var pair = currency.Pair{
|
||||
Delimiter: "_",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.LTC,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Delimiter: "_",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.LTC,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
|
||||
response, err := p.SubmitOrder(pair,
|
||||
exchange.BuyOrderSide,
|
||||
exchange.LimitOrderType,
|
||||
1,
|
||||
10,
|
||||
"hi")
|
||||
|
||||
response, err := p.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -302,26 +302,30 @@ func (p *Poloniex) GetExchangeHistory(currencyPair currency.Pair, assetType asse
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (p *Poloniex) SubmitOrder(currencyPair currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
|
||||
func (p *Poloniex) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
fillOrKill := orderType == exchange.MarketOrderType
|
||||
isBuyOrder := side == exchange.BuyOrderSide
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
response, err := p.PlaceOrder(currencyPair.String(),
|
||||
price,
|
||||
amount,
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
fillOrKill := order.OrderType == exchange.MarketOrderType
|
||||
isBuyOrder := order.OrderSide == exchange.BuyOrderSide
|
||||
response, err := p.PlaceOrder(order.Pair.String(),
|
||||
order.Price,
|
||||
order.Amount,
|
||||
false,
|
||||
fillOrKill,
|
||||
isBuyOrder)
|
||||
|
||||
if response.OrderNumber > 0 {
|
||||
submitOrderResponse.OrderID = fmt.Sprintf("%v", response.OrderNumber)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
submitOrderResponse.IsOrderPlaced = true
|
||||
}
|
||||
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
|
||||
@@ -372,12 +372,19 @@ func TestSubmitOrder(t *testing.T) {
|
||||
t.Skip("API keys set, canManipulateRealOrders false, skipping test")
|
||||
}
|
||||
|
||||
var pair = currency.Pair{
|
||||
Delimiter: "_",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USD,
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Delimiter: "_",
|
||||
Base: currency.BTC,
|
||||
Quote: currency.USD,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
response, err := y.SubmitOrder(pair, exchange.BuyOrderSide, exchange.LimitOrderType, 1, 10, "hi")
|
||||
response, err := y.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -281,22 +281,28 @@ func (y *Yobit) GetExchangeHistory(p currency.Pair, assetType assets.AssetType)
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
// Yobit only supports limit orders
|
||||
func (y *Yobit) SubmitOrder(p currency.Pair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
|
||||
func (y *Yobit) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if orderType != exchange.LimitOrderType {
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
if order.OrderType != exchange.LimitOrderType {
|
||||
return submitOrderResponse, errors.New("only limit orders are allowed")
|
||||
}
|
||||
|
||||
response, err := y.Trade(p.String(), side.ToString(), amount, price)
|
||||
response, err := y.Trade(order.Pair.String(), order.OrderSide.ToString(),
|
||||
order.Amount, order.Price)
|
||||
if response > 0 {
|
||||
submitOrderResponse.OrderID = fmt.Sprintf("%v", response)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
submitOrderResponse.IsOrderPlaced = true
|
||||
}
|
||||
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
|
||||
@@ -288,25 +288,25 @@ func areTestAPIKeysSet() bool {
|
||||
func TestSubmitOrder(t *testing.T) {
|
||||
z.SetDefaults()
|
||||
TestSetup(t)
|
||||
|
||||
if areTestAPIKeysSet() && !canManipulateRealOrders {
|
||||
t.Skip(fmt.Sprintf("ApiKey: %s. Can place orders: %v",
|
||||
z.API.Credentials.Key,
|
||||
canManipulateRealOrders))
|
||||
}
|
||||
var pair = currency.Pair{
|
||||
Delimiter: "_",
|
||||
Base: currency.QTUM,
|
||||
Quote: currency.USDT,
|
||||
|
||||
var orderSubmission = &exchange.OrderSubmission{
|
||||
Pair: currency.Pair{
|
||||
Delimiter: "_",
|
||||
Base: currency.QTUM,
|
||||
Quote: currency.USD,
|
||||
},
|
||||
OrderSide: exchange.BuyOrderSide,
|
||||
OrderType: exchange.LimitOrderType,
|
||||
Price: 1,
|
||||
Amount: 1,
|
||||
ClientID: "meowOrder",
|
||||
}
|
||||
|
||||
response, err := z.SubmitOrder(pair,
|
||||
exchange.BuyOrderSide,
|
||||
exchange.LimitOrderType,
|
||||
1,
|
||||
10,
|
||||
"hi")
|
||||
|
||||
response, err := z.SubmitOrder(orderSubmission)
|
||||
if areTestAPIKeysSet() && (err != nil || !response.IsOrderPlaced) {
|
||||
t.Errorf("Order failed to be placed: %v", err)
|
||||
} else if !areTestAPIKeysSet() && err == nil {
|
||||
|
||||
@@ -300,32 +300,36 @@ func (z *ZB) GetExchangeHistory(p currency.Pair, assetType assets.AssetType) ([]
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (z *ZB) SubmitOrder(p currency.Pair, side exchange.OrderSide, _ exchange.OrderType, amount, price float64, _ string) (exchange.SubmitOrderResponse, error) {
|
||||
func (z *ZB) SubmitOrder(order *exchange.OrderSubmission) (exchange.SubmitOrderResponse, error) {
|
||||
var submitOrderResponse exchange.SubmitOrderResponse
|
||||
var oT SpotNewOrderRequestParamsType
|
||||
if order == nil {
|
||||
return submitOrderResponse, exchange.ErrOrderSubmissionIsNil
|
||||
}
|
||||
|
||||
if side == exchange.BuyOrderSide {
|
||||
if err := order.Validate(); err != nil {
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
var oT SpotNewOrderRequestParamsType
|
||||
if order.OrderSide == exchange.BuyOrderSide {
|
||||
oT = SpotNewOrderRequestParamsTypeBuy
|
||||
} else {
|
||||
oT = SpotNewOrderRequestParamsTypeSell
|
||||
}
|
||||
|
||||
var params = SpotNewOrderRequestParams{
|
||||
Amount: amount,
|
||||
Price: price,
|
||||
Symbol: strings.ToLower(p.String()),
|
||||
Amount: order.Amount,
|
||||
Price: order.Price,
|
||||
Symbol: order.Pair.Lower().String(),
|
||||
Type: oT,
|
||||
}
|
||||
response, err := z.SpotNewOrder(params)
|
||||
|
||||
if response > 0 {
|
||||
submitOrderResponse.OrderID = fmt.Sprintf("%v", response)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
submitOrderResponse.IsOrderPlaced = true
|
||||
}
|
||||
|
||||
return submitOrderResponse, err
|
||||
}
|
||||
|
||||
|
||||
737
gctrpc/rpc.pb.go
737
gctrpc/rpc.pb.go
File diff suppressed because it is too large
Load Diff
@@ -9,13 +9,13 @@ It translates gRPC into RESTful JSON APIs.
|
||||
package gctrpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/utilities"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
@@ -45,7 +45,10 @@ func request_GoCryptoTrader_GetExchanges_0(ctx context.Context, marshaler runtim
|
||||
var protoReq GetExchangesRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_GetExchanges_0); err != nil {
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetExchanges_0); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
@@ -79,7 +82,10 @@ func request_GoCryptoTrader_GetExchangeInfo_0(ctx context.Context, marshaler run
|
||||
var protoReq GenericExchangeNameRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_GetExchangeInfo_0); err != nil {
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetExchangeInfo_0); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
@@ -88,6 +94,26 @@ func request_GoCryptoTrader_GetExchangeInfo_0(ctx context.Context, marshaler run
|
||||
|
||||
}
|
||||
|
||||
var (
|
||||
filter_GoCryptoTrader_GetExchangeOTPCode_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
|
||||
)
|
||||
|
||||
func request_GoCryptoTrader_GetExchangeOTPCode_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GenericExchangeNameRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetExchangeOTPCode_0); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
msg, err := client.GetExchangeOTPCode(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
func request_GoCryptoTrader_EnableExchange_0(ctx context.Context, marshaler runtime.Marshaler, client GoCryptoTraderClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq GenericExchangeNameRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
@@ -165,7 +191,10 @@ func request_GoCryptoTrader_GetAccountInfo_0(ctx context.Context, marshaler runt
|
||||
var protoReq GetAccountInfoRequest
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_GoCryptoTrader_GetAccountInfo_0); err != nil {
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_GoCryptoTrader_GetAccountInfo_0); err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
|
||||
}
|
||||
|
||||
@@ -567,6 +596,26 @@ func RegisterGoCryptoTraderHandlerClient(ctx context.Context, mux *runtime.Serve
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("GET", pattern_GoCryptoTrader_GetExchangeOTPCode_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_GoCryptoTrader_GetExchangeOTPCode_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_GoCryptoTrader_GetExchangeOTPCode_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_GoCryptoTrader_EnableExchange_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
@@ -1079,6 +1128,8 @@ var (
|
||||
|
||||
pattern_GoCryptoTrader_GetExchangeInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getexchangeinfo"}, ""))
|
||||
|
||||
pattern_GoCryptoTrader_GetExchangeOTPCode_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getexchangeotp"}, ""))
|
||||
|
||||
pattern_GoCryptoTrader_EnableExchange_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "enableexchange"}, ""))
|
||||
|
||||
pattern_GoCryptoTrader_GetTicker_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "getticker"}, ""))
|
||||
@@ -1139,6 +1190,8 @@ var (
|
||||
|
||||
forward_GoCryptoTrader_GetExchangeInfo_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_GoCryptoTrader_GetExchangeOTPCode_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_GoCryptoTrader_EnableExchange_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_GoCryptoTrader_GetTicker_0 = runtime.ForwardResponseMessage
|
||||
|
||||
@@ -28,6 +28,10 @@ message GetExchangesResponse {
|
||||
string exchanges = 1;
|
||||
}
|
||||
|
||||
message GetExchangeOTPReponse {
|
||||
string otp_code = 1;
|
||||
}
|
||||
|
||||
message DisableExchangeRequest {
|
||||
string exchange = 1;
|
||||
}
|
||||
@@ -412,6 +416,12 @@ service GoCryptoTrader {
|
||||
};
|
||||
}
|
||||
|
||||
rpc GetExchangeOTPCode (GenericExchangeNameRequest) returns (GetExchangeOTPReponse) {
|
||||
option (google.api.http) = {
|
||||
get: "/v1/getexchangeotp"
|
||||
};
|
||||
}
|
||||
|
||||
rpc EnableExchange (GenericExchangeNameRequest) returns (GenericExchangeNameResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v1/enableexchange"
|
||||
|
||||
@@ -303,6 +303,30 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/getexchangeotp": {
|
||||
"get": {
|
||||
"operationId": "GetExchangeOTPCode",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/gctrpcGetExchangeOTPReponse"
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "exchange",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"GoCryptoTrader"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v1/getexchanges": {
|
||||
"get": {
|
||||
"operationId": "GetExchanges",
|
||||
@@ -1072,6 +1096,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"gctrpcGetExchangeOTPReponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"otp_code": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"gctrpcGetExchangesResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
Reference in New Issue
Block a user