diff --git a/src/blockchain/ohlcs.js b/src/blockchain/ohlcs.js
index 913ea25..77ef4f9 100644
--- a/src/blockchain/ohlcs.js
+++ b/src/blockchain/ohlcs.js
@@ -1,14 +1,48 @@
import {socket} from "@/socket.js";
-import {useStore} from "@/store/store.js";
-import {Exchange} from "@/blockchain/orderlib.js";
-import {uniswapV3PoolAddress} from "@/blockchain/uniswap.js";
-import {ethers, FixedNumber} from "ethers";
-import {uniswapV3PoolAbi} from "@/blockchain/abi.js";
const ohlcSubCounts = {} // key is route and value is a subscription counter
export const WIDE_PRICE_FORMAT = {decimals:38, width:512, signed:false}; // 38 decimals is 127 bits
+export function subOHLC( chainId, pool, period ) {
+ const key = `${pool}|${period}`
+ const ckey = `${chainId}|${key}`
+ if (!(key in ohlcSubCounts) || ohlcSubCounts[ckey] === 0) {
+ ohlcSubCounts[ckey] = 1
+ socket.emit('subOHLCs', chainId, [key])
+ } else
+ ohlcSubCounts[ckey]++
+}
+
+
+export function unsubOHLC( chainId, pool, period ) {
+ for( const [pool,period] of poolPeriods ) {
+ const key = `${pool}|${period}`
+ const ckey = `${chainId}|${key}`
+ if (!(ckey in ohlcSubCounts) || ohlcSubCounts[ckey] === 0) {
+ console.error('overdecremented ohlc', pool, period)
+ ohlcSubCounts[ckey] = 1
+ } else {
+ ohlcSubCounts[ckey]--
+ if (ohlcSubCounts[key] === 0)
+ socket.emit('unsubOHLCs', chainId, [key])
+ }
+ }
+}
+
+
+export function unsubAllOHLCs( chainId ) {
+ const chainStr = chainId.toString() + '|'
+ const toUnsub = []
+ for( const k of Object.keys(ohlcSubCounts) ) {
+ if (k.startsWith(chainStr))
+ toUnsub.push(k.slice(chainStr.length))
+ }
+ if( toUnsub.length )
+ socket.emit('unsubOHLCs', chainId, toUnsub )
+}
+
+
export function subOHLCs( chainId, poolPeriods ) {
if( !poolPeriods.length )
return
@@ -18,9 +52,8 @@ export function subOHLCs( chainId, poolPeriods ) {
if (!(key in ohlcSubCounts) || ohlcSubCounts[key] === 0) {
ohlcSubCounts[key] = 1
toSub.push(key)
- } else {
+ } else
ohlcSubCounts[key]++
- }
}
if( toSub.length )
socket.emit('subOHLCs', chainId, toSub)
@@ -33,16 +66,12 @@ export function unsubOHLCs( chainId, poolPeriods ) {
if (!(key in ohlcSubCounts) || ohlcSubCounts[key] === 0) {
console.error('overdecremented',pool,period)
ohlcSubCounts[key] = 1
- toSub.push(key)
} else {
ohlcSubCounts[key]--
- if( ohlcSubCounts[key] == 0 )
+ if( ohlcSubCounts[key] === 0 )
toUnsub.push(key)
}
}
if( toUnsub.length )
socket.emit('unsubOHLCs', chainId, toUnsub )
}
-
-// todo get history
-
diff --git a/src/charts/chart.js b/src/charts/chart.js
index 25cc892..6e66f17 100644
--- a/src/charts/chart.js
+++ b/src/charts/chart.js
@@ -1,6 +1,6 @@
import {useChartOrderStore} from "@/orderbuild.js";
import {invokeCallbacks, prototype} from "@/common.js";
-import datafeed, {lookupSymbol} from "@/charts/datafeed.js";
+import {DataFeed, initFeeDropdown, lookupSymbol} from "@/charts/datafeed.js";
export let widget = null
export let chart = null
@@ -47,7 +47,7 @@ export function initWidget(el) {
interval: '15',
container: el,
// datafeed: new Datafeeds.UDFCompatibleDatafeed("https://demo-feed-data.tradingview.com"),
- datafeed: datafeed, // use this for ohlc
+ datafeed: DataFeed, // use this for ohlc
locale: "en",
disabled_features: [],
enabled_features: ['saveload_separate_drawings_storage'],
@@ -62,8 +62,8 @@ export function initWidget(el) {
widget.subscribe('onSelectedLineToolChanged', onSelectedLineToolChanged)
widget.subscribe('mouse_down', mouseDown)
widget.subscribe('mouse_up', mouseUp)
+ widget.headerReady().then(()=>initFeeDropdown(widget))
widget.onChartReady(initChart)
-
}
diff --git a/src/charts/datafeed.js b/src/charts/datafeed.js
index 82cbec7..55ea641 100644
--- a/src/charts/datafeed.js
+++ b/src/charts/datafeed.js
@@ -3,10 +3,50 @@ import {
unsubscribeFromStream,
} from './streaming.js';
-import {jBars} from './jBars.js';
+import {jBars, tvResolutionToPeriodString} from './jBars.js';
import {metadata} from "@/version.js";
import FlexSearch from "flexsearch";
import {useChartOrderStore} from "@/orderbuild.js";
+import {useOrderStore, useStore} from "@/store/store.js";
+import {subOHLC, unsubAllOHLCs, unsubOHLC} from "@/blockchain/ohlcs.js";
+
+let feeDropdown = null
+let widget = null
+
+export function initFeeDropdown(w) {
+ widget = w
+ widget.createDropdown(
+ {
+ title: 'Fees',
+ tooltip: 'Choose Fee Tier',
+ items: [/*{title: 'Automatic Fee Selection', onSelect: () => {console.log('autofees')}}*/],
+ icon: ``,
+ }
+ ).then(dropdown => {feeDropdown = dropdown; updateFeeDropdown()})
+}
+
+
+function updateFeeDropdown() {
+ if (feeDropdown===null) return
+ const symbolItem = useChartOrderStore().selectedSymbol
+ const feeOpts = {
+ items: symbolItem.pools.map((p)=> {
+ return {
+ title: (p[1]/10000).toFixed(3)+'%',
+ onSelect: ()=>selectPool(p),
+ }
+ })
+ }
+ feeDropdown.applyOptions(feeOpts)
+}
+
+
+function selectPool(p) {
+ const co = useChartOrderStore();
+ if ( co.selectedPool === null || co.selectedPool[0] !== p[0] || co.selectedPool[1] !== p[0]) {
+ co.selectedPool = p
+ }
+}
const lastBarsCache = new Map();
@@ -52,11 +92,17 @@ const indexes = {}
const symbolsSeen = {} // keyed by (base,quote) so we only list one pool per pair even if there are many fee tiers
function addSymbol(p, base, quote, inverted) {
+ console.log('addSymbol', p)
+ if (inverted)
+ [base, quote] = [quote,base]
const symbol = base.s + '/' + quote.s
const exchange = ['UNIv2', 'UNIv3'][p.e]
const full_name = exchange + ':' + symbol // + '%' + formatFee(fee)
if (full_name in symbolsSeen) {
// add this pool's address to the existing index but don't create a new symbol
+ const symbolInfo = _symbols[full_name];
+ symbolInfo.pools.push([p.a, p.f])
+ symbolInfo.pools.sort((a,b)=>a[1]-b[1])
indexes[full_name].as.push(p.a)
return
}
@@ -64,7 +110,8 @@ function addSymbol(p, base, quote, inverted) {
const longExchange = ['Uniswap v2', 'Uniswap v3',][p.e]
const description = `${base.n} / ${quote.n}`
const type = 'swap'
- _symbols[full_name] = {symbol, full_name, description, exchange, type, inverted, base, quote, x:p.x}
+ const pools = [[p.a, p.f]]
+ _symbols[full_name] = {symbol, full_name, description, exchange, type, inverted, base, quote, pools, x:p.x}
indexes[full_name] = {
// key
id: full_name,
@@ -97,7 +144,6 @@ async function getAllSymbols() {
tokenMap[t.a] = t
metadata.p.forEach((p)=>{
poolMap[p.a] = p
- // todo inversion
const base = tokenMap[p.b];
const quote = tokenMap[p.q];
if (!base) {
@@ -122,7 +168,7 @@ export function lookupSymbol(fullName) {
return _symbols[fullName]
}
-export default {
+export const DataFeed = {
onReady: (callback) => {
console.log('[onReady]: Method call');
setTimeout(() => callback(configurationData));
@@ -160,12 +206,18 @@ export default {
onResolveErrorCallback('cannot resolve symbol');
return;
}
- useChartOrderStore().selectedSymbol = symbolItem
+ const co = useChartOrderStore();
+ const os = useOrderStore()
+ co.selectedSymbol = symbolItem
+ const pool = symbolItem.pools[Math.trunc(symbolItem.pools.length/2)];
+ // noinspection JSValidateTypes
+ co.selectedPool = [pool]
+ updateFeeDropdown()
// Symbol information object
const symbolInfo = {
ticker: symbolItem.full_name,
name: symbolItem.symbol,
- description: symbolItem.description,
+ description: symbolItem.description + ` ${(pool[1]/10000).toFixed(2)}%`,
type: symbolItem.type,
session: '24x7',
timezone: 'Etc/UTC',
@@ -180,7 +232,6 @@ export default {
volume_precision: 2,
data_status: 'streaming',
};
-
console.log('[resolveSymbol]: Symbol resolved', symbolName);
onSymbolResolvedCallback(symbolInfo);
},
@@ -188,11 +239,11 @@ export default {
getBars: async (symbolInfo, resolution, periodParams, onHistoryCallback, onErrorCallback) => {
const { from, to, firstDataRequest } = periodParams;
console.log('[getBars]: Method call', symbolInfo, resolution, from, to);
-
-
try {
+ // todo need to consider the selected fee tier
let bars, metadata;
- [bars, metadata] = await jBars(from, to, resolution); // This is the one that does all the work
+ const pool = useChartOrderStore().selectedPool;
+ [bars, metadata] = await jBars(pool[0], from, to, resolution); // This is the one that does all the work
if (firstDataRequest) {
lastBarsCache.set(symbolInfo.full_name, {
...bars[bars.length - 1],
@@ -215,6 +266,11 @@ export default {
onResetCacheNeededCallback,
) => {
console.log('[subscribeBars]: Method call with subscriberUID:', subscriberUID);
+ const chainId = useStore().chainId;
+ const poolAddr = useChartOrderStore().selectedPool[0];
+ const period = tvResolutionToPeriodString(resolution);
+ subscriptions[subscriberUID] = [chainId, poolAddr, period]
+ subOHLC(chainId, poolAddr, period)
return; // disable
subscribeOnStream(
symbolInfo,
@@ -228,7 +284,12 @@ export default {
unsubscribeBars: (subscriberUID) => {
console.log('[unsubscribeBars]: Method call with subscriberUID:', subscriberUID);
+ const [chainId, poolAddr, period] = subscriptions[subscriberUID]
+ delete subscriptions[subscriberUID]
+ unsubOHLC(chainId, poolAddr, period)
return; // disable
unsubscribeFromStream(subscriberUID);
},
};
+
+const subscriptions = {}
diff --git a/src/charts/jBars.js b/src/charts/jBars.js
index 055c867..afe2c6a 100644
--- a/src/charts/jBars.js
+++ b/src/charts/jBars.js
@@ -1,11 +1,23 @@
-export async function jBars (from, to, res) {
+import {useStore} from "@/store/store.js";
+const file_res = ['1m', '3m', '5m', '10m', '15m', '30m', '1H', '2H', '4H', '8H', '12H', '1D', '2D', '3D', '1W',];
+const supported_res = ['1', '3', '5', '10', '15', '30', '60', '120', '240', '480', '720', '1D', '2D', '3D', '1W',];
+
+const resMap = {}
+for (const i in file_res) {
+ resMap[supported_res[i]] = file_res[i]
+}
+
+export function tvResolutionToPeriodString(res) {
+ return resMap[res]
+}
+
+export async function jBars (contract, from, to, res) {
console.log('[jBars]: Method call', res, from, to);
- var toDate = new Date(to*1000);
-
- var fromDate = new Date(from*1000);
- if (res=="1W") { // for 1W, day must be Sunday
+ const toDate = new Date(to*1000);
+ const fromDate = new Date(from*1000);
+ if (res==="1W") { // for 1W, day must be Sunday
const day = fromDate.getUTCDay(); // 0<=day<7
fromDate.setDate(fromDate.getDate() + (7-day)%7 );
}
@@ -30,11 +42,9 @@ export async function jBars (from, to, res) {
console.log("fromDate:", fromDate.toUTCString());
console.log("toDate: ", toDate.toUTCString());
- const contract = "0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443";
+ contract = "0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443";
// const contract = "0xC6962004f452bE9203591991D15f6b388e09E8D0";
- const file_res = ['1m', '3m', '5m', '10m', '15m', '30m', '1H', '2H', '4H', '8H', '12H', '1D', '2D', '3D', '1W',];
- const supported_res = ['1', '3', '5', '10', '15', '30', '60', '120', '240', '480', '720', '1D', '2D', '3D', '1W',];
const daily_res = ['1', '3', '5', '10', '15', '30'];
const single_res = ['1W'];
@@ -59,13 +69,13 @@ export async function jBars (from, to, res) {
// Fetch one sample file as needed
- if (iFile == undefined ? true :
- is_monthly_res ? iFile.getUTCMonth() != iDate.getUTCMonth() :
- is_daily_res ? iFile.getUTCDate() != iDate.getUTCDate() :
+ if (iFile === undefined ? true :
+ is_monthly_res ? iFile.getUTCMonth() !== iDate.getUTCMonth() :
+ is_daily_res ? iFile.getUTCDate() !== iDate.getUTCDate() :
false // is_single_res
) {
- const fres = file_res[supported_res.indexOf(res)]
+ const fres = tvResolutionToPeriodString(res)
const yr = iDate.getUTCFullYear();
const yrdir = is_single_res ? "" : `/${yr}`;
const mo = String(iDate.getUTCMonth()+1).padStart(2, '0'); // January is month 0 in Date object
@@ -73,6 +83,9 @@ export async function jBars (from, to, res) {
const yrmo = !is_single_res ? `-${yr}${mo}` : "";
const server = "https://alpha.dexorder.trade"
+ // todo use correct chainId not a hardcoded 42161
+ // const chainId = useStore().chainId
+ // let url = `${server}/ohlc/${chainId}/${contract}/${fres}${yrdir}/${contract}-${fres}${yrmo}${date}.json`;
let url = `${server}/ohlc/42161/${contract}/${fres}${yrdir}/${contract}-${fres}${yrmo}${date}.json`;
let response = await fetch(url);
@@ -105,7 +118,7 @@ export async function jBars (from, to, res) {
const insert_missing_samples = false;
const visible_missing_samples = false;
- if (ohlcDate == undefined) {
+ if (ohlcDate === undefined) {
bar = {
time: iDate.getTime(),
}
@@ -120,7 +133,7 @@ export async function jBars (from, to, res) {
}
// file exists, but ohlc sample not for this time, insert missing sample
- else if ( iDate.getTime() != ohlcDate.getTime() ) {
+ else if ( iDate.getTime() !== ohlcDate.getTime() ) {
bar = {
time: iDate.getTime(),
}
@@ -145,14 +158,14 @@ export async function jBars (from, to, res) {
iohlc++;
}
- if (bar != undefined) bars.push(bar);
+ if (bar !== undefined) bars.push(bar);
// Increment loop variable
if (supported_res.indexOf(res)1day
iDate.setUTCDate(iDate.getUTCDate()+1);
@@ -168,7 +181,7 @@ export async function jBars (from, to, res) {
// This is probably not a safe assumption. The alternative would be to search
// backward to find beginning of history. How far to search?
- let noData = bars.length == 0;
+ let noData = bars.length === 0;
if (noData) console.log("noData == true!");
return [bars, {noData}];
}
diff --git a/src/orderbuild.js b/src/orderbuild.js
index 701ab50..1d4c97f 100644
--- a/src/orderbuild.js
+++ b/src/orderbuild.js
@@ -31,6 +31,7 @@ export const useChartOrderStore = defineStore('chart_orders', () => {
const orders = ref([])
const selectedOrder = ref(null)
const selectedSymbol = ref(null)
+ const selectedPool = ref(null)
const drawing = ref(false)
const drawingCallbacks = ref(null) // only during draw mode
@@ -53,7 +54,7 @@ export const useChartOrderStore = defineStore('chart_orders', () => {
}
return {
- chartReady, selectedSymbol, orders, drawing, drawingCallbacks, newOrder, removeOrder,
+ chartReady, selectedSymbol, selectedPool, orders, drawing, drawingCallbacks, newOrder, removeOrder,
}
})
diff --git a/src/routeFinder.js b/src/routeFinder.js
deleted file mode 100644
index e69de29..0000000
diff --git a/src/socket.js b/src/socket.js
index 331a816..4952e29 100644
--- a/src/socket.js
+++ b/src/socket.js
@@ -21,7 +21,7 @@ socket.on('p', async (chainId, pool, price) => {
s.poolPrices[[chainId,pool]] = price
})
-socket.on('ohlcs', async (chainId, pool, ohlcs) => {
+socket.on('ohlc', async (chainId, pool, ohlcs) => {
console.log('pool bars', pool, ohlcs)
})