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 }