dero-swaps/price.go
2024-04-20 22:21:02 +02:00

307 lines
7.0 KiB
Go

package main
import (
"bytes"
"dero-swap/cfg"
"dero-swap/clients"
"dero-swap/coin"
"dero-swap/dero"
"dero-swap/monero"
"encoding/json"
"io"
"log"
"net/http"
"strconv"
"time"
)
func GetMarket(pair string, provider int) (prices Swap_Price) {
resp, err := http.Get(pair)
if err != nil {
log.Printf("Market: HTTP Get: %v\n", err)
return Swap_Price{}
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Printf("Market: Read Body: %v\n", err)
return Swap_Price{}
}
var market Price_Provider
if err := json.Unmarshal(body, &market); err != nil {
log.Printf("Market: Cannot unmarshal data: %v\n", err)
return Swap_Price{}
}
switch provider {
case TO:
prices.Ask, err = strconv.ParseFloat(market.Ask, 64)
prices.Bid, err = strconv.ParseFloat(market.Bid, 64)
prices.Median, err = strconv.ParseFloat(market.Price, 64)
case XEGGEX:
prices.Ask, err = strconv.ParseFloat(market.BestAsk, 64)
prices.Bid, err = strconv.ParseFloat(market.BestBid, 64)
prices.Median, err = strconv.ParseFloat(market.LastPrice, 64)
}
if err != nil {
log.Printf("Market: Cannot convert string: %v\n", err)
return
}
return
}
// Get Pair values
// TODO: configurable fees; clean-up
func GetPrice(pair string) (bid float64, ask float64) {
var base, base_usd Swap_Price
var atomicUnits uint = 8
var simple bool
switch pair {
case coin.BTCDERO, coin.DEROBTC:
simple = true
for i := range DERO_BTC {
if base = GetMarket(DERO_BTC[i], i); base.Ask > 0 && base.Bid > 0 {
break
}
}
case coin.LTCDERO, coin.DEROLTC:
for i := range LTC_USDT {
if base = GetMarket(LTC_USDT[i], i); base.Ask > 0 && base.Bid > 0 {
break
}
}
case coin.XMRDERO, coin.DEROXMR:
atomicUnits = 12
for i := range XMR_USDT {
if base = GetMarket(XMR_USDT[i], i); base.Ask > 0 && base.Bid > 0 {
break
}
}
case coin.ARRRDERO, coin.DEROARRR:
base = GetMarket(ARRR_USDT, TO)
}
if base.Ask == 0 || base.Bid == 0 {
return 0, 0
}
if simple {
bid = base.Bid - (base.Bid * cfg.SwapFees.Swap.Bid / 100)
bid = coin.RoundFloat(bid, atomicUnits)
ask = base.Ask + (base.Ask * cfg.SwapFees.Swap.Ask / 100)
ask = coin.RoundFloat(ask, atomicUnits)
return
}
for i := range DERO_USDT {
if base_usd = GetMarket(DERO_USDT[i], i); base_usd.Ask > 0 && base_usd.Bid > 0 {
break
}
}
if base_usd.Ask == 0 || base_usd.Bid == 0 {
return 0, 0
}
bid = base_usd.Bid - (base_usd.Bid * cfg.SwapFees.Swap.Bid / 100)
ask = base_usd.Ask + (base_usd.Ask * cfg.SwapFees.Swap.Ask / 100)
bid = coin.RoundFloat(bid/base.Bid, atomicUnits)
ask = coin.RoundFloat(ask/base.Ask, atomicUnits)
return
}
// TODO: simplify
func UpdateMarkets() {
var btc, ltc, xmr, arrr float64
var derobtc, deroltc, deroxmr, deroarrr float64
for p := range coin.SimplePairs {
switch p {
case coin.XMRDERO, coin.DEROXMR:
deroxmr, xmr = GetPrice(p)
case coin.ARRRDERO, coin.DEROARRR:
deroarrr, arrr = GetPrice(p)
case coin.LTCDERO, coin.DEROLTC:
deroltc, ltc = GetPrice(p)
case coin.BTCDERO, coin.DEROBTC:
derobtc, btc = GetPrice(p)
}
}
// sometimes TradeOgre's BID/ASK values are swapped
if derobtc > 0 && btc > 0 && derobtc > btc {
swap := btc
btc = derobtc
derobtc = swap
}
if deroltc > 0 && ltc > 0 && deroltc > ltc {
swap := ltc
ltc = deroltc
deroltc = swap
}
if deroarrr > 0 && arrr > 0 && deroarrr > arrr {
swap := arrr
arrr = deroarrr
deroarrr = swap
}
if deroxmr > 0 && xmr > 0 && deroxmr > xmr {
swap := xmr
xmr = deroxmr
deroxmr = swap
}
mk.Lock()
defer mk.Unlock()
// TODO: simplify
if btc > 0 {
mk.Pairs.BTC = btc
mk.Update[coin.BTCDERO] = time.Now().UnixMilli()
IsPairAvailable[coin.BTCDERO] = true
} else {
t := time.UnixMilli(mk.Update[coin.BTCDERO])
if time.Since(t) > time.Minute*2 {
IsPairAvailable[coin.BTCDERO] = false
log.Println("BTC->DERO disabled")
}
}
if ltc > 0 {
mk.Pairs.LTC = ltc
mk.Update[coin.LTCDERO] = time.Now().UnixMilli()
IsPairAvailable[coin.LTCDERO] = true
} else {
t := time.UnixMilli(mk.Update[coin.LTCDERO])
if time.Since(t) > time.Minute*2 {
IsPairAvailable[coin.LTCDERO] = false
log.Println("LTC->DERO disabled")
}
}
if arrr > 0 {
mk.Pairs.ARRR = arrr
mk.Update[coin.ARRRDERO] = time.Now().UnixMilli()
IsPairAvailable[coin.ARRRDERO] = true
} else {
t := time.UnixMilli(mk.Update[coin.ARRRDERO])
if time.Since(t) > time.Minute*2 {
IsPairAvailable[coin.ARRRDERO] = false
log.Println("ARRR->DERO disabled")
}
}
if xmr > 0 {
mk.Pairs.XMR = xmr
mk.Update[coin.XMRDERO] = time.Now().UnixMilli()
IsPairAvailable[coin.XMRDERO] = true
} else {
t := time.UnixMilli(mk.Update[coin.XMRDERO])
if time.Since(t) > time.Minute*2 {
IsPairAvailable[coin.XMRDERO] = false
log.Println("XMR->DERO disabled")
}
}
if deroltc > 0 {
mk.Pairs.DEROLTC = deroltc
mk.Update[coin.DEROLTC] = time.Now().UnixMilli()
IsPairAvailable[coin.DEROLTC] = true
} else {
t := time.UnixMilli(mk.Update[coin.DEROLTC])
if time.Since(t) > time.Minute*2 {
IsPairAvailable[coin.DEROLTC] = false
log.Println("DERO->LTC disabled")
}
}
if derobtc > 0 {
mk.Pairs.DEROBTC = derobtc
mk.Update[coin.DEROBTC] = time.Now().UnixMilli()
IsPairAvailable[coin.DEROBTC] = true
} else {
t := time.UnixMilli(mk.Update[coin.DEROBTC])
if time.Since(t) > time.Minute*2 {
IsPairAvailable[coin.DEROBTC] = false
log.Println("DERO->BTC disabled")
}
}
if deroarrr > 0 {
mk.Pairs.DEROARRR = deroarrr
mk.Update[coin.DEROARRR] = time.Now().UnixMilli()
IsPairAvailable[coin.DEROARRR] = true
} else {
t := time.UnixMilli(mk.Update[coin.DEROARRR])
if time.Since(t) > time.Minute*2 {
IsPairAvailable[coin.DEROARRR] = false
log.Println("DERO->ARRR disabled")
}
}
if deroxmr > 0 {
mk.Pairs.DEROXMR = deroxmr
mk.Update[coin.DEROXMR] = time.Now().UnixMilli()
IsPairAvailable[coin.DEROXMR] = true
} else {
t := time.UnixMilli(mk.Update[coin.DEROXMR])
if time.Since(t) > time.Minute*2 {
IsPairAvailable[coin.DEROXMR] = false
log.Println("DERO->XMR disabled")
}
}
var data bytes.Buffer
var out WS_Message
out.Method = "market"
out.Result = mk.Pairs
encoder := json.NewEncoder(&data)
err := encoder.Encode(out)
if err != nil {
log.Printf("Market: %v\n", err)
return
}
SendWSData(nil, data.Bytes())
data.Reset()
out.Method = "balance"
out.Result = UpdatePool()
err = encoder.Encode(out)
if err != nil {
log.Printf("Balance: %v\n", err)
return
}
SendWSData(nil, data.Bytes())
}
// TODO: reduce function calls
func UpdatePool() Swap_Balance {
lock.Lock()
defer lock.Unlock()
var balance Swap_Balance
balance.Dero = dero.GetBalance()
for p := range coin.SimplePairs {
switch p {
case coin.XMRDERO, coin.DEROXMR:
balance.XMR = monero.GetBalance()
case coin.ARRRDERO, coin.DEROARRR:
balance.ARRR = coin.XTCGetBalance(p)
case coin.LTCDERO, coin.DEROLTC:
balance.LTC = coin.XTCGetBalance(p)
case coin.BTCDERO, coin.DEROBTC:
balance.BTC = coin.XTCGetBalance(p)
}
}
balance.External = clients.GetExternalBalances()
return balance
}