Skip to content

Commit

Permalink
feat: Flatten response [DEV-1794] (#5)
Browse files Browse the repository at this point in the history
* docs: readme
* Convert JSON keys to camelCase

Co-authored-by: Ankur Banerjee <ankurdotb@users.noreply.github.com>
  • Loading branch information
benyam7 and ankurdotb authored Oct 10, 2022
1 parent e81e613 commit 2bc89b3
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 96 deletions.
95 changes: 32 additions & 63 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

This repository contains a smaller helper app that fetches the latest price of a given token from the [CoinGecko's API](https://www.coingecko.com/en/api) and compares its price across different markets to detect if it is susceptible to arbitrage opportunities.

This helper app is used by the [Cosmos Custom Data API](https://github.com/cheqd/market-monitoring) to trigger alerts in case there are arbitrage opportunities flagged.
This helper app is used by the [Cosmos Custom Data API](https://github.com/cheqd/data-api) to trigger alerts in case there are arbitrage opportunities flagged.

## 📖 Usage

Expand All @@ -28,88 +28,57 @@ Compares the token price for given token, e.g., CHEQ against a given token pair

```json
{
"market_a": {
"coin_pair": "osmosis",
"market": "Osmosis",
"price": 0.03487637
},
"market_b": {
"coin_pair": "tether",
"market": "Uniswap (v3)",
"price": 0.0368916
},
"percentage_delta": 5.615959320014207
"marketPairId": "01",
"marketName1": "Osmosis",
"coinPrice1": 0.03859061,
"coinPair1": "osmosis",
"marketName2": "Gate.io",
"coinPrice2": 0.03902044,
"coinPair2": "tether",
"arbitragePossible": false,
"percentageDelta": 1.1076515521952248
}
```

#### `prices`

An array of items containing `coin_pair`, `market` and `price` of given token on that market and token pair.
An array of items containing `coinPair`, `marketName` and `coinPrice` of given token on that market and token pair.

```json
{
"coin_pair": "osmosis",
"market": "Osmosis",
"price": 0.03487637
"coinPair": "osmosis",
"marketName": "Osmosis",
"coinPrice": 0.03487637
}
```

#### `hasArbitrageOpportunities`

A boolean which will set to `true` if *any* of the market pairs have a percentage difference that exceeds the defined threshold.

```json
"hasArbitrageOpportunities": true
```

### Sample Response

```json
{
"arbitrageOpportunities": [
"prices": [
{
"market_a": {
"coin_pair": "osmosis",
"market": "Osmosis",
"price": 0.03487637
},
"market_b": {
"coin_pair": "tether",
"market": "BitMart",
"price": 0.03575065
},
"percentage_delta": 2.475766356841914
"marketName": "Osmosis",
"coinPair": "osmosis",
"coinPrice": 0.03859061
},
{
"market_a": {
"coin_pair": "osmosis",
"market": "Osmosis",
"price": 0.03487637
},
"market_b": {
"coin_pair": "tether",
"market": "Uniswap (v3)",
"price": 0.0368916
},
"percentage_delta": 5.615959320014207
"marketName": "Gate.io",
"coinPair": "tether",
"coinPrice": 0.03902044
}
],
"hasArbitrageOpportunities": true,
"prices": [
{
"coin_pair": "osmosis",
"market": "Osmosis",
"price": 0.03487637
},
{
"coin_pair": "tether",
"market": "BitMart",
"price": 0.03575065
},
"arbitrageOpportunities": [
{
"coin_pair": "tether",
"market": "Uniswap (v3)",
"price": 0.0368916
"marketPairId": "01",
"marketName1": "Osmosis",
"coinPrice1": 0.03859061,
"coinPair1": "osmosis",
"marketName2": "Gate.io",
"coinPrice2": 0.03902044,
"coinPair2": "tether",
"arbitragePossible": false,
"percentageDelta": 1.1076515521952248
}
]
}
Expand Down Expand Up @@ -154,4 +123,4 @@ Please reach out to us there for discussions, help, and feedback on the project.

## 🙋 Find us elsewhere

[![Telegram](https://img.shields.io/badge/Telegram-2CA5E0?style=for-the-badge\&logo=telegram\&logoColor=white)](https://t.me/cheqd) [![Discord](https://img.shields.io/badge/Discord-7289DA?style=for-the-badge\&logo=discord\&logoColor=white)](http://cheqd.link/discord-github) [![Twitter](https://img.shields.io/badge/Twitter-1DA1F2?style=for-the-badge\&logo=twitter\&logoColor=white)](https://twitter.com/intent/follow?screen\_name=cheqd\_io) [![LinkedIn](https://img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge\&logo=linkedin\&logoColor=white)](http://cheqd.link/linkedin) [![Slack](https://img.shields.io/badge/Slack-4A154B?style=for-the-badge\&logo=slack\&logoColor=white)](http://cheqd.link/join-cheqd-slack) [![Medium](https://img.shields.io/badge/Medium-12100E?style=for-the-badge\&logo=medium\&logoColor=white)](https://blog.cheqd.io) [![YouTube](https://img.shields.io/badge/YouTube-FF0000?style=for-the-badge\&logo=youtube\&logoColor=white)](https://www.youtube.com/channel/UCBUGvvH6t3BAYo5u41hJPzw/)
[![Telegram](https://img.shields.io/badge/Telegram-2CA5E0?style=for-the-badge&logo=telegram&logoColor=white)](https://t.me/cheqd) [![Discord](https://img.shields.io/badge/Discord-7289DA?style=for-the-badge&logo=discord&logoColor=white)](http://cheqd.link/discord-github) [![Twitter](https://img.shields.io/badge/Twitter-1DA1F2?style=for-the-badge&logo=twitter&logoColor=white)](https://twitter.com/intent/follow?screen_name=cheqd_io) [![LinkedIn](https://img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white)](http://cheqd.link/linkedin) [![Slack](https://img.shields.io/badge/Slack-4A154B?style=for-the-badge&logo=slack&logoColor=white)](http://cheqd.link/join-cheqd-slack) [![Medium](https://img.shields.io/badge/Medium-12100E?style=for-the-badge&logo=medium&logoColor=white)](https://blog.cheqd.io) [![YouTube](https://img.shields.io/badge/YouTube-FF0000?style=for-the-badge&logo=youtube&logoColor=white)](https://www.youtube.com/channel/UCBUGvvH6t3BAYo5u41hJPzw/)
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 17 additions & 13 deletions src/app/flagArbitrage.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
const { MARKET_ARBITRAGE_THRESHOLD } = require("../helpers/constants");
const { MARKET_ARBITRAGE_THRESHOLD } = require('../helpers/constants');

class FlagArbitrage {
calculateDifferencePercentage(price_a, price_b) {
return 100 * Math.abs((price_a - price_b) / ((price_a + price_b) / 2));
}

arbitrageOpportunities(prices) {
var arbitrageOpportunities = [];

let arbitrageOpportunities = [];
for (let i = 0; i < prices.length; i++) {
for (let j = i + 1; j < prices.length; j++) {
const percentage_delta = this.calculateDifferencePercentage(
prices[i].price,
prices[j].price
const percentageDelta = this.calculateDifferencePercentage(
prices[i].coinPrice,
prices[j].coinPrice
);
if (percentage_delta > MARKET_ARBITRAGE_THRESHOLD) {
arbitrageOpportunities.push({
market_a: prices[i],
market_b: prices[j],
percentage_delta: percentage_delta
});
}

arbitrageOpportunities.push({
marketPairId: `${i}${j}`,
marketName1: prices[i].marketName,
coinPair1: prices[i].coinPair,
coinPrice1: prices[i].coinPrice,
marketName2: prices[j].marketName,
coinPair2: prices[j].coinPair,
coinPrice2: prices[j].coinPrice,
arbitragePossible: percentageDelta > MARKET_ARBITRAGE_THRESHOLD,
percentageDelta: percentageDelta,
});
}
}
return arbitrageOpportunities;
Expand Down
7 changes: 3 additions & 4 deletions src/app/pricesAndPossibleArbitrageOpportunities.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const { getCoinGeckoTickersDataForToken } = require("../api/coinGeckoTickers");
const { FlagArbitrage } = require("./flagArbitrage");
const { Tickers } = require("./tickers");
const { getCoinGeckoTickersDataForToken } = require('../api/coinGeckoTickers');
const { FlagArbitrage } = require('./flagArbitrage');
const { Tickers } = require('./tickers');

async function pricesAndPossibleArbitrageOpportunities() {
const coinGeckoData = await getCoinGeckoTickersDataForToken();
Expand All @@ -12,7 +12,6 @@ async function pricesAndPossibleArbitrageOpportunities() {
return {
prices: prices,
arbitrageOpportunities: arbitrageOpportunities,
hasArbitrageOpportunities: arbitrageOpportunities.length > 0,
};
}

Expand Down
12 changes: 6 additions & 6 deletions src/app/tickers.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
const { COINGECKO_TOKEN_ID } = require("../helpers/constants");
const { COINGECKO_TOKEN_ID } = require('../helpers/constants');

class Tickers {
getPrices(coingeckoTickers) {
const prices = [];
coingeckoTickers.forEach((ticker) => {
let coin_pair = {
market: ticker.market.name,
coin_pair:
let coinPair = {
marketName: ticker.marketName.name,
coinPair:
ticker.coin_id === COINGECKO_TOKEN_ID
? ticker.target_coin_id
: ticker.coin_id,
price: ticker.converted_last.usd,
coinPrice: ticker.converted_last.usd,
};
prices.push(coin_pair);
prices.push(coinPair);
});
return prices;
}
Expand Down
3 changes: 2 additions & 1 deletion src/helpers/constants.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const MARKET_ARBITRAGE_THRESHOLD = process.env.MARKET_ARBITRAGE_THRESHOLD || 10.0;
const MARKET_ARBITRAGE_THRESHOLD =
process.env.MARKET_ARBITRAGE_THRESHOLD || 10.0;
const COINGECKO_TOKEN_ID = process.env.COINGECKO_TOKEN_ID || 'cheqd-network';

exports.MARKET_ARBITRAGE_THRESHOLD = MARKET_ARBITRAGE_THRESHOLD;
Expand Down
8 changes: 4 additions & 4 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
const express = require("express");
const createError = require("http-errors");
const express = require('express');
const createError = require('http-errors');
const app = express();

const arbitrageRouter = require("./routes/arbitrage");
const arbitrageRouter = require('./routes/arbitrage');

app.use("/", arbitrageRouter);
app.use('/', arbitrageRouter);
app.use((_, __, next) => {
next(createError(404));
});
Expand Down
6 changes: 3 additions & 3 deletions src/routes/arbitrage.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const express = require("express");
const express = require('express');
const router = express.Router();
const {
pricesAndPossibleArbitrageOpportunities,
} = require("../app/pricesAndPossibleArbitrageOpportunities");
router.get("/", async (_, res, __) => {
} = require('../app/pricesAndPossibleArbitrageOpportunities');
router.get('/', async (_, res, __) => {
const data = await pricesAndPossibleArbitrageOpportunities();
res.json(data);
});
Expand Down

0 comments on commit 2bc89b3

Please sign in to comment.