packages = ["."]
revision = "1c35d901db3da928c72a72d8458480cc9ade058f"
+[[projects]]
+ branch = "master"
+ name = "github.com/jloup/poloniex"
+ packages = ["."]
+ revision = "e75e6fd7991c1d71576ad97de73fc922f24a5fd2"
+
[[projects]]
branch = "master"
name = "github.com/jloup/utils"
revision = "b7b89250c468c06871d3837bee02e2d5c155ae19"
version = "v1.0.0"
+[[projects]]
+ branch = "master"
+ name = "github.com/shopspring/decimal"
+ packages = ["."]
+ revision = "e3482495ff4cba75613e4177ed79825c890058a9"
+
[[projects]]
name = "github.com/ugorji/go"
packages = ["codec"]
"blowfish",
"ssh/terminal"
]
- revision = "650f4a345ab4e5b245a3034b110ebc7299e68186"
+ revision = "432090b8f568c018896cd8a0fb0345872bbac6ce"
+
+[[projects]]
+ branch = "master"
+ name = "golang.org/x/net"
+ packages = ["websocket"]
+ revision = "cbe0f9307d0156177f9dd5dc85da1a31abc5f2fb"
[[projects]]
branch = "master"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
- inputs-digest = "c9af022a586632799c6259f6c48eef8dad7080e36b96e8cb5cb905b316c4cb9b"
+ inputs-digest = "d3c9b3094ed174bcf1631e3a998a75d557c65c195d7a8fd5ca9912f71f334ce1"
solver-name = "gps-cdcl"
solver-version = 1
[prune]
go-tests = true
unused-packages = true
+
+[[constraint]]
+ branch = "master"
+ name = "github.com/jloup/poloniex"
+
+[[constraint]]
+ branch = "master"
+ name = "github.com/shopspring/decimal"
import (
"fmt"
+ "github.com/shopspring/decimal"
"immae.eu/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Front/db"
+ "immae.eu/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Front/markets"
)
type MarketConfigQuery struct {
return config.Config, nil
}
+type MarketBalanceQuery struct {
+ In struct {
+ User db.User
+ Market string
+ Currency string
+ }
+}
+
+var Poloniex *markets.Poloniex
+
+func init() {
+ Poloniex = markets.NewPoloniex()
+ Poloniex.StartTicker()
+}
+
+func (q MarketBalanceQuery) ValidateParams() *Error {
+ if q.In.Market != "poloniex" {
+ return &Error{BadRequest, "invalid market name", fmt.Errorf("'%v' is not a valid market name", q.In.Market)}
+ }
+
+ // TODO: we should request market for available currencies.
+ if q.In.Currency != "BTC" || q.In.Currency != "USDT" {
+ return &Error{BadRequest, "invalid currency, accept [BTC, USDT]", fmt.Errorf("'%v' is not a valid currency", q.In.Currency)}
+ }
+
+ return nil
+}
+
+func (q MarketBalanceQuery) Run() (interface{}, *Error) {
+ config, err := db.GetUserMarketConfig(q.In.User.Id, q.In.Market)
+ if err != nil {
+ return nil, NewInternalError(err)
+ }
+
+ if config.Config["key"] == "" || config.Config["secret"] == "" {
+ return nil, &Error{BadRequest, "your credentials for this market are not setup", fmt.Errorf("'%v' credentials are not setup", q.In.Market)}
+ }
+
+ balance, err := Poloniex.GetBalance(config.Config["key"], config.Config["secret"], q.In.Currency)
+ if err != nil {
+ return nil, NewInternalError(err)
+ }
+
+ result := struct {
+ Balance decimal.Decimal `json:balance`
+ }{balance}
+
+ return &result, nil
+}
+
type UpdateMarketConfigQuery struct {
In struct {
User db.User
[]Route{
{"GET", []gin.HandlerFunc{GetMarketConfig}, "/:name"},
{"POST", []gin.HandlerFunc{UpdateMarketConfig}, "/:name/update"},
+ {"GET", []gin.HandlerFunc{GetMarketBalance}, "/:name/balance"},
},
},
}
RunQuery(query, c)
}
+func GetMarketBalance(c *gin.Context) {
+ query := &MarketBalanceQuery{}
+
+ query.In.User = GetUser(c)
+ query.In.Market = c.Param("name")
+ query.In.Currency = "BTC"
+
+ RunQuery(query, c)
+}
+
func UpdateMarketConfig(c *gin.Context) {
query := &UpdateMarketConfigQuery{}
return '/market/' + params.name;
}
},
+ 'MARKET_BALANCE': {
+ 'type': 'GET',
+ 'auth': true,
+ 'parameters': [
+ {'name': 'name', 'mandatory': true, 'inquery': false},
+ {'name': 'currency', 'mandatory': true, 'inquery': true},
+ ],
+ 'buildUrl': function(params) {
+ return '/market/' + params.name + '/balance';
+ }
+ },
'UPDATE_MARKET': {
'type': 'POST',
'auth': true,
--- /dev/null
+package markets
+
+import (
+ "fmt"
+
+ "github.com/jloup/poloniex"
+ "github.com/shopspring/decimal"
+)
+
+type CurrencyPair struct {
+ Name string
+ Rate decimal.Decimal
+}
+
+type Poloniex struct {
+ TickerCache map[string]CurrencyPair
+
+ publicClient *poloniex.Poloniex
+ updateTickerChan chan CurrencyPair
+}
+
+func NewPoloniex() *Poloniex {
+ client, _ := poloniex.NewClient("", "")
+
+ return &Poloniex{
+ TickerCache: make(map[string]CurrencyPair),
+ updateTickerChan: nil,
+ publicClient: client,
+ }
+}
+
+func (p *Poloniex) GetBalance(apiKey, apiSecret, base_currency string) (decimal.Decimal, error) {
+ client, _ := poloniex.NewClient(apiKey, apiSecret)
+
+ accounts, err := client.TradeReturnAvailableAccountBalances()
+ if err != nil {
+ return decimal.Zero, err
+ }
+
+ marginBalance, err := p.computeAccountBalance(accounts.Margin, base_currency)
+ if err != nil {
+ return decimal.Zero, err
+ }
+
+ exchangeBalance, err := p.computeAccountBalance(accounts.Exchange, base_currency)
+ if err != nil {
+ return decimal.Zero, err
+ }
+
+ return decimal.Sum(marginBalance, exchangeBalance), nil
+}
+
+func (p *Poloniex) computeAccountBalance(account map[string]decimal.Decimal, base_currency string) (decimal.Decimal, error) {
+ var total decimal.Decimal
+
+ for currency, amount := range account {
+ pair, err := p.GetCurrencyPair(base_currency, currency)
+ if err != nil {
+ return decimal.Zero, err
+ }
+
+ total = total.Add(amount.Mul(pair.Rate))
+ }
+
+ return total, nil
+}
+
+func (p *Poloniex) GetCurrencyPair(curr1, curr2 string) (CurrencyPair, error) {
+ pairName := fmt.Sprintf("%s_%s", curr1, curr2)
+
+ fmt.Println(pairName)
+ if curr1 == curr2 {
+ return CurrencyPair{pairName, decimal.NewFromFloat(1.0)}, nil
+ }
+
+ pair, ok := p.TickerCache[pairName]
+ if !ok {
+ pair, err := p.fetchTicker(pairName)
+ if err != nil {
+ return CurrencyPair{}, err
+ }
+
+ return pair, nil
+ }
+
+ return pair, nil
+}
+
+func (p *Poloniex) fetchTicker(pair string) (CurrencyPair, error) {
+ tickers, err := p.publicClient.PubReturnTickers()
+ if err != nil {
+ return CurrencyPair{}, err
+ }
+
+ if ticker, ok := tickers[pair]; ok {
+ pair := CurrencyPair{Name: pair, Rate: ticker.Last}
+
+ if p.updateTickerChan != nil {
+ p.updateTickerChan <- pair
+ }
+
+ return pair, nil
+ } else {
+ return CurrencyPair{}, fmt.Errorf("pair '%v' not in tickers", pair)
+ }
+}
+
+func (p *Poloniex) StartTicker() error {
+ stream, err := poloniex.NewWSClient()
+ if err != nil {
+ return err
+ }
+
+ err = stream.SubscribeTicker()
+ if err != nil {
+ return err
+ }
+
+ p.updateTickerChan = make(chan CurrencyPair)
+
+ go func() {
+ for {
+ quit := false
+ select {
+ case data, ok := <-stream.Subs["ticker"]:
+ if !ok {
+ quit = true
+ } else {
+ ticker := data.(poloniex.WSTicker)
+ p.TickerCache[ticker.CurrencyPair] = CurrencyPair{Name: ticker.CurrencyPair, Rate: decimal.NewFromFloat(ticker.Last)}
+ }
+
+ case pair, ok := <-p.updateTickerChan:
+ if !ok {
+ quit = true
+ } else {
+ p.TickerCache[pair.Name] = pair
+ }
+ }
+ if quit {
+ //TODO: logit
+ p.updateTickerChan = nil
+ break
+ }
+ }
+ }()
+
+ return nil
+}