Skip to content

Commit

Permalink
fetch coin prices when load portfolio
Browse files Browse the repository at this point in the history
  • Loading branch information
domechn committed Dec 2, 2023
1 parent 1967a83 commit f251754
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 36 deletions.
7 changes: 2 additions & 5 deletions src/middlelayers/charts.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import _ from 'lodash'
import { generateRandomColors } from '../utils/color'
import { getDatabase, saveCoinsToDatabase } from './database'
import { AssetChangeData, AssetModel, CoinData, CoinsAmountAndValueChangeData, HistoricalData, LatestAssetsPercentageData, PNLData, TopCoinsPercentageChangeData, TopCoinsRankData, TotalValueData } from './types'
import { AssetChangeData, AssetModel, CoinData, CoinsAmountAndValueChangeData, HistoricalData, LatestAssetsPercentageData, PNLData, TopCoinsPercentageChangeData, TopCoinsRankData, TotalValueData, WalletCoinUSD } from './types'

import { loadPortfolios, queryCoinPrices } from './data'
import { getConfiguration } from './configuration'
Expand All @@ -22,10 +22,7 @@ export async function refreshAllData() {
await saveCoinsToDatabase(coins)
}

async function queryCoinsData(): Promise<(WalletCoin & {
price: number,
usdValue: number,
})[]> {
async function queryCoinsData(): Promise<(WalletCoinUSD)[]> {
const config = await getConfiguration()
if (!config) {
throw new Error("no configuration found,\n please add configuration first")
Expand Down
5 changes: 4 additions & 1 deletion src/middlelayers/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ export async function loadPortfolios(config: CexConfig & TokenConfig): Promise<W
}

async function loadPortfoliosByConfig(config: CexConfig & TokenConfig): Promise<WalletCoin[]> {
const anas = [ERC20ProAnalyzer, CexAnalyzer, SOLAnalyzer, OthersAnalyzer, BTCAnalyzer, DOGEAnalyzer]
// const anas = [ERC20ProAnalyzer, CexAnalyzer, SOLAnalyzer, OthersAnalyzer, BTCAnalyzer, DOGEAnalyzer]
const anas = [CexAnalyzer]
const coinLists = await bluebird.map(anas, async ana => {

const a = new ana(config)
Expand All @@ -58,6 +59,8 @@ async function loadPortfoliosByConfig(config: CexConfig & TokenConfig): Promise<
}, {
concurrency: anas.length,
})
console.log(coinLists);

const assets = combineCoinLists(coinLists)
return assets
}
Expand Down
9 changes: 3 additions & 6 deletions src/middlelayers/database.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import _ from "lodash"
import Database from "tauri-plugin-sql-api"
import { v4 as uuidv4 } from 'uuid'
import { CoinModel, WalletCoin } from './datafetch/types'
import { AssetModel } from './types'
import { CoinModel } from './datafetch/types'
import { AssetModel, WalletCoinUSD } from './types'
import { ASSETS_TABLE_NAME } from './charts'
import md5 from 'md5'

Expand All @@ -19,10 +19,7 @@ export async function getDatabase(): Promise<Database> {
}

// skip where value is less than 1
export async function saveCoinsToDatabase(coins: (WalletCoin & {
price: number,
usdValue: number,
})[]) {
export async function saveCoinsToDatabase(coins: WalletCoinUSD[]) {
const db = await getDatabase()

return saveToDatabase(db, _(coins).map(t => ({
Expand Down
14 changes: 7 additions & 7 deletions src/middlelayers/datafetch/coins/cex/binance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,21 @@ export class BinanceExchange implements Exchanger {
return invoke("query_binance_balance", { apiKey: this.apiKey, apiSecret: this.secret })
}

async fetchCoinsPrice(symbols: string[]): Promise<{ [k: string]: number }> {
async fetchCoinsPrice(): Promise<{ [k: string]: number }> {
// https://api.binance.com/api/v3/ticker/price
const allPrices = await sendHttpRequest<{
symbol: string
price: string
}[]>("GET", "https://api.binance.com/api/v3/ticker/price")

const allPricesMap = _(allPrices).keyBy("symbol").mapValues(v => parseFloat(v.price)).value()
const suffix = "USDT"

const resInUSDT = _(symbols).map(s => ({
symbol: s,
price: allPricesMap[s.toUpperCase() + "USDT"],
})).mapKeys("symbol").mapValues("price").value()
const allPricesMap = _(allPrices).filter(p=>p.symbol.endsWith(suffix)).map(p =>({
symbol: p.symbol.replace(suffix, ""),
price: parseFloat(p.price)
})).keyBy("symbol").mapValues("price").value()

return resInUSDT
return allPricesMap
}

async verifyConfig(): Promise<boolean> {
Expand Down
44 changes: 33 additions & 11 deletions src/middlelayers/datafetch/coins/cex/cex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ export interface Exchanger {
// key is coin symbol, value is amount
fetchTotalBalance(): Promise<{ [k: string]: number }>

// return coins price in exchange by symbol
// return coins price in exchange
// key is coin symbol, value is price in usdt
fetchCoinsPrice(symbols: string[]): Promise<{ [k: string]: number }>
fetchCoinsPrice(): Promise<{ [k: string]: number }>

verifyConfig(): Promise<boolean>
}
Expand Down Expand Up @@ -80,30 +80,52 @@ export class CexAnalyzer implements Analyzer {
async verifyConfigs(): Promise<boolean> {
const verifyResults = await bluebird.map(this.exchanges, async ex => {
return await ex.verifyConfig()
}, {
concurrency: 2,
})

return _(verifyResults).every()
}

async loadPortfolio(): Promise<WalletCoin[]> {
// key is exchange name, value is prices
const cacheCoinPrices = _(await bluebird.map(_(this.exchanges).uniqBy(ex => ex.getExchangeName()).value(), async ex => {
const pricesMap = await ex.fetchCoinsPrice()
return {
exChangeName: ex.getExchangeName(),
pricesMap,
}
})).keyBy("exChangeName").mapValues("pricesMap").value()

const getPrice = (ex: string, symbol: string): { value: number, base: string } | undefined => {
const pm = cacheCoinPrices[ex]
if (!pm) {
return undefined
}
if (symbol === "USDT") {
return {
value: 1,
base: "usdt",
}
}
const price = pm[symbol]
if (!price) {
return undefined
}
return {
value: price,
base: "usdt",
}
}

const coinLists = await bluebird.map(this.exchanges, async ex => {
const portfolio = await this.fetchTotalBalance(ex)
const prices = await ex.fetchCoinsPrice(_(portfolio).keys().value())

// filter all keys are capital
const coins = filterCoinsInPortfolio(ex.getIdentity(), portfolio)
return _(coins).map(c => ({
...c,
price: {
value: prices[c.symbol],
base: "usdt",
},
price: getPrice(ex.getExchangeName(), c.symbol),
} as WalletCoin)
).value()
}, {
concurrency: 2,
})

return _(coinLists).flatten().value()
Expand Down
17 changes: 15 additions & 2 deletions src/middlelayers/datafetch/coins/cex/gate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,21 @@ export class GateExchange implements Exchanger {
return _(resp).reduce((acc, v) => _.mergeWith(acc, v, (a, b) => (a || 0) + (b || 0)), {})
}

async fetchCoinsPrice(symbols: string[]): Promise<{ [k: string]: number }> {
return {}
async fetchCoinsPrice(): Promise<{ [k: string]: number }> {
// https://api.gateio.ws/api/v4/spot/tickers
const allPrices = await sendHttpRequest<{
currency_pair: string
last: string
}[]>("GET", this.endpoint + "/api/v4/spot/tickers")

const suffix = "_USDT"

const allPricesMap = _(allPrices).filter(p=>p.currency_pair.endsWith(suffix)).map(p =>({
symbol: p.currency_pair.replace(suffix, ""),
price: parseFloat(p.last)
})).keyBy("symbol").mapValues("price").value()

return allPricesMap
}

private async fetchSpotBalance(): Promise<{ [k: string]: number }> {
Expand Down
20 changes: 18 additions & 2 deletions src/middlelayers/datafetch/coins/cex/okex.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { invoke } from '@tauri-apps/api'
import { Exchanger } from './cex'
import { sendHttpRequest } from '../../utils/http'
import _ from 'lodash'

export class OkexExchange implements Exchanger {
private readonly apiKey: string
Expand Down Expand Up @@ -36,8 +38,22 @@ export class OkexExchange implements Exchanger {
return invoke("query_okex_balance", { apiKey: this.apiKey, apiSecret: this.secret, password: this.password })
}

async fetchCoinsPrice(symbols: string[]): Promise<{ [k: string]: number }> {
return {}
async fetchCoinsPrice(): Promise<{ [k: string]: number }> {
// https://www.okx.com/api/v5/market/tickers?instType=SPOT
const allPrices = await sendHttpRequest<{
data: {
instId: string
last: string
}[]
}>("GET", "https://www.okx.com/api/v5/market/tickers?instType=SPOT")

const suffix = "-USDT"
const allPricesMap = _(allPrices.data).filter(p=>p.instId.endsWith(suffix)).map(p =>({
symbol: p.instId.replace(suffix, ""),
price: parseFloat(p.last)
})).keyBy("symbol").mapValues("price").value()

return allPricesMap
}

async verifyConfig(): Promise<boolean> {
Expand Down
2 changes: 1 addition & 1 deletion src/middlelayers/datafetch/coins/cex/others.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class OtherCexExchanges implements Exchanger {
throw new Error('Method not implemented.')
}

async fetchCoinsPrice(symbols: string[]): Promise<{ [k: string]: number }> {
async fetchCoinsPrice(): Promise<{ [k: string]: number }> {
throw new Error('Method not implemented.')

}
Expand Down
3 changes: 2 additions & 1 deletion src/middlelayers/datafetch/utils/coins.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { WalletCoinUSD } from '@/middlelayers/types'
import { WalletCoin } from '../types'
import _ from 'lodash'

Expand All @@ -16,7 +17,7 @@ export function combineCoinLists(coinLists: WalletCoin[][]): WalletCoin[] {
).flatten().value()
}

export function calculateTotalValue(coinList: WalletCoin[], priceMap: { [k: string]: number }): (Pick<WalletCoin, "amount" | "symbol" | "wallet"> & { price: number, usdValue: number })[] {
export function calculateTotalValue(coinList: WalletCoin[], priceMap: { [k: string]: number }): WalletCoinUSD[] {
const usdtInUsd = priceMap["USDT"] ?? 1
const getPriceFromWalletCoin = (w: WalletCoin) => {
if (!w.price) {
Expand Down
6 changes: 6 additions & 0 deletions src/middlelayers/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,9 @@ export type CurrencyRateDetail = {
alias: string
symbol: string
}

export type WalletCoinUSD = Pick<WalletCoin, "amount" | "symbol" | "wallet"> & {
// price in usd
price: number,
usdValue: number,
}

0 comments on commit f251754

Please sign in to comment.