@gbozee/ultimate 0.0.2-144 → 0.0.2-145
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 +11 -1
- package/dist/frontend-index.js +118 -3
- package/dist/index.cjs +132 -7
- package/dist/index.d.ts +17 -1
- package/dist/index.js +132 -7
- package/dist/mcp-server.cjs +132 -7
- package/dist/mcp-server.js +132 -7
- package/package.json +1 -1
package/dist/frontend-index.d.ts
CHANGED
|
@@ -139,6 +139,10 @@ export type SignalConfigType = {
|
|
|
139
139
|
first_order_size?: number;
|
|
140
140
|
gap?: number;
|
|
141
141
|
max_size?: number;
|
|
142
|
+
use_kelly?: boolean;
|
|
143
|
+
kelly_prediction_model?: "exponential" | "normal" | "uniform";
|
|
144
|
+
kelly_confidence_factor?: number;
|
|
145
|
+
kelly_minimum_risk?: number;
|
|
142
146
|
};
|
|
143
147
|
declare class Signal {
|
|
144
148
|
focus: number;
|
|
@@ -161,7 +165,11 @@ declare class Signal {
|
|
|
161
165
|
first_order_size: number;
|
|
162
166
|
gap: number;
|
|
163
167
|
max_size: number;
|
|
164
|
-
|
|
168
|
+
use_kelly: boolean;
|
|
169
|
+
kelly_prediction_model: "exponential" | "normal" | "uniform";
|
|
170
|
+
kelly_confidence_factor: number;
|
|
171
|
+
kelly_minimum_risk: number;
|
|
172
|
+
constructor({ focus, budget, percent_change, price_places, decimal_places, zone_risk, fee, support, risk_reward, resistance, risk_per_trade, increase_size, additional_increase, minimum_pnl, take_profit, increase_position, minimum_size, first_order_size, gap, max_size, use_kelly, kelly_prediction_model, kelly_confidence_factor, kelly_minimum_risk, }: SignalConfigType);
|
|
165
173
|
build_entry({ current_price, stop_loss, pnl, stop_percent, kind, risk, no_of_trades, take_profit, }: {
|
|
166
174
|
take_profit?: number;
|
|
167
175
|
no_of_trades?: number;
|
|
@@ -296,6 +304,7 @@ export type AppConfig = {
|
|
|
296
304
|
last_value?: any;
|
|
297
305
|
entries?: any[];
|
|
298
306
|
max_quantity?: number;
|
|
307
|
+
use_kelly?: boolean;
|
|
299
308
|
};
|
|
300
309
|
export type ExtendConfigType = {
|
|
301
310
|
take_profit?: number;
|
|
@@ -814,6 +823,7 @@ export declare class Strategy {
|
|
|
814
823
|
rr?: number;
|
|
815
824
|
max_size?: number;
|
|
816
825
|
max_quantity?: number;
|
|
826
|
+
use_kelly?: boolean;
|
|
817
827
|
};
|
|
818
828
|
identifyGapConfig(payload: {
|
|
819
829
|
factor?: number;
|
package/dist/frontend-index.js
CHANGED
|
@@ -1,3 +1,89 @@
|
|
|
1
|
+
// src/helpers/optimizations.ts
|
|
2
|
+
function calculateTheoreticalKelly({
|
|
3
|
+
current_entry,
|
|
4
|
+
zone_prices,
|
|
5
|
+
kind = "long",
|
|
6
|
+
config = {}
|
|
7
|
+
}) {
|
|
8
|
+
const {
|
|
9
|
+
price_prediction_model = "uniform",
|
|
10
|
+
confidence_factor = 0.6,
|
|
11
|
+
volatility_adjustment = true
|
|
12
|
+
} = config;
|
|
13
|
+
const sorted_prices = kind === "long" ? zone_prices : zone_prices.reverse();
|
|
14
|
+
const current_index = sorted_prices.findIndex((price) => price === current_entry);
|
|
15
|
+
if (current_index === -1)
|
|
16
|
+
return 0.02;
|
|
17
|
+
const win_zones = kind === "long" ? sorted_prices.slice(current_index + 1) : sorted_prices.slice(0, current_index);
|
|
18
|
+
const lose_zones = kind === "long" ? sorted_prices.slice(0, current_index) : sorted_prices.slice(current_index + 1);
|
|
19
|
+
const { win_probability, avg_win_ratio, avg_loss_ratio } = calculateZoneProbabilities({
|
|
20
|
+
current_entry,
|
|
21
|
+
win_zones,
|
|
22
|
+
lose_zones,
|
|
23
|
+
price_prediction_model,
|
|
24
|
+
confidence_factor,
|
|
25
|
+
kind
|
|
26
|
+
});
|
|
27
|
+
const odds_ratio = avg_win_ratio / avg_loss_ratio;
|
|
28
|
+
const loss_probability = 1 - win_probability;
|
|
29
|
+
let kelly_fraction = (win_probability * odds_ratio - loss_probability) / odds_ratio;
|
|
30
|
+
if (volatility_adjustment) {
|
|
31
|
+
const zone_volatility = calculateZoneVolatility(sorted_prices);
|
|
32
|
+
const vol_adjustment = 1 / (1 + zone_volatility);
|
|
33
|
+
kelly_fraction *= vol_adjustment;
|
|
34
|
+
}
|
|
35
|
+
kelly_fraction = Math.max(0.005, Math.min(kelly_fraction, 0.5));
|
|
36
|
+
return to_f(kelly_fraction, "%.4f");
|
|
37
|
+
}
|
|
38
|
+
function calculateZoneProbabilities({
|
|
39
|
+
current_entry,
|
|
40
|
+
win_zones,
|
|
41
|
+
lose_zones,
|
|
42
|
+
price_prediction_model,
|
|
43
|
+
confidence_factor,
|
|
44
|
+
kind
|
|
45
|
+
}) {
|
|
46
|
+
if (win_zones.length === 0 && lose_zones.length === 0) {
|
|
47
|
+
return { win_probability: 0.5, avg_win_ratio: 0.02, avg_loss_ratio: 0.02 };
|
|
48
|
+
}
|
|
49
|
+
let win_probability;
|
|
50
|
+
switch (price_prediction_model) {
|
|
51
|
+
case "uniform":
|
|
52
|
+
win_probability = win_zones.length / (win_zones.length + lose_zones.length);
|
|
53
|
+
break;
|
|
54
|
+
case "normal":
|
|
55
|
+
const win_weight = win_zones.reduce((sum, _, idx) => sum + 1 / (idx + 1), 0);
|
|
56
|
+
const lose_weight = lose_zones.reduce((sum, _, idx) => sum + 1 / (idx + 1), 0);
|
|
57
|
+
win_probability = win_weight / (win_weight + lose_weight);
|
|
58
|
+
break;
|
|
59
|
+
case "exponential":
|
|
60
|
+
const exp_win_weight = win_zones.reduce((sum, _, idx) => sum + Math.exp(-idx * 0.5), 0);
|
|
61
|
+
const exp_lose_weight = lose_zones.reduce((sum, _, idx) => sum + Math.exp(-idx * 0.5), 0);
|
|
62
|
+
win_probability = exp_win_weight / (exp_win_weight + exp_lose_weight);
|
|
63
|
+
break;
|
|
64
|
+
default:
|
|
65
|
+
win_probability = 0.5;
|
|
66
|
+
}
|
|
67
|
+
win_probability = win_probability * confidence_factor + (1 - confidence_factor) * 0.5;
|
|
68
|
+
const avg_win_ratio = win_zones.length > 0 ? win_zones.reduce((sum, price) => {
|
|
69
|
+
return sum + Math.abs(price - current_entry) / current_entry;
|
|
70
|
+
}, 0) / win_zones.length : 0.02;
|
|
71
|
+
const avg_loss_ratio = lose_zones.length > 0 ? lose_zones.reduce((sum, price) => {
|
|
72
|
+
return sum + Math.abs(price - current_entry) / current_entry;
|
|
73
|
+
}, 0) / lose_zones.length : 0.02;
|
|
74
|
+
return {
|
|
75
|
+
win_probability: Math.max(0.1, Math.min(0.9, win_probability)),
|
|
76
|
+
avg_win_ratio: Math.max(0.005, avg_win_ratio),
|
|
77
|
+
avg_loss_ratio: Math.max(0.005, avg_loss_ratio)
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function calculateZoneVolatility(zone_prices) {
|
|
81
|
+
if (zone_prices.length < 2)
|
|
82
|
+
return 0;
|
|
83
|
+
const price_changes = zone_prices.slice(1).map((price, i) => Math.abs(price - zone_prices[i]) / zone_prices[i]);
|
|
84
|
+
return price_changes.reduce((sum, change) => sum + change, 0) / price_changes.length;
|
|
85
|
+
}
|
|
86
|
+
|
|
1
87
|
// src/helpers/trade_signal.ts
|
|
2
88
|
function determine_close_price({
|
|
3
89
|
entry,
|
|
@@ -80,6 +166,10 @@ class Signal {
|
|
|
80
166
|
first_order_size;
|
|
81
167
|
gap = 10;
|
|
82
168
|
max_size = 0;
|
|
169
|
+
use_kelly = false;
|
|
170
|
+
kelly_prediction_model = "exponential";
|
|
171
|
+
kelly_confidence_factor = 0.6;
|
|
172
|
+
kelly_minimum_risk = 0.2;
|
|
83
173
|
constructor({
|
|
84
174
|
focus,
|
|
85
175
|
budget,
|
|
@@ -100,7 +190,11 @@ class Signal {
|
|
|
100
190
|
minimum_size = 0,
|
|
101
191
|
first_order_size = 0,
|
|
102
192
|
gap = 10,
|
|
103
|
-
max_size = 0
|
|
193
|
+
max_size = 0,
|
|
194
|
+
use_kelly = false,
|
|
195
|
+
kelly_prediction_model = "exponential",
|
|
196
|
+
kelly_confidence_factor = 0.6,
|
|
197
|
+
kelly_minimum_risk = 0.2
|
|
104
198
|
}) {
|
|
105
199
|
this.minimum_size = minimum_size;
|
|
106
200
|
this.first_order_size = first_order_size;
|
|
@@ -122,6 +216,10 @@ class Signal {
|
|
|
122
216
|
this.increase_position = increase_position;
|
|
123
217
|
this.gap = gap;
|
|
124
218
|
this.max_size = max_size;
|
|
219
|
+
this.use_kelly = use_kelly;
|
|
220
|
+
this.kelly_prediction_model = kelly_prediction_model;
|
|
221
|
+
this.kelly_confidence_factor = kelly_confidence_factor;
|
|
222
|
+
this.kelly_minimum_risk = kelly_minimum_risk;
|
|
125
223
|
}
|
|
126
224
|
build_entry({
|
|
127
225
|
current_price,
|
|
@@ -560,10 +658,25 @@ class Signal {
|
|
|
560
658
|
}
|
|
561
659
|
const defaultStopLoss = i === 0 ? stop_loss : _base;
|
|
562
660
|
const new_stop = kind === "long" ? this.support : stop_loss;
|
|
661
|
+
let risk_to_use = risk_per_trade;
|
|
662
|
+
if (this.use_kelly) {
|
|
663
|
+
const theoretical_kelly = calculateTheoreticalKelly({
|
|
664
|
+
current_entry: x,
|
|
665
|
+
zone_prices: limit_orders,
|
|
666
|
+
kind,
|
|
667
|
+
config: {
|
|
668
|
+
price_prediction_model: this.kelly_prediction_model,
|
|
669
|
+
confidence_factor: this.kelly_confidence_factor,
|
|
670
|
+
volatility_adjustment: true
|
|
671
|
+
}
|
|
672
|
+
});
|
|
673
|
+
risk_to_use = theoretical_kelly * risk_per_trade / this.kelly_minimum_risk;
|
|
674
|
+
console.log({ risk_per_trade, theoretical_kelly });
|
|
675
|
+
}
|
|
563
676
|
const y = this.build_trade_dict({
|
|
564
677
|
entry: x,
|
|
565
678
|
stop: (this.increase_position ? new_stop : defaultStopLoss) || defaultStopLoss,
|
|
566
|
-
risk:
|
|
679
|
+
risk: risk_to_use,
|
|
567
680
|
arr: limit_orders,
|
|
568
681
|
index: i,
|
|
569
682
|
new_fees: total_incurred_market_fees,
|
|
@@ -1249,7 +1362,8 @@ function buildConfig(app_config, {
|
|
|
1249
1362
|
kind: app_config.kind,
|
|
1250
1363
|
gap,
|
|
1251
1364
|
min_profit: min_profit || app_config.min_profit,
|
|
1252
|
-
rr: rr || 1
|
|
1365
|
+
rr: rr || 1,
|
|
1366
|
+
use_kelly: app_config.use_kelly
|
|
1253
1367
|
};
|
|
1254
1368
|
const instance = new Signal(config);
|
|
1255
1369
|
if (raw_instance) {
|
|
@@ -1261,6 +1375,7 @@ function buildConfig(app_config, {
|
|
|
1261
1375
|
const condition = (kind === "long" ? entry > app_config.support : entry >= app_config.support) && stop >= app_config.support * 0.999;
|
|
1262
1376
|
if (kind === "short") {
|
|
1263
1377
|
}
|
|
1378
|
+
console.log({ entry, stop, condition, working_risk, config });
|
|
1264
1379
|
const result = entry === stop ? [] : condition ? instance.build_entry({
|
|
1265
1380
|
current_price: entry,
|
|
1266
1381
|
stop_loss: stop,
|
package/dist/index.cjs
CHANGED
|
@@ -52870,6 +52870,92 @@ function createGapPairs(arr, gap, item) {
|
|
|
52870
52870
|
return result;
|
|
52871
52871
|
}
|
|
52872
52872
|
|
|
52873
|
+
// src/helpers/optimizations.ts
|
|
52874
|
+
function calculateTheoreticalKelly({
|
|
52875
|
+
current_entry,
|
|
52876
|
+
zone_prices,
|
|
52877
|
+
kind = "long",
|
|
52878
|
+
config: config2 = {}
|
|
52879
|
+
}) {
|
|
52880
|
+
const {
|
|
52881
|
+
price_prediction_model = "uniform",
|
|
52882
|
+
confidence_factor = 0.6,
|
|
52883
|
+
volatility_adjustment = true
|
|
52884
|
+
} = config2;
|
|
52885
|
+
const sorted_prices = kind === "long" ? zone_prices : zone_prices.reverse();
|
|
52886
|
+
const current_index = sorted_prices.findIndex((price) => price === current_entry);
|
|
52887
|
+
if (current_index === -1)
|
|
52888
|
+
return 0.02;
|
|
52889
|
+
const win_zones = kind === "long" ? sorted_prices.slice(current_index + 1) : sorted_prices.slice(0, current_index);
|
|
52890
|
+
const lose_zones = kind === "long" ? sorted_prices.slice(0, current_index) : sorted_prices.slice(current_index + 1);
|
|
52891
|
+
const { win_probability, avg_win_ratio, avg_loss_ratio } = calculateZoneProbabilities({
|
|
52892
|
+
current_entry,
|
|
52893
|
+
win_zones,
|
|
52894
|
+
lose_zones,
|
|
52895
|
+
price_prediction_model,
|
|
52896
|
+
confidence_factor,
|
|
52897
|
+
kind
|
|
52898
|
+
});
|
|
52899
|
+
const odds_ratio = avg_win_ratio / avg_loss_ratio;
|
|
52900
|
+
const loss_probability = 1 - win_probability;
|
|
52901
|
+
let kelly_fraction = (win_probability * odds_ratio - loss_probability) / odds_ratio;
|
|
52902
|
+
if (volatility_adjustment) {
|
|
52903
|
+
const zone_volatility = calculateZoneVolatility(sorted_prices);
|
|
52904
|
+
const vol_adjustment = 1 / (1 + zone_volatility);
|
|
52905
|
+
kelly_fraction *= vol_adjustment;
|
|
52906
|
+
}
|
|
52907
|
+
kelly_fraction = Math.max(0.005, Math.min(kelly_fraction, 0.5));
|
|
52908
|
+
return to_f2(kelly_fraction, "%.4f");
|
|
52909
|
+
}
|
|
52910
|
+
function calculateZoneProbabilities({
|
|
52911
|
+
current_entry,
|
|
52912
|
+
win_zones,
|
|
52913
|
+
lose_zones,
|
|
52914
|
+
price_prediction_model,
|
|
52915
|
+
confidence_factor,
|
|
52916
|
+
kind
|
|
52917
|
+
}) {
|
|
52918
|
+
if (win_zones.length === 0 && lose_zones.length === 0) {
|
|
52919
|
+
return { win_probability: 0.5, avg_win_ratio: 0.02, avg_loss_ratio: 0.02 };
|
|
52920
|
+
}
|
|
52921
|
+
let win_probability;
|
|
52922
|
+
switch (price_prediction_model) {
|
|
52923
|
+
case "uniform":
|
|
52924
|
+
win_probability = win_zones.length / (win_zones.length + lose_zones.length);
|
|
52925
|
+
break;
|
|
52926
|
+
case "normal":
|
|
52927
|
+
const win_weight = win_zones.reduce((sum, _, idx) => sum + 1 / (idx + 1), 0);
|
|
52928
|
+
const lose_weight = lose_zones.reduce((sum, _, idx) => sum + 1 / (idx + 1), 0);
|
|
52929
|
+
win_probability = win_weight / (win_weight + lose_weight);
|
|
52930
|
+
break;
|
|
52931
|
+
case "exponential":
|
|
52932
|
+
const exp_win_weight = win_zones.reduce((sum, _, idx) => sum + Math.exp(-idx * 0.5), 0);
|
|
52933
|
+
const exp_lose_weight = lose_zones.reduce((sum, _, idx) => sum + Math.exp(-idx * 0.5), 0);
|
|
52934
|
+
win_probability = exp_win_weight / (exp_win_weight + exp_lose_weight);
|
|
52935
|
+
break;
|
|
52936
|
+
default:
|
|
52937
|
+
win_probability = 0.5;
|
|
52938
|
+
}
|
|
52939
|
+
win_probability = win_probability * confidence_factor + (1 - confidence_factor) * 0.5;
|
|
52940
|
+
const avg_win_ratio = win_zones.length > 0 ? win_zones.reduce((sum, price) => {
|
|
52941
|
+
return sum + Math.abs(price - current_entry) / current_entry;
|
|
52942
|
+
}, 0) / win_zones.length : 0.02;
|
|
52943
|
+
const avg_loss_ratio = lose_zones.length > 0 ? lose_zones.reduce((sum, price) => {
|
|
52944
|
+
return sum + Math.abs(price - current_entry) / current_entry;
|
|
52945
|
+
}, 0) / lose_zones.length : 0.02;
|
|
52946
|
+
return {
|
|
52947
|
+
win_probability: Math.max(0.1, Math.min(0.9, win_probability)),
|
|
52948
|
+
avg_win_ratio: Math.max(0.005, avg_win_ratio),
|
|
52949
|
+
avg_loss_ratio: Math.max(0.005, avg_loss_ratio)
|
|
52950
|
+
};
|
|
52951
|
+
}
|
|
52952
|
+
function calculateZoneVolatility(zone_prices) {
|
|
52953
|
+
if (zone_prices.length < 2)
|
|
52954
|
+
return 0;
|
|
52955
|
+
const price_changes = zone_prices.slice(1).map((price, i2) => Math.abs(price - zone_prices[i2]) / zone_prices[i2]);
|
|
52956
|
+
return price_changes.reduce((sum, change) => sum + change, 0) / price_changes.length;
|
|
52957
|
+
}
|
|
52958
|
+
|
|
52873
52959
|
// src/helpers/trade_signal.ts
|
|
52874
52960
|
function determine_close_price2({
|
|
52875
52961
|
entry,
|
|
@@ -52952,6 +53038,10 @@ class Signal {
|
|
|
52952
53038
|
first_order_size;
|
|
52953
53039
|
gap = 10;
|
|
52954
53040
|
max_size = 0;
|
|
53041
|
+
use_kelly = false;
|
|
53042
|
+
kelly_prediction_model = "exponential";
|
|
53043
|
+
kelly_confidence_factor = 0.6;
|
|
53044
|
+
kelly_minimum_risk = 0.2;
|
|
52955
53045
|
constructor({
|
|
52956
53046
|
focus,
|
|
52957
53047
|
budget,
|
|
@@ -52972,7 +53062,11 @@ class Signal {
|
|
|
52972
53062
|
minimum_size = 0,
|
|
52973
53063
|
first_order_size = 0,
|
|
52974
53064
|
gap = 10,
|
|
52975
|
-
max_size = 0
|
|
53065
|
+
max_size = 0,
|
|
53066
|
+
use_kelly = false,
|
|
53067
|
+
kelly_prediction_model = "exponential",
|
|
53068
|
+
kelly_confidence_factor = 0.6,
|
|
53069
|
+
kelly_minimum_risk = 0.2
|
|
52976
53070
|
}) {
|
|
52977
53071
|
this.minimum_size = minimum_size;
|
|
52978
53072
|
this.first_order_size = first_order_size;
|
|
@@ -52994,6 +53088,10 @@ class Signal {
|
|
|
52994
53088
|
this.increase_position = increase_position;
|
|
52995
53089
|
this.gap = gap;
|
|
52996
53090
|
this.max_size = max_size;
|
|
53091
|
+
this.use_kelly = use_kelly;
|
|
53092
|
+
this.kelly_prediction_model = kelly_prediction_model;
|
|
53093
|
+
this.kelly_confidence_factor = kelly_confidence_factor;
|
|
53094
|
+
this.kelly_minimum_risk = kelly_minimum_risk;
|
|
52997
53095
|
}
|
|
52998
53096
|
build_entry({
|
|
52999
53097
|
current_price,
|
|
@@ -53432,10 +53530,25 @@ class Signal {
|
|
|
53432
53530
|
}
|
|
53433
53531
|
const defaultStopLoss = i2 === 0 ? stop_loss : _base;
|
|
53434
53532
|
const new_stop = kind === "long" ? this.support : stop_loss;
|
|
53533
|
+
let risk_to_use = risk_per_trade;
|
|
53534
|
+
if (this.use_kelly) {
|
|
53535
|
+
const theoretical_kelly = calculateTheoreticalKelly({
|
|
53536
|
+
current_entry: x,
|
|
53537
|
+
zone_prices: limit_orders,
|
|
53538
|
+
kind,
|
|
53539
|
+
config: {
|
|
53540
|
+
price_prediction_model: this.kelly_prediction_model,
|
|
53541
|
+
confidence_factor: this.kelly_confidence_factor,
|
|
53542
|
+
volatility_adjustment: true
|
|
53543
|
+
}
|
|
53544
|
+
});
|
|
53545
|
+
risk_to_use = theoretical_kelly * risk_per_trade / this.kelly_minimum_risk;
|
|
53546
|
+
console.log({ risk_per_trade, theoretical_kelly });
|
|
53547
|
+
}
|
|
53435
53548
|
const y = this.build_trade_dict({
|
|
53436
53549
|
entry: x,
|
|
53437
53550
|
stop: (this.increase_position ? new_stop : defaultStopLoss) || defaultStopLoss,
|
|
53438
|
-
risk:
|
|
53551
|
+
risk: risk_to_use,
|
|
53439
53552
|
arr: limit_orders,
|
|
53440
53553
|
index: i2,
|
|
53441
53554
|
new_fees: total_incurred_market_fees,
|
|
@@ -53647,7 +53760,8 @@ function buildConfig(app_config, {
|
|
|
53647
53760
|
kind: app_config.kind,
|
|
53648
53761
|
gap,
|
|
53649
53762
|
min_profit: min_profit || app_config.min_profit,
|
|
53650
|
-
rr: rr || 1
|
|
53763
|
+
rr: rr || 1,
|
|
53764
|
+
use_kelly: app_config.use_kelly
|
|
53651
53765
|
};
|
|
53652
53766
|
const instance = new Signal(config2);
|
|
53653
53767
|
if (raw_instance) {
|
|
@@ -53659,6 +53773,7 @@ function buildConfig(app_config, {
|
|
|
53659
53773
|
const condition = (kind === "long" ? entry > app_config.support : entry >= app_config.support) && stop >= app_config.support * 0.999;
|
|
53660
53774
|
if (kind === "short") {
|
|
53661
53775
|
}
|
|
53776
|
+
console.log({ entry, stop, condition, working_risk, config: config2 });
|
|
53662
53777
|
const result = entry === stop ? [] : condition ? instance.build_entry({
|
|
53663
53778
|
current_price: entry,
|
|
53664
53779
|
stop_loss: stop,
|
|
@@ -58935,15 +59050,25 @@ class ExchangeAccount {
|
|
|
58935
59050
|
}
|
|
58936
59051
|
}
|
|
58937
59052
|
async increasePositionAtStop(payload) {
|
|
58938
|
-
const { symbol, kind, place = false } = payload;
|
|
59053
|
+
const { symbol, kind, place = false, increase = true } = payload;
|
|
58939
59054
|
const position2 = await this.syncAccount({
|
|
58940
59055
|
symbol,
|
|
58941
59056
|
kind,
|
|
58942
59057
|
as_view: true
|
|
58943
59058
|
});
|
|
58944
59059
|
console.log(position2);
|
|
58945
|
-
|
|
58946
|
-
|
|
59060
|
+
let price_params = {
|
|
59061
|
+
price: payload.price,
|
|
59062
|
+
quantity: payload.quantity
|
|
59063
|
+
};
|
|
59064
|
+
if (!payload.price && position2 && position2.stop_loss) {
|
|
59065
|
+
price_params.price = position2.stop_loss.price;
|
|
59066
|
+
}
|
|
59067
|
+
if (!payload.quantity && position2 && position2.stop_loss) {
|
|
59068
|
+
price_params.quantity = position2.stop_loss.quantity;
|
|
59069
|
+
}
|
|
59070
|
+
if (price_params.price && price_params.quantity) {
|
|
59071
|
+
const { price, quantity } = price_params;
|
|
58947
59072
|
const symbol_config = await this.recomputeSymbolConfig({
|
|
58948
59073
|
symbol
|
|
58949
59074
|
});
|
|
@@ -58963,7 +59088,7 @@ class ExchangeAccount {
|
|
|
58963
59088
|
quantity,
|
|
58964
59089
|
price_places,
|
|
58965
59090
|
decimal_places,
|
|
58966
|
-
increase
|
|
59091
|
+
increase,
|
|
58967
59092
|
place
|
|
58968
59093
|
});
|
|
58969
59094
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1016,6 +1016,7 @@ export declare class Strategy {
|
|
|
1016
1016
|
rr?: number;
|
|
1017
1017
|
max_size?: number;
|
|
1018
1018
|
max_quantity?: number;
|
|
1019
|
+
use_kelly?: boolean;
|
|
1019
1020
|
};
|
|
1020
1021
|
identifyGapConfig(payload: {
|
|
1021
1022
|
factor?: number;
|
|
@@ -1129,6 +1130,10 @@ export type SignalConfigType = {
|
|
|
1129
1130
|
first_order_size?: number;
|
|
1130
1131
|
gap?: number;
|
|
1131
1132
|
max_size?: number;
|
|
1133
|
+
use_kelly?: boolean;
|
|
1134
|
+
kelly_prediction_model?: "exponential" | "normal" | "uniform";
|
|
1135
|
+
kelly_confidence_factor?: number;
|
|
1136
|
+
kelly_minimum_risk?: number;
|
|
1132
1137
|
};
|
|
1133
1138
|
declare class Signal {
|
|
1134
1139
|
focus: number;
|
|
@@ -1151,7 +1156,11 @@ declare class Signal {
|
|
|
1151
1156
|
first_order_size: number;
|
|
1152
1157
|
gap: number;
|
|
1153
1158
|
max_size: number;
|
|
1154
|
-
|
|
1159
|
+
use_kelly: boolean;
|
|
1160
|
+
kelly_prediction_model: "exponential" | "normal" | "uniform";
|
|
1161
|
+
kelly_confidence_factor: number;
|
|
1162
|
+
kelly_minimum_risk: number;
|
|
1163
|
+
constructor({ focus, budget, percent_change, price_places, decimal_places, zone_risk, fee, support, risk_reward, resistance, risk_per_trade, increase_size, additional_increase, minimum_pnl, take_profit, increase_position, minimum_size, first_order_size, gap, max_size, use_kelly, kelly_prediction_model, kelly_confidence_factor, kelly_minimum_risk, }: SignalConfigType);
|
|
1155
1164
|
build_entry({ current_price, stop_loss, pnl, stop_percent, kind, risk, no_of_trades, take_profit, }: {
|
|
1156
1165
|
take_profit?: number;
|
|
1157
1166
|
no_of_trades?: number;
|
|
@@ -1274,6 +1283,7 @@ export type AppConfig = {
|
|
|
1274
1283
|
last_value?: any;
|
|
1275
1284
|
entries?: any[];
|
|
1276
1285
|
max_quantity?: number;
|
|
1286
|
+
use_kelly?: boolean;
|
|
1277
1287
|
};
|
|
1278
1288
|
export type ExtendConfigType = {
|
|
1279
1289
|
take_profit?: number;
|
|
@@ -2072,6 +2082,9 @@ declare class ExchangeAccount$1 {
|
|
|
2072
2082
|
symbol: string;
|
|
2073
2083
|
kind: "long" | "short";
|
|
2074
2084
|
place?: boolean;
|
|
2085
|
+
price?: number;
|
|
2086
|
+
quantity?: number;
|
|
2087
|
+
increase?: boolean;
|
|
2075
2088
|
}): Promise<any>;
|
|
2076
2089
|
triggerTradeFromConfig(payload: {
|
|
2077
2090
|
symbol: string;
|
|
@@ -2198,6 +2211,7 @@ declare class ExchangeAccount$1 {
|
|
|
2198
2211
|
rr?: number;
|
|
2199
2212
|
max_size?: number;
|
|
2200
2213
|
max_quantity?: number;
|
|
2214
|
+
use_kelly?: boolean;
|
|
2201
2215
|
}>;
|
|
2202
2216
|
runSimulation(payload: {
|
|
2203
2217
|
symbol: string;
|
|
@@ -2378,6 +2392,7 @@ declare class ExchangeAccount$1 {
|
|
|
2378
2392
|
rr?: number;
|
|
2379
2393
|
max_size?: number;
|
|
2380
2394
|
max_quantity?: number;
|
|
2395
|
+
use_kelly?: boolean;
|
|
2381
2396
|
};
|
|
2382
2397
|
last_value: any;
|
|
2383
2398
|
config: {
|
|
@@ -2645,6 +2660,7 @@ declare class App {
|
|
|
2645
2660
|
rr?: number;
|
|
2646
2661
|
max_size?: number;
|
|
2647
2662
|
max_quantity?: number;
|
|
2663
|
+
use_kelly?: boolean;
|
|
2648
2664
|
};
|
|
2649
2665
|
last_value: any;
|
|
2650
2666
|
config: {
|
package/dist/index.js
CHANGED
|
@@ -52818,6 +52818,92 @@ function createGapPairs(arr, gap, item) {
|
|
|
52818
52818
|
return result;
|
|
52819
52819
|
}
|
|
52820
52820
|
|
|
52821
|
+
// src/helpers/optimizations.ts
|
|
52822
|
+
function calculateTheoreticalKelly({
|
|
52823
|
+
current_entry,
|
|
52824
|
+
zone_prices,
|
|
52825
|
+
kind = "long",
|
|
52826
|
+
config: config2 = {}
|
|
52827
|
+
}) {
|
|
52828
|
+
const {
|
|
52829
|
+
price_prediction_model = "uniform",
|
|
52830
|
+
confidence_factor = 0.6,
|
|
52831
|
+
volatility_adjustment = true
|
|
52832
|
+
} = config2;
|
|
52833
|
+
const sorted_prices = kind === "long" ? zone_prices : zone_prices.reverse();
|
|
52834
|
+
const current_index = sorted_prices.findIndex((price) => price === current_entry);
|
|
52835
|
+
if (current_index === -1)
|
|
52836
|
+
return 0.02;
|
|
52837
|
+
const win_zones = kind === "long" ? sorted_prices.slice(current_index + 1) : sorted_prices.slice(0, current_index);
|
|
52838
|
+
const lose_zones = kind === "long" ? sorted_prices.slice(0, current_index) : sorted_prices.slice(current_index + 1);
|
|
52839
|
+
const { win_probability, avg_win_ratio, avg_loss_ratio } = calculateZoneProbabilities({
|
|
52840
|
+
current_entry,
|
|
52841
|
+
win_zones,
|
|
52842
|
+
lose_zones,
|
|
52843
|
+
price_prediction_model,
|
|
52844
|
+
confidence_factor,
|
|
52845
|
+
kind
|
|
52846
|
+
});
|
|
52847
|
+
const odds_ratio = avg_win_ratio / avg_loss_ratio;
|
|
52848
|
+
const loss_probability = 1 - win_probability;
|
|
52849
|
+
let kelly_fraction = (win_probability * odds_ratio - loss_probability) / odds_ratio;
|
|
52850
|
+
if (volatility_adjustment) {
|
|
52851
|
+
const zone_volatility = calculateZoneVolatility(sorted_prices);
|
|
52852
|
+
const vol_adjustment = 1 / (1 + zone_volatility);
|
|
52853
|
+
kelly_fraction *= vol_adjustment;
|
|
52854
|
+
}
|
|
52855
|
+
kelly_fraction = Math.max(0.005, Math.min(kelly_fraction, 0.5));
|
|
52856
|
+
return to_f2(kelly_fraction, "%.4f");
|
|
52857
|
+
}
|
|
52858
|
+
function calculateZoneProbabilities({
|
|
52859
|
+
current_entry,
|
|
52860
|
+
win_zones,
|
|
52861
|
+
lose_zones,
|
|
52862
|
+
price_prediction_model,
|
|
52863
|
+
confidence_factor,
|
|
52864
|
+
kind
|
|
52865
|
+
}) {
|
|
52866
|
+
if (win_zones.length === 0 && lose_zones.length === 0) {
|
|
52867
|
+
return { win_probability: 0.5, avg_win_ratio: 0.02, avg_loss_ratio: 0.02 };
|
|
52868
|
+
}
|
|
52869
|
+
let win_probability;
|
|
52870
|
+
switch (price_prediction_model) {
|
|
52871
|
+
case "uniform":
|
|
52872
|
+
win_probability = win_zones.length / (win_zones.length + lose_zones.length);
|
|
52873
|
+
break;
|
|
52874
|
+
case "normal":
|
|
52875
|
+
const win_weight = win_zones.reduce((sum, _, idx) => sum + 1 / (idx + 1), 0);
|
|
52876
|
+
const lose_weight = lose_zones.reduce((sum, _, idx) => sum + 1 / (idx + 1), 0);
|
|
52877
|
+
win_probability = win_weight / (win_weight + lose_weight);
|
|
52878
|
+
break;
|
|
52879
|
+
case "exponential":
|
|
52880
|
+
const exp_win_weight = win_zones.reduce((sum, _, idx) => sum + Math.exp(-idx * 0.5), 0);
|
|
52881
|
+
const exp_lose_weight = lose_zones.reduce((sum, _, idx) => sum + Math.exp(-idx * 0.5), 0);
|
|
52882
|
+
win_probability = exp_win_weight / (exp_win_weight + exp_lose_weight);
|
|
52883
|
+
break;
|
|
52884
|
+
default:
|
|
52885
|
+
win_probability = 0.5;
|
|
52886
|
+
}
|
|
52887
|
+
win_probability = win_probability * confidence_factor + (1 - confidence_factor) * 0.5;
|
|
52888
|
+
const avg_win_ratio = win_zones.length > 0 ? win_zones.reduce((sum, price) => {
|
|
52889
|
+
return sum + Math.abs(price - current_entry) / current_entry;
|
|
52890
|
+
}, 0) / win_zones.length : 0.02;
|
|
52891
|
+
const avg_loss_ratio = lose_zones.length > 0 ? lose_zones.reduce((sum, price) => {
|
|
52892
|
+
return sum + Math.abs(price - current_entry) / current_entry;
|
|
52893
|
+
}, 0) / lose_zones.length : 0.02;
|
|
52894
|
+
return {
|
|
52895
|
+
win_probability: Math.max(0.1, Math.min(0.9, win_probability)),
|
|
52896
|
+
avg_win_ratio: Math.max(0.005, avg_win_ratio),
|
|
52897
|
+
avg_loss_ratio: Math.max(0.005, avg_loss_ratio)
|
|
52898
|
+
};
|
|
52899
|
+
}
|
|
52900
|
+
function calculateZoneVolatility(zone_prices) {
|
|
52901
|
+
if (zone_prices.length < 2)
|
|
52902
|
+
return 0;
|
|
52903
|
+
const price_changes = zone_prices.slice(1).map((price, i2) => Math.abs(price - zone_prices[i2]) / zone_prices[i2]);
|
|
52904
|
+
return price_changes.reduce((sum, change) => sum + change, 0) / price_changes.length;
|
|
52905
|
+
}
|
|
52906
|
+
|
|
52821
52907
|
// src/helpers/trade_signal.ts
|
|
52822
52908
|
function determine_close_price2({
|
|
52823
52909
|
entry,
|
|
@@ -52900,6 +52986,10 @@ class Signal {
|
|
|
52900
52986
|
first_order_size;
|
|
52901
52987
|
gap = 10;
|
|
52902
52988
|
max_size = 0;
|
|
52989
|
+
use_kelly = false;
|
|
52990
|
+
kelly_prediction_model = "exponential";
|
|
52991
|
+
kelly_confidence_factor = 0.6;
|
|
52992
|
+
kelly_minimum_risk = 0.2;
|
|
52903
52993
|
constructor({
|
|
52904
52994
|
focus,
|
|
52905
52995
|
budget,
|
|
@@ -52920,7 +53010,11 @@ class Signal {
|
|
|
52920
53010
|
minimum_size = 0,
|
|
52921
53011
|
first_order_size = 0,
|
|
52922
53012
|
gap = 10,
|
|
52923
|
-
max_size = 0
|
|
53013
|
+
max_size = 0,
|
|
53014
|
+
use_kelly = false,
|
|
53015
|
+
kelly_prediction_model = "exponential",
|
|
53016
|
+
kelly_confidence_factor = 0.6,
|
|
53017
|
+
kelly_minimum_risk = 0.2
|
|
52924
53018
|
}) {
|
|
52925
53019
|
this.minimum_size = minimum_size;
|
|
52926
53020
|
this.first_order_size = first_order_size;
|
|
@@ -52942,6 +53036,10 @@ class Signal {
|
|
|
52942
53036
|
this.increase_position = increase_position;
|
|
52943
53037
|
this.gap = gap;
|
|
52944
53038
|
this.max_size = max_size;
|
|
53039
|
+
this.use_kelly = use_kelly;
|
|
53040
|
+
this.kelly_prediction_model = kelly_prediction_model;
|
|
53041
|
+
this.kelly_confidence_factor = kelly_confidence_factor;
|
|
53042
|
+
this.kelly_minimum_risk = kelly_minimum_risk;
|
|
52945
53043
|
}
|
|
52946
53044
|
build_entry({
|
|
52947
53045
|
current_price,
|
|
@@ -53380,10 +53478,25 @@ class Signal {
|
|
|
53380
53478
|
}
|
|
53381
53479
|
const defaultStopLoss = i2 === 0 ? stop_loss : _base;
|
|
53382
53480
|
const new_stop = kind === "long" ? this.support : stop_loss;
|
|
53481
|
+
let risk_to_use = risk_per_trade;
|
|
53482
|
+
if (this.use_kelly) {
|
|
53483
|
+
const theoretical_kelly = calculateTheoreticalKelly({
|
|
53484
|
+
current_entry: x,
|
|
53485
|
+
zone_prices: limit_orders,
|
|
53486
|
+
kind,
|
|
53487
|
+
config: {
|
|
53488
|
+
price_prediction_model: this.kelly_prediction_model,
|
|
53489
|
+
confidence_factor: this.kelly_confidence_factor,
|
|
53490
|
+
volatility_adjustment: true
|
|
53491
|
+
}
|
|
53492
|
+
});
|
|
53493
|
+
risk_to_use = theoretical_kelly * risk_per_trade / this.kelly_minimum_risk;
|
|
53494
|
+
console.log({ risk_per_trade, theoretical_kelly });
|
|
53495
|
+
}
|
|
53383
53496
|
const y = this.build_trade_dict({
|
|
53384
53497
|
entry: x,
|
|
53385
53498
|
stop: (this.increase_position ? new_stop : defaultStopLoss) || defaultStopLoss,
|
|
53386
|
-
risk:
|
|
53499
|
+
risk: risk_to_use,
|
|
53387
53500
|
arr: limit_orders,
|
|
53388
53501
|
index: i2,
|
|
53389
53502
|
new_fees: total_incurred_market_fees,
|
|
@@ -53595,7 +53708,8 @@ function buildConfig(app_config, {
|
|
|
53595
53708
|
kind: app_config.kind,
|
|
53596
53709
|
gap,
|
|
53597
53710
|
min_profit: min_profit || app_config.min_profit,
|
|
53598
|
-
rr: rr || 1
|
|
53711
|
+
rr: rr || 1,
|
|
53712
|
+
use_kelly: app_config.use_kelly
|
|
53599
53713
|
};
|
|
53600
53714
|
const instance = new Signal(config2);
|
|
53601
53715
|
if (raw_instance) {
|
|
@@ -53607,6 +53721,7 @@ function buildConfig(app_config, {
|
|
|
53607
53721
|
const condition = (kind === "long" ? entry > app_config.support : entry >= app_config.support) && stop >= app_config.support * 0.999;
|
|
53608
53722
|
if (kind === "short") {
|
|
53609
53723
|
}
|
|
53724
|
+
console.log({ entry, stop, condition, working_risk, config: config2 });
|
|
53610
53725
|
const result = entry === stop ? [] : condition ? instance.build_entry({
|
|
53611
53726
|
current_price: entry,
|
|
53612
53727
|
stop_loss: stop,
|
|
@@ -58883,15 +58998,25 @@ class ExchangeAccount {
|
|
|
58883
58998
|
}
|
|
58884
58999
|
}
|
|
58885
59000
|
async increasePositionAtStop(payload) {
|
|
58886
|
-
const { symbol, kind, place = false } = payload;
|
|
59001
|
+
const { symbol, kind, place = false, increase = true } = payload;
|
|
58887
59002
|
const position2 = await this.syncAccount({
|
|
58888
59003
|
symbol,
|
|
58889
59004
|
kind,
|
|
58890
59005
|
as_view: true
|
|
58891
59006
|
});
|
|
58892
59007
|
console.log(position2);
|
|
58893
|
-
|
|
58894
|
-
|
|
59008
|
+
let price_params = {
|
|
59009
|
+
price: payload.price,
|
|
59010
|
+
quantity: payload.quantity
|
|
59011
|
+
};
|
|
59012
|
+
if (!payload.price && position2 && position2.stop_loss) {
|
|
59013
|
+
price_params.price = position2.stop_loss.price;
|
|
59014
|
+
}
|
|
59015
|
+
if (!payload.quantity && position2 && position2.stop_loss) {
|
|
59016
|
+
price_params.quantity = position2.stop_loss.quantity;
|
|
59017
|
+
}
|
|
59018
|
+
if (price_params.price && price_params.quantity) {
|
|
59019
|
+
const { price, quantity } = price_params;
|
|
58895
59020
|
const symbol_config = await this.recomputeSymbolConfig({
|
|
58896
59021
|
symbol
|
|
58897
59022
|
});
|
|
@@ -58911,7 +59036,7 @@ class ExchangeAccount {
|
|
|
58911
59036
|
quantity,
|
|
58912
59037
|
price_places,
|
|
58913
59038
|
decimal_places,
|
|
58914
|
-
increase
|
|
59039
|
+
increase,
|
|
58915
59040
|
place
|
|
58916
59041
|
});
|
|
58917
59042
|
}
|
package/dist/mcp-server.cjs
CHANGED
|
@@ -59565,6 +59565,92 @@ function createGapPairs(arr, gap, item) {
|
|
|
59565
59565
|
return result;
|
|
59566
59566
|
}
|
|
59567
59567
|
|
|
59568
|
+
// src/helpers/optimizations.ts
|
|
59569
|
+
function calculateTheoreticalKelly({
|
|
59570
|
+
current_entry,
|
|
59571
|
+
zone_prices,
|
|
59572
|
+
kind = "long",
|
|
59573
|
+
config: config2 = {}
|
|
59574
|
+
}) {
|
|
59575
|
+
const {
|
|
59576
|
+
price_prediction_model = "uniform",
|
|
59577
|
+
confidence_factor = 0.6,
|
|
59578
|
+
volatility_adjustment = true
|
|
59579
|
+
} = config2;
|
|
59580
|
+
const sorted_prices = kind === "long" ? zone_prices : zone_prices.reverse();
|
|
59581
|
+
const current_index = sorted_prices.findIndex((price) => price === current_entry);
|
|
59582
|
+
if (current_index === -1)
|
|
59583
|
+
return 0.02;
|
|
59584
|
+
const win_zones = kind === "long" ? sorted_prices.slice(current_index + 1) : sorted_prices.slice(0, current_index);
|
|
59585
|
+
const lose_zones = kind === "long" ? sorted_prices.slice(0, current_index) : sorted_prices.slice(current_index + 1);
|
|
59586
|
+
const { win_probability, avg_win_ratio, avg_loss_ratio } = calculateZoneProbabilities({
|
|
59587
|
+
current_entry,
|
|
59588
|
+
win_zones,
|
|
59589
|
+
lose_zones,
|
|
59590
|
+
price_prediction_model,
|
|
59591
|
+
confidence_factor,
|
|
59592
|
+
kind
|
|
59593
|
+
});
|
|
59594
|
+
const odds_ratio = avg_win_ratio / avg_loss_ratio;
|
|
59595
|
+
const loss_probability = 1 - win_probability;
|
|
59596
|
+
let kelly_fraction = (win_probability * odds_ratio - loss_probability) / odds_ratio;
|
|
59597
|
+
if (volatility_adjustment) {
|
|
59598
|
+
const zone_volatility = calculateZoneVolatility(sorted_prices);
|
|
59599
|
+
const vol_adjustment = 1 / (1 + zone_volatility);
|
|
59600
|
+
kelly_fraction *= vol_adjustment;
|
|
59601
|
+
}
|
|
59602
|
+
kelly_fraction = Math.max(0.005, Math.min(kelly_fraction, 0.5));
|
|
59603
|
+
return to_f2(kelly_fraction, "%.4f");
|
|
59604
|
+
}
|
|
59605
|
+
function calculateZoneProbabilities({
|
|
59606
|
+
current_entry,
|
|
59607
|
+
win_zones,
|
|
59608
|
+
lose_zones,
|
|
59609
|
+
price_prediction_model,
|
|
59610
|
+
confidence_factor,
|
|
59611
|
+
kind
|
|
59612
|
+
}) {
|
|
59613
|
+
if (win_zones.length === 0 && lose_zones.length === 0) {
|
|
59614
|
+
return { win_probability: 0.5, avg_win_ratio: 0.02, avg_loss_ratio: 0.02 };
|
|
59615
|
+
}
|
|
59616
|
+
let win_probability;
|
|
59617
|
+
switch (price_prediction_model) {
|
|
59618
|
+
case "uniform":
|
|
59619
|
+
win_probability = win_zones.length / (win_zones.length + lose_zones.length);
|
|
59620
|
+
break;
|
|
59621
|
+
case "normal":
|
|
59622
|
+
const win_weight = win_zones.reduce((sum, _, idx) => sum + 1 / (idx + 1), 0);
|
|
59623
|
+
const lose_weight = lose_zones.reduce((sum, _, idx) => sum + 1 / (idx + 1), 0);
|
|
59624
|
+
win_probability = win_weight / (win_weight + lose_weight);
|
|
59625
|
+
break;
|
|
59626
|
+
case "exponential":
|
|
59627
|
+
const exp_win_weight = win_zones.reduce((sum, _, idx) => sum + Math.exp(-idx * 0.5), 0);
|
|
59628
|
+
const exp_lose_weight = lose_zones.reduce((sum, _, idx) => sum + Math.exp(-idx * 0.5), 0);
|
|
59629
|
+
win_probability = exp_win_weight / (exp_win_weight + exp_lose_weight);
|
|
59630
|
+
break;
|
|
59631
|
+
default:
|
|
59632
|
+
win_probability = 0.5;
|
|
59633
|
+
}
|
|
59634
|
+
win_probability = win_probability * confidence_factor + (1 - confidence_factor) * 0.5;
|
|
59635
|
+
const avg_win_ratio = win_zones.length > 0 ? win_zones.reduce((sum, price) => {
|
|
59636
|
+
return sum + Math.abs(price - current_entry) / current_entry;
|
|
59637
|
+
}, 0) / win_zones.length : 0.02;
|
|
59638
|
+
const avg_loss_ratio = lose_zones.length > 0 ? lose_zones.reduce((sum, price) => {
|
|
59639
|
+
return sum + Math.abs(price - current_entry) / current_entry;
|
|
59640
|
+
}, 0) / lose_zones.length : 0.02;
|
|
59641
|
+
return {
|
|
59642
|
+
win_probability: Math.max(0.1, Math.min(0.9, win_probability)),
|
|
59643
|
+
avg_win_ratio: Math.max(0.005, avg_win_ratio),
|
|
59644
|
+
avg_loss_ratio: Math.max(0.005, avg_loss_ratio)
|
|
59645
|
+
};
|
|
59646
|
+
}
|
|
59647
|
+
function calculateZoneVolatility(zone_prices) {
|
|
59648
|
+
if (zone_prices.length < 2)
|
|
59649
|
+
return 0;
|
|
59650
|
+
const price_changes = zone_prices.slice(1).map((price, i2) => Math.abs(price - zone_prices[i2]) / zone_prices[i2]);
|
|
59651
|
+
return price_changes.reduce((sum, change) => sum + change, 0) / price_changes.length;
|
|
59652
|
+
}
|
|
59653
|
+
|
|
59568
59654
|
// src/helpers/trade_signal.ts
|
|
59569
59655
|
function determine_close_price2({
|
|
59570
59656
|
entry,
|
|
@@ -59647,6 +59733,10 @@ class Signal {
|
|
|
59647
59733
|
first_order_size;
|
|
59648
59734
|
gap = 10;
|
|
59649
59735
|
max_size = 0;
|
|
59736
|
+
use_kelly = false;
|
|
59737
|
+
kelly_prediction_model = "exponential";
|
|
59738
|
+
kelly_confidence_factor = 0.6;
|
|
59739
|
+
kelly_minimum_risk = 0.2;
|
|
59650
59740
|
constructor({
|
|
59651
59741
|
focus,
|
|
59652
59742
|
budget,
|
|
@@ -59667,7 +59757,11 @@ class Signal {
|
|
|
59667
59757
|
minimum_size = 0,
|
|
59668
59758
|
first_order_size = 0,
|
|
59669
59759
|
gap = 10,
|
|
59670
|
-
max_size = 0
|
|
59760
|
+
max_size = 0,
|
|
59761
|
+
use_kelly = false,
|
|
59762
|
+
kelly_prediction_model = "exponential",
|
|
59763
|
+
kelly_confidence_factor = 0.6,
|
|
59764
|
+
kelly_minimum_risk = 0.2
|
|
59671
59765
|
}) {
|
|
59672
59766
|
this.minimum_size = minimum_size;
|
|
59673
59767
|
this.first_order_size = first_order_size;
|
|
@@ -59689,6 +59783,10 @@ class Signal {
|
|
|
59689
59783
|
this.increase_position = increase_position;
|
|
59690
59784
|
this.gap = gap;
|
|
59691
59785
|
this.max_size = max_size;
|
|
59786
|
+
this.use_kelly = use_kelly;
|
|
59787
|
+
this.kelly_prediction_model = kelly_prediction_model;
|
|
59788
|
+
this.kelly_confidence_factor = kelly_confidence_factor;
|
|
59789
|
+
this.kelly_minimum_risk = kelly_minimum_risk;
|
|
59692
59790
|
}
|
|
59693
59791
|
build_entry({
|
|
59694
59792
|
current_price,
|
|
@@ -60127,10 +60225,25 @@ class Signal {
|
|
|
60127
60225
|
}
|
|
60128
60226
|
const defaultStopLoss = i2 === 0 ? stop_loss : _base;
|
|
60129
60227
|
const new_stop = kind === "long" ? this.support : stop_loss;
|
|
60228
|
+
let risk_to_use = risk_per_trade;
|
|
60229
|
+
if (this.use_kelly) {
|
|
60230
|
+
const theoretical_kelly = calculateTheoreticalKelly({
|
|
60231
|
+
current_entry: x,
|
|
60232
|
+
zone_prices: limit_orders,
|
|
60233
|
+
kind,
|
|
60234
|
+
config: {
|
|
60235
|
+
price_prediction_model: this.kelly_prediction_model,
|
|
60236
|
+
confidence_factor: this.kelly_confidence_factor,
|
|
60237
|
+
volatility_adjustment: true
|
|
60238
|
+
}
|
|
60239
|
+
});
|
|
60240
|
+
risk_to_use = theoretical_kelly * risk_per_trade / this.kelly_minimum_risk;
|
|
60241
|
+
console.log({ risk_per_trade, theoretical_kelly });
|
|
60242
|
+
}
|
|
60130
60243
|
const y = this.build_trade_dict({
|
|
60131
60244
|
entry: x,
|
|
60132
60245
|
stop: (this.increase_position ? new_stop : defaultStopLoss) || defaultStopLoss,
|
|
60133
|
-
risk:
|
|
60246
|
+
risk: risk_to_use,
|
|
60134
60247
|
arr: limit_orders,
|
|
60135
60248
|
index: i2,
|
|
60136
60249
|
new_fees: total_incurred_market_fees,
|
|
@@ -60342,7 +60455,8 @@ function buildConfig(app_config, {
|
|
|
60342
60455
|
kind: app_config.kind,
|
|
60343
60456
|
gap,
|
|
60344
60457
|
min_profit: min_profit || app_config.min_profit,
|
|
60345
|
-
rr: rr || 1
|
|
60458
|
+
rr: rr || 1,
|
|
60459
|
+
use_kelly: app_config.use_kelly
|
|
60346
60460
|
};
|
|
60347
60461
|
const instance = new Signal(config2);
|
|
60348
60462
|
if (raw_instance) {
|
|
@@ -60354,6 +60468,7 @@ function buildConfig(app_config, {
|
|
|
60354
60468
|
const condition = (kind === "long" ? entry > app_config.support : entry >= app_config.support) && stop >= app_config.support * 0.999;
|
|
60355
60469
|
if (kind === "short") {
|
|
60356
60470
|
}
|
|
60471
|
+
console.log({ entry, stop, condition, working_risk, config: config2 });
|
|
60357
60472
|
const result = entry === stop ? [] : condition ? instance.build_entry({
|
|
60358
60473
|
current_price: entry,
|
|
60359
60474
|
stop_loss: stop,
|
|
@@ -65611,15 +65726,25 @@ class ExchangeAccount {
|
|
|
65611
65726
|
}
|
|
65612
65727
|
}
|
|
65613
65728
|
async increasePositionAtStop(payload) {
|
|
65614
|
-
const { symbol, kind, place = false } = payload;
|
|
65729
|
+
const { symbol, kind, place = false, increase = true } = payload;
|
|
65615
65730
|
const position2 = await this.syncAccount({
|
|
65616
65731
|
symbol,
|
|
65617
65732
|
kind,
|
|
65618
65733
|
as_view: true
|
|
65619
65734
|
});
|
|
65620
65735
|
console.log(position2);
|
|
65621
|
-
|
|
65622
|
-
|
|
65736
|
+
let price_params = {
|
|
65737
|
+
price: payload.price,
|
|
65738
|
+
quantity: payload.quantity
|
|
65739
|
+
};
|
|
65740
|
+
if (!payload.price && position2 && position2.stop_loss) {
|
|
65741
|
+
price_params.price = position2.stop_loss.price;
|
|
65742
|
+
}
|
|
65743
|
+
if (!payload.quantity && position2 && position2.stop_loss) {
|
|
65744
|
+
price_params.quantity = position2.stop_loss.quantity;
|
|
65745
|
+
}
|
|
65746
|
+
if (price_params.price && price_params.quantity) {
|
|
65747
|
+
const { price, quantity } = price_params;
|
|
65623
65748
|
const symbol_config = await this.recomputeSymbolConfig({
|
|
65624
65749
|
symbol
|
|
65625
65750
|
});
|
|
@@ -65639,7 +65764,7 @@ class ExchangeAccount {
|
|
|
65639
65764
|
quantity,
|
|
65640
65765
|
price_places,
|
|
65641
65766
|
decimal_places,
|
|
65642
|
-
increase
|
|
65767
|
+
increase,
|
|
65643
65768
|
place
|
|
65644
65769
|
});
|
|
65645
65770
|
}
|
package/dist/mcp-server.js
CHANGED
|
@@ -59542,6 +59542,92 @@ function createGapPairs(arr, gap, item) {
|
|
|
59542
59542
|
return result;
|
|
59543
59543
|
}
|
|
59544
59544
|
|
|
59545
|
+
// src/helpers/optimizations.ts
|
|
59546
|
+
function calculateTheoreticalKelly({
|
|
59547
|
+
current_entry,
|
|
59548
|
+
zone_prices,
|
|
59549
|
+
kind = "long",
|
|
59550
|
+
config: config2 = {}
|
|
59551
|
+
}) {
|
|
59552
|
+
const {
|
|
59553
|
+
price_prediction_model = "uniform",
|
|
59554
|
+
confidence_factor = 0.6,
|
|
59555
|
+
volatility_adjustment = true
|
|
59556
|
+
} = config2;
|
|
59557
|
+
const sorted_prices = kind === "long" ? zone_prices : zone_prices.reverse();
|
|
59558
|
+
const current_index = sorted_prices.findIndex((price) => price === current_entry);
|
|
59559
|
+
if (current_index === -1)
|
|
59560
|
+
return 0.02;
|
|
59561
|
+
const win_zones = kind === "long" ? sorted_prices.slice(current_index + 1) : sorted_prices.slice(0, current_index);
|
|
59562
|
+
const lose_zones = kind === "long" ? sorted_prices.slice(0, current_index) : sorted_prices.slice(current_index + 1);
|
|
59563
|
+
const { win_probability, avg_win_ratio, avg_loss_ratio } = calculateZoneProbabilities({
|
|
59564
|
+
current_entry,
|
|
59565
|
+
win_zones,
|
|
59566
|
+
lose_zones,
|
|
59567
|
+
price_prediction_model,
|
|
59568
|
+
confidence_factor,
|
|
59569
|
+
kind
|
|
59570
|
+
});
|
|
59571
|
+
const odds_ratio = avg_win_ratio / avg_loss_ratio;
|
|
59572
|
+
const loss_probability = 1 - win_probability;
|
|
59573
|
+
let kelly_fraction = (win_probability * odds_ratio - loss_probability) / odds_ratio;
|
|
59574
|
+
if (volatility_adjustment) {
|
|
59575
|
+
const zone_volatility = calculateZoneVolatility(sorted_prices);
|
|
59576
|
+
const vol_adjustment = 1 / (1 + zone_volatility);
|
|
59577
|
+
kelly_fraction *= vol_adjustment;
|
|
59578
|
+
}
|
|
59579
|
+
kelly_fraction = Math.max(0.005, Math.min(kelly_fraction, 0.5));
|
|
59580
|
+
return to_f2(kelly_fraction, "%.4f");
|
|
59581
|
+
}
|
|
59582
|
+
function calculateZoneProbabilities({
|
|
59583
|
+
current_entry,
|
|
59584
|
+
win_zones,
|
|
59585
|
+
lose_zones,
|
|
59586
|
+
price_prediction_model,
|
|
59587
|
+
confidence_factor,
|
|
59588
|
+
kind
|
|
59589
|
+
}) {
|
|
59590
|
+
if (win_zones.length === 0 && lose_zones.length === 0) {
|
|
59591
|
+
return { win_probability: 0.5, avg_win_ratio: 0.02, avg_loss_ratio: 0.02 };
|
|
59592
|
+
}
|
|
59593
|
+
let win_probability;
|
|
59594
|
+
switch (price_prediction_model) {
|
|
59595
|
+
case "uniform":
|
|
59596
|
+
win_probability = win_zones.length / (win_zones.length + lose_zones.length);
|
|
59597
|
+
break;
|
|
59598
|
+
case "normal":
|
|
59599
|
+
const win_weight = win_zones.reduce((sum, _, idx) => sum + 1 / (idx + 1), 0);
|
|
59600
|
+
const lose_weight = lose_zones.reduce((sum, _, idx) => sum + 1 / (idx + 1), 0);
|
|
59601
|
+
win_probability = win_weight / (win_weight + lose_weight);
|
|
59602
|
+
break;
|
|
59603
|
+
case "exponential":
|
|
59604
|
+
const exp_win_weight = win_zones.reduce((sum, _, idx) => sum + Math.exp(-idx * 0.5), 0);
|
|
59605
|
+
const exp_lose_weight = lose_zones.reduce((sum, _, idx) => sum + Math.exp(-idx * 0.5), 0);
|
|
59606
|
+
win_probability = exp_win_weight / (exp_win_weight + exp_lose_weight);
|
|
59607
|
+
break;
|
|
59608
|
+
default:
|
|
59609
|
+
win_probability = 0.5;
|
|
59610
|
+
}
|
|
59611
|
+
win_probability = win_probability * confidence_factor + (1 - confidence_factor) * 0.5;
|
|
59612
|
+
const avg_win_ratio = win_zones.length > 0 ? win_zones.reduce((sum, price) => {
|
|
59613
|
+
return sum + Math.abs(price - current_entry) / current_entry;
|
|
59614
|
+
}, 0) / win_zones.length : 0.02;
|
|
59615
|
+
const avg_loss_ratio = lose_zones.length > 0 ? lose_zones.reduce((sum, price) => {
|
|
59616
|
+
return sum + Math.abs(price - current_entry) / current_entry;
|
|
59617
|
+
}, 0) / lose_zones.length : 0.02;
|
|
59618
|
+
return {
|
|
59619
|
+
win_probability: Math.max(0.1, Math.min(0.9, win_probability)),
|
|
59620
|
+
avg_win_ratio: Math.max(0.005, avg_win_ratio),
|
|
59621
|
+
avg_loss_ratio: Math.max(0.005, avg_loss_ratio)
|
|
59622
|
+
};
|
|
59623
|
+
}
|
|
59624
|
+
function calculateZoneVolatility(zone_prices) {
|
|
59625
|
+
if (zone_prices.length < 2)
|
|
59626
|
+
return 0;
|
|
59627
|
+
const price_changes = zone_prices.slice(1).map((price, i2) => Math.abs(price - zone_prices[i2]) / zone_prices[i2]);
|
|
59628
|
+
return price_changes.reduce((sum, change) => sum + change, 0) / price_changes.length;
|
|
59629
|
+
}
|
|
59630
|
+
|
|
59545
59631
|
// src/helpers/trade_signal.ts
|
|
59546
59632
|
function determine_close_price2({
|
|
59547
59633
|
entry,
|
|
@@ -59624,6 +59710,10 @@ class Signal {
|
|
|
59624
59710
|
first_order_size;
|
|
59625
59711
|
gap = 10;
|
|
59626
59712
|
max_size = 0;
|
|
59713
|
+
use_kelly = false;
|
|
59714
|
+
kelly_prediction_model = "exponential";
|
|
59715
|
+
kelly_confidence_factor = 0.6;
|
|
59716
|
+
kelly_minimum_risk = 0.2;
|
|
59627
59717
|
constructor({
|
|
59628
59718
|
focus,
|
|
59629
59719
|
budget,
|
|
@@ -59644,7 +59734,11 @@ class Signal {
|
|
|
59644
59734
|
minimum_size = 0,
|
|
59645
59735
|
first_order_size = 0,
|
|
59646
59736
|
gap = 10,
|
|
59647
|
-
max_size = 0
|
|
59737
|
+
max_size = 0,
|
|
59738
|
+
use_kelly = false,
|
|
59739
|
+
kelly_prediction_model = "exponential",
|
|
59740
|
+
kelly_confidence_factor = 0.6,
|
|
59741
|
+
kelly_minimum_risk = 0.2
|
|
59648
59742
|
}) {
|
|
59649
59743
|
this.minimum_size = minimum_size;
|
|
59650
59744
|
this.first_order_size = first_order_size;
|
|
@@ -59666,6 +59760,10 @@ class Signal {
|
|
|
59666
59760
|
this.increase_position = increase_position;
|
|
59667
59761
|
this.gap = gap;
|
|
59668
59762
|
this.max_size = max_size;
|
|
59763
|
+
this.use_kelly = use_kelly;
|
|
59764
|
+
this.kelly_prediction_model = kelly_prediction_model;
|
|
59765
|
+
this.kelly_confidence_factor = kelly_confidence_factor;
|
|
59766
|
+
this.kelly_minimum_risk = kelly_minimum_risk;
|
|
59669
59767
|
}
|
|
59670
59768
|
build_entry({
|
|
59671
59769
|
current_price,
|
|
@@ -60104,10 +60202,25 @@ class Signal {
|
|
|
60104
60202
|
}
|
|
60105
60203
|
const defaultStopLoss = i2 === 0 ? stop_loss : _base;
|
|
60106
60204
|
const new_stop = kind === "long" ? this.support : stop_loss;
|
|
60205
|
+
let risk_to_use = risk_per_trade;
|
|
60206
|
+
if (this.use_kelly) {
|
|
60207
|
+
const theoretical_kelly = calculateTheoreticalKelly({
|
|
60208
|
+
current_entry: x,
|
|
60209
|
+
zone_prices: limit_orders,
|
|
60210
|
+
kind,
|
|
60211
|
+
config: {
|
|
60212
|
+
price_prediction_model: this.kelly_prediction_model,
|
|
60213
|
+
confidence_factor: this.kelly_confidence_factor,
|
|
60214
|
+
volatility_adjustment: true
|
|
60215
|
+
}
|
|
60216
|
+
});
|
|
60217
|
+
risk_to_use = theoretical_kelly * risk_per_trade / this.kelly_minimum_risk;
|
|
60218
|
+
console.log({ risk_per_trade, theoretical_kelly });
|
|
60219
|
+
}
|
|
60107
60220
|
const y = this.build_trade_dict({
|
|
60108
60221
|
entry: x,
|
|
60109
60222
|
stop: (this.increase_position ? new_stop : defaultStopLoss) || defaultStopLoss,
|
|
60110
|
-
risk:
|
|
60223
|
+
risk: risk_to_use,
|
|
60111
60224
|
arr: limit_orders,
|
|
60112
60225
|
index: i2,
|
|
60113
60226
|
new_fees: total_incurred_market_fees,
|
|
@@ -60319,7 +60432,8 @@ function buildConfig(app_config, {
|
|
|
60319
60432
|
kind: app_config.kind,
|
|
60320
60433
|
gap,
|
|
60321
60434
|
min_profit: min_profit || app_config.min_profit,
|
|
60322
|
-
rr: rr || 1
|
|
60435
|
+
rr: rr || 1,
|
|
60436
|
+
use_kelly: app_config.use_kelly
|
|
60323
60437
|
};
|
|
60324
60438
|
const instance = new Signal(config2);
|
|
60325
60439
|
if (raw_instance) {
|
|
@@ -60331,6 +60445,7 @@ function buildConfig(app_config, {
|
|
|
60331
60445
|
const condition = (kind === "long" ? entry > app_config.support : entry >= app_config.support) && stop >= app_config.support * 0.999;
|
|
60332
60446
|
if (kind === "short") {
|
|
60333
60447
|
}
|
|
60448
|
+
console.log({ entry, stop, condition, working_risk, config: config2 });
|
|
60334
60449
|
const result = entry === stop ? [] : condition ? instance.build_entry({
|
|
60335
60450
|
current_price: entry,
|
|
60336
60451
|
stop_loss: stop,
|
|
@@ -65588,15 +65703,25 @@ class ExchangeAccount {
|
|
|
65588
65703
|
}
|
|
65589
65704
|
}
|
|
65590
65705
|
async increasePositionAtStop(payload) {
|
|
65591
|
-
const { symbol, kind, place = false } = payload;
|
|
65706
|
+
const { symbol, kind, place = false, increase = true } = payload;
|
|
65592
65707
|
const position2 = await this.syncAccount({
|
|
65593
65708
|
symbol,
|
|
65594
65709
|
kind,
|
|
65595
65710
|
as_view: true
|
|
65596
65711
|
});
|
|
65597
65712
|
console.log(position2);
|
|
65598
|
-
|
|
65599
|
-
|
|
65713
|
+
let price_params = {
|
|
65714
|
+
price: payload.price,
|
|
65715
|
+
quantity: payload.quantity
|
|
65716
|
+
};
|
|
65717
|
+
if (!payload.price && position2 && position2.stop_loss) {
|
|
65718
|
+
price_params.price = position2.stop_loss.price;
|
|
65719
|
+
}
|
|
65720
|
+
if (!payload.quantity && position2 && position2.stop_loss) {
|
|
65721
|
+
price_params.quantity = position2.stop_loss.quantity;
|
|
65722
|
+
}
|
|
65723
|
+
if (price_params.price && price_params.quantity) {
|
|
65724
|
+
const { price, quantity } = price_params;
|
|
65600
65725
|
const symbol_config = await this.recomputeSymbolConfig({
|
|
65601
65726
|
symbol
|
|
65602
65727
|
});
|
|
@@ -65616,7 +65741,7 @@ class ExchangeAccount {
|
|
|
65616
65741
|
quantity,
|
|
65617
65742
|
price_places,
|
|
65618
65743
|
decimal_places,
|
|
65619
|
-
increase
|
|
65744
|
+
increase,
|
|
65620
65745
|
place
|
|
65621
65746
|
});
|
|
65622
65747
|
}
|