rate limit DCA
This commit is contained in:
256
src/components/chart/DateBuilder.vue
Normal file
256
src/components/chart/DateBuilder.vue
Normal file
@@ -0,0 +1,256 @@
|
||||
<template class="d-flex align-content-center flex-column" style="height: 100%; width: 100%;">
|
||||
<rung-builder name='Dates' :order="order" :builder="builder" v-model="timeEndpoints"
|
||||
:shape="VLine"
|
||||
:mode="1" :flip="flipped" :orientation="0"
|
||||
:get-model-value="getModelValue" :set-model-value="setModelValue"
|
||||
:get-points-value="getPointsValue"
|
||||
:set-values="setValues" :set-weights="setWeights"
|
||||
:std-width="stdWidth" :build-tranches="buildTranches">
|
||||
|
||||
<!--
|
||||
<v-list style="background-color: inherit">
|
||||
<v-list-item v-for="t in absoluteTimes">{{t}}</v-list-item>
|
||||
</v-list>
|
||||
-->
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<absolute-time-entry v-model="absTimeA"/>
|
||||
</td>
|
||||
<td class="weight">{{ weights.length ? allocationTexts[0] : '' }}</td>
|
||||
</tr>
|
||||
<tr v-if="weights.length>2" v-for="i in weights.length-2" class="ml-5"> <!-- vue uses 1-based loops -->
|
||||
<td class="d-flex justify-end pr-3">{{ dateStrings[i] }}</td>
|
||||
<td class="weight">{{ allocationTexts[i] }}</td>
|
||||
</tr>
|
||||
<tr v-if="weights.length>1">
|
||||
<td>
|
||||
<absolute-time-entry v-model="absTimeB"/>
|
||||
</td>
|
||||
<td class="weight">{{ allocationTexts[weights.length-1] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
</rung-builder>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {builderDefaults, DEFAULT_SLIPPAGE, MIN_EXECUTION_TIME, useChartOrderStore} from "@/orderbuild.js";
|
||||
import {allocationText, VLine} from "@/charts/shape.js";
|
||||
import {sideColor} from "@/misc.js";
|
||||
import {useOrderStore, usePrefStore, useStore} from "@/store/store.js";
|
||||
import {DISTANT_FUTURE, MAX_FRACTION, newTranche} from "@/blockchain/orderlib.js";
|
||||
import RungBuilder from "@/components/chart/RungBuilder.vue";
|
||||
import {computed, ref, watchEffect} from "vue";
|
||||
import {chart, dragging} from "@/charts/chart.js";
|
||||
import AbsoluteTimeEntry from "@/components/AbsoluteTimeEntry.vue";
|
||||
import {DateTime} from "luxon";
|
||||
|
||||
const s = useStore()
|
||||
const os = useOrderStore()
|
||||
const co = useChartOrderStore()
|
||||
const prefs = usePrefStore()
|
||||
const props = defineProps(['order', 'builder'])
|
||||
const emit = defineEmits(['update:builder'])
|
||||
|
||||
const minWidth = computed(()=>co.intervalSecs)
|
||||
const stdWidth = computed(()=>10 * minWidth.value)
|
||||
|
||||
function computeDefaultColor() {
|
||||
const index = props.order.builders.indexOf(props.builder)
|
||||
return sideColor(props.order.buy, index)
|
||||
}
|
||||
|
||||
const defaultColor = computeDefaultColor()
|
||||
|
||||
builderDefaults(props.builder, {
|
||||
timeA: s.clock, // todo relative 0
|
||||
timeB: null,
|
||||
// relative: true, // todo
|
||||
relative: false,
|
||||
slippage: DEFAULT_SLIPPAGE,
|
||||
rungs: 1,
|
||||
skew: 0,
|
||||
color: defaultColor,
|
||||
buy: true,
|
||||
})
|
||||
|
||||
const times = ref([])
|
||||
const weights = ref([])
|
||||
|
||||
const amountSymbol = computed(()=>props.order.amountIsTokenA ? co.selectedSymbol.base.s : co.selectedSymbol.quote.s )
|
||||
const allocationTexts = computed(()=>weights.value.map((w)=>allocationText(props.order.buy, w, w * props.order.amount, co.selectedSymbol.base.s, amountSymbol.value)))
|
||||
|
||||
const endTimes = computed(()=>{
|
||||
const ts = times.value
|
||||
const window = Math.max(MIN_EXECUTION_TIME, Math.floor((ts[ts.length-1]-ts[0])/props.builder.rungs))
|
||||
return ts.map((t)=>t+window)
|
||||
})
|
||||
const absoluteTimes = computed(()=>{
|
||||
// console.log('absoluteTimes', props.builder.relative, times.value)
|
||||
// if (!props.builder.relative)
|
||||
return times.value
|
||||
// const now = s.clock
|
||||
// return times.value.map((t)=>now+t)
|
||||
})
|
||||
|
||||
const dateStrings = computed(()=>absoluteTimes.value.map((t)=>{
|
||||
const n = DateTime.fromSeconds(t).setZone(prefs.timezone)
|
||||
const y = n.toLocaleString({year:'numeric'})
|
||||
const m = n.toLocaleString({month:'long'})
|
||||
const d = n.toLocaleString({day:'numeric'})
|
||||
const h = n.toLocaleString({hour:'numeric', minute:'numeric'})
|
||||
return `${y} ${m} ${d} ${h}`
|
||||
}))
|
||||
|
||||
watchEffect(()=>{
|
||||
// auto scroll
|
||||
if (!dragging && absoluteTimes.value.length) {
|
||||
const endTime = absoluteTimes.value[absoluteTimes.value.length-1]
|
||||
const range = chart.getVisibleRange()
|
||||
const width = range.to - range.from
|
||||
const now = s.clock
|
||||
const extra = (Math.max(0,endTime - now) + stdWidth.value/2) / width
|
||||
// console.log('visrange', range, width, absV)
|
||||
if (range.to < endTime) {
|
||||
// console.log('scrolling')
|
||||
chart.setVisibleRange({from: now - width, to: now}, {
|
||||
percentRightMargin: Math.round(100 * extra),
|
||||
applyDefaultRightMargin: false
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const absTimeA = computed({
|
||||
get() { return _timeEndpoints.value[0] },
|
||||
set(v) {
|
||||
if (v!==null)
|
||||
v = Number(v)
|
||||
updateA(v)
|
||||
}
|
||||
})
|
||||
const absTimeB = computed({
|
||||
get() { return _timeEndpoints.value[1] },
|
||||
set(v) {
|
||||
if (v!==null)
|
||||
v = Number(v)
|
||||
updateB(v)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const _timeEndpoints = ref([props.builder.timeA, props.builder.timeB])
|
||||
const timeEndpoints = computed({
|
||||
get() { return _timeEndpoints.value},
|
||||
set(v) {
|
||||
const [a, b] = v
|
||||
update(a,b, true, true)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
function updateA(a) {
|
||||
update(a, _timeEndpoints.value[1], true, false)
|
||||
}
|
||||
|
||||
|
||||
function updateB(b) {
|
||||
update(_timeEndpoints.value[0], b, false, true)
|
||||
}
|
||||
|
||||
|
||||
function update(a, b, updateA, updateB) {
|
||||
if (updateA) {
|
||||
const minB = a + minWidth.value
|
||||
if (b < minB)
|
||||
b = minB
|
||||
}
|
||||
if (updateB) {
|
||||
const maxA = b - minWidth.value
|
||||
if (a > maxA)
|
||||
a = maxA
|
||||
}
|
||||
_timeEndpoints.value = [a, b]
|
||||
const newBuilder = {...props.builder}
|
||||
newBuilder.timeA = a
|
||||
newBuilder.timeB = b
|
||||
emit('update:builder', newBuilder)
|
||||
}
|
||||
|
||||
const flipped = computed(()=>{
|
||||
const a = props.builder.timeA
|
||||
const b = props.builder.timeB
|
||||
return a !== null && b !== null && a > b
|
||||
})
|
||||
|
||||
function buildTranches() {
|
||||
const order = props.order
|
||||
const builder = props.builder
|
||||
const tranches = []
|
||||
const warnings = []
|
||||
|
||||
console.log('buildTranches', builder, order, tranches)
|
||||
const ts = times.value
|
||||
const ets = endTimes.value
|
||||
const ws = weights.value
|
||||
console.log('buildTranches times ends weights', ts, ets, ws)
|
||||
for(let i=0; i<ts.length; i++) {
|
||||
const endTime = Math.max(ets[i],ts[i]+60);
|
||||
console.log('time check', endTime, s.clock)
|
||||
if (endTime <= s.clock)
|
||||
warnings.push(`Tranche already expired at ${new Date(endTime*1000)}`)
|
||||
const t = newTranche({
|
||||
fraction: ws[i] * MAX_FRACTION,
|
||||
startTime: ts[i],
|
||||
endTime, // always give at least 60 seconds of window to execute
|
||||
slippage: builder.slippage,
|
||||
})
|
||||
tranches.push(t)
|
||||
}
|
||||
return {tranches, warnings}
|
||||
}
|
||||
|
||||
|
||||
function getModelValue(model) {
|
||||
if(!model) {
|
||||
console.log('getModelValue', model)
|
||||
return null
|
||||
}
|
||||
return model.time
|
||||
}
|
||||
|
||||
function getPointsValue(points) {
|
||||
return points[0].price
|
||||
}
|
||||
|
||||
function setModelValue(model, value) {
|
||||
// console.log('DCA set model value', model, value)
|
||||
// const v = value === null ? null : props.builder.relative ? s.clock + Math.round(value) : Math.round(value)
|
||||
const v = value === null ? null : Math.round(value)
|
||||
if (model.time !== v) {
|
||||
// console.log('DCA do set time', v)
|
||||
model.time = v
|
||||
}
|
||||
}
|
||||
|
||||
function setValues(values) {
|
||||
times.value = values.map((t)=>Math.round(t))
|
||||
}
|
||||
|
||||
function setWeights(ws) { weights.value = ws }
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
td.weight {
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user