chart data fixes
This commit is contained in:
71
bin/dev
71
bin/dev
@@ -553,6 +553,9 @@ deep_restart() {
|
|||||||
# Force restart iceberg-catalog since it depends on postgres and minio
|
# Force restart iceberg-catalog since it depends on postgres and minio
|
||||||
echo -e "${GREEN}→${NC} Force restarting iceberg-catalog (depends on postgres/minio)..."
|
echo -e "${GREEN}→${NC} Force restarting iceberg-catalog (depends on postgres/minio)..."
|
||||||
kubectl delete pod -l app=iceberg-catalog 2>/dev/null || true
|
kubectl delete pod -l app=iceberg-catalog 2>/dev/null || true
|
||||||
|
# Remove all sandbox deployments and services to free quota
|
||||||
|
echo -e "${GREEN}→${NC} Removing all sandbox deployments and services..."
|
||||||
|
kubectl delete deployments,services --all -n dexorder-sandboxes 2>/dev/null || true
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo -e "${RED}Error: Unknown service '$service'${NC}"
|
echo -e "${RED}Error: Unknown service '$service'${NC}"
|
||||||
@@ -711,19 +714,75 @@ case "$COMMAND" in
|
|||||||
rebuild_images
|
rebuild_images
|
||||||
deploy_services
|
deploy_services
|
||||||
else
|
else
|
||||||
# Multiple services specified
|
# Multiple services specified: rebuild ALL first, then deploy ALL together.
|
||||||
|
# Deploying one-at-a-time causes each deploy to revert the previous service's
|
||||||
|
# image tag override (each kubectl apply -k . only carries one tag at a time).
|
||||||
|
sandbox_requested=0
|
||||||
|
deploy_services_list=()
|
||||||
|
|
||||||
for service in "$@"; do
|
for service in "$@"; do
|
||||||
rebuild_images "$service"
|
rebuild_images "$service"
|
||||||
|
|
||||||
# Special handling for sandbox: delete sandbox deployments instead of applying kustomization
|
|
||||||
if [ "$service" == "sandbox" ]; then
|
if [ "$service" == "sandbox" ]; then
|
||||||
echo -e "${GREEN}→${NC} Deleting user container deployments in dexorder-sandboxes namespace..."
|
sandbox_requested=1
|
||||||
kubectl delete deployments --all -n dexorder-sandboxes 2>/dev/null || true
|
|
||||||
echo -e "${GREEN}✓ User containers will be recreated by gateway on next login${NC}"
|
|
||||||
else
|
else
|
||||||
deploy_service "$service"
|
deploy_services_list+=("$service")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Deploy all non-sandbox services together in one kustomize apply
|
||||||
|
if [ ${#deploy_services_list[@]} -gt 0 ]; then
|
||||||
|
if [ -f "$ROOT_DIR/.dev-image-tag" ]; then
|
||||||
|
source "$ROOT_DIR/.dev-image-tag"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$ROOT_DIR/deploy/k8s/dev"
|
||||||
|
|
||||||
|
# Template gateway-config if gateway is in the list
|
||||||
|
for svc in "${deploy_services_list[@]}"; do
|
||||||
|
if [ "$svc" == "gateway" ]; then
|
||||||
|
sed -i "s/SANDBOX_TAG_PLACEHOLDER/$SANDBOX_TAG/g" "$ROOT_DIR/deploy/k8s/dev/configs/gateway-config.yaml"
|
||||||
|
sed -i "s/SIDECAR_TAG_PLACEHOLDER/$SIDECAR_TAG/g" "$ROOT_DIR/deploy/k8s/dev/configs/gateway-config.yaml"
|
||||||
|
"$SCRIPT_DIR/config-update" dev
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Build the images stanza for all services at once
|
||||||
|
echo "" >> kustomization.yaml
|
||||||
|
echo "# Image tags (added by bin/dev)" >> kustomization.yaml
|
||||||
|
echo "images:" >> kustomization.yaml
|
||||||
|
for svc in "${deploy_services_list[@]}"; do
|
||||||
|
case "$svc" in
|
||||||
|
relay) echo " - name: dexorder/ai-relay" >> kustomization.yaml; echo " newTag: $RELAY_TAG" >> kustomization.yaml ;;
|
||||||
|
ingestor) echo " - name: dexorder/ai-ingestor" >> kustomization.yaml; echo " newTag: $INGEST_TAG" >> kustomization.yaml ;;
|
||||||
|
flink) echo " - name: dexorder/ai-flink" >> kustomization.yaml; echo " newTag: $FLINK_TAG" >> kustomization.yaml ;;
|
||||||
|
gateway) echo " - name: dexorder/ai-gateway" >> kustomization.yaml; echo " newTag: $GATEWAY_TAG" >> kustomization.yaml ;;
|
||||||
|
web) echo " - name: dexorder/ai-web" >> kustomization.yaml; echo " newTag: $WEB_TAG" >> kustomization.yaml ;;
|
||||||
|
lifecycle-sidecar|sidecar) echo " - name: dexorder/ai-lifecycle-sidecar" >> kustomization.yaml; echo " newTag: $SIDECAR_TAG" >> kustomization.yaml ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
kubectl apply -k .
|
||||||
|
|
||||||
|
sed -i '/# Image tags (added by bin\/dev)/,$d' kustomization.yaml
|
||||||
|
|
||||||
|
# Restore gateway-config placeholders if gateway was deployed
|
||||||
|
for svc in "${deploy_services_list[@]}"; do
|
||||||
|
if [ "$svc" == "gateway" ]; then
|
||||||
|
sed -i "s/$SANDBOX_TAG/SANDBOX_TAG_PLACEHOLDER/g" "$ROOT_DIR/deploy/k8s/dev/configs/gateway-config.yaml"
|
||||||
|
sed -i "s/$SIDECAR_TAG/SIDECAR_TAG_PLACEHOLDER/g" "$ROOT_DIR/deploy/k8s/dev/configs/gateway-config.yaml"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Handle sandbox separately
|
||||||
|
if [ "$sandbox_requested" == "1" ]; then
|
||||||
|
echo -e "${GREEN}→${NC} Deleting user container deployments in dexorder-sandboxes namespace..."
|
||||||
|
kubectl delete deployments --all -n dexorder-sandboxes 2>/dev/null || true
|
||||||
|
echo -e "${GREEN}✓ User containers will be recreated by gateway on next login${NC}"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
rebuild)
|
rebuild)
|
||||||
|
|||||||
@@ -255,6 +255,19 @@ generatorOptions:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import {
|
|||||||
type SnapshotMessage,
|
type SnapshotMessage,
|
||||||
type PatchMessage,
|
type PatchMessage,
|
||||||
} from '../workspace/index.js';
|
} from '../workspace/index.js';
|
||||||
import { resolutionToSeconds } from '../types/ohlc.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Safe JSON stringifier that handles BigInt values
|
* Safe JSON stringifier that handles BigInt values
|
||||||
@@ -487,7 +486,7 @@ export class WebSocketHandler {
|
|||||||
}
|
}
|
||||||
const history = await ohlcService.fetchOHLC(
|
const history = await ohlcService.fetchOHLC(
|
||||||
payload.symbol,
|
payload.symbol,
|
||||||
resolutionToSeconds(payload.resolution),
|
payload.period_seconds,
|
||||||
payload.from_time,
|
payload.from_time,
|
||||||
payload.to_time,
|
payload.to_time,
|
||||||
payload.countback
|
payload.countback
|
||||||
|
|||||||
@@ -525,9 +525,9 @@ export class DuckDBClient {
|
|||||||
// Check if we have continuous data
|
// Check if we have continuous data
|
||||||
// For now, simple check: if we have any data, assume complete
|
// For now, simple check: if we have any data, assume complete
|
||||||
// TODO: Implement proper gap detection by checking for missing periods
|
// TODO: Implement proper gap detection by checking for missing periods
|
||||||
const periodMicros = BigInt(period_seconds) * 1000000n;
|
const periodNanos = BigInt(period_seconds) * 1_000_000_000n;
|
||||||
// end_time is exclusive, so expected count = (end - start) / period (no +1)
|
// end_time is exclusive, so expected count = (end - start) / period (no +1)
|
||||||
const expectedBars = Number((end_time - start_time) / periodMicros);
|
const expectedBars = Number((end_time - start_time) / periodNanos);
|
||||||
|
|
||||||
if (data.length < expectedBars * 0.95) { // Allow 5% tolerance
|
if (data.length < expectedBars * 0.95) { // Allow 5% tolerance
|
||||||
this.logger.debug({
|
this.logger.debug({
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ onBeforeUnmount(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.main-splitter :deep(.p-splitter-gutter-handle) {
|
.main-splitter :deep(.p-splitter-gutter-handle) {
|
||||||
background: #1c1c1c !important;
|
background: #2e2e2e !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart-panel,
|
.chart-panel,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const rooms = computed(() => [{
|
|||||||
avatar: null,
|
avatar: null,
|
||||||
users: [
|
users: [
|
||||||
{ _id: CURRENT_USER_ID, username: 'You' },
|
{ _id: CURRENT_USER_ID, username: 'You' },
|
||||||
{ _id: AGENT_ID, username: 'AI Agent', status: { state: isConnected.value ? 'online' : 'offline' } }
|
{ _id: AGENT_ID, username: 'AI Agent' }
|
||||||
],
|
],
|
||||||
unreadCount: 0,
|
unreadCount: 0,
|
||||||
typingUsers: isAgentProcessing.value ? [AGENT_ID] : []
|
typingUsers: isAgentProcessing.value ? [AGENT_ID] : []
|
||||||
|
|||||||
@@ -79,6 +79,19 @@ export class WebSocketDatafeed implements IBasicDataFeed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private handleMessage(message: any): void {
|
private handleMessage(message: any): void {
|
||||||
|
// On reconnect the server sends a fresh 'connected' message.
|
||||||
|
// Any pending requests were sent on the old socket and will never be answered,
|
||||||
|
// so reject them immediately so TradingView can retry on the new connection.
|
||||||
|
if (message.type === 'connected' && this.pendingRequests.size > 0) {
|
||||||
|
console.warn('[TradingView Datafeed] WebSocket reconnected — rejecting', this.pendingRequests.size, 'stale pending request(s)')
|
||||||
|
for (const [requestId, pending] of this.pendingRequests) {
|
||||||
|
clearTimeout(pending.timeout)
|
||||||
|
pending.reject(new Error('WebSocket reconnected'))
|
||||||
|
this.pendingRequests.delete(requestId)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Handle responses to pending requests
|
// Handle responses to pending requests
|
||||||
if (message.request_id && this.pendingRequests.has(message.request_id)) {
|
if (message.request_id && this.pendingRequests.has(message.request_id)) {
|
||||||
console.log('[TradingView Datafeed] Found pending request for:', message.request_id)
|
console.log('[TradingView Datafeed] Found pending request for:', message.request_id)
|
||||||
@@ -186,10 +199,12 @@ export class WebSocketDatafeed implements IBasicDataFeed {
|
|||||||
if (response.symbol_info) {
|
if (response.symbol_info) {
|
||||||
console.log('[TradingView Datafeed] Resolved symbol info:', response.symbol_info)
|
console.log('[TradingView Datafeed] Resolved symbol info:', response.symbol_info)
|
||||||
|
|
||||||
// Store the denominators for this symbol
|
// Derive scale denominators from Nautilus precision fields.
|
||||||
|
// price_precision=2 → tick divisor=100 (prices stored as integer cents)
|
||||||
|
// size_precision=6 → base divisor=1_000_000 (volumes stored as integer micro-units)
|
||||||
const symbolKey = response.symbol_info.ticker || response.symbol_info.name
|
const symbolKey = response.symbol_info.ticker || response.symbol_info.name
|
||||||
const tickDenom = response.symbol_info.tick_denominator || 1
|
const tickDenom = Math.pow(10, response.symbol_info.price_precision ?? 0)
|
||||||
const baseDenom = response.symbol_info.base_denominator || 1
|
const baseDenom = Math.pow(10, response.symbol_info.size_precision ?? 0)
|
||||||
|
|
||||||
this.symbolDenominators.set(symbolKey, {
|
this.symbolDenominators.set(symbolKey, {
|
||||||
tick: tickDenom,
|
tick: tickDenom,
|
||||||
@@ -232,7 +247,7 @@ export class WebSocketDatafeed implements IBasicDataFeed {
|
|||||||
this.sendRequest<any>({
|
this.sendRequest<any>({
|
||||||
type: 'get_bars',
|
type: 'get_bars',
|
||||||
symbol: symbolKey,
|
symbol: symbolKey,
|
||||||
resolution: resolution,
|
period_seconds: intervalToSeconds(resolution),
|
||||||
from_time: periodParams.from,
|
from_time: periodParams.from,
|
||||||
to_time: periodParams.to,
|
to_time: periodParams.to,
|
||||||
countback: periodParams.countBack
|
countback: periodParams.countBack
|
||||||
|
|||||||
Reference in New Issue
Block a user