@gbozee/ultimate 0.0.2-143 → 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 +147 -17
- package/dist/index.d.ts +19 -1
- package/dist/index.js +147 -17
- package/dist/mcp-server.cjs +147 -17
- package/dist/mcp-server.js +147 -17
- 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,
|
|
@@ -55256,16 +55371,18 @@ class BaseExchange {
|
|
|
55256
55371
|
price_places = "%.1f",
|
|
55257
55372
|
symbol
|
|
55258
55373
|
} = payload;
|
|
55374
|
+
const order_payload = orders.map((order) => ({
|
|
55375
|
+
...order,
|
|
55376
|
+
price: order.entry,
|
|
55377
|
+
kind,
|
|
55378
|
+
side: kind.toLowerCase() === "long" ? "buy" : "sell",
|
|
55379
|
+
stop: undefined
|
|
55380
|
+
}));
|
|
55259
55381
|
return await this._createLimitPurchaseOrders({
|
|
55260
55382
|
symbol,
|
|
55261
55383
|
price_places,
|
|
55262
55384
|
decimal_places,
|
|
55263
|
-
orders:
|
|
55264
|
-
...order,
|
|
55265
|
-
price: order.entry,
|
|
55266
|
-
kind,
|
|
55267
|
-
side: kind.toLowerCase() === "long" ? "buy" : "sell"
|
|
55268
|
-
}))
|
|
55385
|
+
orders: order_payload
|
|
55269
55386
|
});
|
|
55270
55387
|
}
|
|
55271
55388
|
async placeMarketOrder(payload) {
|
|
@@ -58933,15 +59050,25 @@ class ExchangeAccount {
|
|
|
58933
59050
|
}
|
|
58934
59051
|
}
|
|
58935
59052
|
async increasePositionAtStop(payload) {
|
|
58936
|
-
const { symbol, kind, place = false } = payload;
|
|
59053
|
+
const { symbol, kind, place = false, increase = true } = payload;
|
|
58937
59054
|
const position2 = await this.syncAccount({
|
|
58938
59055
|
symbol,
|
|
58939
59056
|
kind,
|
|
58940
59057
|
as_view: true
|
|
58941
59058
|
});
|
|
58942
59059
|
console.log(position2);
|
|
58943
|
-
|
|
58944
|
-
|
|
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;
|
|
58945
59072
|
const symbol_config = await this.recomputeSymbolConfig({
|
|
58946
59073
|
symbol
|
|
58947
59074
|
});
|
|
@@ -58961,7 +59088,7 @@ class ExchangeAccount {
|
|
|
58961
59088
|
quantity,
|
|
58962
59089
|
price_places,
|
|
58963
59090
|
decimal_places,
|
|
58964
|
-
increase
|
|
59091
|
+
increase,
|
|
58965
59092
|
place
|
|
58966
59093
|
});
|
|
58967
59094
|
}
|
|
@@ -58973,7 +59100,8 @@ class ExchangeAccount {
|
|
|
58973
59100
|
place = true,
|
|
58974
59101
|
stop,
|
|
58975
59102
|
use_current,
|
|
58976
|
-
ignore_config
|
|
59103
|
+
ignore_config,
|
|
59104
|
+
risky
|
|
58977
59105
|
} = payload;
|
|
58978
59106
|
const position2 = await this.syncAccount({
|
|
58979
59107
|
symbol,
|
|
@@ -58987,7 +59115,7 @@ class ExchangeAccount {
|
|
|
58987
59115
|
let condition = ignore_config ? true : position2?.config;
|
|
58988
59116
|
if (condition) {
|
|
58989
59117
|
let entry = payload.tp ? position2.entry || config2.entry : config2.entry;
|
|
58990
|
-
const v = stop ? "place_stop_orders" : "place_limit_orders";
|
|
59118
|
+
const v = stop ? "place_stop_orders" : risky ? "dangerous_entry_orders" : "place_limit_orders";
|
|
58991
59119
|
return await this.placeSharedOrder(v, {
|
|
58992
59120
|
symbol,
|
|
58993
59121
|
entry,
|
|
@@ -60010,7 +60138,8 @@ class ExchangeAccount {
|
|
|
60010
60138
|
cancel,
|
|
60011
60139
|
stop,
|
|
60012
60140
|
ignore_config,
|
|
60013
|
-
target_pnl
|
|
60141
|
+
target_pnl,
|
|
60142
|
+
risky
|
|
60014
60143
|
} = payload;
|
|
60015
60144
|
if (cancel) {
|
|
60016
60145
|
await this.cancelOrders({
|
|
@@ -60025,7 +60154,8 @@ class ExchangeAccount {
|
|
|
60025
60154
|
raw: payload.raw,
|
|
60026
60155
|
stop,
|
|
60027
60156
|
ignore_config,
|
|
60028
|
-
place
|
|
60157
|
+
place,
|
|
60158
|
+
risky
|
|
60029
60159
|
});
|
|
60030
60160
|
}
|
|
60031
60161
|
await this.syncAccount({
|
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;
|
|
@@ -2082,6 +2095,7 @@ declare class ExchangeAccount$1 {
|
|
|
2082
2095
|
stop?: boolean;
|
|
2083
2096
|
use_current?: boolean;
|
|
2084
2097
|
ignore_config?: boolean;
|
|
2098
|
+
risky?: boolean;
|
|
2085
2099
|
}): Promise<any>;
|
|
2086
2100
|
verifyStopLoss(payload: {
|
|
2087
2101
|
symbol: string;
|
|
@@ -2197,6 +2211,7 @@ declare class ExchangeAccount$1 {
|
|
|
2197
2211
|
rr?: number;
|
|
2198
2212
|
max_size?: number;
|
|
2199
2213
|
max_quantity?: number;
|
|
2214
|
+
use_kelly?: boolean;
|
|
2200
2215
|
}>;
|
|
2201
2216
|
runSimulation(payload: {
|
|
2202
2217
|
symbol: string;
|
|
@@ -2316,6 +2331,7 @@ declare class ExchangeAccount$1 {
|
|
|
2316
2331
|
cancel?: boolean;
|
|
2317
2332
|
ignore_config?: boolean;
|
|
2318
2333
|
target_pnl?: number;
|
|
2334
|
+
risky?: boolean;
|
|
2319
2335
|
}): Promise<any>;
|
|
2320
2336
|
updateConfigPnl(payload: {
|
|
2321
2337
|
symbol: string;
|
|
@@ -2376,6 +2392,7 @@ declare class ExchangeAccount$1 {
|
|
|
2376
2392
|
rr?: number;
|
|
2377
2393
|
max_size?: number;
|
|
2378
2394
|
max_quantity?: number;
|
|
2395
|
+
use_kelly?: boolean;
|
|
2379
2396
|
};
|
|
2380
2397
|
last_value: any;
|
|
2381
2398
|
config: {
|
|
@@ -2643,6 +2660,7 @@ declare class App {
|
|
|
2643
2660
|
rr?: number;
|
|
2644
2661
|
max_size?: number;
|
|
2645
2662
|
max_quantity?: number;
|
|
2663
|
+
use_kelly?: boolean;
|
|
2646
2664
|
};
|
|
2647
2665
|
last_value: any;
|
|
2648
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,
|
|
@@ -55204,16 +55319,18 @@ class BaseExchange {
|
|
|
55204
55319
|
price_places = "%.1f",
|
|
55205
55320
|
symbol
|
|
55206
55321
|
} = payload;
|
|
55322
|
+
const order_payload = orders.map((order) => ({
|
|
55323
|
+
...order,
|
|
55324
|
+
price: order.entry,
|
|
55325
|
+
kind,
|
|
55326
|
+
side: kind.toLowerCase() === "long" ? "buy" : "sell",
|
|
55327
|
+
stop: undefined
|
|
55328
|
+
}));
|
|
55207
55329
|
return await this._createLimitPurchaseOrders({
|
|
55208
55330
|
symbol,
|
|
55209
55331
|
price_places,
|
|
55210
55332
|
decimal_places,
|
|
55211
|
-
orders:
|
|
55212
|
-
...order,
|
|
55213
|
-
price: order.entry,
|
|
55214
|
-
kind,
|
|
55215
|
-
side: kind.toLowerCase() === "long" ? "buy" : "sell"
|
|
55216
|
-
}))
|
|
55333
|
+
orders: order_payload
|
|
55217
55334
|
});
|
|
55218
55335
|
}
|
|
55219
55336
|
async placeMarketOrder(payload) {
|
|
@@ -58881,15 +58998,25 @@ class ExchangeAccount {
|
|
|
58881
58998
|
}
|
|
58882
58999
|
}
|
|
58883
59000
|
async increasePositionAtStop(payload) {
|
|
58884
|
-
const { symbol, kind, place = false } = payload;
|
|
59001
|
+
const { symbol, kind, place = false, increase = true } = payload;
|
|
58885
59002
|
const position2 = await this.syncAccount({
|
|
58886
59003
|
symbol,
|
|
58887
59004
|
kind,
|
|
58888
59005
|
as_view: true
|
|
58889
59006
|
});
|
|
58890
59007
|
console.log(position2);
|
|
58891
|
-
|
|
58892
|
-
|
|
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;
|
|
58893
59020
|
const symbol_config = await this.recomputeSymbolConfig({
|
|
58894
59021
|
symbol
|
|
58895
59022
|
});
|
|
@@ -58909,7 +59036,7 @@ class ExchangeAccount {
|
|
|
58909
59036
|
quantity,
|
|
58910
59037
|
price_places,
|
|
58911
59038
|
decimal_places,
|
|
58912
|
-
increase
|
|
59039
|
+
increase,
|
|
58913
59040
|
place
|
|
58914
59041
|
});
|
|
58915
59042
|
}
|
|
@@ -58921,7 +59048,8 @@ class ExchangeAccount {
|
|
|
58921
59048
|
place = true,
|
|
58922
59049
|
stop,
|
|
58923
59050
|
use_current,
|
|
58924
|
-
ignore_config
|
|
59051
|
+
ignore_config,
|
|
59052
|
+
risky
|
|
58925
59053
|
} = payload;
|
|
58926
59054
|
const position2 = await this.syncAccount({
|
|
58927
59055
|
symbol,
|
|
@@ -58935,7 +59063,7 @@ class ExchangeAccount {
|
|
|
58935
59063
|
let condition = ignore_config ? true : position2?.config;
|
|
58936
59064
|
if (condition) {
|
|
58937
59065
|
let entry = payload.tp ? position2.entry || config2.entry : config2.entry;
|
|
58938
|
-
const v = stop ? "place_stop_orders" : "place_limit_orders";
|
|
59066
|
+
const v = stop ? "place_stop_orders" : risky ? "dangerous_entry_orders" : "place_limit_orders";
|
|
58939
59067
|
return await this.placeSharedOrder(v, {
|
|
58940
59068
|
symbol,
|
|
58941
59069
|
entry,
|
|
@@ -59958,7 +60086,8 @@ class ExchangeAccount {
|
|
|
59958
60086
|
cancel,
|
|
59959
60087
|
stop,
|
|
59960
60088
|
ignore_config,
|
|
59961
|
-
target_pnl
|
|
60089
|
+
target_pnl,
|
|
60090
|
+
risky
|
|
59962
60091
|
} = payload;
|
|
59963
60092
|
if (cancel) {
|
|
59964
60093
|
await this.cancelOrders({
|
|
@@ -59973,7 +60102,8 @@ class ExchangeAccount {
|
|
|
59973
60102
|
raw: payload.raw,
|
|
59974
60103
|
stop,
|
|
59975
60104
|
ignore_config,
|
|
59976
|
-
place
|
|
60105
|
+
place,
|
|
60106
|
+
risky
|
|
59977
60107
|
});
|
|
59978
60108
|
}
|
|
59979
60109
|
await this.syncAccount({
|
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,
|
|
@@ -61932,16 +62047,18 @@ class BaseExchange {
|
|
|
61932
62047
|
price_places = "%.1f",
|
|
61933
62048
|
symbol
|
|
61934
62049
|
} = payload;
|
|
62050
|
+
const order_payload = orders.map((order) => ({
|
|
62051
|
+
...order,
|
|
62052
|
+
price: order.entry,
|
|
62053
|
+
kind,
|
|
62054
|
+
side: kind.toLowerCase() === "long" ? "buy" : "sell",
|
|
62055
|
+
stop: undefined
|
|
62056
|
+
}));
|
|
61935
62057
|
return await this._createLimitPurchaseOrders({
|
|
61936
62058
|
symbol,
|
|
61937
62059
|
price_places,
|
|
61938
62060
|
decimal_places,
|
|
61939
|
-
orders:
|
|
61940
|
-
...order,
|
|
61941
|
-
price: order.entry,
|
|
61942
|
-
kind,
|
|
61943
|
-
side: kind.toLowerCase() === "long" ? "buy" : "sell"
|
|
61944
|
-
}))
|
|
62061
|
+
orders: order_payload
|
|
61945
62062
|
});
|
|
61946
62063
|
}
|
|
61947
62064
|
async placeMarketOrder(payload) {
|
|
@@ -65609,15 +65726,25 @@ class ExchangeAccount {
|
|
|
65609
65726
|
}
|
|
65610
65727
|
}
|
|
65611
65728
|
async increasePositionAtStop(payload) {
|
|
65612
|
-
const { symbol, kind, place = false } = payload;
|
|
65729
|
+
const { symbol, kind, place = false, increase = true } = payload;
|
|
65613
65730
|
const position2 = await this.syncAccount({
|
|
65614
65731
|
symbol,
|
|
65615
65732
|
kind,
|
|
65616
65733
|
as_view: true
|
|
65617
65734
|
});
|
|
65618
65735
|
console.log(position2);
|
|
65619
|
-
|
|
65620
|
-
|
|
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;
|
|
65621
65748
|
const symbol_config = await this.recomputeSymbolConfig({
|
|
65622
65749
|
symbol
|
|
65623
65750
|
});
|
|
@@ -65637,7 +65764,7 @@ class ExchangeAccount {
|
|
|
65637
65764
|
quantity,
|
|
65638
65765
|
price_places,
|
|
65639
65766
|
decimal_places,
|
|
65640
|
-
increase
|
|
65767
|
+
increase,
|
|
65641
65768
|
place
|
|
65642
65769
|
});
|
|
65643
65770
|
}
|
|
@@ -65649,7 +65776,8 @@ class ExchangeAccount {
|
|
|
65649
65776
|
place = true,
|
|
65650
65777
|
stop,
|
|
65651
65778
|
use_current,
|
|
65652
|
-
ignore_config
|
|
65779
|
+
ignore_config,
|
|
65780
|
+
risky
|
|
65653
65781
|
} = payload;
|
|
65654
65782
|
const position2 = await this.syncAccount({
|
|
65655
65783
|
symbol,
|
|
@@ -65663,7 +65791,7 @@ class ExchangeAccount {
|
|
|
65663
65791
|
let condition = ignore_config ? true : position2?.config;
|
|
65664
65792
|
if (condition) {
|
|
65665
65793
|
let entry = payload.tp ? position2.entry || config2.entry : config2.entry;
|
|
65666
|
-
const v = stop ? "place_stop_orders" : "place_limit_orders";
|
|
65794
|
+
const v = stop ? "place_stop_orders" : risky ? "dangerous_entry_orders" : "place_limit_orders";
|
|
65667
65795
|
return await this.placeSharedOrder(v, {
|
|
65668
65796
|
symbol,
|
|
65669
65797
|
entry,
|
|
@@ -66686,7 +66814,8 @@ class ExchangeAccount {
|
|
|
66686
66814
|
cancel,
|
|
66687
66815
|
stop,
|
|
66688
66816
|
ignore_config,
|
|
66689
|
-
target_pnl
|
|
66817
|
+
target_pnl,
|
|
66818
|
+
risky
|
|
66690
66819
|
} = payload;
|
|
66691
66820
|
if (cancel) {
|
|
66692
66821
|
await this.cancelOrders({
|
|
@@ -66701,7 +66830,8 @@ class ExchangeAccount {
|
|
|
66701
66830
|
raw: payload.raw,
|
|
66702
66831
|
stop,
|
|
66703
66832
|
ignore_config,
|
|
66704
|
-
place
|
|
66833
|
+
place,
|
|
66834
|
+
risky
|
|
66705
66835
|
});
|
|
66706
66836
|
}
|
|
66707
66837
|
await this.syncAccount({
|
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,
|
|
@@ -61909,16 +62024,18 @@ class BaseExchange {
|
|
|
61909
62024
|
price_places = "%.1f",
|
|
61910
62025
|
symbol
|
|
61911
62026
|
} = payload;
|
|
62027
|
+
const order_payload = orders.map((order) => ({
|
|
62028
|
+
...order,
|
|
62029
|
+
price: order.entry,
|
|
62030
|
+
kind,
|
|
62031
|
+
side: kind.toLowerCase() === "long" ? "buy" : "sell",
|
|
62032
|
+
stop: undefined
|
|
62033
|
+
}));
|
|
61912
62034
|
return await this._createLimitPurchaseOrders({
|
|
61913
62035
|
symbol,
|
|
61914
62036
|
price_places,
|
|
61915
62037
|
decimal_places,
|
|
61916
|
-
orders:
|
|
61917
|
-
...order,
|
|
61918
|
-
price: order.entry,
|
|
61919
|
-
kind,
|
|
61920
|
-
side: kind.toLowerCase() === "long" ? "buy" : "sell"
|
|
61921
|
-
}))
|
|
62038
|
+
orders: order_payload
|
|
61922
62039
|
});
|
|
61923
62040
|
}
|
|
61924
62041
|
async placeMarketOrder(payload) {
|
|
@@ -65586,15 +65703,25 @@ class ExchangeAccount {
|
|
|
65586
65703
|
}
|
|
65587
65704
|
}
|
|
65588
65705
|
async increasePositionAtStop(payload) {
|
|
65589
|
-
const { symbol, kind, place = false } = payload;
|
|
65706
|
+
const { symbol, kind, place = false, increase = true } = payload;
|
|
65590
65707
|
const position2 = await this.syncAccount({
|
|
65591
65708
|
symbol,
|
|
65592
65709
|
kind,
|
|
65593
65710
|
as_view: true
|
|
65594
65711
|
});
|
|
65595
65712
|
console.log(position2);
|
|
65596
|
-
|
|
65597
|
-
|
|
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;
|
|
65598
65725
|
const symbol_config = await this.recomputeSymbolConfig({
|
|
65599
65726
|
symbol
|
|
65600
65727
|
});
|
|
@@ -65614,7 +65741,7 @@ class ExchangeAccount {
|
|
|
65614
65741
|
quantity,
|
|
65615
65742
|
price_places,
|
|
65616
65743
|
decimal_places,
|
|
65617
|
-
increase
|
|
65744
|
+
increase,
|
|
65618
65745
|
place
|
|
65619
65746
|
});
|
|
65620
65747
|
}
|
|
@@ -65626,7 +65753,8 @@ class ExchangeAccount {
|
|
|
65626
65753
|
place = true,
|
|
65627
65754
|
stop,
|
|
65628
65755
|
use_current,
|
|
65629
|
-
ignore_config
|
|
65756
|
+
ignore_config,
|
|
65757
|
+
risky
|
|
65630
65758
|
} = payload;
|
|
65631
65759
|
const position2 = await this.syncAccount({
|
|
65632
65760
|
symbol,
|
|
@@ -65640,7 +65768,7 @@ class ExchangeAccount {
|
|
|
65640
65768
|
let condition = ignore_config ? true : position2?.config;
|
|
65641
65769
|
if (condition) {
|
|
65642
65770
|
let entry = payload.tp ? position2.entry || config2.entry : config2.entry;
|
|
65643
|
-
const v = stop ? "place_stop_orders" : "place_limit_orders";
|
|
65771
|
+
const v = stop ? "place_stop_orders" : risky ? "dangerous_entry_orders" : "place_limit_orders";
|
|
65644
65772
|
return await this.placeSharedOrder(v, {
|
|
65645
65773
|
symbol,
|
|
65646
65774
|
entry,
|
|
@@ -66663,7 +66791,8 @@ class ExchangeAccount {
|
|
|
66663
66791
|
cancel,
|
|
66664
66792
|
stop,
|
|
66665
66793
|
ignore_config,
|
|
66666
|
-
target_pnl
|
|
66794
|
+
target_pnl,
|
|
66795
|
+
risky
|
|
66667
66796
|
} = payload;
|
|
66668
66797
|
if (cancel) {
|
|
66669
66798
|
await this.cancelOrders({
|
|
@@ -66678,7 +66807,8 @@ class ExchangeAccount {
|
|
|
66678
66807
|
raw: payload.raw,
|
|
66679
66808
|
stop,
|
|
66680
66809
|
ignore_config,
|
|
66681
|
-
place
|
|
66810
|
+
place,
|
|
66811
|
+
risky
|
|
66682
66812
|
});
|
|
66683
66813
|
}
|
|
66684
66814
|
await this.syncAccount({
|