@gbozee/ultimate 0.0.2-174 → 0.0.2-177

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.
@@ -1,3 +1,134 @@
1
+ // src/helpers/distributions.ts
2
+ function generateArithmetic(payload) {
3
+ const { margin_range, risk_reward, kind, price_places = "%.1f" } = payload;
4
+ const difference = Math.abs(margin_range[1] - margin_range[0]);
5
+ const spread = difference / risk_reward;
6
+ return Array.from({ length: risk_reward + 1 }, (_, i) => {
7
+ const price = kind === "long" ? margin_range[1] - spread * i : margin_range[0] + spread * i;
8
+ return to_f(price, price_places);
9
+ });
10
+ }
11
+ function generateGeometric(payload) {
12
+ const { margin_range, risk_reward, kind, price_places = "%.1f", percent_change } = payload;
13
+ const effectivePercentChange = percent_change ?? Math.abs(margin_range[1] / margin_range[0] - 1) / risk_reward;
14
+ return Array.from({ length: risk_reward + 1 }, (_, i) => {
15
+ const price = kind === "long" ? margin_range[1] * Math.pow(1 - effectivePercentChange, i) : margin_range[0] * Math.pow(1 + effectivePercentChange, i);
16
+ return to_f(price, price_places);
17
+ });
18
+ }
19
+ function approximateInverseNormal(p) {
20
+ p = Math.max(0.0001, Math.min(0.9999, p));
21
+ if (p < 0.5) {
22
+ const t = Math.sqrt(-2 * Math.log(p));
23
+ const c0 = 2.515517, c1 = 0.802853, c2 = 0.010328;
24
+ const d1 = 1.432788, d2 = 0.189269, d3 = 0.001308;
25
+ return -(t - (c0 + c1 * t + c2 * t * t) / (1 + d1 * t + d2 * t * t + d3 * t * t * t));
26
+ } else {
27
+ const t = Math.sqrt(-2 * Math.log(1 - p));
28
+ const c0 = 2.515517, c1 = 0.802853, c2 = 0.010328;
29
+ const d1 = 1.432788, d2 = 0.189269, d3 = 0.001308;
30
+ return t - (c0 + c1 * t + c2 * t * t) / (1 + d1 * t + d2 * t * t + d3 * t * t * t);
31
+ }
32
+ }
33
+ function generateNormal(payload) {
34
+ const { margin_range, risk_reward, kind, price_places = "%.1f", stdDevFactor = 6 } = payload;
35
+ const mean = (margin_range[0] + margin_range[1]) / 2;
36
+ const stdDev = Math.abs(margin_range[1] - margin_range[0]) / stdDevFactor;
37
+ const skew = kind === "long" ? -0.2 : 0.2;
38
+ const adjustedMean = mean + stdDev * skew;
39
+ const entries = Array.from({ length: risk_reward + 1 }, (_, i) => {
40
+ const p = (i + 0.5) / (risk_reward + 1);
41
+ const z = approximateInverseNormal(p);
42
+ let price = adjustedMean + stdDev * z;
43
+ price = Math.max(margin_range[0], Math.min(margin_range[1], price));
44
+ return to_f(price, price_places);
45
+ });
46
+ return entries.sort((a, b) => a - b);
47
+ }
48
+ function generateExponential(payload) {
49
+ const { margin_range, risk_reward, kind, price_places = "%.1f", lambda } = payload;
50
+ const range = Math.abs(margin_range[1] - margin_range[0]);
51
+ const effectiveLambda = lambda || 2.5;
52
+ return Array.from({ length: risk_reward + 1 }, (_, i) => {
53
+ const t = i / risk_reward;
54
+ const exponentialProgress = 1 - Math.exp(-effectiveLambda * t);
55
+ const price = kind === "long" ? margin_range[1] - range * exponentialProgress : margin_range[0] + range * exponentialProgress;
56
+ return to_f(price, price_places);
57
+ });
58
+ }
59
+ function generateInverseExponential(payload) {
60
+ const { margin_range, risk_reward, kind, price_places = "%.1f", curveFactor = 2 } = payload;
61
+ const range = Math.abs(margin_range[1] - margin_range[0]);
62
+ return Array.from({ length: risk_reward + 1 }, (_, i) => {
63
+ const t = i / risk_reward;
64
+ const progress = (Math.exp(curveFactor * t) - 1) / (Math.exp(curveFactor) - 1);
65
+ const price = kind === "long" ? margin_range[1] - range * progress : margin_range[0] + range * progress;
66
+ return to_f(price, price_places);
67
+ });
68
+ }
69
+ function getEntries(params) {
70
+ const {
71
+ kind,
72
+ distribution,
73
+ margin_range,
74
+ risk_reward,
75
+ price_places = "%.1f",
76
+ distribution_params = {}
77
+ } = params;
78
+ let entries = [];
79
+ switch (distribution) {
80
+ case "arithmetic":
81
+ entries = generateArithmetic({
82
+ margin_range,
83
+ risk_reward,
84
+ kind,
85
+ price_places,
86
+ percent_change: distribution_params.curveFactor
87
+ });
88
+ break;
89
+ case "geometric":
90
+ entries = generateGeometric({
91
+ margin_range,
92
+ risk_reward,
93
+ kind,
94
+ price_places,
95
+ percent_change: distribution_params.curveFactor
96
+ });
97
+ break;
98
+ case "normal":
99
+ entries = generateNormal({
100
+ margin_range,
101
+ risk_reward,
102
+ kind,
103
+ price_places,
104
+ stdDevFactor: distribution_params.stdDevFactor
105
+ });
106
+ break;
107
+ case "exponential":
108
+ entries = generateExponential({
109
+ margin_range,
110
+ risk_reward,
111
+ kind,
112
+ price_places,
113
+ lambda: distribution_params.lambda
114
+ });
115
+ break;
116
+ case "inverse-exponential":
117
+ entries = generateInverseExponential({
118
+ margin_range,
119
+ risk_reward,
120
+ kind,
121
+ price_places,
122
+ curveFactor: distribution_params.curveFactor
123
+ });
124
+ break;
125
+ default:
126
+ throw new Error(`Unknown distribution type: ${distribution}`);
127
+ }
128
+ return entries.sort((a, b) => a - b);
129
+ }
130
+ var distributions_default = getEntries;
131
+
1
132
  // src/helpers/optimizations.ts
2
133
  function calculateTheoreticalKelly({
3
134
  current_entry,
@@ -267,6 +398,10 @@ class Signal {
267
398
  kelly_minimum_risk = 0.2;
268
399
  kelly_func = "theoretical";
269
400
  symbol;
401
+ distribution = {
402
+ long: "arithmetic",
403
+ short: "geometric"
404
+ };
270
405
  constructor({
271
406
  focus,
272
407
  symbol,
@@ -293,8 +428,12 @@ class Signal {
293
428
  kelly_prediction_model = "exponential",
294
429
  kelly_confidence_factor = 0.6,
295
430
  kelly_minimum_risk = 0.2,
296
- kelly_func = "theoretical"
431
+ kelly_func = "theoretical",
432
+ full_distribution
297
433
  }) {
434
+ if (full_distribution) {
435
+ this.distribution = full_distribution;
436
+ }
298
437
  this.symbol = symbol;
299
438
  this.minimum_size = minimum_size;
300
439
  this.first_order_size = first_order_size;
@@ -330,7 +469,8 @@ class Signal {
330
469
  kind = "long",
331
470
  risk,
332
471
  no_of_trades = 1,
333
- take_profit
472
+ take_profit,
473
+ distribution
334
474
  }) {
335
475
  let _stop_loss = stop_loss;
336
476
  if (!_stop_loss && stop_percent) {
@@ -339,16 +479,22 @@ class Signal {
339
479
  const percent_change = _stop_loss ? Math.max(current_price, _stop_loss) / Math.min(current_price, _stop_loss) - 1 : this.percent_change;
340
480
  const _no_of_trades = no_of_trades || this.risk_reward;
341
481
  let _resistance = current_price * Math.pow(1 + percent_change, 1);
482
+ const simple_support = Math.min(current_price, stop_loss);
483
+ const simple_resistance = Math.max(current_price, stop_loss);
342
484
  const derivedConfig = {
343
485
  ...this,
344
486
  percent_change,
345
487
  focus: current_price,
346
- resistance: _resistance,
488
+ resistance: distribution ? simple_resistance : _resistance,
347
489
  risk_per_trade: risk / this.risk_reward,
348
490
  minimum_pnl: pnl,
349
491
  risk_reward: _no_of_trades,
350
492
  take_profit: take_profit || this.take_profit,
351
- support: kind === "long" ? _stop_loss : this.support
493
+ support: distribution ? simple_support : kind === "long" ? _stop_loss : this.support,
494
+ full_distribution: distribution ? {
495
+ ...this.distribution,
496
+ [kind]: distribution
497
+ } : undefined
352
498
  };
353
499
  const instance = new Signal(derivedConfig);
354
500
  if (kind === "short") {}
@@ -564,12 +710,31 @@ class Signal {
564
710
  }
565
711
  this.zone_risk = original;
566
712
  }
713
+ get_future_zones_simple({
714
+ current_price,
715
+ kind = "long",
716
+ raw
717
+ }) {
718
+ const margin_zones = [this.support, this.resistance];
719
+ const distribution = this.distribution ? this.distribution[kind] : "geometric";
720
+ console.log("margin_zones", { margin_zones, distribution });
721
+ let _kind = distribution === "inverse-exponential" ? kind === "long" ? "short" : "long" : kind;
722
+ const entries = distributions_default({
723
+ margin_range: margin_zones,
724
+ kind: _kind,
725
+ distribution,
726
+ risk_reward: this.risk_reward,
727
+ price_places: this.price_places
728
+ });
729
+ return entries.sort((a, b) => a - b);
730
+ }
567
731
  get_future_zones({
568
732
  current_price,
569
733
  kind = "long",
570
734
  raw
571
735
  }) {
572
736
  if (raw) {}
737
+ return this.get_future_zones_simple({ current_price, raw, kind });
573
738
  const margin_range = this.get_margin_range(current_price, kind);
574
739
  let margin_zones = this.get_margin_zones({ current_price });
575
740
  let remaining_zones = margin_zones.filter((x) => JSON.stringify(x) != JSON.stringify(margin_range));
@@ -1439,7 +1604,9 @@ function buildConfig(app_config, {
1439
1604
  kelly_confidence_factor = 0.95,
1440
1605
  kelly_minimum_risk = 0.2,
1441
1606
  kelly_prediction_model = "exponential",
1442
- kelly_func = "theoretical"
1607
+ kelly_func = "theoretical",
1608
+ min_avg_size = 0,
1609
+ distribution
1443
1610
  }) {
1444
1611
  let fee = app_config.fee / 100;
1445
1612
  let working_risk = risk || app_config.risk_per_trade;
@@ -1486,9 +1653,12 @@ function buildConfig(app_config, {
1486
1653
  stop_loss: stop,
1487
1654
  risk: working_risk,
1488
1655
  kind: kind || app_config.kind,
1489
- no_of_trades: trade_no
1656
+ no_of_trades: trade_no,
1657
+ distribution
1490
1658
  }) || [] : [];
1491
- return computeTotalAverageForEachTrade(result, config);
1659
+ const new_trades = computeTotalAverageForEachTrade(result, config);
1660
+ let filtered = new_trades.filter((o) => o.avg_size > min_avg_size);
1661
+ return computeTotalAverageForEachTrade(filtered, config);
1492
1662
  }
1493
1663
  function buildAvg({
1494
1664
  _trades,
@@ -1553,7 +1723,8 @@ function get_app_config_and_max_size(config, payload) {
1553
1723
  kelly_confidence_factor: payload.kelly_confidence_factor,
1554
1724
  kelly_minimum_risk: payload.kelly_minimum_risk,
1555
1725
  kelly_prediction_model: payload.kelly_prediction_model,
1556
- kelly_func: payload.kelly_func
1726
+ kelly_func: payload.kelly_func,
1727
+ distribution: payload.distribution
1557
1728
  });
1558
1729
  const max_size = initialResult[0]?.avg_size;
1559
1730
  const last_value = initialResult[0];
@@ -1591,7 +1762,8 @@ function buildAppConfig(config, payload) {
1591
1762
  kelly_confidence_factor: payload.kelly_confidence_factor,
1592
1763
  kelly_minimum_risk: payload.kelly_minimum_risk,
1593
1764
  kelly_prediction_model: payload.kelly_prediction_model,
1594
- kelly_func: payload.kelly_func
1765
+ kelly_func: payload.kelly_func,
1766
+ distribution: payload.distribution
1595
1767
  });
1596
1768
  app_config.max_size = max_size;
1597
1769
  app_config.entry = payload.entry || app_config.entry;
@@ -1608,7 +1780,7 @@ function buildAppConfig(config, payload) {
1608
1780
  return app_config;
1609
1781
  }
1610
1782
  function getOptimumStopAndRisk(app_config, params) {
1611
- const { max_size, target_stop } = params;
1783
+ const { max_size, target_stop, distribution } = params;
1612
1784
  const isLong = app_config.kind === "long";
1613
1785
  const stopRange = Math.abs(app_config.entry - target_stop) * 0.5;
1614
1786
  let low_stop = isLong ? target_stop - stopRange : Math.max(target_stop - stopRange, app_config.entry);
@@ -1631,7 +1803,8 @@ function getOptimumStopAndRisk(app_config, params) {
1631
1803
  increase: true,
1632
1804
  gap: app_config.gap,
1633
1805
  price_places: app_config.price_places,
1634
- decimal_places: app_config.decimal_places
1806
+ decimal_places: app_config.decimal_places,
1807
+ distribution
1635
1808
  });
1636
1809
  if (result.length === 0) {
1637
1810
  if (isLong) {
@@ -1685,7 +1858,8 @@ function getOptimumStopAndRisk(app_config, params) {
1685
1858
  increase: true,
1686
1859
  gap: app_config.gap,
1687
1860
  price_places: app_config.price_places,
1688
- decimal_places: app_config.decimal_places
1861
+ decimal_places: app_config.decimal_places,
1862
+ distribution
1689
1863
  });
1690
1864
  if (result.length === 0) {
1691
1865
  high_risk = mid_risk;
@@ -1730,7 +1904,8 @@ function getOptimumStopAndRisk(app_config, params) {
1730
1904
  increase: true,
1731
1905
  gap: app_config.gap,
1732
1906
  price_places: app_config.price_places,
1733
- decimal_places: app_config.decimal_places
1907
+ decimal_places: app_config.decimal_places,
1908
+ distribution
1734
1909
  });
1735
1910
  if (result.length === 0)
1736
1911
  continue;
@@ -1853,7 +2028,8 @@ function generateOptimumAppConfig(config, payload, position2) {
1853
2028
  }, {
1854
2029
  entry: payload.entry,
1855
2030
  stop: payload.stop,
1856
- kind: position2.kind
2031
+ kind: position2.kind,
2032
+ distribution: payload.distribution
1857
2033
  });
1858
2034
  current_app_config.max_size = max_size;
1859
2035
  current_app_config.last_value = last_value;
@@ -1868,7 +2044,8 @@ function generateOptimumAppConfig(config, payload, position2) {
1868
2044
  increase: true,
1869
2045
  gap: current_app_config.gap,
1870
2046
  price_places: current_app_config.price_places,
1871
- decimal_places: current_app_config.decimal_places
2047
+ decimal_places: current_app_config.decimal_places,
2048
+ distribution: payload.distribution
1872
2049
  });
1873
2050
  if (full_trades.length === 0) {
1874
2051
  high_risk = mid_risk;
@@ -1932,7 +2109,8 @@ function determineOptimumReward(payload) {
1932
2109
  increase = true,
1933
2110
  low_range = 1,
1934
2111
  high_range = 199,
1935
- target_loss
2112
+ target_loss,
2113
+ distribution
1936
2114
  } = payload;
1937
2115
  const criterion = app_config.strategy || "quantity";
1938
2116
  const risk_rewards = createArray(low_range, high_range, 1);
@@ -1946,7 +2124,8 @@ function determineOptimumReward(payload) {
1946
2124
  increase,
1947
2125
  kind: app_config.kind,
1948
2126
  gap: app_config.gap,
1949
- decimal_places: app_config.decimal_places
2127
+ decimal_places: app_config.decimal_places,
2128
+ distribution
1950
2129
  });
1951
2130
  let total = 0;
1952
2131
  let max = -Infinity;
@@ -2111,14 +2290,17 @@ function determineOptimumRisk(config, payload, params) {
2111
2290
  };
2112
2291
  }
2113
2292
  function computeRiskReward(payload) {
2114
- const { app_config, entry, stop, risk_per_trade, target_loss } = payload;
2293
+ const { app_config, entry, stop, risk_per_trade, target_loss, distribution } = payload;
2115
2294
  const kind = entry > stop ? "long" : "short";
2116
2295
  app_config.kind = kind;
2117
2296
  app_config.entry = entry;
2118
2297
  app_config.stop = stop;
2119
2298
  app_config.risk_per_trade = risk_per_trade;
2120
- const result = determineOptimumReward({ app_config, target_loss });
2121
- console.log("result", result, "target_loss", target_loss);
2299
+ const result = determineOptimumReward({
2300
+ app_config,
2301
+ target_loss,
2302
+ distribution
2303
+ });
2122
2304
  return result;
2123
2305
  }
2124
2306
  function getRiskReward(payload) {
@@ -2128,21 +2310,24 @@ function getRiskReward(payload) {
2128
2310
  risk,
2129
2311
  global_config,
2130
2312
  force_exact_risk = false,
2131
- target_loss
2313
+ target_loss,
2314
+ distribution
2132
2315
  } = payload;
2133
2316
  const { entries, last_value, ...app_config } = buildAppConfig(global_config, {
2134
2317
  entry,
2135
2318
  stop,
2136
2319
  risk_reward: 30,
2137
2320
  risk,
2138
- symbol: global_config.symbol
2321
+ symbol: global_config.symbol,
2322
+ distribution
2139
2323
  });
2140
2324
  const risk_reward = computeRiskReward({
2141
2325
  app_config,
2142
2326
  entry,
2143
2327
  stop,
2144
2328
  risk_per_trade: risk,
2145
- target_loss
2329
+ target_loss,
2330
+ distribution
2146
2331
  });
2147
2332
  if (force_exact_risk) {
2148
2333
  const new_risk_per_trade = determineOptimumRisk(global_config, {
@@ -2150,7 +2335,8 @@ function getRiskReward(payload) {
2150
2335
  stop,
2151
2336
  risk_reward,
2152
2337
  risk,
2153
- symbol: global_config.symbol
2338
+ symbol: global_config.symbol,
2339
+ distribution
2154
2340
  }, {
2155
2341
  highest_risk: risk
2156
2342
  }).optimal_risk;
@@ -2299,6 +2485,98 @@ function calculate_factor(payload) {
2299
2485
  calculated_factor = to_f(calculated_factor, places);
2300
2486
  return calculated_factor;
2301
2487
  }
2488
+ function calculateFactorFromTakeProfit(payload) {
2489
+ const { long, short, knownTp, tpType, price_places = "%.4f" } = payload;
2490
+ const gap = Math.abs(long.entry - short.entry);
2491
+ const max_quantity = Math.max(long.quantity, short.quantity);
2492
+ const gapLoss = gap * max_quantity;
2493
+ if (gapLoss === 0) {
2494
+ return 0;
2495
+ }
2496
+ let factor;
2497
+ if (tpType === "long") {
2498
+ const longPercent = knownTp / long.entry - 1;
2499
+ factor = longPercent * short.entry * short.quantity / gapLoss;
2500
+ } else {
2501
+ const shortPercent = short.entry / knownTp - 1;
2502
+ factor = shortPercent * long.entry * long.quantity / gapLoss;
2503
+ }
2504
+ return to_f(factor, price_places);
2505
+ }
2506
+ function calculateFactorFromSellQuantity(payload) {
2507
+ const {
2508
+ long,
2509
+ short,
2510
+ knownSellQuantity,
2511
+ sellType,
2512
+ sell_factor = 1,
2513
+ price_places = "%.4f"
2514
+ } = payload;
2515
+ if (knownSellQuantity < 0.00001) {
2516
+ return 0;
2517
+ }
2518
+ const gap = Math.abs(long.entry - short.entry);
2519
+ const max_quantity = Math.max(long.quantity, short.quantity);
2520
+ const gapLoss = gap * max_quantity;
2521
+ if (gapLoss === 0) {
2522
+ return 0;
2523
+ }
2524
+ let low_factor = 0.001;
2525
+ let high_factor = 1;
2526
+ let best_factor = 0;
2527
+ let best_diff = Infinity;
2528
+ const tolerance = 0.00001;
2529
+ const max_iterations = 150;
2530
+ let expansions = 0;
2531
+ const max_expansions = 15;
2532
+ let iterations = 0;
2533
+ while (iterations < max_iterations) {
2534
+ iterations++;
2535
+ const mid_factor = (low_factor + high_factor) / 2;
2536
+ const testResult = generateGapTp({
2537
+ long,
2538
+ short,
2539
+ factor: mid_factor,
2540
+ sell_factor,
2541
+ price_places: "%.8f",
2542
+ decimal_places: "%.8f"
2543
+ });
2544
+ const testSellQty = sellType === "long" ? testResult.sell_quantity.long : testResult.sell_quantity.short;
2545
+ const diff = Math.abs(testSellQty - knownSellQuantity);
2546
+ if (diff < best_diff) {
2547
+ best_diff = diff;
2548
+ best_factor = mid_factor;
2549
+ }
2550
+ if (diff < tolerance) {
2551
+ return to_f(mid_factor, price_places);
2552
+ }
2553
+ if (testSellQty < knownSellQuantity) {
2554
+ low_factor = mid_factor;
2555
+ if (mid_factor > high_factor * 0.9 && expansions < max_expansions) {
2556
+ const ratio = knownSellQuantity / testSellQty;
2557
+ if (ratio > 2) {
2558
+ high_factor = high_factor * Math.min(ratio, 10);
2559
+ } else {
2560
+ high_factor = high_factor * 2;
2561
+ }
2562
+ expansions++;
2563
+ continue;
2564
+ }
2565
+ } else {
2566
+ high_factor = mid_factor;
2567
+ }
2568
+ if (Math.abs(high_factor - low_factor) < 0.00001 && diff > tolerance) {
2569
+ if (testSellQty < knownSellQuantity && expansions < max_expansions) {
2570
+ high_factor = high_factor * 1.1;
2571
+ expansions++;
2572
+ continue;
2573
+ } else {
2574
+ break;
2575
+ }
2576
+ }
2577
+ }
2578
+ return to_f(best_factor, price_places);
2579
+ }
2302
2580
  function determineRewardFactor(payload) {
2303
2581
  const { quantity, avg_qty, minimum_pnl, risk } = payload;
2304
2582
  const reward_factor = minimum_pnl / risk;
@@ -2513,7 +2791,7 @@ function constructAppConfig(payload) {
2513
2791
  kelly_minimum_risk: kelly_config?.kelly_minimum_risk ?? kelly?.kelly_minimum_risk,
2514
2792
  kelly_prediction_model: kelly_config?.kelly_prediction_model ?? kelly?.kelly_prediction_model
2515
2793
  };
2516
- const appConfig = buildAppConfig(global_config, options);
2794
+ const { entries: _entries, ...appConfig } = buildAppConfig(global_config, options);
2517
2795
  return appConfig;
2518
2796
  }
2519
2797
  function generateDangerousConfig(payload) {
@@ -3084,6 +3362,8 @@ export {
3084
3362
  computeSellZones,
3085
3363
  computeRiskReward,
3086
3364
  computeProfitDetail,
3365
+ calculateFactorFromTakeProfit,
3366
+ calculateFactorFromSellQuantity,
3087
3367
  buildConfig,
3088
3368
  buildAvg,
3089
3369
  buildAppConfig,