Files
web/src/charts/ordershapes.js
2025-03-19 21:04:24 -04:00

167 lines
6.9 KiB
JavaScript

import {DISTANT_FUTURE, DISTANT_PAST, MAX_FRACTION} from "@/blockchain/orderlib.js";
import {allocationText, DLine, HLine} from "@/charts/shape.js";
import {createShape, deleteShapeId} from "@/charts/chart.js";
import {sideColor} from "@/misc.js";
import {useChartOrderStore} from "@/orderbuild.js";
import {timestamp} from "@/common.js";
export class OrderShapes {
constructor(symbol, orderStatus) {
this.symbol = symbol
this.status = orderStatus
this.trancheShapes = [];
this.update(orderStatus)
}
update(orderStatus) {
for (const old of this.trancheShapes)
old.delete()
this.status = orderStatus
this.trancheShapes = []
const maxAllocation = Math.max(...orderStatus.order.tranches.map((ts)=>ts.fraction)) / MAX_FRACTION
if (orderStatus.trancheStatus !== undefined)
for (let i = 0; i < orderStatus.trancheStatus.length; i++)
this.trancheShapes.push(new TrancheShapes(this.symbol, this.status, i, maxAllocation));
}
show() {for (const t of this.trancheShapes) t.show();}
hide() {for (const t of this.trancheShapes) t.hide();}
delete() {for (const t of this.trancheShapes) t.delete(); this.shapes=[]}
}
class TrancheShapes {
constructor(symbol, orderStatus, trancheIndex, maxAllocation) {
if (symbol.inverted !== orderStatus.order.inverted) {
console.log('OrderShapes.createLine(): symbol has wrong inverson for this order')
return
}
this.symbol = symbol
this.status = orderStatus
this.trancheIndex = trancheIndex
this.tranche = orderStatus.order.tranches[trancheIndex]
this.trancheStatus = orderStatus.trancheStatus[trancheIndex]
this.shapes = []
this.fills = []
this.maxAllocation = maxAllocation
this.createShapes()
}
createShapes() {
const t = this.tranche
// console.log('create tranche shapes', t)
if (t.marketOrder) {
if (t.startTime !== DISTANT_PAST) {
// todo vertical line
}
} else {
// check lines
const amount = this.status.order.amount * BigInt(t.fraction) / BigInt(MAX_FRACTION)
const buy = this.status.order.tokenIn === this.symbol.quote.a
this.createLine(t.minLine.slope, t.minLine.intercept, amount, buy);
this.createLine(t.maxLine.slope, t.maxLine.intercept, amount, !buy);
}
for (const f of this.status.trancheStatus[this.trancheIndex].fills)
this.createFillPoint(f)
}
createFillPoint(f) {
// console.log('draw fill', f, this.symbol)
const order = this.status.order;
const symbol = this.symbol
const scale = 10**symbol.decimals;
const buy = order.tokenIn === this.symbol.quote.a
const amountIsBase = buy !== this.status.order.amountIsInput
const filledAmount = this.status.order.amountIsInput ? f.filledIn : f.filledOut;
const amount = Number(filledAmount)
* 10 ** -(amountIsBase ? this.symbol.base.d : this.symbol.quote.d)
const weight = Number(filledAmount) / Number(this.status.order.amount)
const amountSymbol = amountIsBase ? this.symbol.base.s : this.symbol.quote.s
// console.log('fillpoint', buy, filledAmount, this.status.order, this.symbol)
const time = f.time
const out = Number(f.filledOut) / (1-this.status.order.route.fee/1000000)
let price = out / Number(f.filledIn)
if (buy)
price = 1/price
price *= scale
// console.log('price', price)
const channel = buy?'low':'high';
const text = allocationText(buy, weight, amount, amountSymbol, amountIsBase ? null : this.symbol.base.s, 1, '\n')
const s = createShape(buy?'arrow_up':'arrow_down', {time, price}, {channel,text,lock:true})
// console.log('created fill shape at', time, price)
this.fills.push(s)
}
createLine(slope, intercept, amountBigInt, breakout) {
if (intercept === 0 && slope === 0) return
const t = this.tranche
const status = this.status
const symbol = this.symbol
const scale = 10**symbol.decimals;
const buy = status.order.tokenIn === this.symbol.quote.a
const decimals = buy ? this.symbol.base.d : this.symbol.quote.d;
amountBigInt = BigInt(amountBigInt)
const amount = Number(amountBigInt) * 10 ** - decimals
const amountSymbol = buy ? this.symbol.base.s : this.symbol.quote.s
const color = sideColor(buy)
const maxAllocation = this.maxAllocation
const allocation = t.fraction / MAX_FRACTION
const ts = this.trancheStatus
let sum = 0n
for (let i=0; i<ts.fills.length; i++)
sum += ts.fills[i].filled
const completed = (amountBigInt - sum) < status.order.minFillAmount
const extraText = completed ? '✓' : null
const textLocation = breakout === buy ? 'above' : 'below'
// line active
if (slope === 0) {
let price = intercept
price *= scale
// horizontal line
// console.log('hline', price)
const model = {
price, breakout, color, extraText, textLocation,
allocation, maxAllocation, amount, amountSymbol, buy,
}
const s = new HLine(model, null, null, null, true)
this.shapes.push(s)
} else {
// diagonal line
let startTime = t.startTime
if (startTime === DISTANT_PAST)
startTime = timestamp() - useChartOrderStore().intervalSecs * 20 // 20 bars ago
let endTime = t.endTime
if (endTime === DISTANT_FUTURE)
endTime = timestamp() // use "now" as the drawing point's time
let startPrice = (intercept + slope * startTime);
let endPrice = (intercept + slope * endTime);
startPrice *= scale
endPrice *= scale
// console.log('dline', startTime, endTime, DISTANT_FUTURE, startPrice, endPrice)
// noinspection EqualityComparisonWithCoercionJS
const model = {
pointA: {time: startTime, price: startPrice},
pointB: {time: endTime, price: endPrice},
extendLeft: t.startTime === DISTANT_PAST,
extendRight: t.endTime === DISTANT_FUTURE,
breakout, color, extraText, textLocation,
allocation, maxAllocation, amount, amountSymbol, buy,
}
const s = new DLine(model, null, null, null, true)
this.shapes.push(s)
}
}
// todo like this?
show() {for (const s of this.shapes) s.show();}
hide() {for (const s of this.shapes) s.hide();}
delete() {
for (const s of this.shapes)
s.delete();
this.shapes=[]
for (const s of this.fills)
deleteShapeId(s)
}
}