Compare commits
2 Commits
488e9f45f1
...
b2ed48492b
| Author | SHA1 | Date | |
|---|---|---|---|
| b2ed48492b | |||
| ebf70dd10c |
@@ -19,8 +19,15 @@ export function subOHLC( chainId, pool, period ) {
|
|||||||
|
|
||||||
export function refreshOHLCSubs() {
|
export function refreshOHLCSubs() {
|
||||||
const keys = []
|
const keys = []
|
||||||
|
let chainId = null
|
||||||
for (const key of Object.keys(ohlcSubCounts)) {
|
for (const key of Object.keys(ohlcSubCounts)) {
|
||||||
const [chainId, pool, period] = key.split('|')
|
const [curChainId, pool, period] = key.split('|')
|
||||||
|
if (chainId === null)
|
||||||
|
chainId = curChainId
|
||||||
|
else if (chainId !== curChainId) {
|
||||||
|
console.error('refreshOHLCSubs: mixed chainIds')
|
||||||
|
continue
|
||||||
|
}
|
||||||
keys.push(`${pool}|${period}`)
|
keys.push(`${pool}|${period}`)
|
||||||
}
|
}
|
||||||
socket.emit('subOHLCs', chainId, keys)
|
socket.emit('subOHLCs', chainId, keys)
|
||||||
|
|||||||
@@ -53,3 +53,7 @@ export function dirtyItems(a, b) {
|
|||||||
result[k] = b[k]
|
result[k] = b[k]
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function copyPoints(points) {
|
||||||
|
return points.map((p)=>({time: p.time, price: p.price}))
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {DataFeed, feelessTickerKey, getAllSymbols, lookupSymbol} from "@/charts/
|
|||||||
import {intervalToSeconds, SingletonCoroutine} from "@/misc.js";
|
import {intervalToSeconds, SingletonCoroutine} from "@/misc.js";
|
||||||
import {useStore} from "@/store/store.js";
|
import {useStore} from "@/store/store.js";
|
||||||
import {tvCustomThemes} from "../../theme.js";
|
import {tvCustomThemes} from "../../theme.js";
|
||||||
|
import {copyPoints} from "@/charts/chart-misc.js";
|
||||||
|
|
||||||
export let widget = null
|
export let widget = null
|
||||||
export let chart = null
|
export let chart = null
|
||||||
@@ -310,10 +311,14 @@ function onSelectedLineToolChanged() {
|
|||||||
|
|
||||||
export let dragging = false
|
export let dragging = false
|
||||||
export let draggingShapeIds = []
|
export let draggingShapeIds = []
|
||||||
|
let draggingShapeStartPoints = null
|
||||||
|
let mouseClickPoint = null
|
||||||
|
|
||||||
function mouseDown() {
|
function mouseDown() {
|
||||||
// console.log('mouseDown')
|
// console.log('mouseDown')
|
||||||
// todo push into drawing event queue instead, then set dragging there
|
// todo push into drawing event queue instead, then set dragging there
|
||||||
dragging = true
|
dragging = true
|
||||||
|
mouseClickPoint = {time: crosshairPoint.time, price: crosshairPoint.price}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mouseUp() {
|
function mouseUp() {
|
||||||
@@ -341,7 +346,11 @@ function doHandleCrosshairMovement(point) {
|
|||||||
if (selection.length)
|
if (selection.length)
|
||||||
draggingShapeIds = selection
|
draggingShapeIds = selection
|
||||||
// console.log('dragging selected', draggingShapeIds)
|
// console.log('dragging selected', draggingShapeIds)
|
||||||
for (const shapeId of draggingShapeIds) {
|
const initStartPoints = draggingShapeStartPoints === null
|
||||||
|
if (initStartPoints)
|
||||||
|
draggingShapeStartPoints = []
|
||||||
|
for (const i in draggingShapeIds) {
|
||||||
|
const shapeId = draggingShapeIds[i]
|
||||||
let shape
|
let shape
|
||||||
try {
|
try {
|
||||||
shape = chart.getShapeById(shapeId);
|
shape = chart.getShapeById(shapeId);
|
||||||
@@ -349,15 +358,33 @@ function doHandleCrosshairMovement(point) {
|
|||||||
catch (e) {
|
catch (e) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const points = structuredClone(shape.getPoints());
|
const shapePoints = shape.getPoints();
|
||||||
const lpbe = shape._model._linePointBeingEdited
|
const lpbe = shape._model._linePointBeingEdited
|
||||||
points[lpbe] = point
|
const points = [];
|
||||||
|
const dt = point.time - mouseClickPoint.time
|
||||||
|
const dp = point.price - mouseClickPoint.price
|
||||||
|
if (initStartPoints)
|
||||||
|
draggingShapeStartPoints.push(copyPoints(shapePoints))
|
||||||
|
for (const j in shapePoints) {
|
||||||
|
if (lpbe!==null)
|
||||||
|
// if this is the point being dragged, set it to the cursor position. otherwise pass it unchanged.
|
||||||
|
points.push(j===lpbe ? {time: point.time, price: point.price} : shapePoints[j])
|
||||||
|
else {
|
||||||
|
// lpbe is null if the user is dragging the entire object (translation,) meaning all points are affected
|
||||||
|
// todo use the origin of any selected point
|
||||||
|
const startPoint = draggingShapeStartPoints[i][j];
|
||||||
|
// push a point that applies the cursor delta to the starting position of the shape
|
||||||
|
points.push({time: startPoint.time + dt, price: startPoint.price + dp})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//console.log('lpbe', lpbe)
|
||||||
// console.log('drag calling onPoints', points, shape, lpbe)
|
// console.log('drag calling onPoints', points, shape, lpbe)
|
||||||
invokeCallbacks(shapeCallbacks[shapeId], 'onPoints', shapeId, shape, points)
|
invokeCallbacks(shapeCallbacks[shapeId], 'onPoints', shapeId, shape, points)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (draggingShapeIds.length > 0) {
|
else if (draggingShapeIds.length > 0) {
|
||||||
draggingShapeIds = []
|
draggingShapeIds = []
|
||||||
|
draggingShapeStartPoints = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ class TrancheShapes {
|
|||||||
price *= scale
|
price *= scale
|
||||||
// console.log('price', price)
|
// console.log('price', price)
|
||||||
const channel = buy?'low':'high';
|
const channel = buy?'low':'high';
|
||||||
const text = allocationText(buy, weight, amount, amountSymbol, '\n')
|
const text = allocationText(buy, weight, amount, amountSymbol, amountIsBase ? null : this.symbol.base.s, '\n')
|
||||||
const s = createShape(buy?'arrow_up':'arrow_down', {time, price}, {channel,text,lock:true})
|
const s = createShape(buy?'arrow_up':'arrow_down', {time, price}, {channel,text,lock:true})
|
||||||
// console.log('created fill shape at', time, price)
|
// console.log('created fill shape at', time, price)
|
||||||
this.fills.push(s)
|
this.fills.push(s)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {invokeCallback, mixin} from "@/common.js";
|
|||||||
import {chart, createShape, deleteShapeId, dragging, draggingShapeIds, drawShape, widget} from "@/charts/chart.js";
|
import {chart, createShape, deleteShapeId, dragging, draggingShapeIds, drawShape, widget} from "@/charts/chart.js";
|
||||||
import Color from "color";
|
import Color from "color";
|
||||||
import {dirtyItems, dirtyPoints, nearestOhlcStart} from "@/charts/chart-misc.js";
|
import {dirtyItems, dirtyPoints, nearestOhlcStart} from "@/charts/chart-misc.js";
|
||||||
import {defined} from "@/misc.js";
|
import {defined, toPrecision} from "@/misc.js";
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -39,8 +39,7 @@ export const ShapeType = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function allocationText(buy, weight, amount, symbol, separator = ' ') {
|
export function allocationText(buy, weight, amount, baseSymbol, amountSymbol = null, separator = ' ') {
|
||||||
// set breakout=true for a buy breakout and breakout=false for a sell breakout
|
|
||||||
const hasAmount = amount !== null && amount !== undefined && amount > 0
|
const hasAmount = amount !== null && amount !== undefined && amount > 0
|
||||||
if (hasAmount)
|
if (hasAmount)
|
||||||
amount = Number(amount)
|
amount = Number(amount)
|
||||||
@@ -52,11 +51,14 @@ export function allocationText(buy, weight, amount, symbol, separator = ' ') {
|
|||||||
let text = buy === undefined ? '' : buy ? 'Buy ' : 'Sell '
|
let text = buy === undefined ? '' : buy ? 'Buy ' : 'Sell '
|
||||||
if (hasWeight)
|
if (hasWeight)
|
||||||
text += `${(weight * 100).toFixed(1)}%`
|
text += `${(weight * 100).toFixed(1)}%`
|
||||||
const hasSymbol = symbol !== null && symbol !== undefined
|
const hasSymbol = baseSymbol !== null && baseSymbol !== undefined
|
||||||
if (hasAmount && hasSymbol) {
|
if (hasAmount && hasSymbol) {
|
||||||
if (hasWeight)
|
if (hasWeight)
|
||||||
text += separator
|
text += separator
|
||||||
text += `${amount.toPrecision(3).toLocaleString('fullwide')} ${symbol}`
|
if (amountSymbol!==null && amountSymbol!==baseSymbol)
|
||||||
|
text += `${baseSymbol} worth ${toPrecision(amount,3)} ${amountSymbol}`
|
||||||
|
else
|
||||||
|
text += `${toPrecision(amount,3)} ${baseSymbol}`
|
||||||
}
|
}
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
@@ -110,6 +112,7 @@ export class Shape {
|
|||||||
this.model.maxAllocation = null
|
this.model.maxAllocation = null
|
||||||
// both amount and amountSymbol must be set in order to display amount text
|
// both amount and amountSymbol must be set in order to display amount text
|
||||||
this.model.amount = null
|
this.model.amount = null
|
||||||
|
this.model.baseSymbol = null
|
||||||
this.model.amountSymbol = null
|
this.model.amountSymbol = null
|
||||||
this.model.extraText = null
|
this.model.extraText = null
|
||||||
this.model.textLocation = null // defaults to 'above' if not set
|
this.model.textLocation = null // defaults to 'above' if not set
|
||||||
@@ -132,6 +135,8 @@ export class Shape {
|
|||||||
this.model.amount = model.amount
|
this.model.amount = model.amount
|
||||||
if (defined(model.amountSymbol))
|
if (defined(model.amountSymbol))
|
||||||
this.model.amountSymbol = model.amountSymbol
|
this.model.amountSymbol = model.amountSymbol
|
||||||
|
if (defined(model.baseSymbol))
|
||||||
|
this.model.baseSymbol = model.baseSymbol
|
||||||
if (defined(model.extraText))
|
if (defined(model.extraText))
|
||||||
this.model.extraText = model.extraText
|
this.model.extraText = model.extraText
|
||||||
if (defined(model.breakout))
|
if (defined(model.breakout))
|
||||||
@@ -164,7 +169,7 @@ export class Shape {
|
|||||||
newProps.linecolor = color
|
newProps.linecolor = color
|
||||||
|
|
||||||
// text label
|
// text label
|
||||||
let text = allocationText(this.model.buy, this.model.allocation, this.model.amount, this.model.amountSymbol)
|
let text = allocationText(this.model.buy, this.model.allocation, this.model.amount, this.model.baseSymbol, this.model.amountSymbol)
|
||||||
if (this.model.breakout)
|
if (this.model.breakout)
|
||||||
text += ' ' + (this.model.textLocation==='above' ? '▲Breakout▲' : '▼Breakout▼')
|
text += ' ' + (this.model.textLocation==='above' ? '▲Breakout▲' : '▼Breakout▼')
|
||||||
if (this.model.extraText)
|
if (this.model.extraText)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template class="d-flex align-content-center flex-column" style="height: 100%; width: 100%;">
|
||||||
<rung-builder name='DCA' :order="order" :builder="builder" v-model="timeEndpoints"
|
<rung-builder name='DCA' :order="order" :builder="builder" v-model="timeEndpoints"
|
||||||
:shape="VLine"
|
:shape="VLine"
|
||||||
:mode="1" :flip="flipped" :orientation="0"
|
:mode="1" :flip="flipped" :orientation="0"
|
||||||
@@ -82,7 +82,7 @@ const times = ref([])
|
|||||||
const weights = ref([])
|
const weights = ref([])
|
||||||
|
|
||||||
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 )
|
||||||
const allocationTexts = computed(()=>weights.value.map((w)=>allocationText(props.order.buy, w, w*props.order.amount, amountSymbol.value)))
|
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 endTimes = computed(()=>{
|
||||||
if (props.builder.rungs === 1)
|
if (props.builder.rungs === 1)
|
||||||
|
|||||||
@@ -349,7 +349,7 @@ function setWeights(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 )
|
||||||
|
|
||||||
const allocationTexts = computed(()=>weights.value.map((w)=>allocationText(props.order.buy, w, w*props.order.amount, amountSymbol.value)))
|
const allocationTexts = computed(()=>weights.value.map((w)=>allocationText(props.order.buy, w, w * props.order.amount, co.selectedSymbol.base.s, amountSymbol.value)))
|
||||||
|
|
||||||
const stdWidth = computed(()=>[0, co.meanRange, 0, co.meanRange])
|
const stdWidth = computed(()=>[0, co.meanRange, 0, co.meanRange])
|
||||||
|
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ function setPrices(ps) {prices.value = ps}
|
|||||||
function setWeights(ws) { weights.value = ws }
|
function setWeights(ws) { weights.value = 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 )
|
||||||
const allocationTexts = computed(()=>weights.value.map((w)=>allocationText(props.order.buy, w, w*props.order.amount, amountSymbol.value)))
|
const allocationTexts = computed(()=>weights.value.map((w)=>allocationText(props.order.buy, w, w * props.order.amount, co.selectedSymbol.base.s, amountSymbol.value)))
|
||||||
const color = computed(()=>props.builder.color ? props.builder.color : defaultColor)
|
const color = computed(()=>props.builder.color ? props.builder.color : defaultColor)
|
||||||
const stdWidth = computed(()=>co.meanRange)
|
const stdWidth = computed(()=>co.meanRange)
|
||||||
const description = computed(()=>{
|
const description = computed(()=>{
|
||||||
|
|||||||
@@ -330,6 +330,7 @@ function makeModel(index) {
|
|||||||
allocation: alloc,
|
allocation: alloc,
|
||||||
maxAllocation: Math.max(...weights.value),
|
maxAllocation: Math.max(...weights.value),
|
||||||
amount: props.order.amount * alloc,
|
amount: props.order.amount * alloc,
|
||||||
|
baseSymbol: co.selectedSymbol.base.s,
|
||||||
amountSymbol: amountSymbol.value,
|
amountSymbol: amountSymbol.value,
|
||||||
textLocation: above ? 'above' : 'below',
|
textLocation: above ? 'above' : 'below',
|
||||||
breakout: props.builder.breakout,
|
breakout: props.builder.breakout,
|
||||||
|
|||||||
@@ -251,3 +251,11 @@ export function computeInterceptSlope(time0, price0, time1, price1) {
|
|||||||
export function defined(v) {
|
export function defined(v) {
|
||||||
return v !== undefined && v !== null
|
return v !== undefined && v !== null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function toPrecision(value, significantDigits = 3) {
|
||||||
|
if (!isFinite(value)) return value.toString(); // Handle Infinity and NaN
|
||||||
|
if (value === 0) return "0"; // Special case for 0
|
||||||
|
const magnitude = Math.floor(Math.log10(Math.abs(value)));
|
||||||
|
const decimalsNeeded = Math.max(0, significantDigits - 1 - magnitude);
|
||||||
|
return value.toFixed(decimalsNeeded); // Use toFixed to completely avoid scientific notation
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user