orderbuild fixes; tranche table work; ordershapes still broken

This commit is contained in:
tim
2024-09-20 04:12:23 -04:00
parent ce54b943ea
commit e5d5c9c0d8
10 changed files with 111 additions and 48 deletions

View File

@@ -1,4 +1,4 @@
import {ethers} from "ethers"; import {ethers, FixedNumber} from "ethers";
const UNISWAPV3_POOL_INIT_CODE_HASH = '0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54' const UNISWAPV3_POOL_INIT_CODE_HASH = '0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54'
const uniswapV3Addresses = { const uniswapV3Addresses = {
@@ -27,3 +27,13 @@ export function uniswapV3PoolAddress(chainId, tokenAddrA, tokenAddrB, fee) {
} }
return ethers.getCreate2Address(factory, salt, UNISWAPV3_POOL_INIT_CODE_HASH) return ethers.getCreate2Address(factory, salt, UNISWAPV3_POOL_INIT_CODE_HASH)
} }
export function uniswapV3AveragePrice(amountIn, amountOut, fee) {
if (amountIn === 0n || amountOut === 0n) return null
const fmtX18 = {decimals: 18, width: 256, signed: false};
let result = FixedNumber.fromValue(amountOut, 0, fmtX18)
.div(FixedNumber.fromValue(amountIn, 0, fmtX18)).toUnsafeFloat()
result /= (1 - fee / 1_000_000) // adjust for pool fee
return result
}

View File

@@ -8,7 +8,6 @@ export let widget = null
export let chart = null export let chart = null
export let crosshairPoint = null export let crosshairPoint = null
let symbolChangedCbs = [] // callbacks for TV's chart.onSymbolChanged() let symbolChangedCbs = [] // callbacks for TV's chart.onSymbolChanged()
let lastSymbolChangedArgs = null
const s = useStore() const s = useStore()
@@ -23,17 +22,22 @@ export function removeSymbolChangedCallback(cb) {
} }
function changeSymbol(symbol) { function changeSymbol(symbol) {
console.log('change symbol', symbol)
if (symbol===null) if (symbol===null)
co.selectedSymbol = null co.selectedSymbol = null
else { else {
const info = lookupSymbol(symbol.full_name) const info = lookupSymbol(symbol.ticker)
lastSymbolChangedArgs = info
symbolChangedCbs.forEach((cb) => cb(info)) symbolChangedCbs.forEach((cb) => cb(info))
co.selectedSymbol = info co.selectedSymbol = info
} }
} }
export function setSymbol(symbol) {
widget.activeChart().setSymbol(symbol.ticker)
}
function changeInterval(interval, _timeframe) { function changeInterval(interval, _timeframe) {
co.intervalSecs = intervalToSeconds(interval) co.intervalSecs = intervalToSeconds(interval)
DataFeed.intervalChanged(co.intervalSecs) DataFeed.intervalChanged(co.intervalSecs)
@@ -100,7 +104,14 @@ function initChart() {
s.timeZone = tzapi.getTimezone().id s.timeZone = tzapi.getTimezone().id
// chart.onHoveredSourceChanged().subscribe(null, ()=>console.log('hovered source changed', arguments)) // chart.onHoveredSourceChanged().subscribe(null, ()=>console.log('hovered source changed', arguments))
// chart.selection().onChanged().subscribe(null, s => console.log('selection', chart.selection().allSources())); // chart.selection().onChanged().subscribe(null, s => console.log('selection', chart.selection().allSources()));
changeSymbol(chart.symbolExt()) const symbolExt = chart.symbolExt();
console.log('symbolExt', symbolExt);
if(symbolExt) {
changeSymbol(symbolExt)
}
else {
changeSymbol(null)
}
changeInterval(widget.symbolInterval().interval) changeInterval(widget.symbolInterval().interval)
co.chartReady = true co.chartReady = true
console.log('chart ready') console.log('chart ready')

View File

@@ -222,7 +222,17 @@ function getAllSymbols() {
} }
export function lookupSymbol(key) { // lookup by ticker which is "0xbaseAddress/0xquoteAddress" export function lookupSymbol(key) { // lookup by ticker which is "0xbaseAddress/0xquoteAddress"
return getAllSymbols()[key] const symbols = getAllSymbols();
if (!(key in symbols)) {
// check the inverted symbol
const [base,quote] = key.split('/')
key = quote+'/'+base
if (!(key in symbols)) {
console.error('no symbol found for key', key, symbols)
return null
}
}
return symbols[key]
} }
function checkBar(bar, msg) { function checkBar(bar, msg) {

View File

@@ -70,7 +70,7 @@ class TrancheShapes {
// console.log('price', price) // console.log('price', price)
const channel = buy?'low':'high'; const channel = buy?'low':'high';
const text = (buy ? 'Buy ' : 'Sell ') + allocationText(null, f.filled, '') const text = (buy ? 'Buy ' : 'Sell ') + allocationText(null, f.filled, '')
const s = createShape(buy?'arrow_up':'arrow_down', {time, price}, {channel,text}) const s = createShape(buy?'arrow_up':'arrow_down', {time, price}, {channel,text,lock:true})
console.log('created fill shape at', time, price) console.log('created fill shape at', time, price)
this.fills.push(s) this.fills.push(s)
} }
@@ -90,7 +90,7 @@ class TrancheShapes {
price: intercept * scale, price: intercept * scale,
color, color,
// todo allocation maxAllocation amount amountSymbol // todo allocation maxAllocation amount amountSymbol
}) }, null, null, null, true)
this.shapes.push(s) this.shapes.push(s)
} else { } else {
// diagonal line // diagonal line
@@ -103,7 +103,7 @@ class TrancheShapes {
extendRight: t.endTime === DISTANT_FUTURE, extendRight: t.endTime === DISTANT_FUTURE,
color, color,
// todo allocation maxAllocation amount amountSymbol // todo allocation maxAllocation amount amountSymbol
}) }, null, null, null, true)
this.shapes.push(s) this.shapes.push(s)
} }
} }

View File

@@ -41,7 +41,7 @@ export const ShapeType = {
export class Shape { export class Shape {
constructor(type, onModel=null, onDelete=null, props=null) { constructor(type, onModel=null, onDelete=null, props=null, readonly=false, overrides={}) {
// the Shape object manages synchronizing internal data with a corresponding TradingView shape // the Shape object manages synchronizing internal data with a corresponding TradingView shape
// each shape in the class hierarchy overrides setModel() to cause effects in TradingView // each shape in the class hierarchy overrides setModel() to cause effects in TradingView
@@ -64,7 +64,10 @@ export class Shape {
this.tvCallbacks = null this.tvCallbacks = null
this.ourPoints = null this.ourPoints = null
this.tvPoints = null this.tvPoints = null
this.creationOptions = {disableSave:true, disableUndo:true, disableSelection:false} this.creationOptions = readonly ?
{disableSave:true, disableUndo:true, disableSelection:true, lock:true} :
{disableSave:true, disableUndo:true, disableSelection:false}
this.creationOptions.overrides = overrides
this.ourProps = {} this.ourProps = {}
if (props !== null) if (props !== null)
this.ourProps = mixin(props, this.ourProps) this.ourProps = mixin(props, this.ourProps)
@@ -437,8 +440,13 @@ export class Line extends Shape {
export class HLine extends Line { export class HLine extends Line {
constructor(model, onModel=null, onDelete=null, props=null) { constructor(model, onModel=null, onDelete=null, props=null, readonly=false) {
super(ShapeType.HLine, onModel, onDelete, props) super(ShapeType.HLine, onModel, onDelete, props, readonly, {
// todo this isnt working
linestyle: 0,
linewidth: 2,
italic: false,
})
// Model // Model
this.model.price = null this.model.price = null
@@ -513,8 +521,10 @@ export class VLine extends Line {
export class DLine extends Line { export class DLine extends Line {
constructor(model, onModel=null, onDelete=null, props=null) { constructor(model, onModel=null, onDelete=null, props=null, readonly=false) {
super(ShapeType.Ray, onModel, onDelete, props) super(ShapeType.Ray, onModel, onDelete, props, readonly,{
// todo style overrides
})
// Model // Model
this.model.pointA = null // {time:..., price:...} this.model.pointA = null // {time:..., price:...}

View File

@@ -31,12 +31,9 @@
</suspense> </suspense>
/ /
</span> </span>
<suspense>
<token-amount :chain-id="item.chainId" :addr="item.order.tokenOut" :amount="item.order.amount"/>
</suspense>
</span> </span>
<suspense> <suspense>
<token-symbol :addr="item.order.tokenOut" v-if="item.order.amountIsInput"/> <token-amount :chain-id="item.chainId" :addr="item.order.tokenOut" :amount="item.order.amount"/>
</suspense> </suspense>
</template> </template>
<!-- <!--
@@ -84,7 +81,7 @@
<!-- </template>--> <!-- </template>-->
<template v-slot:expanded-row="{item}"> <template v-slot:expanded-row="{item}">
<tr v-for="(t, i) in item.order.tranches"> <tr v-for="(t, i) in item.order.tranches">
<td class="text-right"> <td class="text-right" colspan="2">
Tranche {{ i + 1 }} Tranche {{ i + 1 }}
<div class="text-right"> <div class="text-right">
<div v-if="s.clock < item.trancheStatus[i].startTime"> <div v-if="s.clock < item.trancheStatus[i].startTime">
@@ -99,15 +96,15 @@
<line-price class="mx-3" v-if="!t.marketOrder" <line-price class="mx-3" v-if="!t.marketOrder"
:base="item.token0" :quote="item.token1" :base="item.token0" :quote="item.token1"
:b="t.minLine.intercept" :m="t.minLine.slope" :is-min="true" :b="t.minLine.intercept" :m="t.minLine.slope" :is-min="true"
:buy="item.order.tokenIn===item.token1" :show-btn="true"/> :buy="item.order.tokenIn===item.token1" :show-btn="false"/>
<line-price class="mx-3" v-if="!t.marketOrder" <line-price class="mx-3" v-if="!t.marketOrder"
:base="item.token0" :quote="item.token1" :base="item.token0" :quote="item.token1"
:b="t.maxLine.intercept" :m="t.maxLine.slope" :is-min="false" :b="t.maxLine.intercept" :m="t.maxLine.slope" :is-min="false"
:buy="item.order.tokenIn===item.token1" :show-btn="true"/> :buy="item.order.tokenIn===item.token1" :show-btn="false"/>
</div> </div>
</td> </td>
<td class="text-center w-33"> <td class="text-right">
<suspense> <suspense>
<span v-if="item.state > OrderState.Signing"> <span v-if="item.state > OrderState.Signing">
<pulse :touch="item.trancheStatus[i].filledIn"> <pulse :touch="item.trancheStatus[i].filledIn">
@@ -122,12 +119,13 @@
</span> </span>
</suspense> </suspense>
</td> </td>
<td class="text-center w-33"> <td class="text-right w-33">
<suspense> <suspense>
<span v-if="item.state > OrderState.Signing"> <span v-if="item.state > OrderState.Signing">
<pulse :touch="item.trancheStatus[i].filledOut"> <pulse :touch="item.trancheStatus[i].filledOut">
<token-amount :chain-id="item.chainId" :addr="item.order.tokenOut" :amount="item.trancheStatus[i].filledOut" :raw="true"/> <token-amount :chain-id="item.chainId" :addr="item.order.tokenOut" :amount="item.trancheStatus[i].filledOut" :raw="true"/>
</pulse> </pulse>
<token-symbol :addr="item.order.tokenOut"/>
</span> </span>
</suspense> </suspense>
<suspense> <suspense>
@@ -137,8 +135,12 @@
</span> </span>
</suspense> </suspense>
</td> </td>
<td>avg price</td> <td class="text-right">
<td>status</td> <suspense>
<pair-price :base="item.order.tokenIn" :quote="item.order.tokenOut" :value="item.trancheStatus[i].avg" :show-btn="false"/>
</suspense>
</td>
<td>{{ item.trancheStatus[i].status }}(todo:status)</td>
</tr> </tr>
</template> </template>
</v-data-table> </v-data-table>
@@ -149,7 +151,7 @@
import LinePrice from "@/components/LinePrice.vue"; import LinePrice from "@/components/LinePrice.vue";
import {FixedNumber} from "ethers"; import {FixedNumber} from "ethers";
import {useStore} from "@/store/store"; import {useStore} from "@/store/store";
import {computed, defineAsyncComponent, ref, watch, watchEffect} from "vue"; import {computed, defineAsyncComponent, onUnmounted, ref, watch} from "vue";
import Btn from "@/components/Btn.vue" import Btn from "@/components/Btn.vue"
import {cancelOrder, PendingOrderState, useWalletStore} from "@/blockchain/wallet.js"; import {cancelOrder, PendingOrderState, useWalletStore} from "@/blockchain/wallet.js";
import {timestampString} from "@/misc.js"; import {timestampString} from "@/misc.js";
@@ -158,6 +160,8 @@ import Pulse from "@/components/Pulse.vue";
import {OrderShapes} from "@/charts/ordershapes.js"; import {OrderShapes} from "@/charts/ordershapes.js";
import {useChartOrderStore} from "@/orderbuild.js"; import {useChartOrderStore} from "@/orderbuild.js";
import {lookupSymbol} from "@/charts/datafeed.js"; import {lookupSymbol} from "@/charts/datafeed.js";
import {setSymbol} from "@/charts/chart.js";
import {uniswapV3AveragePrice} from "@/blockchain/uniswap.js";
const PairPrice = defineAsyncComponent(()=>import("@/components/PairPrice.vue")) const PairPrice = defineAsyncComponent(()=>import("@/components/PairPrice.vue"))
const TokenAmount = defineAsyncComponent(()=>import('./TokenAmount.vue')) const TokenAmount = defineAsyncComponent(()=>import('./TokenAmount.vue'))
@@ -169,6 +173,7 @@ const ws = useWalletStore()
const props = defineProps(['vault']) const props = defineProps(['vault'])
const vaultAddr = computed(()=>props.vault?props.vault:s.vault) const vaultAddr = computed(()=>props.vault?props.vault:s.vault)
const selected = ref([]) const selected = ref([])
watch(selected, ()=>{ watch(selected, ()=>{
const statusIndex = {} const statusIndex = {}
for (const order of orders.value) for (const order of orders.value)
@@ -184,6 +189,10 @@ watch(selected, ()=>{
[base, quote] = [quote, base] [base, quote] = [quote, base]
const symbolKey = `${base}/${quote}` const symbolKey = `${base}/${quote}`
const symbol = lookupSymbol(symbolKey) const symbol = lookupSymbol(symbolKey)
if (co.selectedSymbol.ticker !== symbolKey) {
co.selectedSymbol = symbol
setSymbol(symbol)
}
orderShapes[id] = new OrderShapes(symbol, status) orderShapes[id] = new OrderShapes(symbol, status)
} }
} }
@@ -196,8 +205,13 @@ watch(selected, ()=>{
} }
} }
}) })
const orderShapes = {} const orderShapes = {}
onUnmounted(()=>{
for (const s of Object.values(orderShapes))
s.delete()
})
const datatableHeaders = [ const datatableHeaders = [
{title: 'Date', align: 'start', key: 'start'}, {title: 'Date', align: 'start', key: 'start'},
@@ -283,14 +297,12 @@ const orders = computed(()=>{
return t return t
}) })
*/ */
const fmtX18 = {decimals: 18, width: 256, signed: false};
st.filled = o.amountIsInput ? st.filledIn : st.filledOut st.filled = o.amountIsInput ? st.filledIn : st.filledOut
const fee = st.order.route.fee;
if(st.filled === 0n) if(st.filled === 0n)
st.avg = null st.avg = null
else { else
st.avg = FixedNumber.fromValue(status.filledOut, 0, fmtX18) st.avg = uniswapV3AveragePrice(status.filledIn, status.filledOut, fee)
.div(FixedNumber.fromValue(status.filledIn, 0, fmtX18)).toUnsafeFloat() * (1+st.order.route.fee/1_000_000);
}
st.amountToken = o.amountIsInput ? o.tokenIn : o.tokenOut st.amountToken = o.amountIsInput ? o.tokenIn : o.tokenOut
st.input = o.amountIsInput ? o.amount : 0 st.input = o.amountIsInput ? o.amount : 0
st.output = !o.amountIsInput ? o.amount : 0 st.output = !o.amountIsInput ? o.amount : 0
@@ -308,6 +320,7 @@ const orders = computed(()=>{
ts.filledIn = filledIn ts.filledIn = filledIn
ts.filledOut = filledOut ts.filledOut = filledOut
ts.filled = o.amountIsInput ? filledIn : filledOut ts.filled = o.amountIsInput ? filledIn : filledOut
ts.avg = uniswapV3AveragePrice(filledIn, filledOut, fee)
} }
} }
} }

View File

@@ -12,10 +12,14 @@ const token = await getToken(props.chainId, props.addr)
const fmtAmount = computed(() => { const fmtAmount = computed(() => {
if( props.amount === null || props.amount === undefined ) if( props.amount === null || props.amount === undefined )
return '' return ''
return FixedNumber.fromValue(props.amount, token.d, { const fixed = FixedNumber.fromValue(props.amount, token.d, {
width: 256, width: 256,
decimals: token.d decimals: token.d
}).toUnsafeFloat().toPrecision(5) // todo precision })
let p = fixed.toUnsafeFloat().toPrecision(5);
if (p.includes('e'))
p = parseFloat(p)
return p
}) })
</script> </script>

View File

@@ -140,39 +140,40 @@ export function linePointsValue(time0, price0, time1, price1, unixTime = null) {
export function applyLinePoint(tranche, symbol, buy, price0) { export function applyLinePoint(tranche, symbol, buy, price0) {
console.log('applyLinePoint', buy?'BUY':'SELL', symbol, price0) console.log('applyLinePoint', buy?'BUY':'SELL', symbol, price0)
if (symbol.inverted)
price0 = 1/price0
price0 *= 10 ** -symbol.decimals price0 *= 10 ** -symbol.decimals
const inverted = buy === symbol.inverted
if (inverted)
price0 = 1/price0
applyLine(tranche, symbol, buy, price0, 0) applyLine(tranche, symbol, buy, price0, 0)
} }
export function applyLinePoints(tranche, symbol, buy, time0, price0, time1, price1) { export function applyLinePoints(tranche, symbol, buy, time0, price0, time1, price1) {
if (symbol.inverted) {
price0 = 1/price0
price1 = 1/price1
}
const scale = 10 ** -symbol.decimals const scale = 10 ** -symbol.decimals
price0 *= scale price0 *= scale
price1 *= scale price1 *= scale
const inverted = buy === symbol.inverted
if (inverted) {
price0 = 1/price0
price1 = 1/price1
}
const [intercept, slope] = computeInterceptSlope(time0, price0, time1, price1); const [intercept, slope] = computeInterceptSlope(time0, price0, time1, price1);
applyLine(tranche, symbol, buy, intercept, slope) applyLine(tranche, symbol, buy, intercept, slope)
} }
function applyLine(tranche, symbol, buy, intercept, slope) { function applyLine(tranche, symbol, buy, intercept, slope, isMaximum=false) {
let m = slope let m = slope
let b = intercept let b = intercept
const isMinimum = !buy; console.log('applyLine current line value', isMaximum?'min':'max', m*timestamp()+b, 1./(m*timestamp()+b))
console.log('applyLine current line value', isMinimum?'min':'max', m*timestamp()+b, 1./(m*timestamp()+b))
m = encodeIEE754(m) m = encodeIEE754(m)
b = encodeIEE754(b) b = encodeIEE754(b)
if (isMinimum) { if (isMaximum) {
tranche.minLine.intercept = b;
tranche.minLine.slope = m;
} else {
tranche.maxLine.intercept = b; tranche.maxLine.intercept = b;
tranche.maxLine.slope = m; tranche.maxLine.slope = m;
} else {
tranche.minLine.intercept = b;
tranche.minLine.slope = m;
} }
tranche.marketOrder = false; tranche.marketOrder = false;
} }

View File

@@ -98,8 +98,8 @@ socket.on( 'of', (chainId, vault, orderIndex, filled)=>{
let orderIn = 0n let orderIn = 0n
let orderOut = 0n let orderOut = 0n
const ts = status.trancheStatus[i]
for (const i in filled) { for (const i in filled) {
const ts = status.trancheStatus[i]
let filledIn = 0n let filledIn = 0n
let filledOut = 0n let filledOut = 0n
const [activationTime, fills] = filled[i]; const [activationTime, fills] = filled[i];

View File

@@ -1,5 +1,9 @@
@use "vars" as v; @use "vars" as v;
html {
overflow-y: hidden;
}
#app { #app {
a { a {