LimitBuilder as RungBuilder; HLine fixes; cant place orders yet
This commit is contained in:
@@ -150,7 +150,7 @@ export class Shape {
|
|||||||
doCreate() {
|
doCreate() {
|
||||||
// createShape(this.type, this.points, {overrides:this.props}, new ShapeTVCallbacks(this))
|
// createShape(this.type, this.points, {overrides:this.props}, new ShapeTVCallbacks(this))
|
||||||
this.tvPoints = [...this.ourPoints]
|
this.tvPoints = [...this.ourPoints]
|
||||||
this.id = createShape(this.type, this.ourPoints, {overrides:this.ourProps}, new ShapeTVCallbacks(this))
|
createShape(this.type, this.ourPoints, {overrides:this.ourProps}, new ShapeTVCallbacks(this))
|
||||||
if (this.debug) console.log('created', this.type.name, this.ourPoints, this.id)
|
if (this.debug) console.log('created', this.type.name, this.ourPoints, this.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +171,7 @@ export class Shape {
|
|||||||
setPoints(points) {
|
setPoints(points) {
|
||||||
// setting points to null will delete the shape from the chart. setting points to a valid value will cause the
|
// setting points to null will delete the shape from the chart. setting points to a valid value will cause the
|
||||||
// shape to be drawn.
|
// shape to be drawn.
|
||||||
if (this.debug) console.log('setPoints', points)
|
if (this.debug) console.log('setPoints', points, this.id)
|
||||||
this.ourPoints = points
|
this.ourPoints = points
|
||||||
if (points === null || !points.length)
|
if (points === null || !points.length)
|
||||||
this.delete()
|
this.delete()
|
||||||
@@ -185,15 +185,6 @@ export class Shape {
|
|||||||
if (this.debug) console.log('not dragging. use setPoints.')
|
if (this.debug) console.log('not dragging. use setPoints.')
|
||||||
s.setPoints(points)
|
s.setPoints(points)
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
if (this.debug) console.log('dragging. use QUIET setPoints.')
|
|
||||||
// quiet setPoints doesn't disturb tool editing mode
|
|
||||||
const i = s._pointsConverter.apiPointsToDataSource(points)
|
|
||||||
// s._model.startChangingLinetool(this._source)
|
|
||||||
s._model.changeLinePoints(s._source, i)
|
|
||||||
// s._model.endChangingLinetool(!0)
|
|
||||||
s._source.createServerPoints()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,6 +283,7 @@ class ShapeTVCallbacks {
|
|||||||
|
|
||||||
onCreate(shapeId, _tvShape, points, props) {
|
onCreate(shapeId, _tvShape, points, props) {
|
||||||
this.creating = true
|
this.creating = true
|
||||||
|
this.shape.id = shapeId
|
||||||
invokeCallback(this.shape, 'onCreate', points, props)
|
invokeCallback(this.shape, 'onCreate', points, props)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,10 +363,23 @@ 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) {
|
||||||
super(ShapeType.HLine, model, onModel, onDelete, props)
|
super(ShapeType.HLine, onModel, onDelete, props)
|
||||||
|
|
||||||
|
// Model
|
||||||
|
this.model.price = null
|
||||||
|
|
||||||
|
this.setModel(model) // call setModel at the end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete() {
|
||||||
|
this.model.price = null
|
||||||
|
super.delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
setModel(model) {
|
setModel(model) {
|
||||||
|
console.log('hline setModel', model)
|
||||||
|
super.setModel(model)
|
||||||
if (model.price !== undefined && model.price !== this.model.price) {
|
if (model.price !== undefined && model.price !== this.model.price) {
|
||||||
this.model.price = model.price
|
this.model.price = model.price
|
||||||
this.setPoints([{time:0,price:this.model.price}])
|
this.setPoints([{time:0,price:this.model.price}])
|
||||||
@@ -383,19 +388,8 @@ export class HLine extends Line {
|
|||||||
|
|
||||||
onPoints(points) {
|
onPoints(points) {
|
||||||
super.onPoints(points);
|
super.onPoints(points);
|
||||||
}
|
const price = points[0].price;
|
||||||
|
this.updateModel({price})
|
||||||
pointsFromModel(model) {
|
|
||||||
if (model.price === null || model.price===undefined) return null
|
|
||||||
// take any time available, or 0
|
|
||||||
const time =
|
|
||||||
this.ourPoints !== null && this.ourPoints.length > 0 ? this.ourPoints[0].time :
|
|
||||||
this.tvPoints !== null && this.tvPoints.length > 0 ? this.tvPoints[0].time : 0
|
|
||||||
return [{time:time, price:model.price}]
|
|
||||||
}
|
|
||||||
|
|
||||||
pointsToModel(points) {
|
|
||||||
return {price: this.ourPoints[0].price}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -410,9 +404,9 @@ function timeAdjustmentTooSmall(orig, newValue) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function ohlcStart(time) {
|
function nearestOhlcStart(time) {
|
||||||
const period = useChartOrderStore().intervalSecs
|
const period = useChartOrderStore().intervalSecs
|
||||||
return Math.floor(time/period) * period
|
return Math.round(time/period) * period
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -426,38 +420,33 @@ export class VLine extends Line {
|
|||||||
this.setModel(model) // call setModel at the end
|
this.setModel(model) // call setModel at the end
|
||||||
}
|
}
|
||||||
|
|
||||||
onPoints(points) {
|
delete() {
|
||||||
if (this.debug) console.log('vline onPoints', this.ourPoints, points)
|
this.model.time = null
|
||||||
super.onPoints(points);
|
super.delete()
|
||||||
const orig = this.ourPoints && this.ourPoints.length ? this.ourPoints[0].time : null
|
|
||||||
if (!timeAdjustmentTooSmall(orig, points[0].time)) {
|
|
||||||
if (this.debug) console.log('updateModel', points[0].time)
|
|
||||||
this.updateModel({time: points[0].time})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
setModel(model) {
|
setModel(model) {
|
||||||
if (this.debug) console.log('vline setModel', this.model.time, model )
|
if (this.debug) console.log('vline setModel', this.model.time, model )
|
||||||
super.setModel(model)
|
super.setModel(model)
|
||||||
if (model.time !== undefined && model.time !== this.model.time) {
|
if (model.time !== undefined && model.time !== this.model.time) {
|
||||||
this.model.time = model.time
|
this.model.time = model.time
|
||||||
const time = ohlcStart(model.time);
|
const time = nearestOhlcStart(model.time);
|
||||||
if (this.debug) console.log('vline setPoints', this.id, time)
|
if (this.debug) console.log('vline setPoints', this.id, time)
|
||||||
this.setPoints([{time, price:1}])
|
this.setPoints([{time, price:0}])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete() {
|
onPoints(points) {
|
||||||
this.model.time = null
|
if (this.debug) console.log('vline onPoints', this.ourPoints, points)
|
||||||
super.delete()
|
super.onPoints(points);
|
||||||
|
const orig = this.ourPoints && this.ourPoints.length ? this.ourPoints[0].time : null
|
||||||
|
const time = points[0].time;
|
||||||
|
if (!timeAdjustmentTooSmall(orig, time)) {
|
||||||
|
if (this.debug) console.log('updateModel', time)
|
||||||
|
this.updateModel({time: time})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dirtyPoints(pointsA, pointsB) {
|
|
||||||
const a = pointsA ? pointsA[0].time : null
|
|
||||||
const b = pointsB ? pointsB[0].time : null
|
|
||||||
const result = !timeAdjustmentTooSmall(a, b)
|
|
||||||
if (this.debug) console.log('vline dirty points?', a, b, result)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<v-btn variant="plain" v-bind="props" icon="mdi-dots-vertical"/>
|
<v-btn variant="plain" v-bind="props" icon="mdi-dots-vertical"/>
|
||||||
</template>
|
</template>
|
||||||
<v-list>
|
<v-list>
|
||||||
<!-- <v-list-subheader :title="'Limit '+ (lineAPrice?lineAPrice.toPrecision(5):'')"/>-->
|
<!-- <v-list-subheader :title="'Limit '+ (priceA?priceA.toPrecision(5):'')"/>-->
|
||||||
<v-list-item title="Delete" key="withdraw" value="withdraw" prepend-icon="mdi-delete" color="red"
|
<v-list-item title="Delete" key="withdraw" value="withdraw" prepend-icon="mdi-delete" color="red"
|
||||||
@click="deleteMyBuilder"/>
|
@click="deleteMyBuilder"/>
|
||||||
</v-list>
|
</v-list>
|
||||||
|
|||||||
@@ -1,95 +1,60 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- todo extract builder-panel --> <!-- :builder="builder" color-tag="limit" -->
|
<rung-builder :name="prices.length>1?'Ladder':'Limit'" :order="order" :builder="builder"
|
||||||
<row-bar :color="builder.color">
|
v-model:value-a="priceA" v-model:value-b="priceB" :mode="0" :shape="HLine"
|
||||||
<color-band :color="builder.color"/>
|
:get-model-value="getModelValue" :set-model-value="setModelValue"
|
||||||
<div style="min-width: 3em; font-size: larger" :style="colorStyle"
|
:set-values="setPrices" :set-weights="setWeights"
|
||||||
class="align-self-start ml-2 pt-3">{{ rungs === 1 ? 'Limit' : 'Ladder' }}</div>
|
:std-width="stdWidth" :build-tranches="buildTranches">
|
||||||
<div>
|
<table>
|
||||||
<v-text-field type="number" v-model="rungs"
|
<tbody>
|
||||||
density="compact" hide-details class="mx-1 my-2" variant="outlined"
|
<template v-if="prices.length>1">
|
||||||
label="Rungs"
|
|
||||||
:color="color" :base-color="color" min="1"
|
|
||||||
:disabled="!lineAPrice"
|
|
||||||
style="width: 4.5em;"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<table>
|
|
||||||
<tbody>
|
|
||||||
<template v-if="rungs>1">
|
|
||||||
<tr>
|
|
||||||
<td><v-text-field type="number" v-model="lineBPrice" min="0"
|
|
||||||
density="compact" hide-details class="mx-1 my-2" variant="outlined"
|
|
||||||
label="Price"
|
|
||||||
:color="color" :base-color="color"
|
|
||||||
style="flex: 6em"
|
|
||||||
/></td>
|
|
||||||
<td class="weight">{{allocationText(weights[weights.length-1])}}</td>
|
|
||||||
</tr>
|
|
||||||
<tr v-for="num in rungs-2" class="ml-5">
|
|
||||||
<td class="pl-5">{{prices[prices.length-1-num]}}</td>
|
|
||||||
<td class="weight">{{allocationText(weights[prices.length-1-num])}}</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><v-text-field type="number" v-model="lineAPrice" min="0"
|
<td>
|
||||||
|
<v-text-field type="number" v-model="priceB" min="0"
|
||||||
|
density="compact" hide-details class="mx-1 my-2" variant="outlined"
|
||||||
|
label="Price"
|
||||||
|
:color="color" :base-color="color"
|
||||||
|
style="flex: 6em"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td class="weight">{{ allocationText(weights[weights.length - 1]) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-for="i in prices.length-2" class="ml-5">
|
||||||
|
<td class="pl-5">{{ prices[prices.length-i-1] }}</td>
|
||||||
|
<td class="weight">{{ allocationText(weights[prices.length-i-1]) }}</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<v-text-field type="number" v-model="priceA" min="0"
|
||||||
density="compact" hide-details class="mx-1 my-2" variant="outlined"
|
density="compact" hide-details class="mx-1 my-2" variant="outlined"
|
||||||
label="Price"
|
label="Price"
|
||||||
:color="color" :base-color="color"
|
:color="color" :base-color="color"
|
||||||
style="flex: 6em"
|
style="flex: 6em"
|
||||||
/></td>
|
/>
|
||||||
<td class="weight">{{weights.length?allocationText(weights[0]):''}}</td>
|
</td>
|
||||||
</tr>
|
<td class="weight">{{ weights.length ? allocationText(weights[0]) : '' }}</td>
|
||||||
</tbody>
|
</tr>
|
||||||
</table>
|
</tbody>
|
||||||
<div v-if="co.drawing" class="d-flex align-center pl-3"><v-icon icon="mdi-chat-alert-outline" color="grey" class="mr-1"/>Click the chart!</div>
|
</table>
|
||||||
<div v-if="rungs>1" class="mx-2 d-flex align-center">
|
</rung-builder>
|
||||||
<v-slider v-if="rungs>1" direction="vertical" min="-100" max="100" v-model="skew100" class="no-slider-bg ml-2 mr-4" hide-details/>
|
|
||||||
<v-text-field type="number" v-model="skew100" min="-100" max="100"
|
|
||||||
density="compact" hide-details variant="outlined" label="Skew" step="5"
|
|
||||||
:color="color" :base-color="color">
|
|
||||||
<template v-slot:prepend>
|
|
||||||
<v-btn icon="mdi-scale-balance" variant="plain" @click="builder.skew=0" :color="color"/>
|
|
||||||
</template>
|
|
||||||
</v-text-field>
|
|
||||||
</div>
|
|
||||||
<div class="align-self-center">
|
|
||||||
<v-menu>
|
|
||||||
<template v-slot:activator="{ props }">
|
|
||||||
<v-btn variant="plain" v-bind="props" icon="mdi-dots-vertical"/>
|
|
||||||
</template>
|
|
||||||
<v-list>
|
|
||||||
<v-list-subheader :title="'Limit '+ (lineAPrice?lineAPrice.toPrecision(5):'')"/>
|
|
||||||
<v-list-item title="Delete" key="withdraw" value="withdraw" prepend-icon="mdi-delete" color="red"
|
|
||||||
@click="deleteBuilder"/>
|
|
||||||
</v-list>
|
|
||||||
</v-menu>
|
|
||||||
</div>
|
|
||||||
</row-bar>
|
|
||||||
|
|
||||||
<!-- <v-col>-->
|
|
||||||
<!-- <v-btn v-if="!co.drawing" prepend-icon="mdi-ray-vertex" @click="draw" variant="tonal">Draw</v-btn>-->
|
|
||||||
<!-- <v-btn v-if="co.drawing" prepend-icon="mdi-ray-vertex" @click="cancelDrawing" variant="tonal">Cancel Drawing</v-btn>-->
|
|
||||||
<!-- </v-col>-->
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {computed, onBeforeUnmount, onMounted, onUnmounted, onUpdated, watch, watchEffect} from "vue";
|
import {applyLine2, builderDefaults, useChartOrderStore} from "@/orderbuild.js";
|
||||||
import {cancelDrawing, chart} from "@/charts/chart.js";
|
import {sideColor} from "@/misc.js";
|
||||||
import {applyLine2, builderDefaults, builderFuncs, useChartOrderStore} from "@/orderbuild.js";
|
|
||||||
import Color from "color";
|
|
||||||
import {HLine} from "@/charts/shape.js";
|
|
||||||
import {lightenColor2, sideColor} from "@/misc.js";
|
|
||||||
import {useTheme} from "vuetify";
|
import {useTheme} from "vuetify";
|
||||||
import {useOrderStore} from "@/store/store.js";
|
import {useOrderStore, useStore} from "@/store/store.js";
|
||||||
import RowBar from "@/components/chart/RowBar.vue";
|
|
||||||
import ColorBand from "@/components/chart/ColorBand.vue";
|
|
||||||
import {MAX_FRACTION, newTranche} from "@/blockchain/orderlib.js";
|
import {MAX_FRACTION, newTranche} from "@/blockchain/orderlib.js";
|
||||||
|
import RungBuilder from "@/components/chart/RungBuilder.vue";
|
||||||
|
import {computed, ref} from "vue";
|
||||||
|
import {chart} from "@/charts/chart.js";
|
||||||
|
import {HLine} from "@/charts/shape.js";
|
||||||
|
|
||||||
|
const s = useStore()
|
||||||
const os = useOrderStore()
|
const os = useOrderStore()
|
||||||
const co = useChartOrderStore()
|
const co = useChartOrderStore()
|
||||||
const theme = useTheme().current
|
const theme = useTheme().current
|
||||||
const props = defineProps(['order', 'builder'])
|
const props = defineProps(['order', 'builder'])
|
||||||
const emit = defineEmits(['update:builder'])
|
|
||||||
|
|
||||||
function computeDefaultColor() {
|
function computeDefaultColor() {
|
||||||
const index = props.order.builders.indexOf(props.builder)
|
const index = props.order.builders.indexOf(props.builder)
|
||||||
@@ -109,18 +74,6 @@ builderDefaults(props.builder, {
|
|||||||
color: defaultColor,
|
color: defaultColor,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// validity checks
|
|
||||||
watchEffect(()=>{
|
|
||||||
const order = props.order
|
|
||||||
const builder = props.builder
|
|
||||||
props.builder.valid =
|
|
||||||
order && builder &&
|
|
||||||
builder.rungs >= 1 && builder.priceA &&
|
|
||||||
(builder.rungs < 2 || builder.priceB)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
function buildTranches() {
|
function buildTranches() {
|
||||||
const order = props.order
|
const order = props.order
|
||||||
const builder = props.builder
|
const builder = props.builder
|
||||||
@@ -133,6 +86,7 @@ function buildTranches() {
|
|||||||
const p = ps[i]
|
const p = ps[i]
|
||||||
const w = ws[i]
|
const w = ws[i]
|
||||||
const t = newTranche({
|
const t = newTranche({
|
||||||
|
// todo start/end
|
||||||
fraction: w * MAX_FRACTION,
|
fraction: w * MAX_FRACTION,
|
||||||
})
|
})
|
||||||
const symbol = co.selectedSymbol
|
const symbol = co.selectedSymbol
|
||||||
@@ -144,94 +98,20 @@ function buildTranches() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// todo move this code and the validity check into a supercomponent
|
const priceA = computed({
|
||||||
let lastId = props.builder.id
|
|
||||||
builderFuncs[props.builder.id] = buildTranches
|
|
||||||
onUpdated(()=>{
|
|
||||||
if (lastId !== props.builder.id ) {
|
|
||||||
delete builderFuncs[lastId]
|
|
||||||
builderFuncs[props.builder.id] = buildTranches
|
|
||||||
lastId = props.builder.id
|
|
||||||
}
|
|
||||||
})
|
|
||||||
onUnmounted(() => delete builderFuncs[lastId])
|
|
||||||
|
|
||||||
|
|
||||||
const skew100 = computed( {
|
|
||||||
get() {return props.builder.skew*100},
|
|
||||||
set(v) {props.builder.skew = v/100; }
|
|
||||||
} )
|
|
||||||
|
|
||||||
// we keep two special control lines as the edge of the ranges and then deletable lines in-between
|
|
||||||
|
|
||||||
const lineAPrice = computed({
|
|
||||||
get() { return props.builder.priceA },
|
get() { return props.builder.priceA },
|
||||||
set(v) {
|
set(v) {
|
||||||
props.builder.priceA = v===null ? v : Number(v)
|
props.builder.priceA = v===null ? v : Number(v)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const lineA = new HLine(
|
const priceB = computed({
|
||||||
{price:null,color:defaultColor},
|
|
||||||
function (line) {props.builder.priceA = line.price; props.builder.color = line.color; },
|
|
||||||
deleteBuilder
|
|
||||||
)
|
|
||||||
|
|
||||||
const lineBPrice = computed({
|
|
||||||
get() { return props.builder.priceB },
|
get() { return props.builder.priceB },
|
||||||
set(v) {
|
set(v) {
|
||||||
props.builder.priceB = v===null ? v : Number(v)
|
props.builder.priceB = v===null ? v : Number(v)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const lineB = new HLine(
|
|
||||||
{price:null,color:props.builder.color.value},
|
|
||||||
(line)=>{props.builder.priceB = line.price; props.builder.color = line.color; },
|
|
||||||
deleteBuilder
|
|
||||||
)
|
|
||||||
|
|
||||||
const adjustInteriorLine = (line) => {
|
|
||||||
props.builder.color = line.color
|
|
||||||
}
|
|
||||||
|
|
||||||
let interiorLines = []
|
|
||||||
|
|
||||||
function createInteriorLine(price, lineProps) {
|
|
||||||
const line = new HLine({price: price, color: props.builder.color}, adjustInteriorLine, deleteBuilder, lineProps)
|
|
||||||
line.onPoints = function (points) {
|
|
||||||
const delta = points[0].price - this.model.price
|
|
||||||
if (delta !== 0) {
|
|
||||||
props.builder.priceA += delta
|
|
||||||
props.builder.priceB += delta
|
|
||||||
}
|
|
||||||
}
|
|
||||||
interiorLines.push(line)
|
|
||||||
line.create()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function removeInteriorLine() {
|
|
||||||
if (interiorLines.length) {
|
|
||||||
const line = interiorLines.pop()
|
|
||||||
line.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const color = computed({
|
|
||||||
get() {return props.builder.color},
|
|
||||||
set(v) {
|
|
||||||
const maxLightness = 60
|
|
||||||
const c = new Color(v).hsl()
|
|
||||||
props.builder.color = c.saturation <= maxLightness ? v : c.lightness(maxLightness).string()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const faintColor = computed(() => lightenColor2(color.value))
|
|
||||||
const colorStyle = computed(() => {
|
|
||||||
return {'color': color.value}
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
const start = computed({
|
const start = computed({
|
||||||
get() {return props.builder.start || 0},
|
get() {return props.builder.start || 0},
|
||||||
set(v) {props.builder.start=v; },
|
set(v) {props.builder.start=v; },
|
||||||
@@ -242,6 +122,18 @@ const end = computed({
|
|||||||
set(v) {props.builder.end=v; },
|
set(v) {props.builder.end=v; },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const prices = ref([])
|
||||||
|
const weights = ref([])
|
||||||
|
function setPrices(ps) {
|
||||||
|
console.log('setPrices', ps)
|
||||||
|
prices.value = ps
|
||||||
|
}
|
||||||
|
function setWeights(ws) { weights.value = ws }
|
||||||
|
|
||||||
|
|
||||||
|
const color = computed(()=>props.builder.color ? props.builder.color : defaultColor)
|
||||||
|
|
||||||
function computeRange() {
|
function computeRange() {
|
||||||
let range = 0
|
let range = 0
|
||||||
const series = chart.getSeries()
|
const series = chart.getSeries()
|
||||||
@@ -260,91 +152,13 @@ function computeRange() {
|
|||||||
return range
|
return range
|
||||||
}
|
}
|
||||||
|
|
||||||
const rungs = computed({
|
const stdWidth = 2*computeRange() // todo make reactive
|
||||||
get() {
|
|
||||||
return props.builder.rungs
|
|
||||||
},
|
|
||||||
set(r) {
|
|
||||||
if (!r) {
|
|
||||||
props.builder.rungs = 1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r = Number(r)
|
|
||||||
props.builder.rungs = r
|
|
||||||
if ( r > 0 && props.builder.priceB === null ) {
|
|
||||||
// convert single line to a range
|
|
||||||
const range = 2 * computeRange()
|
|
||||||
const mid = props.builder.priceA
|
|
||||||
props.builder.priceA = mid - range/2
|
|
||||||
props.builder.priceB = mid + range/2
|
|
||||||
}
|
|
||||||
else if ( r === 1 && props.builder.priceB !== null ) {
|
|
||||||
// convert from a range to a single line
|
|
||||||
props.builder.priceA = (props.builder.priceA + props.builder.priceB) / 2
|
|
||||||
props.builder.priceB = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
const prices = computed(()=>{
|
|
||||||
let a = props.builder.priceA
|
|
||||||
let b = props.builder.priceB
|
|
||||||
const r = props.builder.rungs
|
|
||||||
// console.log('prices for', a, b, r)
|
|
||||||
if ( a===null || !r ) return [] // no data
|
|
||||||
if (r===1) return [a] // single line
|
|
||||||
const spacing = (b-a)/(r-1)
|
|
||||||
// console.log('spacing', a, b)
|
|
||||||
const result = []
|
|
||||||
for( let i=0; i<r; i++ )
|
|
||||||
result.push(a+i*spacing)
|
|
||||||
// console.log('prices', result)
|
|
||||||
return result
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
const weights = computed(() => {
|
|
||||||
const n = props.builder.rungs
|
|
||||||
const s = -props.builder.skew
|
|
||||||
if (n === 1) return [1]
|
|
||||||
const result = []
|
|
||||||
if (s === 0) {
|
|
||||||
// equal weighted
|
|
||||||
for (let i = 0; i < n; i++)
|
|
||||||
result.push(1 / n)
|
|
||||||
} else 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 {
|
|
||||||
for (let i = 0; i < n; i++)
|
|
||||||
result.push((1 - s * (2 * i / (n - 1) - 1)) / n)
|
|
||||||
}
|
|
||||||
// console.log('weights', result)
|
|
||||||
return result
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
const colors = computed( ()=> {
|
|
||||||
const color = props.builder.color !== null ? props.builder.color
|
|
||||||
: props.buy ? theme.value.colors.success : theme.value.colors.error
|
|
||||||
const c = new Color(color).rgb()
|
|
||||||
const ws = weights.value;
|
|
||||||
const max = Math.max(...ws)
|
|
||||||
const ns = ws.map((w)=>w/max) // set largest weight to 100%
|
|
||||||
const adj = ns.map((w)=>c.alpha(Math.pow(w,0.67))) // https://en.wikipedia.org/wiki/Stevens's_power_law
|
|
||||||
return adj.map((a)=>a.string())
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
const amountSymbol = computed(()=>props.order.amountIsTokenA ? co.selectedSymbol.base.s : co.selectedSymbol.quote.s )
|
const amountSymbol = computed(()=>props.order.amountIsTokenA ? co.selectedSymbol.base.s : co.selectedSymbol.quote.s )
|
||||||
|
|
||||||
|
|
||||||
|
// todo move into misc and use in shape as well
|
||||||
function allocationText(weight) {
|
function allocationText(weight) {
|
||||||
const alloc = props.builder.allocation
|
const alloc = props.builder.allocation
|
||||||
if (alloc===null) return ''
|
if (alloc===null) return ''
|
||||||
@@ -354,89 +168,26 @@ function allocationText(weight) {
|
|||||||
return `${(w*100).toFixed(1)}% = ${a.toLocaleString('fullwide')} ${amountSymbol.value}`
|
return `${(w*100).toFixed(1)}% = ${a.toLocaleString('fullwide')} ${amountSymbol.value}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getModelValue(model) {
|
||||||
function adjustShapes() {
|
if(!model)
|
||||||
// this is where all the lines are created or adjusted
|
return null
|
||||||
// console.log('adjustShapes()')
|
return model.price
|
||||||
const limits = prices.value
|
|
||||||
if (limits.length)
|
|
||||||
cancelDrawing()
|
|
||||||
const colorStrings = colors.value
|
|
||||||
// line properties
|
|
||||||
const lps = weights.value.map((w)=>{return {text:allocationText(w), textcolor:color.value, showLabel: true}}) // todo make this label innate to HLine with allocation and amount set in the model
|
|
||||||
if( limits.length === 0 ) {
|
|
||||||
lineA.delete()
|
|
||||||
lineB.delete()
|
|
||||||
for( const line of interiorLines )
|
|
||||||
line.delete()
|
|
||||||
interiorLines = []
|
|
||||||
}
|
|
||||||
else if (limits.length === 1) {
|
|
||||||
//
|
|
||||||
// SINGLE LINE
|
|
||||||
//
|
|
||||||
if (!lineA.beingDragged())
|
|
||||||
lineA.setModel({price:limits[0], color: colorStrings[0]}, lps[0])
|
|
||||||
lineB.delete()
|
|
||||||
if (interiorLines.length) {
|
|
||||||
for( const line of interiorLines )
|
|
||||||
line.delete()
|
|
||||||
interiorLines = []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//
|
|
||||||
// PRICE RANGE
|
|
||||||
//
|
|
||||||
if (!lineA.beingDragged())
|
|
||||||
lineA.setModel({price:limits[0], color: colorStrings[0]}, lps[0])
|
|
||||||
if (!lineB.beingDragged()) {
|
|
||||||
const last = colorStrings.length - 1
|
|
||||||
lineB.setModel({price: limits[last], color: colorStrings[last]}, lps[last])
|
|
||||||
}
|
|
||||||
|
|
||||||
const numInterior = Math.max(0,limits.length-2);
|
|
||||||
// trim excess interior lines
|
|
||||||
while( interiorLines.length > numInterior )
|
|
||||||
removeInteriorLine()
|
|
||||||
|
|
||||||
// adjust the interior line prices and/or add lines
|
|
||||||
for( let i=0; i<limits.length-2; i++ ) {
|
|
||||||
const limit = limits[1+i]
|
|
||||||
if (i === interiorLines.length)
|
|
||||||
createInteriorLine(limit, lps[1+i])
|
|
||||||
else if (!interiorLines[i].beingDragged())
|
|
||||||
interiorLines[i].setModel({price:limit, color: colorStrings[1+i]}, lps[1+i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setModelValue(model, value) {
|
||||||
watchEffect(adjustShapes)
|
if (model.price !== value)
|
||||||
// const autoAdjust = computed(adjustShapes)
|
model.price = value
|
||||||
|
|
||||||
|
|
||||||
function deleteShapes() {
|
|
||||||
lineA.delete()
|
|
||||||
lineB.delete()
|
|
||||||
for (const line of interiorLines)
|
|
||||||
line.delete()
|
|
||||||
interiorLines = []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setValues(values) {
|
||||||
function deleteBuilder() {
|
prices.value = values
|
||||||
props.order.builders = props.order.builders.filter((b)=>b!==props.builder)
|
|
||||||
deleteShapes()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function valueFromPoints(points) {
|
||||||
if (!props.builder.priceA)
|
const result = points[0].time;
|
||||||
lineA.createOrDraw(); // initiate drawing mode
|
console.log('valueFromPoints', points, result);
|
||||||
|
return result
|
||||||
onMounted(adjustShapes)
|
}
|
||||||
onBeforeUnmount(deleteShapes)
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ const props = defineProps({
|
|||||||
getModelValue: Function, // getModelValue(model) -> value
|
getModelValue: Function, // getModelValue(model) -> value
|
||||||
setModelValue: Function, // setModelValue(model,value) -> void
|
setModelValue: Function, // setModelValue(model,value) -> void
|
||||||
setValues: Function, // setValues(values:Array) -> void
|
setValues: Function, // setValues(values:Array) -> void
|
||||||
|
setWeights: Function, // setValues(values:Array) -> void
|
||||||
})
|
})
|
||||||
|
|
||||||
const skew100 = computed( {
|
const skew100 = computed( {
|
||||||
@@ -137,7 +138,12 @@ const values = computed(()=>{
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const weights = computed(() => linearWeights(props.builder.rungs, -props.builder.skew))
|
const weights = computed(() => {
|
||||||
|
const ws = linearWeights(props.builder.rungs, -props.builder.skew)
|
||||||
|
if (props.setWeights)
|
||||||
|
props.setWeights(ws)
|
||||||
|
return ws
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
const amountSymbol = computed(()=>props.order.amountIsTokenA ? co.selectedSymbol.base.s : co.selectedSymbol.quote.s )
|
const amountSymbol = computed(()=>props.order.amountIsTokenA ? co.selectedSymbol.base.s : co.selectedSymbol.quote.s )
|
||||||
@@ -182,7 +188,6 @@ function allocText(weight) {
|
|||||||
// we keep two special control shapes as the edges of the range, with deletable shapes in-between
|
// we keep two special control shapes as the edges of the range, with deletable shapes in-between
|
||||||
|
|
||||||
function createShape(value, model, onModel, onDelete) {
|
function createShape(value, model, onModel, onDelete) {
|
||||||
console.log('createShape setModelValue', model, value)
|
|
||||||
props.setModelValue(model, value)
|
props.setModelValue(model, value)
|
||||||
return new props.shape(model, onModel, onDelete) // props.shape is the constructor function
|
return new props.shape(model, onModel, onDelete) // props.shape is the constructor function
|
||||||
}
|
}
|
||||||
@@ -199,7 +204,6 @@ function translateOnDrag(shape) {
|
|||||||
oldOnPoints.call(this, points)
|
oldOnPoints.call(this, points)
|
||||||
const cur = props.getModelValue(this.model)
|
const cur = props.getModelValue(this.model)
|
||||||
const delta = cur - prev
|
const delta = cur - prev
|
||||||
console.log('move prev/cur', prev, cur, delta)
|
|
||||||
if (delta !== 0) {
|
if (delta !== 0) {
|
||||||
valueA.value += delta
|
valueA.value += delta
|
||||||
valueB.value += delta
|
valueB.value += delta
|
||||||
@@ -280,6 +284,7 @@ function adjustShapes() {
|
|||||||
const model = {text: allocText(ws[0]), color: colorStrings[0]};
|
const model = {text: allocText(ws[0]), color: colorStrings[0]};
|
||||||
console.log('single shape A setModelValue', model, vs[0])
|
console.log('single shape A setModelValue', model, vs[0])
|
||||||
props.setModelValue(model, vs[0])
|
props.setModelValue(model, vs[0])
|
||||||
|
console.log('shapeA setModel', model, shapeA.id)
|
||||||
shapeA.setModel(model)
|
shapeA.setModel(model)
|
||||||
}
|
}
|
||||||
shapeB.delete()
|
shapeB.delete()
|
||||||
|
|||||||
Reference in New Issue
Block a user