sveltekit-ui 1.1.50 → 1.1.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/.vercel/output/config.json +4 -0
  2. package/.vercel/output/functions/![-]/catchall.func/.svelte-kit/output/server/chunks/environment.js +1 -1
  3. package/.vercel/output/functions/![-]/catchall.func/.svelte-kit/output/server/chunks/index4.svelte.js +6 -4
  4. package/.vercel/output/functions/![-]/catchall.func/.svelte-kit/output/server/chunks/internal.js +1 -1
  5. package/.vercel/output/functions/![-]/catchall.func/.svelte-kit/output/server/chunks/server.js +79 -28
  6. package/.vercel/output/functions/![-]/catchall.func/.svelte-kit/output/server/entries/pages/trader/_page.server.js +295 -0
  7. package/.vercel/output/functions/![-]/catchall.func/.svelte-kit/output/server/entries/pages/trader/_page.svelte.js +237 -0
  8. package/.vercel/output/functions/![-]/catchall.func/.svelte-kit/output/server/nodes/0.js +1 -1
  9. package/.vercel/output/functions/![-]/catchall.func/.svelte-kit/output/server/nodes/1.js +1 -1
  10. package/.vercel/output/functions/![-]/catchall.func/.svelte-kit/output/server/nodes/2.js +1 -1
  11. package/.vercel/output/functions/![-]/catchall.func/.svelte-kit/output/server/nodes/3.js +1 -1
  12. package/.vercel/output/functions/![-]/catchall.func/.svelte-kit/output/server/nodes/4.js +1 -1
  13. package/.vercel/output/functions/![-]/catchall.func/.svelte-kit/output/server/nodes/5.js +1 -1
  14. package/.vercel/output/functions/![-]/catchall.func/.svelte-kit/output/server/nodes/6.js +1 -1
  15. package/.vercel/output/functions/![-]/catchall.func/.svelte-kit/output/server/nodes/7.js +10 -0
  16. package/.vercel/output/functions/![-]/catchall.func/.svelte-kit/vercel-tmp/manifest.js +10 -2
  17. package/.vercel/output/static/_app/immutable/assets/7.P3YGQ1pf.css +1 -0
  18. package/.vercel/output/static/_app/immutable/chunks/BAahZk91.js +1 -0
  19. package/.vercel/output/static/_app/immutable/chunks/{DytjpG3-.js → BPT8hUnt.js} +1 -1
  20. package/.vercel/output/static/_app/immutable/chunks/{CYNOZRXD.js → BwSlGzN3.js} +1 -1
  21. package/.vercel/output/static/_app/immutable/chunks/{CwRDtGo7.js → C1JBm0c-.js} +1 -1
  22. package/.vercel/output/static/_app/immutable/chunks/{X7N3eqG8.js → C7ic0CC_.js} +1 -1
  23. package/.vercel/output/static/_app/immutable/chunks/{DstdO2wO.js → C_Pzoflo.js} +1 -1
  24. package/.vercel/output/static/_app/immutable/chunks/{DYeG_PLy.js → DthNgRcX.js} +1 -1
  25. package/.vercel/output/static/_app/immutable/chunks/dNI4qYWa.js +11 -0
  26. package/.vercel/output/static/_app/immutable/chunks/k2CTozZP.js +3 -0
  27. package/.vercel/output/static/_app/immutable/chunks/{DbQYcTxj.js → reUr01i3.js} +1 -1
  28. package/.vercel/output/static/_app/immutable/entry/app.BmAvfs9y.js +2 -0
  29. package/.vercel/output/static/_app/immutable/entry/start.BjMIYpc9.js +1 -0
  30. package/.vercel/output/static/_app/immutable/nodes/{0.DjBmDT2W.js → 0.Ny4VwAhi.js} +1 -1
  31. package/.vercel/output/static/_app/immutable/nodes/1.DPmv3j1s.js +1 -0
  32. package/.vercel/output/static/_app/immutable/nodes/{2.B2Mv2js_.js → 2.WACi8m_j.js} +1 -1
  33. package/.vercel/output/static/_app/immutable/nodes/{3.DfTdFu6f.js → 3.BR0VfEGG.js} +1 -1
  34. package/.vercel/output/static/_app/immutable/nodes/{4.-NUgN-qG.js → 4.CaKMIjEy.js} +1 -1
  35. package/.vercel/output/static/_app/immutable/nodes/{5.DQzwEh_Q.js → 5.CBcmyemr.js} +1 -1
  36. package/.vercel/output/static/_app/immutable/nodes/{6.CHqFV7yW.js → 6.DBqG0cc_.js} +1 -1
  37. package/.vercel/output/static/_app/immutable/nodes/7.B7MLhsUA.js +1 -0
  38. package/.vercel/output/static/_app/version.json +1 -1
  39. package/dist/Components/Chart/index.svelte.js +5 -6
  40. package/package.json +4 -4
  41. package/src/lib/Components/Chart/index.svelte.js +5 -6
  42. package/.vercel/output/static/_app/immutable/chunks/CHLFeW6J.js +0 -1
  43. package/.vercel/output/static/_app/immutable/chunks/CQvXHdV8.js +0 -3
  44. package/.vercel/output/static/_app/immutable/chunks/qWZacszp.js +0 -11
  45. package/.vercel/output/static/_app/immutable/entry/app.icZmSapd.js +0 -2
  46. package/.vercel/output/static/_app/immutable/entry/start.CfrdPaSv.js +0 -1
  47. package/.vercel/output/static/_app/immutable/nodes/1.cXeHVOr1.js +0 -1
  48. /package/.vercel/output/static/_app/immutable/chunks/{C7Ex2Xh-.js → Du1izqHO.js} +0 -0
  49. /package/.vercel/output/static/_app/immutable/chunks/{B0BvW1x3.js → DxLN9Q9A.js} +0 -0
  50. /package/.vercel/output/static/_app/immutable/chunks/{Cfug8aQt.js → v_jBEYI6.js} +0 -0
@@ -52,6 +52,10 @@
52
52
  "src": "^/test/?(?:/__data.json)?$",
53
53
  "dest": "/test"
54
54
  },
55
+ {
56
+ "src": "^/trader/?(?:/__data.json)?$",
57
+ "dest": "/trader"
58
+ },
55
59
  {
56
60
  "src": "/.*",
57
61
  "dest": "/![-]/catchall"
@@ -37,7 +37,7 @@ function set_public_env(environment) {
37
37
  }
38
38
  //#endregion
39
39
  //#region \0virtual:__sveltekit/environment
40
- var version = "1774028366820";
40
+ var version = "1774227794535";
41
41
  var prerendering = false;
42
42
  function set_building() {}
43
43
  function set_prerendering() {
@@ -171,6 +171,7 @@ function create_chart_manager(config) {
171
171
  const y_axis_width = 70;
172
172
  const x_axis_height = 30;
173
173
  let range_to_show_default = 500;
174
+ let min_visible_bars = 20;
174
175
  let x_edge_padding_ratio = .5;
175
176
  let datasets = null;
176
177
  let chart_prepped = null;
@@ -255,6 +256,7 @@ function create_chart_manager(config) {
255
256
  function scroll_frame(e, shown_start_x, shown_end_x, min_x_scrollable, max_x_scrollable, frame_width, min_period_sec = 60, max_range_sec = null) {
256
257
  const shown_x_range = shown_end_x - shown_start_x;
257
258
  const change_sec = (Math.round(shown_x_range * .01 / 60) + 1) * 60;
259
+ const min_range_sec = Math.max(Number(min_visible_bars) || 0, 1) * min_period_sec;
258
260
  const max_allowed_range_sec = Number.isFinite(max_range_sec) && max_range_sec > 0 ? max_range_sec : min_period_sec * 1e4;
259
261
  let shown_end_x_loc = shown_end_x;
260
262
  let shown_start_x_loc = shown_start_x;
@@ -266,7 +268,7 @@ function create_chart_manager(config) {
266
268
  shown_end_x_loc += change_sec;
267
269
  }
268
270
  let x_pd = e.offsetX / frame_width;
269
- if (e.deltaY < -1 && shown_x_range > 90 * min_period_sec) {
271
+ if (e.deltaY < -1 && shown_x_range > min_range_sec) {
270
272
  shown_start_x_loc += Math.round(change_sec * x_pd / 10) * 10;
271
273
  shown_end_x_loc -= Math.round(change_sec * (1 - x_pd) / 10) * 10;
272
274
  } else if (e.deltaY > 1 && shown_x_range < max_allowed_range_sec) {
@@ -1142,7 +1144,8 @@ function create_chart_manager(config) {
1142
1144
  return n < min ? min : n > max ? max : n;
1143
1145
  }
1144
1146
  function clamp_range_to_limits(range_sec) {
1145
- return clamp(range_sec, 90 * (get_min_period_sec_from_chart() ?? 60), get_max_range_sec_from_chart() ?? range_sec);
1147
+ const min_period_sec = get_min_period_sec_from_chart() ?? 60;
1148
+ return clamp(range_sec, Math.max(Number(min_visible_bars) || 0, 1) * min_period_sec, get_max_range_sec_from_chart() ?? range_sec);
1146
1149
  }
1147
1150
  function clamp_y_range_to_limits(range_y, axis) {
1148
1151
  const data_low_y = axis?.data_low_y;
@@ -1483,9 +1486,7 @@ function create_chart_manager(config) {
1483
1486
  min_x_scrollable = all_data_start_x_loc + Math.floor(range_to_show * .1);
1484
1487
  max_x_scrollable = all_data_end_x_loc - Math.floor(range_to_show * .1);
1485
1488
  if (shown_start_x == null || isNaN(shown_start_x)) shown_start_x = shown_start_x_loc;
1486
- else if (shown_start_x >= max_x_scrollable) shown_start_x = min_x_scrollable;
1487
1489
  if (shown_end_x == null || isNaN(shown_end_x)) shown_end_x = all_data_end_x_loc;
1488
- else if (shown_end_x <= min_x_scrollable) shown_end_x = max_x_scrollable;
1489
1490
  let prepped_frames = [];
1490
1491
  for (let frame of frames) {
1491
1492
  const frame_height = frame_width() / frame.aspect_ratio;
@@ -1755,6 +1756,7 @@ function create_chart_manager(config) {
1755
1756
  function init(config) {
1756
1757
  console.log("init_chart", config);
1757
1758
  range_to_show_default = config?.range_to_show_default ?? 500;
1759
+ min_visible_bars = config?.min_visible_bars ?? 20;
1758
1760
  x_edge_padding_ratio = config?.x_edge_padding_ratio ?? .5;
1759
1761
  datasets = config?.datasets;
1760
1762
  chart_prepped = prep_chart({ ...config?.chart });
@@ -773,7 +773,7 @@ var options = {
773
773
  app: ({ head, body, assets, nonce, env }) => "<!DOCTYPE html>\n<html lang=\"en\" data-theme=\"\">\n <head>\n <meta charset=\"utf-8\" />\n <link rel=\"icon\" type=\"image/svg+xml\" sizes=\"any\" href=\"" + assets + "/favicon.svg\" />\n <link rel=\"icon\" type=\"image/png\" href=\"" + assets + "/favicon.png\" />\n <link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"" + assets + "/apple-touch-icon.png\" />\n <link rel=\"manifest\" href=\"" + assets + "/site.webmanifest\" />\n <meta name=\"theme-color\" media=\"(prefers-color-scheme: light)\" content=\"oklch(97% 0.01 261)\" />\n <meta name=\"theme-color\" media=\"(prefers-color-scheme: dark)\" content=\"oklch(3% 0.01 261)\" />\n <meta name=\"viewport\" content=\"width=device-width\" />\n " + head + "\n </head>\n <body data-sveltekit-preload-data=\"hover\">\n <div style=\"display: contents\">" + body + "</div>\n </body>\n</html>\n",
774
774
  error: ({ status, message }) => "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <title>" + message + "</title>\n\n <style>\n body {\n --bg: white;\n --fg: #222;\n --divider: #ccc;\n background: var(--bg);\n color: var(--fg);\n font-family:\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n 'Segoe UI',\n Roboto,\n Oxygen,\n Ubuntu,\n Cantarell,\n 'Open Sans',\n 'Helvetica Neue',\n sans-serif;\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100vh;\n margin: 0;\n }\n\n .error {\n display: flex;\n align-items: center;\n max-width: 32rem;\n margin: 0 1rem;\n }\n\n .status {\n font-weight: 200;\n font-size: 3rem;\n line-height: 1;\n position: relative;\n top: -0.05rem;\n }\n\n .message {\n border-left: 1px solid var(--divider);\n padding: 0 0 0 1rem;\n margin: 0 0 0 1rem;\n min-height: 2.5rem;\n display: flex;\n align-items: center;\n }\n\n .message h1 {\n font-weight: 400;\n font-size: 1em;\n margin: 0;\n }\n\n @media (prefers-color-scheme: dark) {\n body {\n --bg: #222;\n --fg: #ddd;\n --divider: #666;\n }\n }\n </style>\n </head>\n <body>\n <div class=\"error\">\n <span class=\"status\">" + status + "</span>\n <div class=\"message\">\n <h1>" + message + "</h1>\n </div>\n </div>\n </body>\n</html>\n"
775
775
  },
776
- version_hash: "1af1iqb"
776
+ version_hash: "os530e"
777
777
  };
778
778
  async function get_hooks() {
779
779
  let handle;
@@ -484,15 +484,16 @@ var uid = 1;
484
484
  var Batch = class Batch {
485
485
  id = uid++;
486
486
  /**
487
- * The current values of any sources that are updated in this batch
487
+ * The current values of any signals that are updated in this batch.
488
+ * Tuple format: [value, is_derived] (note: is_derived is false for deriveds, too, if they were overridden via assignment)
488
489
  * They keys of this map are identical to `this.#previous`
489
- * @type {Map<Source, any>}
490
+ * @type {Map<Value, [any, boolean]>}
490
491
  */
491
492
  current = /* @__PURE__ */ new Map();
492
493
  /**
493
- * The values of any sources that are updated in this batch _before_ those updates took place.
494
+ * The values of any signals (sources and deriveds) that are updated in this batch _before_ those updates took place.
494
495
  * They keys of this map are identical to `this.#current`
495
- * @type {Map<Source, any>}
496
+ * @type {Map<Value, any>}
496
497
  */
497
498
  previous = /* @__PURE__ */ new Map();
498
499
  /**
@@ -507,13 +508,15 @@ var Batch = class Batch {
507
508
  */
508
509
  #discard_callbacks = /* @__PURE__ */ new Set();
509
510
  /**
510
- * The number of async effects that are currently in flight
511
+ * Async effects that are currently in flight
512
+ * @type {Map<Effect, number>}
511
513
  */
512
- #pending = 0;
514
+ #pending = /* @__PURE__ */ new Map();
513
515
  /**
514
- * The number of async effects that are currently in flight, _not_ inside a pending boundary
516
+ * Async effects that are currently in flight, _not_ inside a pending boundary
517
+ * @type {Map<Effect, number>}
515
518
  */
516
- #blocking_pending = 0;
519
+ #blocking_pending = /* @__PURE__ */ new Map();
517
520
  /**
518
521
  * A deferred that resolves when the batch is committed, used with `settled()`
519
522
  * TODO replace with Promise.withResolvers once supported widely enough
@@ -545,8 +548,25 @@ var Batch = class Batch {
545
548
  #skipped_branches = /* @__PURE__ */ new Map();
546
549
  is_fork = false;
547
550
  #decrement_queued = false;
551
+ /** @type {Set<Batch>} */
552
+ #blockers = /* @__PURE__ */ new Set();
548
553
  #is_deferred() {
549
- return this.is_fork || this.#blocking_pending > 0;
554
+ return this.is_fork || this.#blocking_pending.size > 0;
555
+ }
556
+ #is_blocked() {
557
+ for (const batch of this.#blockers) for (const effect of batch.#blocking_pending.keys()) {
558
+ var skipped = false;
559
+ var e = effect;
560
+ while (e.parent !== null) {
561
+ if (this.#skipped_branches.has(e)) {
562
+ skipped = true;
563
+ break;
564
+ }
565
+ e = e.parent;
566
+ }
567
+ if (!skipped) return true;
568
+ }
569
+ return false;
550
570
  }
551
571
  /**
552
572
  * Add an effect to the #skipped_branches map and reset its children
@@ -618,12 +638,12 @@ var Batch = class Batch {
618
638
  }
619
639
  collected_effects = null;
620
640
  legacy_updates = null;
621
- if (this.#is_deferred()) {
641
+ if (this.#is_deferred() || this.#is_blocked()) {
622
642
  this.#defer_effects(render_effects);
623
643
  this.#defer_effects(effects);
624
644
  for (const [e, t] of this.#skipped_branches) reset_branch(e, t);
625
645
  } else {
626
- if (this.#pending === 0) batches.delete(this);
646
+ if (this.#pending.size === 0) batches.delete(this);
627
647
  this.#dirty_effects.clear();
628
648
  this.#maybe_dirty_effects.clear();
629
649
  for (const fn of this.#commit_callbacks) fn(this);
@@ -689,13 +709,14 @@ var Batch = class Batch {
689
709
  /**
690
710
  * Associate a change to a given source with the current
691
711
  * batch, noting its previous and current values
692
- * @param {Source} source
712
+ * @param {Value} source
693
713
  * @param {any} old_value
714
+ * @param {boolean} [is_derived]
694
715
  */
695
- capture(source, old_value) {
716
+ capture(source, old_value, is_derived = false) {
696
717
  if (old_value !== UNINITIALIZED && !this.previous.has(source)) this.previous.set(source, old_value);
697
718
  if ((source.f & 8388608) === 0) {
698
- this.current.set(source, source.v);
719
+ this.current.set(source, [source.v, is_derived]);
699
720
  batch_values?.set(source, source.v);
700
721
  }
701
722
  }
@@ -732,9 +753,12 @@ var Batch = class Batch {
732
753
  var is_earlier = batch.id < this.id;
733
754
  /** @type {Source[]} */
734
755
  var sources = [];
735
- for (const [source, value] of this.current) {
736
- if (batch.current.has(source)) if (is_earlier && value !== batch.current.get(source)) batch.current.set(source, value);
737
- else continue;
756
+ for (const [source, [value, is_derived]] of this.current) {
757
+ if (batch.current.has(source)) {
758
+ var batch_value = batch.current.get(source)[0];
759
+ if (is_earlier && value !== batch_value) batch.current.set(source, [value, is_derived]);
760
+ else continue;
761
+ }
738
762
  sources.push(source);
739
763
  }
740
764
  var others = [...batch.current.keys()].filter((s) => !this.current.has(s));
@@ -755,22 +779,40 @@ var Batch = class Batch {
755
779
  batch.deactivate();
756
780
  }
757
781
  }
782
+ for (const batch of batches) if (batch.#blockers.has(this)) {
783
+ batch.#blockers.delete(this);
784
+ if (batch.#blockers.size === 0 && !batch.#is_deferred()) {
785
+ batch.activate();
786
+ batch.#process();
787
+ }
788
+ }
758
789
  }
759
790
  /**
760
- *
761
791
  * @param {boolean} blocking
792
+ * @param {Effect} effect
762
793
  */
763
- increment(blocking) {
764
- this.#pending += 1;
765
- if (blocking) this.#blocking_pending += 1;
794
+ increment(blocking, effect) {
795
+ let pending_count = this.#pending.get(effect) ?? 0;
796
+ this.#pending.set(effect, pending_count + 1);
797
+ if (blocking) {
798
+ let blocking_pending_count = this.#blocking_pending.get(effect) ?? 0;
799
+ this.#blocking_pending.set(effect, blocking_pending_count + 1);
800
+ }
766
801
  }
767
802
  /**
768
803
  * @param {boolean} blocking
804
+ * @param {Effect} effect
769
805
  * @param {boolean} skip - whether to skip updates (because this is triggered by a stale reaction)
770
806
  */
771
- decrement(blocking, skip) {
772
- this.#pending -= 1;
773
- if (blocking) this.#blocking_pending -= 1;
807
+ decrement(blocking, effect, skip) {
808
+ let pending_count = this.#pending.get(effect) ?? 0;
809
+ if (pending_count === 1) this.#pending.delete(effect);
810
+ else this.#pending.set(effect, pending_count - 1);
811
+ if (blocking) {
812
+ let blocking_pending_count = this.#blocking_pending.get(effect) ?? 0;
813
+ if (blocking_pending_count === 1) this.#blocking_pending.delete(effect);
814
+ else this.#blocking_pending.set(effect, blocking_pending_count - 1);
815
+ }
774
816
  if (this.#decrement_queued || skip) return;
775
817
  this.#decrement_queued = true;
776
818
  queue_micro_task(() => {
@@ -817,10 +859,19 @@ var Batch = class Batch {
817
859
  batch_values = null;
818
860
  return;
819
861
  }
820
- batch_values = new Map(this.current);
862
+ batch_values = /* @__PURE__ */ new Map();
863
+ for (const [source, [value]] of this.current) batch_values.set(source, value);
821
864
  for (const batch of batches) {
822
865
  if (batch === this || batch.is_fork) continue;
823
- for (const [source, previous] of batch.previous) if (!batch_values.has(source)) batch_values.set(source, previous);
866
+ var intersects = false;
867
+ var differs = false;
868
+ if (batch.id < this.id) for (const [source, [, is_derived]] of batch.current) {
869
+ if (is_derived) continue;
870
+ intersects ||= this.current.has(source);
871
+ differs ||= !this.current.has(source);
872
+ }
873
+ if (intersects && differs) this.#blockers.add(batch);
874
+ else for (const [source, previous] of batch.previous) if (!batch_values.has(source)) batch_values.set(source, previous);
824
875
  }
825
876
  }
826
877
  /**
@@ -1453,7 +1504,7 @@ function update_derived(derived) {
1453
1504
  derived.wv = increment_write_version();
1454
1505
  if (!current_batch?.is_fork || derived.deps === null) {
1455
1506
  derived.v = value;
1456
- current_batch?.capture(derived, old_value);
1507
+ current_batch?.capture(derived, old_value, true);
1457
1508
  if (derived.deps === null) {
1458
1509
  set_signal_status(derived, CLEAN);
1459
1510
  return;
@@ -2071,7 +2122,7 @@ function destroy_effect(effect, remove_dom = true) {
2071
2122
  effect.f |= DESTROYED;
2072
2123
  var parent = effect.parent;
2073
2124
  if (parent !== null && parent.first !== null) unlink_effect(effect);
2074
- effect.next = effect.prev = effect.teardown = effect.ctx = effect.deps = effect.fn = effect.nodes = effect.ac = null;
2125
+ effect.next = effect.prev = effect.teardown = effect.ctx = effect.deps = effect.fn = effect.nodes = effect.ac = effect.b = null;
2075
2126
  }
2076
2127
  /**
2077
2128
  *
@@ -0,0 +1,295 @@
1
+ //#region src/lib/server/trader_data.js
2
+ var PERIOD_TO_SEC = {
3
+ "5m": 300,
4
+ "1h": 3600,
5
+ "1d": 86400,
6
+ "1w": 604800
7
+ };
8
+ var COINBASE_MAX_WINDOW_BARS = 290;
9
+ var TRADER_MARKETS = [
10
+ {
11
+ key: "btc_usd",
12
+ label: "BTC / USD",
13
+ description: "Coinbase execution charts plus an Aster weekly context chart."
14
+ },
15
+ {
16
+ key: "doge_usd",
17
+ label: "DOGE / USD",
18
+ description: "DOGE spot structure across fast, medium, and higher timeframes."
19
+ },
20
+ {
21
+ key: "doge_btc",
22
+ label: "DOGE / BTC",
23
+ description: "Relative-strength anchor chart for sizing ideas."
24
+ }
25
+ ];
26
+ var TRADER_CHART_SPECS = [
27
+ {
28
+ id: "btc_usd_coinbase_5m",
29
+ market_key: "btc_usd",
30
+ pair_label: "BTC / USD",
31
+ title: "BTC / USD 5m",
32
+ source_key: "coinbase",
33
+ source_label: "Coinbase",
34
+ source_symbol: "BTC-USD",
35
+ period: "5m",
36
+ target_bars: 350
37
+ },
38
+ {
39
+ id: "btc_usd_coinbase_1h",
40
+ market_key: "btc_usd",
41
+ pair_label: "BTC / USD",
42
+ title: "BTC / USD 1h",
43
+ source_key: "coinbase",
44
+ source_label: "Coinbase",
45
+ source_symbol: "BTC-USD",
46
+ period: "1h",
47
+ target_bars: 350
48
+ },
49
+ {
50
+ id: "btc_usd_coinbase_1d",
51
+ market_key: "btc_usd",
52
+ pair_label: "BTC / USD",
53
+ title: "BTC / USD 1d",
54
+ source_key: "coinbase",
55
+ source_label: "Coinbase",
56
+ source_symbol: "BTC-USD",
57
+ period: "1d",
58
+ target_bars: 350
59
+ },
60
+ {
61
+ id: "btc_usd_aster_1w",
62
+ market_key: "btc_usd",
63
+ pair_label: "BTC / USD",
64
+ title: "BTC / USD 1w",
65
+ source_key: "aster",
66
+ source_label: "Aster",
67
+ source_symbol: "BTCUSDT",
68
+ period: "1w",
69
+ target_bars: 240,
70
+ source_note: "Weekly context uses Aster perpetual BTCUSDT candles."
71
+ },
72
+ {
73
+ id: "doge_usd_coinbase_5m",
74
+ market_key: "doge_usd",
75
+ pair_label: "DOGE / USD",
76
+ title: "DOGE / USD 5m",
77
+ source_key: "coinbase",
78
+ source_label: "Coinbase",
79
+ source_symbol: "DOGE-USD",
80
+ period: "5m",
81
+ target_bars: 350
82
+ },
83
+ {
84
+ id: "doge_usd_coinbase_1h",
85
+ market_key: "doge_usd",
86
+ pair_label: "DOGE / USD",
87
+ title: "DOGE / USD 1h",
88
+ source_key: "coinbase",
89
+ source_label: "Coinbase",
90
+ source_symbol: "DOGE-USD",
91
+ period: "1h",
92
+ target_bars: 350
93
+ },
94
+ {
95
+ id: "doge_usd_coinbase_1d",
96
+ market_key: "doge_usd",
97
+ pair_label: "DOGE / USD",
98
+ title: "DOGE / USD 1d",
99
+ source_key: "coinbase",
100
+ source_label: "Coinbase",
101
+ source_symbol: "DOGE-USD",
102
+ period: "1d",
103
+ target_bars: 350
104
+ },
105
+ {
106
+ id: "doge_usd_aster_1w",
107
+ market_key: "doge_usd",
108
+ pair_label: "DOGE / USD",
109
+ title: "DOGE / USD 1w",
110
+ source_key: "aster",
111
+ source_label: "Aster",
112
+ source_symbol: "DOGEUSDT",
113
+ period: "1w",
114
+ target_bars: 240,
115
+ source_note: "Weekly context uses Aster perpetual DOGEUSDT candles."
116
+ },
117
+ {
118
+ id: "doge_btc_coinbase_1d",
119
+ market_key: "doge_btc",
120
+ pair_label: "DOGE / BTC",
121
+ title: "DOGE / BTC 1d",
122
+ source_key: "coinbase",
123
+ source_label: "Coinbase",
124
+ source_symbol: "DOGE-BTC",
125
+ period: "1d",
126
+ target_bars: 900,
127
+ source_note: "Fetched in multiple Coinbase windows to get deeper daily history."
128
+ }
129
+ ];
130
+ function create_fetch_error(response, body) {
131
+ const body_message = typeof body?.message === "string" ? body.message : null;
132
+ const fallback = `Request failed with status ${response.status}`;
133
+ return new Error(body_message || fallback);
134
+ }
135
+ async function fetch_json(fetcher, url) {
136
+ const response = await fetcher(url, { headers: { accept: "application/json" } });
137
+ const body = await response.json();
138
+ if (!response.ok) throw create_fetch_error(response, body);
139
+ if (body?.message && !Array.isArray(body)) throw new Error(body.message);
140
+ return body;
141
+ }
142
+ function dedupe_klines(klines) {
143
+ const by_open_time = /* @__PURE__ */ new Map();
144
+ for (let kline of klines || []) {
145
+ if (!Number.isFinite(kline?.open_time)) continue;
146
+ by_open_time.set(kline.open_time, kline);
147
+ }
148
+ return [...by_open_time.values()].sort((a, b) => a.open_time - b.open_time);
149
+ }
150
+ function normalize_coinbase_klines(raw_klines) {
151
+ if (!Array.isArray(raw_klines)) throw new Error("Coinbase candles response was not an array.");
152
+ return dedupe_klines(raw_klines.map((kline) => {
153
+ return {
154
+ open_time: Number(kline?.[0]),
155
+ l: Number(kline?.[1]),
156
+ h: Number(kline?.[2]),
157
+ o: Number(kline?.[3]),
158
+ c: Number(kline?.[4]),
159
+ v: Number(kline?.[5])
160
+ };
161
+ }));
162
+ }
163
+ function normalize_aster_klines(raw_klines) {
164
+ if (!Array.isArray(raw_klines)) throw new Error("Aster klines response was not an array.");
165
+ return dedupe_klines(raw_klines.map((kline) => {
166
+ return {
167
+ open_time: Math.floor(Number(kline?.[0]) / 1e3),
168
+ o: Number(kline?.[1]),
169
+ h: Number(kline?.[2]),
170
+ l: Number(kline?.[3]),
171
+ c: Number(kline?.[4]),
172
+ v: Number(kline?.[5])
173
+ };
174
+ }));
175
+ }
176
+ async function fetch_coinbase_recent_klines(fetcher, product_id, period) {
177
+ return normalize_coinbase_klines(await fetch_json(fetcher, `https://api.exchange.coinbase.com/products/${product_id}/candles?granularity=${PERIOD_TO_SEC[period]}`));
178
+ }
179
+ async function fetch_coinbase_paged_klines(fetcher, product_id, period, target_bars) {
180
+ const granularity_sec = PERIOD_TO_SEC[period];
181
+ let remaining_bars = target_bars;
182
+ let end_time_ms = Date.now();
183
+ let combined_klines = [];
184
+ let safety = 0;
185
+ while (remaining_bars > 0 && safety < 12) {
186
+ const bars_this_call = Math.min(remaining_bars, COINBASE_MAX_WINDOW_BARS);
187
+ const window_ms = bars_this_call * granularity_sec * 1e3;
188
+ const start_time_iso = new Date(end_time_ms - window_ms).toISOString();
189
+ const end_time_iso = new Date(end_time_ms).toISOString();
190
+ const next_klines = normalize_coinbase_klines(await fetch_json(fetcher, `https://api.exchange.coinbase.com/products/${product_id}/candles?${new URLSearchParams({
191
+ granularity: String(granularity_sec),
192
+ start: start_time_iso,
193
+ end: end_time_iso
194
+ }).toString()}`));
195
+ if (next_klines.length === 0) break;
196
+ combined_klines = dedupe_klines([...next_klines, ...combined_klines]);
197
+ remaining_bars = target_bars - combined_klines.length;
198
+ end_time_ms = (next_klines[0].open_time - granularity_sec) * 1e3;
199
+ safety += 1;
200
+ if (next_klines.length < bars_this_call * .75) break;
201
+ }
202
+ return combined_klines.slice(-target_bars);
203
+ }
204
+ async function fetch_coinbase_klines(fetcher, product_id, period, target_bars = 350) {
205
+ if ((target_bars ?? 0) > 350) return fetch_coinbase_paged_klines(fetcher, product_id, period, target_bars);
206
+ return (await fetch_coinbase_recent_klines(fetcher, product_id, period)).slice(-target_bars);
207
+ }
208
+ async function fetch_aster_klines(fetcher, symbol, period, target_bars = 240) {
209
+ return normalize_aster_klines(await fetch_json(fetcher, `https://fapi.asterdex.com/fapi/v1/klines?${new URLSearchParams({
210
+ symbol,
211
+ interval: period,
212
+ limit: String(target_bars)
213
+ }).toString()}`));
214
+ }
215
+ async function fetch_chart_klines(fetcher, chart_spec) {
216
+ if (chart_spec.source_key === "coinbase") return fetch_coinbase_klines(fetcher, chart_spec.source_symbol, chart_spec.period, chart_spec.target_bars);
217
+ if (chart_spec.source_key === "aster") return fetch_aster_klines(fetcher, chart_spec.source_symbol, chart_spec.period, chart_spec.target_bars);
218
+ throw new Error(`Unsupported chart source: ${chart_spec.source_key}`);
219
+ }
220
+ function get_chart_result_summary(chart_spec, klines) {
221
+ return {
222
+ ...chart_spec,
223
+ status: "ready",
224
+ error: null,
225
+ klines,
226
+ trendlines: [],
227
+ bar_count: klines.length,
228
+ first_open_time: klines?.[0]?.open_time ?? null,
229
+ last_open_time: klines?.[klines.length - 1]?.open_time ?? null
230
+ };
231
+ }
232
+ function get_chart_error_summary(chart_spec, error) {
233
+ return {
234
+ ...chart_spec,
235
+ status: "error",
236
+ error: error?.message ?? "Failed to load chart data.",
237
+ klines: [],
238
+ trendlines: [],
239
+ bar_count: 0,
240
+ first_open_time: null,
241
+ last_open_time: null
242
+ };
243
+ }
244
+ function build_markets(chart_results) {
245
+ return TRADER_MARKETS.map((market) => {
246
+ return {
247
+ ...market,
248
+ charts: chart_results.filter((chart_result) => chart_result.market_key === market.key)
249
+ };
250
+ });
251
+ }
252
+ async function load_trader_data(fetcher = fetch) {
253
+ const chart_results = await Promise.all(TRADER_CHART_SPECS.map(async (chart_spec) => {
254
+ try {
255
+ return get_chart_result_summary(chart_spec, await fetch_chart_klines(fetcher, chart_spec));
256
+ } catch (error) {
257
+ return get_chart_error_summary(chart_spec, error);
258
+ }
259
+ }));
260
+ return {
261
+ title: "Trader Lab",
262
+ loaded_at_iso: (/* @__PURE__ */ new Date()).toISOString(),
263
+ markets: build_markets(chart_results),
264
+ strategy_seed: {
265
+ total_budget_usd: 1e5,
266
+ leverage_allowed: true,
267
+ rebalance_cadence: "hourly",
268
+ top_trendlines_per_chart: 15,
269
+ notional_by_period_usd: {
270
+ "5m": 1e3,
271
+ "1h": 5e3,
272
+ "1d": 1e4,
273
+ "1w": 15e3
274
+ },
275
+ sizing_signal_chart_id: "doge_btc_coinbase_1d"
276
+ },
277
+ notes: [
278
+ "Step 1 focuses on loading and charting the main BTC, DOGE, and DOGE/BTC structures.",
279
+ "Trendline generation and backtesting hooks are intentionally left for the next pass.",
280
+ "Aster weekly charts use USDT perpetual symbols because that is the available market format there."
281
+ ],
282
+ errors: chart_results.filter((chart_result) => chart_result.status === "error").map((chart_result) => ({
283
+ id: chart_result.id,
284
+ title: chart_result.title,
285
+ error: chart_result.error
286
+ }))
287
+ };
288
+ }
289
+ //#endregion
290
+ //#region src/routes/trader/+page.server.js
291
+ async function load({ fetch }) {
292
+ return { trader: await load_trader_data(fetch) };
293
+ }
294
+ //#endregion
295
+ export { load };