init commit
This commit is contained in:
commit
0b646d8187
|
@ -0,0 +1,3 @@
|
|||
.env
|
||||
node_modules
|
||||
dist
|
|
@ -0,0 +1,37 @@
|
|||
require('dotenv').config();
|
||||
|
||||
module.exports = {
|
||||
development: {
|
||||
use_env_variable: 'TIMESCALEDB_URL',
|
||||
dialect: 'postgres',
|
||||
protocol: 'postgres',
|
||||
dialectOptions: {
|
||||
ssl: {
|
||||
require: true,
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
test: {
|
||||
use_env_variable: 'TIMESCALEDB_URL',
|
||||
dialect: 'postgres',
|
||||
protocol: 'postgres',
|
||||
dialectOptions: {
|
||||
ssl: {
|
||||
require: true,
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
production: {
|
||||
use_env_variable: 'TIMESCALEDB_URL',
|
||||
dialect: 'postgres',
|
||||
protocol: 'postgres',
|
||||
dialectOptions: {
|
||||
ssl: {
|
||||
require: true,
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,43 @@
|
|||
import { Connection, PublicKey } from "@solana/web3.js"
|
||||
import { IDS, MangoClient } from "@blockworks-foundation/mango-client"
|
||||
import PerpMarketStats from "../models/perp_market_stats"
|
||||
|
||||
async function fetchAndPersistStats() {
|
||||
// const clusterUrls = IDS.cluster_urls[cluster]
|
||||
// if (!clusterUrls) return
|
||||
// const client = new MangoClient()
|
||||
// const connection = new Connection(IDS.cluster_urls[cluster], "singleGossip")
|
||||
// const stats: any[][] = await Promise.all(
|
||||
// MANGO_GROUPS.map(async (mangoGroupName) => {
|
||||
// const assets = IDS[cluster].mango_groups?.[mangoGroupName]?.symbols
|
||||
// const mangoGroupId = IDS[cluster].mango_groups?.[mangoGroupName]?.mango_group_pk
|
||||
// const mangoGroupPk = new PublicKey(mangoGroupId)
|
||||
// const mangoGroup = await client.getMangoGroup(connection, mangoGroupPk)
|
||||
// const mangoGroupStats = Object.keys(assets).map((symbol, index) => {
|
||||
// const totalDeposits = mangoGroup.getUiTotalDeposit(index)
|
||||
// const totalBorrows = mangoGroup.getUiTotalBorrow(index)
|
||||
// return {
|
||||
// time: new Date(),
|
||||
// symbol,
|
||||
// totalDeposits,
|
||||
// totalBorrows,
|
||||
// depositInterest: mangoGroup.getDepositRate(index),
|
||||
// borrowInterest: mangoGroup.getBorrowRate(index),
|
||||
// utilization: totalDeposits > 0.0 ? totalBorrows / totalDeposits : 0.0,
|
||||
// mangoGroup: mangoGroupName,
|
||||
// }
|
||||
// })
|
||||
// return mangoGroupStats
|
||||
// })
|
||||
// )
|
||||
// const tableName = cluster === "devnet" ? DevnetStats : MainnetStats
|
||||
// try {
|
||||
// console.log("stats", stats.flat())
|
||||
// await tableName.bulkCreate(stats.flat())
|
||||
// console.log("stats inserted")
|
||||
// } catch (err) {
|
||||
// console.log("failed to insert stats", err)
|
||||
// }
|
||||
}
|
||||
|
||||
export default fetchAndPersistStats
|
|
@ -0,0 +1,11 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface, Sequelize) => {
|
||||
return queryInterface.sequelize.query('CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;');
|
||||
},
|
||||
|
||||
down: (queryInterface, Sequelize) => {
|
||||
return queryInterface.sequelize.query('DROP EXTENSION timescaledb;');
|
||||
},
|
||||
};
|
|
@ -0,0 +1,44 @@
|
|||
"use strict"
|
||||
module.exports = {
|
||||
up: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.createTable(
|
||||
"spot_market_stats",
|
||||
{
|
||||
name: {
|
||||
type: Sequelize.STRING,
|
||||
},
|
||||
publicKey: {
|
||||
type: Sequelize.STRING,
|
||||
},
|
||||
mangoGroup: {
|
||||
type: Sequelize.STRING,
|
||||
},
|
||||
depositRate: {
|
||||
type: Sequelize.DECIMAL,
|
||||
},
|
||||
borrowRate: {
|
||||
type: Sequelize.DECIMAL,
|
||||
},
|
||||
totalDeposits: {
|
||||
type: Sequelize.DECIMAL,
|
||||
},
|
||||
totalBorrows: {
|
||||
type: Sequelize.DECIMAL,
|
||||
},
|
||||
baseOraclePrice: {
|
||||
type: Sequelize.DECIMAL,
|
||||
},
|
||||
time: {
|
||||
type: Sequelize.DATE,
|
||||
},
|
||||
utilization: {
|
||||
type: Sequelize.DECIMAL,
|
||||
},
|
||||
},
|
||||
{ timestamps: false }
|
||||
)
|
||||
},
|
||||
down: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.dropTable("spot_market_stats")
|
||||
},
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
"use strict"
|
||||
module.exports = {
|
||||
up: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.createTable(
|
||||
"perp_market_stats",
|
||||
{
|
||||
name: {
|
||||
type: Sequelize.STRING,
|
||||
},
|
||||
publicKey: {
|
||||
type: Sequelize.STRING,
|
||||
},
|
||||
mangoGroup: {
|
||||
type: Sequelize.STRING,
|
||||
},
|
||||
longFunding: {
|
||||
type: Sequelize.DECIMAL,
|
||||
},
|
||||
shortFunding: {
|
||||
type: Sequelize.DECIMAL,
|
||||
},
|
||||
openInterest: {
|
||||
type: Sequelize.DECIMAL,
|
||||
},
|
||||
baseOraclePrice: {
|
||||
type: Sequelize.DECIMAL,
|
||||
},
|
||||
time: {
|
||||
type: Sequelize.DATE,
|
||||
},
|
||||
},
|
||||
{ timestamps: false }
|
||||
)
|
||||
},
|
||||
down: async (queryInterface, Sequelize) => {
|
||||
await queryInterface.dropTable("perp_market_stats")
|
||||
},
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
"use strict"
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface, Sequelize) => {
|
||||
return queryInterface.sequelize.query("SELECT create_hypertable('spot_market_stats', 'time');")
|
||||
},
|
||||
|
||||
down: (queryInterface, Sequelize) => {},
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
"use strict"
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface, Sequelize) => {
|
||||
return queryInterface.sequelize.query("SELECT create_hypertable('perp_market_stats', 'time');")
|
||||
},
|
||||
|
||||
down: (queryInterface, Sequelize) => {},
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
"use strict"
|
||||
const Sequelize = require("sequelize")
|
||||
const env = process.env.NODE_ENV || "development"
|
||||
const config = require(__dirname + "/../config/config.js")[env]
|
||||
const db = {}
|
||||
|
||||
const sequelize = new Sequelize(process.env[config.use_env_variable], config)
|
||||
|
||||
sequelize
|
||||
.authenticate()
|
||||
.then(function (err) {
|
||||
console.log("Connection has been established successfully.")
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.log("Unable to connect to the database:", err)
|
||||
})
|
||||
|
||||
db.sequelize = sequelize
|
||||
db.Sequelize = Sequelize
|
||||
|
||||
module.exports = db
|
|
@ -0,0 +1,47 @@
|
|||
import { Sequelize, DataTypes } from "sequelize"
|
||||
import db from "./index"
|
||||
|
||||
const PerpMarketStats = db.sequelize.define(
|
||||
"perp_market_stats",
|
||||
{
|
||||
name: { type: DataTypes.STRING, allowNull: false },
|
||||
longFunding: {
|
||||
type: DataTypes.DECIMAL,
|
||||
get() {
|
||||
const value = this.getDataValue("longFunding")
|
||||
return value ? parseFloat(value) : null
|
||||
},
|
||||
},
|
||||
shortFunding: {
|
||||
type: DataTypes.DECIMAL,
|
||||
get() {
|
||||
const value = this.getDataValue("shortFunding")
|
||||
return value ? parseFloat(value) : null
|
||||
},
|
||||
},
|
||||
openInterest: {
|
||||
type: DataTypes.DECIMAL,
|
||||
get() {
|
||||
const value = this.getDataValue("openInterest")
|
||||
return value === null ? null : parseFloat(value)
|
||||
},
|
||||
},
|
||||
baseOraclePrice: {
|
||||
type: DataTypes.DECIMAL,
|
||||
get() {
|
||||
const value = this.getDataValue("baseOraclePrice")
|
||||
return value === null ? null : parseFloat(value)
|
||||
},
|
||||
},
|
||||
mangoGroup: DataTypes.STRING,
|
||||
publicKey: DataTypes.STRING,
|
||||
time: DataTypes.DATE,
|
||||
},
|
||||
{
|
||||
timestamps: false,
|
||||
}
|
||||
)
|
||||
|
||||
PerpMarketStats.removeAttribute("id")
|
||||
|
||||
export default PerpMarketStats
|
|
@ -0,0 +1,48 @@
|
|||
import { Sequelize, DataTypes } from "sequelize"
|
||||
import db from "./index"
|
||||
|
||||
const PerpMarketStats = db.sequelize.define(
|
||||
"perp_market_stats",
|
||||
{
|
||||
symbol: { type: DataTypes.STRING, allowNull: false },
|
||||
totalDeposits: {
|
||||
type: DataTypes.DECIMAL,
|
||||
get() {
|
||||
const value = this.getDataValue("totalDeposits")
|
||||
return value ? parseFloat(value) : null
|
||||
},
|
||||
},
|
||||
totalBorrows: {
|
||||
type: DataTypes.DECIMAL,
|
||||
get() {
|
||||
const value = this.getDataValue("totalBorrows")
|
||||
return value ? parseFloat(value) : null
|
||||
},
|
||||
},
|
||||
depositRate: {
|
||||
type: DataTypes.DECIMAL,
|
||||
get() {
|
||||
const value = this.getDataValue("depositRate")
|
||||
return value === null ? null : parseFloat(value)
|
||||
},
|
||||
},
|
||||
borrowRate: {
|
||||
type: DataTypes.DECIMAL,
|
||||
get() {
|
||||
const value = this.getDataValue("borrowRate")
|
||||
return value === null ? null : parseFloat(value)
|
||||
},
|
||||
},
|
||||
mangoGroup: DataTypes.STRING,
|
||||
publicKey: DataTypes.STRING,
|
||||
utilization: DataTypes.DECIMAL,
|
||||
time: DataTypes.DATE,
|
||||
},
|
||||
{
|
||||
timestamps: false,
|
||||
}
|
||||
)
|
||||
|
||||
PerpMarketStats.removeAttribute("id")
|
||||
|
||||
export default PerpMarketStats
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"name": "mango-stats-v3",
|
||||
"version": "0.0.1",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "tsc --resolveJsonModule",
|
||||
"dev": "ts-node-dev src/index.ts",
|
||||
"clean": "rm -rf dist",
|
||||
"start": "node dist/src/index.js",
|
||||
"lint": "eslint src/**/*.ts",
|
||||
"format": "eslint src/**/*.ts --fix",
|
||||
"db-setup": "sequelize db:drop && sequelize db:create tsdb && sequelize db:migrate",
|
||||
"db-migrate": "sequelize db:migrate"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.11",
|
||||
"@types/node": "^14.14.28",
|
||||
"@types/validator": "^13.1.3",
|
||||
"@typescript-eslint/eslint-plugin": "^4.15.1",
|
||||
"@typescript-eslint/parser": "^4.15.1",
|
||||
"eslint": "^7.20.0",
|
||||
"prettier": "^2.2.1",
|
||||
"ts-node-dev": "^1.1.1",
|
||||
"typescript": "^4.1.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blockworks-foundation/mango-client": "^3.0.1",
|
||||
"@solana/web3.js": "^1.18.0",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"node-cron": "^2.0.3",
|
||||
"pg": "^8.5.1",
|
||||
"pg-hstore": "^2.3.3",
|
||||
"sequelize": "^6.5.0",
|
||||
"sequelize-cli": "^6.2.0",
|
||||
"ts-node": "^9.1.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
import dotenv from "dotenv"
|
||||
dotenv.config()
|
||||
import express from "express"
|
||||
import cors from "cors"
|
||||
import { Op, QueryTypes } from "sequelize"
|
||||
import PerpMarketStats from "../models/perp_market_stats"
|
||||
import { sequelize } from "../models"
|
||||
|
||||
const app = express()
|
||||
|
||||
app.use(express.json(), cors())
|
||||
|
||||
app.get("/", async (req, res) => {
|
||||
try {
|
||||
const mangoGroup = (req.query.mangoGroup as string) || ""
|
||||
let stats
|
||||
if (mangoGroup === "BTC_ETH_USDT" || mangoGroup === "BTC_ETH_SOL_SRM_USDC") {
|
||||
stats = await sequelize.query(
|
||||
`SELECT time_bucket('60 minutes', time) AS "hourly",
|
||||
"symbol",
|
||||
avg("totalDeposits")::float AS "totalDeposits",
|
||||
avg("totalBorrows")::float AS "totalBorrows",
|
||||
avg("utilization")::float AS "utilization",
|
||||
avg("depositInterest")::float AS "depositInterest",
|
||||
avg("borrowInterest")::float AS "borrowInterest",
|
||||
min("time") AS "time"
|
||||
FROM mainnet_stats
|
||||
WHERE time > current_date - interval '90' day AND "mangoGroup" = :mangoGroup
|
||||
GROUP BY "hourly", "symbol"
|
||||
ORDER BY "hourly" ASC;`,
|
||||
{
|
||||
replacements: { mangoGroup },
|
||||
type: QueryTypes.SELECT,
|
||||
}
|
||||
)
|
||||
} else {
|
||||
stats = await PerpMarketStats.findAll({
|
||||
order: [["time", "ASC"]],
|
||||
where: { mangoGroup: { [Op.or]: [null, mangoGroup] } },
|
||||
})
|
||||
}
|
||||
res.send(stats)
|
||||
} catch (e) {
|
||||
console.log("Error inserting data", e)
|
||||
}
|
||||
})
|
||||
|
||||
app.get("/current", async (req, res) => {
|
||||
try {
|
||||
const stats = await PerpMarketStats.findAll({ limit: 3, order: [["time", "DESC"]] })
|
||||
res.send(stats)
|
||||
} catch (e) {
|
||||
console.log("Error inserting data", e)
|
||||
}
|
||||
})
|
||||
|
||||
app.listen(process.env.PORT, () => console.log(`Server listening at http://localhost:${process.env.PORT}`))
|
|
@ -0,0 +1,3 @@
|
|||
import fetchAndPersistStats from '../lib/fetchStats';
|
||||
|
||||
fetchAndPersistStats();
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"compilerOptions":
|
||||
{
|
||||
"allowJs": true,
|
||||
"target": "es2019",
|
||||
"module": "commonjs",
|
||||
"outDir": "dist",
|
||||
"sourceMap": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
},
|
||||
"skipLibCheck": true,
|
||||
"include": ["src/**/*.ts", "lib/**/*.ts", "config"],
|
||||
"exclude": ["node_modules"],
|
||||
}
|
Loading…
Reference in New Issue