@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.
@@ -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
- 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, }: SignalConfigType);
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;
@@ -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: risk_per_trade,
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: risk_per_trade,
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: orders.map((order) => ({
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
- if (position2 && position2.stop_loss) {
58944
- const { price, quantity } = position2.stop_loss;
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: true,
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
- 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, }: SignalConfigType);
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: risk_per_trade,
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: orders.map((order) => ({
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
- if (position2 && position2.stop_loss) {
58892
- const { price, quantity } = position2.stop_loss;
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: true,
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({
@@ -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: risk_per_trade,
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: orders.map((order) => ({
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
- if (position2 && position2.stop_loss) {
65620
- const { price, quantity } = position2.stop_loss;
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: true,
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({
@@ -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: risk_per_trade,
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: orders.map((order) => ({
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
- if (position2 && position2.stop_loss) {
65597
- const { price, quantity } = position2.stop_loss;
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: true,
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({
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gbozee/ultimate",
3
3
  "type": "module",
4
- "version": "0.0.2-143",
4
+ "version": "0.0.2-145",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",