import {routeInverted, timestamp} from "@/misc.js"; import {MAX_FRACTION, newTranche} from "@/blockchain/orderlib.js"; import {useOrderStore} from "@/store/store.js"; import {encodeIEE754} from "@/common.js"; import {defineStore} from "pinia"; import {computed, ref} from "vue"; function unimplemented() { throw Error('Unimplemented') } // Builders are data objects which store a configuration state function TrancheBuilder( component, options = {}) { const id = crypto.randomUUID() return {id, component, options, points: {}, shapes: {}, props: {}, build: unimplemented} } export const useChartOrderStore = defineStore('chart_orders', () => { const builderIdList = ref([]) // this is where we keep the UI ordering const builderList = computed(()=>{ console.log('builder list', builderIdList.value.map((id)=>builderDict.value[id])) return builderIdList.value.map((id)=>builderDict.value[id]) }) // convenience const builderDict = ref({}) // builders stored by id const drawing = ref(false) const drawingCallbacks = ref(null) // only during draw mode function addBuilder(component, options={}) { const b = TrancheBuilder(component,options) builderIdList.value.push(b.id) builderDict.value[b.id] = b } function touchBuilder(builder) { // noinspection SillyAssignmentJS builderIdList.value = builderIdList.value builderDict.value[builder.id] = builder } function removeBuilder(builder) { builderIdList.value = builderIdList.value.filter((v)=>v!==builder.id) delete builderDict.value[builder.id] } return { builderList, builderDict, drawing, drawingCallbacks, addBuilder, removeBuilder, touchBuilder } }) export function applyLimit(tranche, price = null, isMinimum = null) { if (price === null) { const os = useOrderStore() price = os.limitPrice if (!price) return } applyLine(tranche, price, 0, isMinimum) } function computeInterceptSlope(time0, price0, time1, price1) { if (!time0 || !price0 && price0 !== 0 || !time1 || !price1 && price1 !== 0) throw Error(`invalid line points data ${time0} ${price0} ${time1} ${price1}`) const t0 = time0 const t1 = time1 if (t0 === t1) throw Error("line points' times must be different") const slope = (price1 - price0) / (t1 - t0) const intercept = price1 - slope * t1 return [intercept, slope] } export function linePointsValue(time0, price0, time1, price1, unixTime = null) { if (unixTime === null) unixTime = timestamp() try { const [intercept, slope] = computeInterceptSlope(time0, price0, time1, price1) return intercept + unixTime * slope } catch (e) { console.log('error computing line', e) return null } } export function applyLinePoints(tranche, time0, price0, time1, price1, isMinimum = null) { const [intercept, slope] = computeInterceptSlope(time0, price0, time1, price1); applyLine(tranche, intercept, slope, isMinimum) } export function applyLine(tranche, intercept, slope, isMinimum = null) { console.log('intercept, slope', intercept, slope) // intercept and slope are still in "human" units of decimal-adjusted prices const os = useOrderStore() const route = os.route const inverted = routeInverted(route) const scale = 10 ** (os.tokenA.decimals - os.tokenB.decimals) let m = inverted ? -scale / slope : slope / scale let b = inverted ? scale / intercept : intercept / scale const cur = b + timestamp() * m console.log('inverted b, m, cur', inverted, b, m, cur) m = encodeIEE754(m) b = encodeIEE754(b) if (isMinimum === null) isMinimum = os.limitIsMinimum console.log('limit is minimum', isMinimum) if (isMinimum) { tranche.minIntercept = b; tranche.minSlope = m; } else { tranche.maxIntercept = b; tranche.maxSlope = m; } tranche.marketOrder = false; } export function timesliceTranches() { const ts = [] const os = useOrderStore() const n = os.tranches // num tranches const interval = os.interval; const timeUnitIndex = os.timeUnitIndex; let duration = timeUnitIndex === 0 ? interval * 60 : // minutes timeUnitIndex === 1 ? interval * 60 * 60 : // hours interval * 24 * 60 * 60; // days let window if (!os.intervalIsTotal) { window = duration duration *= n // duration is the total time for all tranches } else { window = Math.round(duration / n) } if (window < 60) window = 60 // always allow at least one minute for execution const amtPerTranche = Math.ceil(MAX_FRACTION / n) duration -= 15 // subtract 15 seconds so the last tranche completes before the deadline for (let i = 0; i < n; i++) { const start = Math.floor(i * (duration / Math.max((n - 1), 1))) const end = start + window ts.push(newTranche({ fraction: amtPerTranche, startTimeIsRelative: true, startTime: start, endTimeIsRelative: true, endTime: end, })) } return ts }