-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* chore(gemini adapter): correct comments * chore(gemini adapter): linting * chore(gemini adapter): linting * feat(exchange adapters): add bitget adapter * test(bitget adapter): fix test * test(bitget adapter): correct test descriptions * chore(bitget adapter): make linter happpyy again * chore: update certificate --------- Co-authored-by: nina / ნინა <barbakadzeninaa@gmail.com>
- Loading branch information
1 parent
220baea
commit c87cedf
Showing
4 changed files
with
209 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import { Exchange } from '../utils' | ||
import { BaseExchangeAdapter, ExchangeDataType, Ticker, Trade } from './base' | ||
|
||
export class BitgetAdapter extends BaseExchangeAdapter { | ||
baseApiUrl = 'https://api.bitget.com/api' | ||
|
||
readonly _exchangeName = Exchange.BITGET | ||
// cloudflare cert | ||
readonly _certFingerprint256 = | ||
'3A:BB:E6:3D:AF:75:6C:50:16:B6:B8:5F:52:01:5F:D8:E8:AC:BE:27:7C:50:87:B1:27:A6:05:63:A8:41:ED:8A' | ||
|
||
async fetchTicker(): Promise<Ticker> { | ||
return this.parseTicker( | ||
await this.fetchFromApi( | ||
ExchangeDataType.TICKER, | ||
`spot/v1/market/ticker?symbol=${this.pairSymbol}_SPBL` | ||
) | ||
) | ||
} | ||
|
||
async fetchTrades(): Promise<Trade[]> { | ||
/** | ||
* Trades are cool, but empty arrays are cooler. | ||
* @bogdan, 01/2023 | ||
*/ | ||
return [] | ||
} | ||
|
||
protected generatePairSymbol(): string { | ||
const base = BaseExchangeAdapter.standardTokenSymbolMap.get(this.config.baseCurrency) | ||
const quote = BaseExchangeAdapter.standardTokenSymbolMap.get(this.config.quoteCurrency) | ||
|
||
return `${base}${quote}` | ||
} | ||
|
||
/** | ||
* Parses the json responses from the ticker and summary endpoints into the | ||
* standard format for a Ticker object | ||
* | ||
* @param pubtickerJson json response from the ticker endpoint | ||
* spot/v1/market/ticker?symbol=/${this.pairSymbol}_SPBL | ||
* https://api.bitget.com/api/spot/v1/market/ticker?symbol=BTCBRL_SPBL | ||
* https://bitgetlimited.github.io/apidoc/en/spot/#get-single-ticker | ||
* | ||
* {"code":"00000", | ||
* "data": | ||
* { | ||
* "baseVol":"9.18503", // (price symbol, e.g. "USD") The volume denominated in the price currency | ||
* "buyOne":"121890", // buy one price | ||
* "close":"121905", // Latest transaction price | ||
* "quoteVol":"1119715.23314", // (price symbol, e.g. "USD") The volume denominated in the quantity currency | ||
* "sellOne":"122012", // sell one price | ||
* "symbol":"BTCBRL", // Symbol | ||
* "ts":"1677490448241", // Timestamp | ||
* }, | ||
* "msg":"success", | ||
* "requestTime":"1677490448872" // Request status | ||
* } | ||
*/ | ||
parseTicker(pubtickerJson: any): Ticker { | ||
const data = pubtickerJson.data || {} | ||
const ticker = { | ||
...this.priceObjectMetadata, | ||
timestamp: Number(data.ts)!, | ||
bid: this.safeBigNumberParse(data.buyOne)!, | ||
ask: this.safeBigNumberParse(data.sellOne)!, | ||
lastPrice: this.safeBigNumberParse(data.close)!, | ||
baseVolume: this.safeBigNumberParse(data.baseVol)!, | ||
quoteVolume: this.safeBigNumberParse(data.quoteVol)!, | ||
} | ||
|
||
this.verifyTicker(ticker) | ||
return ticker | ||
} | ||
|
||
/** | ||
* Checks if the orderbook for the relevant pair is live. If it's not, the price | ||
* data from Ticker + Trade endpoints may be inaccurate. | ||
* | ||
* https://api.bitget.com/api/spot/v1/public/product?symbol=BTCBRL_SPBL | ||
* | ||
* API response example: | ||
* {"code":"00000", | ||
* "data": | ||
* { | ||
* "baseCoin":"BTC", | ||
* "status":"online", | ||
* symbol":"btcbrl_SPBL", | ||
* }, | ||
* "msg":"success", | ||
* "requestTime":"0" | ||
* } | ||
* | ||
* @returns bool | ||
*/ | ||
async isOrderbookLive(): Promise<boolean> { | ||
const res = await this.fetchFromApi( | ||
ExchangeDataType.ORDERBOOK_STATUS, | ||
`spot/v1/public/product?symbol=${this.pairSymbol}_SPBL` | ||
) | ||
|
||
return res.data.status === 'online' | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import BigNumber from 'bignumber.js' | ||
import { baseLogger } from '../../src/default_config' | ||
import { ExchangeAdapterConfig } from '../../src/exchange_adapters/base' | ||
import { BitgetAdapter } from '../../src/exchange_adapters/bitget' | ||
import { Exchange, ExternalCurrency } from '../../src/utils' | ||
|
||
describe('BitgetAdapter', () => { | ||
let bitgetAdapter: BitgetAdapter | ||
const config: ExchangeAdapterConfig = { | ||
baseCurrency: ExternalCurrency.BTC, | ||
baseLogger, | ||
quoteCurrency: ExternalCurrency.BRL, | ||
} | ||
beforeEach(() => { | ||
bitgetAdapter = new BitgetAdapter(config) | ||
}) | ||
afterEach(() => { | ||
jest.clearAllTimers() | ||
jest.clearAllMocks() | ||
}) | ||
|
||
const mockPubtickerJson = { | ||
code: '00000', | ||
data: { | ||
baseVol: '9.18503', // (price symbol, e.g. "USD") The volume denominated in the price currency | ||
buyOne: '121890', // buy one price = bid pice | ||
close: '121905', // Latest transaction price | ||
quoteVol: '1119715.23314', // (price symbol, e.g. "USD") The volume denominated in the quantity currency | ||
sellOne: '122012', // sell one price = ask price | ||
symbol: 'BTCBRL', // Symbol | ||
ts: 1677490448241, // Timestamp | ||
}, | ||
msg: 'success', | ||
requestTime: '1677490448872', // Request status | ||
} | ||
|
||
describe('fetchTrades', () => { | ||
it('returns an empty array', async () => { | ||
const tradesFetched = await bitgetAdapter.fetchTrades() | ||
expect(tradesFetched).toEqual([]) | ||
}) | ||
}) | ||
|
||
describe('parseTicker', () => { | ||
it('handles a response that matches the documentation', () => { | ||
const ticker = bitgetAdapter.parseTicker(mockPubtickerJson) | ||
expect(ticker).toEqual({ | ||
source: Exchange.BITGET, | ||
symbol: bitgetAdapter.standardPairSymbol, | ||
ask: new BigNumber(122012), | ||
baseVolume: new BigNumber(9.18503), | ||
bid: new BigNumber(121890), | ||
lastPrice: new BigNumber(121905), | ||
quoteVolume: new BigNumber(1119715.23314), | ||
timestamp: 1677490448241, | ||
}) | ||
}) | ||
|
||
it('throws an error when a json field mapped to a required ticker field is missing', () => { | ||
expect(() => { | ||
bitgetAdapter.parseTicker({ | ||
...mockPubtickerJson, | ||
data: { | ||
sellOne: undefined, | ||
buyOne: undefined, | ||
close: undefined, | ||
baseVol: undefined, | ||
}, | ||
}) | ||
}).toThrowError('bid, ask, lastPrice, baseVolume not defined') | ||
}) | ||
}) | ||
|
||
describe('isOrderbookLive', () => { | ||
const mockStatusJson = { | ||
code: '00000', | ||
data: { | ||
base_coin: 'BTC', | ||
status: 'online', | ||
symbol: 'btcbrl_SPBL', | ||
}, | ||
msg: 'success', | ||
requestTime: '0', | ||
} | ||
|
||
it("returns false when status isn't 'online'", async () => { | ||
const response = { ...mockStatusJson, data: { status: 'closed' } } | ||
jest.spyOn(bitgetAdapter, 'fetchFromApi').mockReturnValue(Promise.resolve(response)) | ||
expect(await bitgetAdapter.isOrderbookLive()).toEqual(false) | ||
}) | ||
|
||
it("returns true when status is 'online'", async () => { | ||
jest.spyOn(bitgetAdapter, 'fetchFromApi').mockReturnValue(Promise.resolve(mockStatusJson)) | ||
expect(await bitgetAdapter.isOrderbookLive()).toEqual(true) | ||
}) | ||
}) | ||
}) |