diagonal order form
This commit is contained in:
@@ -1,23 +1,15 @@
|
||||
<template>
|
||||
<order title="Diagonal" subtitle="Trade trends and channels" :tranches="buildTranches" :valid="validOrder">
|
||||
<order title="Diagonal" subtitle="Trends and channels" :tranches="buildTranches" :valid="validOrder">
|
||||
<!-- todo times -->
|
||||
<span><i>Coming soon!</i></span>
|
||||
<!--
|
||||
<limit-price :required="true" label="start price" :show-price="false"/>
|
||||
<limit-price store-var="limitPrice2" :required="true" label="end price"/>
|
||||
<v-table>
|
||||
<thead>
|
||||
<tr><td>Fraction</td><td>Amount</td><td>Price</td></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(r,i) in rungsFmt">
|
||||
<td>{{(100*fractions[i]).toFixed(1)}}%</td>
|
||||
<td>{{(amounts[i]).toPrecision(5)}} {{os.amountToken.symbol}}</td>
|
||||
<td>{{r}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</v-table>
|
||||
-->
|
||||
<div class="title">Line Point A</div>
|
||||
<v-divider/>
|
||||
<time-entry v-model="time1" class="mb-0" hide-details="true"/>
|
||||
<limit-price v-model="price1" label="Price A" :required="true"/>
|
||||
<div class="title">Line Point B</div>
|
||||
<v-divider/>
|
||||
<time-entry v-model="time2" hide-details="true"/>
|
||||
<limit-price v-model="price2" label="Price B" :required="true"/>
|
||||
<div><i>Backend support for diagonal lines is coming soon...</i></div>
|
||||
</order>
|
||||
</template>
|
||||
|
||||
@@ -26,65 +18,22 @@ import {useOrderStore} from "@/store/store";
|
||||
import LimitPrice from "@/components/LimitPrice.vue";
|
||||
import Order from "@/components/Order.vue";
|
||||
import {computed, ref} from "vue";
|
||||
import {applyLimit} from "@/orderbuild.js";
|
||||
import {applyLimit, applyLinePoints} from "@/orderbuild.js";
|
||||
import {validateRequired, validateTranches} from "@/validate.js";
|
||||
import {MAX_FRACTION, newTranche} from "@/blockchain/orderlib.js";
|
||||
import TimeEntry from "@/components/TimeEntry.vue";
|
||||
|
||||
const os = useOrderStore()
|
||||
|
||||
const skew = ref(0)
|
||||
const rungs = computed(()=>{
|
||||
if( !os.limitPrice || !os.limitPrice2 )
|
||||
return []
|
||||
const n = os.tranches;
|
||||
const a = parseFloat(os.limitPrice);
|
||||
const b = parseFloat(os.limitPrice2);
|
||||
if( n < 1 || !a || !b ) return []
|
||||
if( n === 1 ) return [(a+b)/2]
|
||||
// num >= 2
|
||||
const result = []
|
||||
const delta = (b-a)/(n-1)
|
||||
for( let i=0; i<n; i++ )
|
||||
result.push(a+i*delta)
|
||||
return result
|
||||
})
|
||||
const rungsFmt = computed(()=>{
|
||||
return rungs.value.map((r)=>r.toPrecision(5)) // todo precisions
|
||||
})
|
||||
const fractions = computed(()=>{
|
||||
const n = os.tranches
|
||||
const s = skew.value / 100
|
||||
const result = []
|
||||
if( s === 1 ) {
|
||||
result.push(1)
|
||||
for( let i=1; i<n; i++ )
|
||||
result.push(0)
|
||||
}
|
||||
else if( s === -1 ) {
|
||||
for( let i=1; i<n; i++ )
|
||||
result.push(0)
|
||||
result.push(1)
|
||||
}
|
||||
else {
|
||||
const mean = 1/n
|
||||
for( let i=0; i<n; i++ )
|
||||
result.push( mean * ( 1 - s * (2*i/(n-1)-1) ) )
|
||||
}
|
||||
return result
|
||||
})
|
||||
const amounts = computed( ()=>fractions.value.map((f)=>f*os.totalAmount) )
|
||||
const time1 = ref(new Date())
|
||||
const price1 = ref(null)
|
||||
const time2 = ref(new Date())
|
||||
const price2 = ref(null)
|
||||
|
||||
function buildTranches() {
|
||||
const ts = []
|
||||
const n = os.tranches
|
||||
for( let i=0; i<n; i++ ) {
|
||||
// todo optional deadline
|
||||
const fraction = Math.min(MAX_FRACTION, Math.ceil(MAX_FRACTION * fractions.value[i]) )
|
||||
const tranche = newTranche({fraction})
|
||||
applyLimit(tranche, rungs.value[i])
|
||||
ts.push(tranche)
|
||||
}
|
||||
return ts
|
||||
const t = newTranche();
|
||||
applyLinePoints(t, time1.value, price1.value, time2.value, price2.value)
|
||||
return [t]
|
||||
}
|
||||
|
||||
function validOrder() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<order title="Ladder" subtitle="Multiple price levels" :tranches="buildTranches" :valid="validOrder">
|
||||
<limit-price :required="true" label="start price" :show-price="false"/>
|
||||
<limit-price store-var="limitPrice2" :required="true" label="end price"/>
|
||||
<limit-price v-model="os.limitPrice" :required="true" label="start price" :show-price="false"/>
|
||||
<limit-price v-model="os.limitPrice2" :required="true" label="end price"/>
|
||||
<v-text-field label="Tranches" type="number" variant="outlined" aria-valuemin="1" min="1" max="255"
|
||||
v-model="os.tranches" :rules="[validateRequired,validateTranches]">
|
||||
<template v-slot:append-inner>tranches</template>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<v-text-field v-model="os[storeVar]" :label="getLabel" type="number"
|
||||
<v-text-field v-model="mv" :label="getLabel" type="number"
|
||||
variant="outlined" aria-valuemin="0" min="0"
|
||||
clearable :rules="rules" v-auto-select>
|
||||
:clearable="!required && clearable" :rules="rules" v-auto-select>
|
||||
<template v-slot:append-inner>
|
||||
<v-btn variant="outlined" @click="os.inverted=!os.inverted">
|
||||
{{ os.pairSymbol }}
|
||||
@@ -26,10 +26,17 @@ import {vAutoSelect} from "@/misc.js";
|
||||
|
||||
const os = useOrderStore()
|
||||
const props = defineProps({
|
||||
modelValue: {required: true},
|
||||
required: {default: false},
|
||||
label: {default: null},
|
||||
storeVar: {default: 'limitPrice'},
|
||||
showPrice: {default: true},
|
||||
clearable: {default: true},
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const mv = computed({
|
||||
get() {return props.modelValue},
|
||||
set(v) {emit('update:modelValue', v)}
|
||||
})
|
||||
const rules = computed(()=>props.required ? [validateAmount, validateRequired] : [validateAmount])
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<v-list-subheader title="Dexorders"/>
|
||||
<v-list-item prepend-icon="mdi-clock-outline" title="DCA" subtitle="Spread order across time" @click="nav('twap')"></v-list-item>
|
||||
<v-list-item prepend-icon="mdi-menu" title="Ladder" subtitle="Multiple price levels" @click="nav('ladder')"></v-list-item>
|
||||
<v-list-item prepend-icon="mdi-vector-line" title="Diagonal" subtitle="Trade trends and channels" @click="nav('diagonal')"></v-list-item>
|
||||
<v-list-item prepend-icon="mdi-vector-line" title="Diagonal" subtitle="Trends and channels" @click="nav('diagonal')"></v-list-item>
|
||||
<v-list-item prepend-icon="mdi-hammer-wrench" title="Custom" subtitle="Create your own" @click="nav('custom')"></v-list-item>
|
||||
</v-list>
|
||||
</v-navigation-drawer>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</div>
|
||||
</v-card-item>
|
||||
<v-card-actions class="d-flex justify-space-evenly mb-4">
|
||||
<v-btn variant="outlined" color="red">Cancel</v-btn>
|
||||
<v-btn variant="outlined" color="red" @click="$router.push('/vault')">Cancel</v-btn>
|
||||
<v-btn variant="flat" color="green" :disabled="!valid()" @click="placeOrder">Place Dexorder</v-btn>
|
||||
</v-card-actions>
|
||||
</phone-card>
|
||||
|
||||
65
src/components/TimeEntry.vue
Normal file
65
src/components/TimeEntry.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div class="d-flex">
|
||||
<v-text-field type="number" min="1" max="31" v-model="day" :label="weekday" :hide-details="hideDetails" style="min-width: 3em; width: 3em" class="all"/>
|
||||
<v-select v-model="month" :items="monthItems" :hide-details="hideDetails" style="min-width: 6em; width: 6em" class="all"/>
|
||||
<v-text-field type="number" v-model="year" :hide-details="hideDetails" style="min-width: 5em; width: 5em" class="all"/>
|
||||
<v-text-field type="time" v-model="time" :hide-details="hideDetails" style="min-width: 8em; width: 8em" class="all"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {useStore} from "@/store/store";
|
||||
import {computed} from "vue";
|
||||
|
||||
const s = useStore()
|
||||
const props = defineProps(['modelValue', 'hideDetails'])
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const day = computed({
|
||||
get() { return s.utc ? props.modelValue.getUTCDate() : props.modelValue.getDate() },
|
||||
set(v) { update(year.value, month.value, v, time.value)},
|
||||
})
|
||||
const weekdayFormat = computed(()=>new Intl.DateTimeFormat(s.utc ? 'utc':undefined,{weekday:"short"}))
|
||||
const weekday = computed(()=>weekdayFormat.value.format(props.modelValue))
|
||||
const month = computed({
|
||||
get() { return s.utc ? props.modelValue.getUTCMonth() : props.modelValue.getMonth() },
|
||||
set(v) { update(year.value, v, day.value, time.value)},
|
||||
})
|
||||
const year = computed({
|
||||
get() { return s.utc ? props.modelValue.getUTCFullYear() : props.modelValue.getFullYear() },
|
||||
set(v) { update(v, month.value, day.value, time.value)},
|
||||
})
|
||||
const time = computed({
|
||||
get() { return '' + (s.utc ? `${props.modelValue.getUTCHours()}:${props.modelValue.getUTCMinutes()}` : `${props.modelValue.getHours()}:${props.modelValue.getMinutes()}` ) },
|
||||
set(v) { update(year.value, month.value, day.value, v) }
|
||||
})
|
||||
|
||||
function monthsForLocale(localeName = undefined, monthFormat = 'long') {
|
||||
const format = new Intl.DateTimeFormat(localeName, {month: monthFormat}).format;
|
||||
return [...Array(12).keys()]
|
||||
.map((m) => format(new Date(Date.UTC(2000, m+1))));
|
||||
}
|
||||
|
||||
let m=0
|
||||
const monthItems = monthsForLocale(undefined, 'short').map((v)=>{return {title:v, value:m++}})
|
||||
|
||||
function buildDate(y, m, d, t) {
|
||||
const [hours,minutes] = t.split(':')
|
||||
console.log('buildDate', y, m, d, hours, minutes)
|
||||
return s.utc ? new Date(Date.UTC(y, m, d, hours, minutes))
|
||||
: new Date(y, m, d, hours, minutes);
|
||||
}
|
||||
|
||||
function update(y,m,d,t) {
|
||||
const date = buildDate(y, m, d, t);
|
||||
emit('update:modelValue', date)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "src/styles/vars" as *;
|
||||
|
||||
.all {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<order title="DCA / TWAP" subtitle="Multiple tranches over a time range" :tranches="buildTranches" :valid="validOrder">
|
||||
<Tranches/>
|
||||
<LimitPrice/>
|
||||
<LimitPrice v-model="os.limitPrice"/>
|
||||
</order>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -77,3 +77,8 @@ export function dateString(seconds) {
|
||||
const date = new Date(seconds*1000)
|
||||
return _dateFormat.format(date)
|
||||
}
|
||||
|
||||
|
||||
export function timestamp(date) {
|
||||
return Math.round(date.getTime() / 1000)
|
||||
}
|
||||
|
||||
@@ -1,31 +1,53 @@
|
||||
import {routeInverted} from "@/misc.js";
|
||||
import {routeInverted, timestamp} from "@/misc.js";
|
||||
import {MAX_FRACTION, newTranche} from "@/blockchain/orderlib.js";
|
||||
import {useOrderStore} from "@/store/store.js";
|
||||
import {encodeIEE754} from "@/blockchain/common.js";
|
||||
|
||||
|
||||
export function applyLimit(tranche, price=null) {
|
||||
const os = useOrderStore()
|
||||
export function applyLimit(tranche, price=null, isAbove=null) {
|
||||
if( price === null ) {
|
||||
const os = useOrderStore()
|
||||
price = os.limitPrice
|
||||
if (!price)
|
||||
return
|
||||
}
|
||||
|
||||
applyLine(tranche, price, 0, isAbove)
|
||||
}
|
||||
|
||||
|
||||
export function applyLinePoints(tranche, date0, price0, date1, price1, isAbove=null) {
|
||||
const os = useOrderStore()
|
||||
if( !date0 || !price0 && price0!==0 || !date1 || !price1 && price1!==0 )
|
||||
return
|
||||
|
||||
const t0 = timestamp(date0);
|
||||
const t1 = timestamp(date1);
|
||||
const slope = (price1-price0)/(t1-t0)
|
||||
const intercept = price1 - slope * t1
|
||||
applyLine(tranche, intercept, slope, isAbove)
|
||||
}
|
||||
|
||||
|
||||
export function applyLine(tranche, intercept, slope, isAbove=null) {
|
||||
// intercept and slope are still in "human" units of decimal-adjusted prices
|
||||
const os = useOrderStore()
|
||||
const route = os.route
|
||||
const inverted = routeInverted(route)
|
||||
const isAbove = os.limitIsMinimum ^ inverted
|
||||
const isRatio = false // todo ratios
|
||||
const decimals = 10 ** (os.tokenA.decimals - os.tokenB.decimals)
|
||||
const limit = encodeIEE754(inverted ? decimals / price : price / decimals)
|
||||
tranche.marketOrder = false;
|
||||
const scale = 10 ** (os.tokenA.decimals - os.tokenB.decimals)
|
||||
const m = encodeIEE754(inverted ? scale / slope : slope / scale)
|
||||
const b = encodeIEE754(inverted ? scale / intercept : intercept / scale)
|
||||
if( isAbove === null )
|
||||
isAbove = os.limitIsMinimum ^ inverted
|
||||
if( isAbove ) {
|
||||
tranche.minIntercept = limit;
|
||||
tranche.minSlope = 0;
|
||||
tranche.minIntercept = b;
|
||||
tranche.minSlope = m;
|
||||
}
|
||||
else {
|
||||
tranche.maxIntercept = limit;
|
||||
tranche.maxSlope = 0;
|
||||
tranche.maxIntercept = b;
|
||||
tranche.maxSlope = m;
|
||||
}
|
||||
tranche.marketOrder = false;
|
||||
}
|
||||
|
||||
export function timesliceTranches() {
|
||||
|
||||
@@ -29,7 +29,7 @@ import {computed, ref} from "vue";
|
||||
|
||||
|
||||
export const useStore = defineStore('app', ()=> {
|
||||
const nav = ref(false)
|
||||
const nav = ref(false) // controls opening navigation drawer
|
||||
|
||||
const _chainId = ref(null)
|
||||
const _chainInfo = ref({})
|
||||
@@ -136,6 +136,7 @@ export const useOrderStore = defineStore('order', ()=> {
|
||||
const timeUnitIndex = ref(0)
|
||||
const routes = ref([])
|
||||
const routesPending = ref(false)
|
||||
const utc = ref(false)
|
||||
|
||||
const validOrder = computed(() => amount.value > 0 && routes.value.length > 0)
|
||||
const route = computed(() => routes.value.length === 0 ? null : routes.value[0])
|
||||
@@ -164,6 +165,6 @@ export const useOrderStore = defineStore('order', ()=> {
|
||||
return {
|
||||
tokenA, tokenB, buy, inverted, amount, amountIsTokenA, amountIsTotal, limitPrice, limitPrice2, tranches,
|
||||
interval, intervalIsTotal, timeUnitIndex, routes, routesPending, validOrder, route, base, quote, pairSymbol,
|
||||
limitIsMinimum, amountToken, amountIsInput, setDefaultTokens, totalAmount, trancheAmount,
|
||||
limitIsMinimum, amountToken, amountIsInput, setDefaultTokens, totalAmount, trancheAmount, utc,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<template>
|
||||
<needs-provider>
|
||||
<needs-signer>
|
||||
<phone-card title="Vault Orders">
|
||||
<phone-card>
|
||||
<v-card-title><v-icon icon="mdi-information-outline" size="small" color="grey-darken-1"/> Vault Orders</v-card-title>
|
||||
<v-card-item>
|
||||
<orders/>
|
||||
</v-card-item>
|
||||
|
||||
Reference in New Issue
Block a user