@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.
- package/dist/frontend-index.d.ts +134 -4
- package/dist/frontend-index.js +305 -25
- package/dist/index.cjs +397 -32
- package/dist/index.d.ts +206 -5
- package/dist/index.js +397 -32
- package/dist/mcp-server.cjs +303 -32
- package/dist/mcp-server.js +303 -32
- package/package.json +1 -1
package/dist/frontend-index.js
CHANGED
|
@@ -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
|
-
|
|
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({
|
|
2121
|
-
|
|
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,
|