@gbozee/ultimate 0.0.2-73 → 0.0.2-75

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.
@@ -402,6 +402,19 @@ export type StrategyPosition = {
402
402
  entry: number;
403
403
  quantity: number;
404
404
  };
405
+ export type GapCloserResult = {
406
+ avg_entry: number;
407
+ avg_size: number;
408
+ loss: number;
409
+ stop: number;
410
+ stop_quantity: number;
411
+ re_entry_quantity: number;
412
+ initial_pnl: number;
413
+ tp: number;
414
+ incurred_loss: number;
415
+ pnl: number;
416
+ remaining_quantity: number;
417
+ };
405
418
  export type Config = {
406
419
  tp_percent: number;
407
420
  short_tp_factor: number;
@@ -438,25 +451,54 @@ export declare class Strategy {
438
451
  get short_tp(): number;
439
452
  generateGapClosingAlgorithm(payload: {
440
453
  kind: "long" | "short";
454
+ ignore_entries?: boolean;
455
+ reduce_ratio?: number;
456
+ sell_factor?: number;
441
457
  }): {
442
- [x: string]: any;
443
- risk: number;
444
- risk_reward: number;
445
458
  last_entry: any;
446
459
  first_entry: any;
447
460
  threshold: any;
461
+ risk: number;
462
+ risk_reward: number;
463
+ spread: number;
464
+ gap_loss: number;
465
+ net_profit: number;
466
+ long: GapCloserResult;
467
+ short: GapCloserResult;
468
+ };
469
+ gapCloserHelper(payload: {
470
+ risk: number;
471
+ entries?: any[];
472
+ kind: "long" | "short";
473
+ sell_factor?: number;
474
+ reduce_ratio?: number;
475
+ }): {
476
+ risk: number;
477
+ risk_reward: number;
478
+ spread: number;
479
+ gap_loss: number;
480
+ net_profit: number;
481
+ long: GapCloserResult;
482
+ short: GapCloserResult;
448
483
  };
449
484
  runIterations(payload: {
450
485
  kind: "long" | "short";
451
486
  iterations: number;
452
487
  risk_reward?: number;
488
+ ignore_entries?: boolean;
489
+ reduce_ratio?: number;
490
+ sell_factor?: number;
453
491
  }): {
454
- [x: string]: any;
455
- risk: number;
456
- risk_reward: number;
457
492
  last_entry: any;
458
493
  first_entry: any;
459
494
  threshold: any;
495
+ risk: number;
496
+ risk_reward: number;
497
+ spread: number;
498
+ gap_loss: number;
499
+ net_profit: number;
500
+ long: GapCloserResult;
501
+ short: GapCloserResult;
460
502
  }[];
461
503
  getPositionAfterTp(payload: {
462
504
  kind: "long" | "short";
@@ -1830,16 +1830,18 @@ class Strategy {
1830
1830
  return this.tp("short");
1831
1831
  }
1832
1832
  generateGapClosingAlgorithm(payload) {
1833
- const { kind } = payload;
1833
+ const {
1834
+ kind,
1835
+ ignore_entries = false,
1836
+ reduce_ratio = 1,
1837
+ sell_factor = 1
1838
+ } = payload;
1834
1839
  const { entry, quantity } = this.position[kind];
1835
1840
  const focus_position = this.position[kind];
1836
1841
  const reverse_kind = kind == "long" ? "short" : "long";
1837
1842
  const reverse_position = this.position[reverse_kind];
1838
1843
  let _entry = this.tp(kind);
1839
1844
  let _stop = this.tp(reverse_kind);
1840
- if (!_entry && !_stop) {
1841
- return null;
1842
- }
1843
1845
  const second_payload = {
1844
1846
  entry: _entry,
1845
1847
  stop: _stop,
@@ -1854,11 +1856,60 @@ class Strategy {
1854
1856
  };
1855
1857
  console.log({ second_payload, third_payload });
1856
1858
  const app_config = generateOptimumAppConfig(this.config.global_config, second_payload, third_payload);
1857
- if (!app_config) {
1858
- return null;
1859
+ let entries = [];
1860
+ let risk_per_trade = this.config.budget;
1861
+ let last_value = null;
1862
+ if (app_config) {
1863
+ let { entries: _entries, ...rest } = app_config;
1864
+ entries = _entries;
1865
+ risk_per_trade = rest.risk_per_trade;
1866
+ last_value = rest.last_value;
1867
+ if (ignore_entries) {
1868
+ entries = [];
1869
+ }
1859
1870
  }
1860
- const { entries, ...rest } = app_config;
1861
- const risk = this.to_f(rest.risk_per_trade);
1871
+ const risk = this.to_f(risk_per_trade);
1872
+ let below_reverse_entries = kind === "long" ? entries.filter((u) => {
1873
+ return u.entry < (reverse_position.entry || focus_position.entry);
1874
+ }) : entries.filter((u) => {
1875
+ return u.entry > (reverse_position.entry || focus_position.entry);
1876
+ });
1877
+ const threshold = below_reverse_entries.at(-1);
1878
+ const result = this.gapCloserHelper({
1879
+ risk,
1880
+ entries,
1881
+ kind,
1882
+ sell_factor,
1883
+ reduce_ratio
1884
+ });
1885
+ return {
1886
+ ...result,
1887
+ last_entry: last_value?.entry,
1888
+ first_entry: entries.at(-1)?.entry,
1889
+ threshold
1890
+ };
1891
+ }
1892
+ gapCloserHelper(payload) {
1893
+ const {
1894
+ risk,
1895
+ entries = [],
1896
+ kind,
1897
+ sell_factor = 1,
1898
+ reduce_ratio = 1
1899
+ } = payload;
1900
+ const { entry, quantity } = this.position[kind];
1901
+ const focus_position = this.position[kind];
1902
+ const reverse_kind = kind == "long" ? "short" : "long";
1903
+ const reverse_position = this.position[reverse_kind];
1904
+ let _entry = this.tp(kind);
1905
+ let _stop = this.tp(reverse_kind);
1906
+ const second_payload = {
1907
+ entry: _entry,
1908
+ stop: _stop,
1909
+ risk_reward: this.config.risk_reward,
1910
+ start_risk: this.pnl(reverse_kind),
1911
+ max_risk: this.config.budget
1912
+ };
1862
1913
  const adjusted_focus_entries = entries.map((entry2) => {
1863
1914
  let adjusted_price = entry2.price;
1864
1915
  if (focus_position.quantity > 0) {
@@ -1878,8 +1929,8 @@ class Strategy {
1878
1929
  price: entry,
1879
1930
  quantity
1880
1931
  }
1881
- ]), rest.decimal_places, rest.price_places);
1882
- const focus_loss = this.to_f((avg.price - second_payload.stop) * avg.quantity);
1932
+ ]), this.config.global_config.decimal_places, this.config.global_config.price_places);
1933
+ const focus_loss = this.to_f(Math.abs(avg.price - second_payload.stop) * avg.quantity);
1883
1934
  let below_reverse_entries = kind === "long" ? entries.filter((u) => {
1884
1935
  return u.entry < (reverse_position.entry || focus_position.entry);
1885
1936
  }) : entries.filter((u) => {
@@ -1905,19 +1956,21 @@ class Strategy {
1905
1956
  price: reverse_position.entry,
1906
1957
  quantity: reverse_position.quantity
1907
1958
  }
1908
- ]), rest.decimal_places, rest.price_places);
1909
- const reverse_pnl = this.to_f((reverse_avg.price - second_payload.stop) * reverse_avg.quantity);
1959
+ ]), this.config.global_config.decimal_places, this.config.global_config.price_places);
1960
+ const sell_quantity = this.to_df(reverse_avg.quantity * sell_factor);
1961
+ const reverse_pnl = this.to_f(Math.abs(reverse_avg.price - second_payload.stop) * sell_quantity);
1910
1962
  const fee_to_pay = this.calculate_fee({
1911
1963
  price: avg.entry,
1912
1964
  quantity: avg.quantity
1913
1965
  }) + this.calculate_fee({
1914
1966
  price: reverse_avg.entry,
1915
- quantity: reverse_avg.quantity
1967
+ quantity: sell_quantity
1916
1968
  });
1917
1969
  const net_reverse_pnl = reverse_pnl - fee_to_pay;
1918
- const ratio = net_reverse_pnl / focus_loss;
1970
+ const ratio = net_reverse_pnl * reduce_ratio / focus_loss;
1919
1971
  const quantity_to_sell = this.to_df(ratio * avg.quantity);
1920
1972
  const remaining_quantity = this.to_df(avg.quantity - quantity_to_sell);
1973
+ const incurred_loss = this.to_f((avg.price - second_payload.stop) * quantity_to_sell);
1921
1974
  return {
1922
1975
  risk,
1923
1976
  risk_reward: this.config.risk_reward,
@@ -1929,7 +1982,8 @@ class Strategy {
1929
1982
  stop_quantity: quantity_to_sell,
1930
1983
  re_entry_quantity: remaining_quantity,
1931
1984
  initial_pnl: this.pnl(kind),
1932
- tp: second_payload.entry
1985
+ tp: second_payload.entry,
1986
+ incurred_loss
1933
1987
  },
1934
1988
  [reverse_kind]: {
1935
1989
  avg_entry: reverse_avg.entry,
@@ -1937,32 +1991,45 @@ class Strategy {
1937
1991
  pnl: reverse_pnl,
1938
1992
  tp: second_payload.stop,
1939
1993
  re_entry_quantity: remaining_quantity,
1940
- initial_pnl: this.pnl(reverse_kind)
1994
+ initial_pnl: this.pnl(reverse_kind),
1995
+ remaining_quantity: this.to_df(reverse_avg.quantity - sell_quantity)
1941
1996
  },
1942
- last_entry: rest.last_value.entry,
1943
- first_entry: entries.at(-1).entry,
1944
- threshold
1997
+ spread: Math.abs(avg.entry - reverse_avg.entry),
1998
+ gap_loss: to_f(Math.abs(avg.entry - reverse_avg.entry) * reverse_avg.quantity, "%.2f"),
1999
+ net_profit: incurred_loss + reverse_pnl
1945
2000
  };
1946
2001
  }
1947
2002
  runIterations(payload) {
1948
- const { kind, iterations } = payload;
2003
+ const {
2004
+ kind,
2005
+ iterations,
2006
+ ignore_entries = false,
2007
+ reduce_ratio = 1,
2008
+ sell_factor = 1
2009
+ } = payload;
1949
2010
  const reverse_kind = kind == "long" ? "short" : "long";
1950
2011
  const result = [];
1951
2012
  let position2 = {
1952
2013
  long: this.position.long,
1953
2014
  short: this.position.short
1954
2015
  };
2016
+ let tp_percent_multiplier = 1;
2017
+ let short_tp_factor_multiplier = 1;
1955
2018
  for (let i = 0;i < iterations; i++) {
1956
2019
  const instance = new Strategy({
1957
2020
  long: position2.long,
1958
2021
  short: position2.short,
1959
2022
  config: {
1960
2023
  ...this.config,
1961
- tp_percent: this.config.tp_percent + i * 4
2024
+ tp_percent: this.config.tp_percent * tp_percent_multiplier,
2025
+ short_tp_factor: this.config.short_tp_factor * short_tp_factor_multiplier
1962
2026
  }
1963
2027
  });
1964
2028
  const algorithm = instance.generateGapClosingAlgorithm({
1965
- kind
2029
+ kind,
2030
+ ignore_entries,
2031
+ reduce_ratio,
2032
+ sell_factor
1966
2033
  });
1967
2034
  if (!algorithm) {
1968
2035
  console.log("No algorithm found");
@@ -1974,9 +2041,31 @@ class Strategy {
1974
2041
  entry: algorithm[kind].avg_entry,
1975
2042
  quantity: algorithm[kind].re_entry_quantity
1976
2043
  };
2044
+ let reverse_entry = algorithm[reverse_kind].tp;
2045
+ let reverse_quantity = algorithm[reverse_kind].re_entry_quantity;
2046
+ if (algorithm[reverse_kind].remaining_quantity > 0) {
2047
+ const purchase_to_occur = {
2048
+ price: reverse_entry,
2049
+ quantity: algorithm[reverse_kind].remaining_quantity
2050
+ };
2051
+ const avg = determine_average_entry_and_size([
2052
+ purchase_to_occur,
2053
+ {
2054
+ price: algorithm[reverse_kind].avg_entry,
2055
+ quantity: reverse_quantity - algorithm[reverse_kind].remaining_quantity
2056
+ }
2057
+ ], this.config.global_config.decimal_places, this.config.global_config.price_places);
2058
+ reverse_entry = avg.entry;
2059
+ reverse_quantity = avg.quantity;
2060
+ if (reverse_kind === "short") {
2061
+ short_tp_factor_multiplier = 2;
2062
+ } else {
2063
+ tp_percent_multiplier = 2;
2064
+ }
2065
+ }
1977
2066
  position2[reverse_kind] = {
1978
- entry: algorithm[reverse_kind].tp,
1979
- quantity: algorithm[reverse_kind].re_entry_quantity
2067
+ entry: reverse_entry,
2068
+ quantity: reverse_quantity
1980
2069
  };
1981
2070
  }
1982
2071
  return result;
package/dist/index.cjs CHANGED
@@ -53890,16 +53890,18 @@ class Strategy {
53890
53890
  return this.tp("short");
53891
53891
  }
53892
53892
  generateGapClosingAlgorithm(payload) {
53893
- const { kind } = payload;
53893
+ const {
53894
+ kind,
53895
+ ignore_entries = false,
53896
+ reduce_ratio = 1,
53897
+ sell_factor = 1
53898
+ } = payload;
53894
53899
  const { entry, quantity } = this.position[kind];
53895
53900
  const focus_position = this.position[kind];
53896
53901
  const reverse_kind = kind == "long" ? "short" : "long";
53897
53902
  const reverse_position = this.position[reverse_kind];
53898
53903
  let _entry = this.tp(kind);
53899
53904
  let _stop = this.tp(reverse_kind);
53900
- if (!_entry && !_stop) {
53901
- return null;
53902
- }
53903
53905
  const second_payload = {
53904
53906
  entry: _entry,
53905
53907
  stop: _stop,
@@ -53914,11 +53916,60 @@ class Strategy {
53914
53916
  };
53915
53917
  console.log({ second_payload, third_payload });
53916
53918
  const app_config = generateOptimumAppConfig(this.config.global_config, second_payload, third_payload);
53917
- if (!app_config) {
53918
- return null;
53919
- }
53920
- const { entries, ...rest } = app_config;
53921
- const risk = this.to_f(rest.risk_per_trade);
53919
+ let entries = [];
53920
+ let risk_per_trade = this.config.budget;
53921
+ let last_value = null;
53922
+ if (app_config) {
53923
+ let { entries: _entries, ...rest } = app_config;
53924
+ entries = _entries;
53925
+ risk_per_trade = rest.risk_per_trade;
53926
+ last_value = rest.last_value;
53927
+ if (ignore_entries) {
53928
+ entries = [];
53929
+ }
53930
+ }
53931
+ const risk = this.to_f(risk_per_trade);
53932
+ let below_reverse_entries = kind === "long" ? entries.filter((u) => {
53933
+ return u.entry < (reverse_position.entry || focus_position.entry);
53934
+ }) : entries.filter((u) => {
53935
+ return u.entry > (reverse_position.entry || focus_position.entry);
53936
+ });
53937
+ const threshold = below_reverse_entries.at(-1);
53938
+ const result = this.gapCloserHelper({
53939
+ risk,
53940
+ entries,
53941
+ kind,
53942
+ sell_factor,
53943
+ reduce_ratio
53944
+ });
53945
+ return {
53946
+ ...result,
53947
+ last_entry: last_value?.entry,
53948
+ first_entry: entries.at(-1)?.entry,
53949
+ threshold
53950
+ };
53951
+ }
53952
+ gapCloserHelper(payload) {
53953
+ const {
53954
+ risk,
53955
+ entries = [],
53956
+ kind,
53957
+ sell_factor = 1,
53958
+ reduce_ratio = 1
53959
+ } = payload;
53960
+ const { entry, quantity } = this.position[kind];
53961
+ const focus_position = this.position[kind];
53962
+ const reverse_kind = kind == "long" ? "short" : "long";
53963
+ const reverse_position = this.position[reverse_kind];
53964
+ let _entry = this.tp(kind);
53965
+ let _stop = this.tp(reverse_kind);
53966
+ const second_payload = {
53967
+ entry: _entry,
53968
+ stop: _stop,
53969
+ risk_reward: this.config.risk_reward,
53970
+ start_risk: this.pnl(reverse_kind),
53971
+ max_risk: this.config.budget
53972
+ };
53922
53973
  const adjusted_focus_entries = entries.map((entry2) => {
53923
53974
  let adjusted_price = entry2.price;
53924
53975
  if (focus_position.quantity > 0) {
@@ -53938,8 +53989,8 @@ class Strategy {
53938
53989
  price: entry,
53939
53990
  quantity
53940
53991
  }
53941
- ]), rest.decimal_places, rest.price_places);
53942
- const focus_loss = this.to_f((avg.price - second_payload.stop) * avg.quantity);
53992
+ ]), this.config.global_config.decimal_places, this.config.global_config.price_places);
53993
+ const focus_loss = this.to_f(Math.abs(avg.price - second_payload.stop) * avg.quantity);
53943
53994
  let below_reverse_entries = kind === "long" ? entries.filter((u) => {
53944
53995
  return u.entry < (reverse_position.entry || focus_position.entry);
53945
53996
  }) : entries.filter((u) => {
@@ -53965,19 +54016,21 @@ class Strategy {
53965
54016
  price: reverse_position.entry,
53966
54017
  quantity: reverse_position.quantity
53967
54018
  }
53968
- ]), rest.decimal_places, rest.price_places);
53969
- const reverse_pnl = this.to_f((reverse_avg.price - second_payload.stop) * reverse_avg.quantity);
54019
+ ]), this.config.global_config.decimal_places, this.config.global_config.price_places);
54020
+ const sell_quantity = this.to_df(reverse_avg.quantity * sell_factor);
54021
+ const reverse_pnl = this.to_f(Math.abs(reverse_avg.price - second_payload.stop) * sell_quantity);
53970
54022
  const fee_to_pay = this.calculate_fee({
53971
54023
  price: avg.entry,
53972
54024
  quantity: avg.quantity
53973
54025
  }) + this.calculate_fee({
53974
54026
  price: reverse_avg.entry,
53975
- quantity: reverse_avg.quantity
54027
+ quantity: sell_quantity
53976
54028
  });
53977
54029
  const net_reverse_pnl = reverse_pnl - fee_to_pay;
53978
- const ratio = net_reverse_pnl / focus_loss;
54030
+ const ratio = net_reverse_pnl * reduce_ratio / focus_loss;
53979
54031
  const quantity_to_sell = this.to_df(ratio * avg.quantity);
53980
54032
  const remaining_quantity = this.to_df(avg.quantity - quantity_to_sell);
54033
+ const incurred_loss = this.to_f((avg.price - second_payload.stop) * quantity_to_sell);
53981
54034
  return {
53982
54035
  risk,
53983
54036
  risk_reward: this.config.risk_reward,
@@ -53989,7 +54042,8 @@ class Strategy {
53989
54042
  stop_quantity: quantity_to_sell,
53990
54043
  re_entry_quantity: remaining_quantity,
53991
54044
  initial_pnl: this.pnl(kind),
53992
- tp: second_payload.entry
54045
+ tp: second_payload.entry,
54046
+ incurred_loss
53993
54047
  },
53994
54048
  [reverse_kind]: {
53995
54049
  avg_entry: reverse_avg.entry,
@@ -53997,32 +54051,45 @@ class Strategy {
53997
54051
  pnl: reverse_pnl,
53998
54052
  tp: second_payload.stop,
53999
54053
  re_entry_quantity: remaining_quantity,
54000
- initial_pnl: this.pnl(reverse_kind)
54054
+ initial_pnl: this.pnl(reverse_kind),
54055
+ remaining_quantity: this.to_df(reverse_avg.quantity - sell_quantity)
54001
54056
  },
54002
- last_entry: rest.last_value.entry,
54003
- first_entry: entries.at(-1).entry,
54004
- threshold
54057
+ spread: Math.abs(avg.entry - reverse_avg.entry),
54058
+ gap_loss: to_f2(Math.abs(avg.entry - reverse_avg.entry) * reverse_avg.quantity, "%.2f"),
54059
+ net_profit: incurred_loss + reverse_pnl
54005
54060
  };
54006
54061
  }
54007
54062
  runIterations(payload) {
54008
- const { kind, iterations } = payload;
54063
+ const {
54064
+ kind,
54065
+ iterations,
54066
+ ignore_entries = false,
54067
+ reduce_ratio = 1,
54068
+ sell_factor = 1
54069
+ } = payload;
54009
54070
  const reverse_kind = kind == "long" ? "short" : "long";
54010
54071
  const result = [];
54011
54072
  let position2 = {
54012
54073
  long: this.position.long,
54013
54074
  short: this.position.short
54014
54075
  };
54076
+ let tp_percent_multiplier = 1;
54077
+ let short_tp_factor_multiplier = 1;
54015
54078
  for (let i2 = 0;i2 < iterations; i2++) {
54016
54079
  const instance = new Strategy({
54017
54080
  long: position2.long,
54018
54081
  short: position2.short,
54019
54082
  config: {
54020
54083
  ...this.config,
54021
- tp_percent: this.config.tp_percent + i2 * 4
54084
+ tp_percent: this.config.tp_percent * tp_percent_multiplier,
54085
+ short_tp_factor: this.config.short_tp_factor * short_tp_factor_multiplier
54022
54086
  }
54023
54087
  });
54024
54088
  const algorithm = instance.generateGapClosingAlgorithm({
54025
- kind
54089
+ kind,
54090
+ ignore_entries,
54091
+ reduce_ratio,
54092
+ sell_factor
54026
54093
  });
54027
54094
  if (!algorithm) {
54028
54095
  console.log("No algorithm found");
@@ -54034,9 +54101,31 @@ class Strategy {
54034
54101
  entry: algorithm[kind].avg_entry,
54035
54102
  quantity: algorithm[kind].re_entry_quantity
54036
54103
  };
54104
+ let reverse_entry = algorithm[reverse_kind].tp;
54105
+ let reverse_quantity = algorithm[reverse_kind].re_entry_quantity;
54106
+ if (algorithm[reverse_kind].remaining_quantity > 0) {
54107
+ const purchase_to_occur = {
54108
+ price: reverse_entry,
54109
+ quantity: algorithm[reverse_kind].remaining_quantity
54110
+ };
54111
+ const avg = determine_average_entry_and_size([
54112
+ purchase_to_occur,
54113
+ {
54114
+ price: algorithm[reverse_kind].avg_entry,
54115
+ quantity: reverse_quantity - algorithm[reverse_kind].remaining_quantity
54116
+ }
54117
+ ], this.config.global_config.decimal_places, this.config.global_config.price_places);
54118
+ reverse_entry = avg.entry;
54119
+ reverse_quantity = avg.quantity;
54120
+ if (reverse_kind === "short") {
54121
+ short_tp_factor_multiplier = 2;
54122
+ } else {
54123
+ tp_percent_multiplier = 2;
54124
+ }
54125
+ }
54037
54126
  position2[reverse_kind] = {
54038
- entry: algorithm[reverse_kind].tp,
54039
- quantity: algorithm[reverse_kind].re_entry_quantity
54127
+ entry: reverse_entry,
54128
+ quantity: reverse_quantity
54040
54129
  };
54041
54130
  }
54042
54131
  return result;
@@ -57669,6 +57758,9 @@ class ExchangeAccount {
57669
57758
  if (threshold_qty > 0 && track_position.quantity >= threshold_qty) {
57670
57759
  should_place_order = true;
57671
57760
  }
57761
+ if (focus_position.quantity === 0) {
57762
+ should_place_order = true;
57763
+ }
57672
57764
  if (track_position.quantity !== focus_position.quantity && focus_position.quantity < track_position.quantity && should_place_order) {
57673
57765
  const remaining_quantity = Math.abs(track_position.quantity - focus_position.quantity);
57674
57766
  await this.placeMarketOrder({
package/dist/index.d.ts CHANGED
@@ -528,6 +528,19 @@ export type StrategyPosition = {
528
528
  entry: number;
529
529
  quantity: number;
530
530
  };
531
+ export type GapCloserResult = {
532
+ avg_entry: number;
533
+ avg_size: number;
534
+ loss: number;
535
+ stop: number;
536
+ stop_quantity: number;
537
+ re_entry_quantity: number;
538
+ initial_pnl: number;
539
+ tp: number;
540
+ incurred_loss: number;
541
+ pnl: number;
542
+ remaining_quantity: number;
543
+ };
531
544
  export type Config = {
532
545
  tp_percent: number;
533
546
  short_tp_factor: number;
@@ -564,25 +577,54 @@ export declare class Strategy {
564
577
  get short_tp(): number;
565
578
  generateGapClosingAlgorithm(payload: {
566
579
  kind: "long" | "short";
580
+ ignore_entries?: boolean;
581
+ reduce_ratio?: number;
582
+ sell_factor?: number;
567
583
  }): {
568
- [x: string]: any;
569
- risk: number;
570
- risk_reward: number;
571
584
  last_entry: any;
572
585
  first_entry: any;
573
586
  threshold: any;
587
+ risk: number;
588
+ risk_reward: number;
589
+ spread: number;
590
+ gap_loss: number;
591
+ net_profit: number;
592
+ long: GapCloserResult;
593
+ short: GapCloserResult;
594
+ };
595
+ gapCloserHelper(payload: {
596
+ risk: number;
597
+ entries?: any[];
598
+ kind: "long" | "short";
599
+ sell_factor?: number;
600
+ reduce_ratio?: number;
601
+ }): {
602
+ risk: number;
603
+ risk_reward: number;
604
+ spread: number;
605
+ gap_loss: number;
606
+ net_profit: number;
607
+ long: GapCloserResult;
608
+ short: GapCloserResult;
574
609
  };
575
610
  runIterations(payload: {
576
611
  kind: "long" | "short";
577
612
  iterations: number;
578
613
  risk_reward?: number;
614
+ ignore_entries?: boolean;
615
+ reduce_ratio?: number;
616
+ sell_factor?: number;
579
617
  }): {
580
- [x: string]: any;
581
- risk: number;
582
- risk_reward: number;
583
618
  last_entry: any;
584
619
  first_entry: any;
585
620
  threshold: any;
621
+ risk: number;
622
+ risk_reward: number;
623
+ spread: number;
624
+ gap_loss: number;
625
+ net_profit: number;
626
+ long: GapCloserResult;
627
+ short: GapCloserResult;
586
628
  }[];
587
629
  getPositionAfterTp(payload: {
588
630
  kind: "long" | "short";
@@ -1457,12 +1499,40 @@ declare class ExchangeAccount$1 {
1457
1499
  iterations?: number;
1458
1500
  raw?: boolean;
1459
1501
  }): Promise<Strategy | {
1460
- [x: string]: any;
1461
- risk: number;
1462
- risk_reward: number;
1463
1502
  last_entry: any;
1464
1503
  first_entry: any;
1465
1504
  threshold: any;
1505
+ risk: number;
1506
+ risk_reward: number;
1507
+ spread: number;
1508
+ gap_loss: number;
1509
+ net_profit: number;
1510
+ long: {
1511
+ avg_entry: number;
1512
+ avg_size: number;
1513
+ loss: number;
1514
+ stop: number;
1515
+ stop_quantity: number;
1516
+ re_entry_quantity: number;
1517
+ initial_pnl: number;
1518
+ tp: number;
1519
+ incurred_loss: number;
1520
+ pnl: number;
1521
+ remaining_quantity: number;
1522
+ };
1523
+ short: {
1524
+ avg_entry: number;
1525
+ avg_size: number;
1526
+ loss: number;
1527
+ stop: number;
1528
+ stop_quantity: number;
1529
+ re_entry_quantity: number;
1530
+ initial_pnl: number;
1531
+ tp: number;
1532
+ incurred_loss: number;
1533
+ pnl: number;
1534
+ remaining_quantity: number;
1535
+ };
1466
1536
  }[]>;
1467
1537
  getCurrentRun(payload: {
1468
1538
  symbol: string;
package/dist/index.js CHANGED
@@ -53845,16 +53845,18 @@ class Strategy {
53845
53845
  return this.tp("short");
53846
53846
  }
53847
53847
  generateGapClosingAlgorithm(payload) {
53848
- const { kind } = payload;
53848
+ const {
53849
+ kind,
53850
+ ignore_entries = false,
53851
+ reduce_ratio = 1,
53852
+ sell_factor = 1
53853
+ } = payload;
53849
53854
  const { entry, quantity } = this.position[kind];
53850
53855
  const focus_position = this.position[kind];
53851
53856
  const reverse_kind = kind == "long" ? "short" : "long";
53852
53857
  const reverse_position = this.position[reverse_kind];
53853
53858
  let _entry = this.tp(kind);
53854
53859
  let _stop = this.tp(reverse_kind);
53855
- if (!_entry && !_stop) {
53856
- return null;
53857
- }
53858
53860
  const second_payload = {
53859
53861
  entry: _entry,
53860
53862
  stop: _stop,
@@ -53869,11 +53871,60 @@ class Strategy {
53869
53871
  };
53870
53872
  console.log({ second_payload, third_payload });
53871
53873
  const app_config = generateOptimumAppConfig(this.config.global_config, second_payload, third_payload);
53872
- if (!app_config) {
53873
- return null;
53874
- }
53875
- const { entries, ...rest } = app_config;
53876
- const risk = this.to_f(rest.risk_per_trade);
53874
+ let entries = [];
53875
+ let risk_per_trade = this.config.budget;
53876
+ let last_value = null;
53877
+ if (app_config) {
53878
+ let { entries: _entries, ...rest } = app_config;
53879
+ entries = _entries;
53880
+ risk_per_trade = rest.risk_per_trade;
53881
+ last_value = rest.last_value;
53882
+ if (ignore_entries) {
53883
+ entries = [];
53884
+ }
53885
+ }
53886
+ const risk = this.to_f(risk_per_trade);
53887
+ let below_reverse_entries = kind === "long" ? entries.filter((u) => {
53888
+ return u.entry < (reverse_position.entry || focus_position.entry);
53889
+ }) : entries.filter((u) => {
53890
+ return u.entry > (reverse_position.entry || focus_position.entry);
53891
+ });
53892
+ const threshold = below_reverse_entries.at(-1);
53893
+ const result = this.gapCloserHelper({
53894
+ risk,
53895
+ entries,
53896
+ kind,
53897
+ sell_factor,
53898
+ reduce_ratio
53899
+ });
53900
+ return {
53901
+ ...result,
53902
+ last_entry: last_value?.entry,
53903
+ first_entry: entries.at(-1)?.entry,
53904
+ threshold
53905
+ };
53906
+ }
53907
+ gapCloserHelper(payload) {
53908
+ const {
53909
+ risk,
53910
+ entries = [],
53911
+ kind,
53912
+ sell_factor = 1,
53913
+ reduce_ratio = 1
53914
+ } = payload;
53915
+ const { entry, quantity } = this.position[kind];
53916
+ const focus_position = this.position[kind];
53917
+ const reverse_kind = kind == "long" ? "short" : "long";
53918
+ const reverse_position = this.position[reverse_kind];
53919
+ let _entry = this.tp(kind);
53920
+ let _stop = this.tp(reverse_kind);
53921
+ const second_payload = {
53922
+ entry: _entry,
53923
+ stop: _stop,
53924
+ risk_reward: this.config.risk_reward,
53925
+ start_risk: this.pnl(reverse_kind),
53926
+ max_risk: this.config.budget
53927
+ };
53877
53928
  const adjusted_focus_entries = entries.map((entry2) => {
53878
53929
  let adjusted_price = entry2.price;
53879
53930
  if (focus_position.quantity > 0) {
@@ -53893,8 +53944,8 @@ class Strategy {
53893
53944
  price: entry,
53894
53945
  quantity
53895
53946
  }
53896
- ]), rest.decimal_places, rest.price_places);
53897
- const focus_loss = this.to_f((avg.price - second_payload.stop) * avg.quantity);
53947
+ ]), this.config.global_config.decimal_places, this.config.global_config.price_places);
53948
+ const focus_loss = this.to_f(Math.abs(avg.price - second_payload.stop) * avg.quantity);
53898
53949
  let below_reverse_entries = kind === "long" ? entries.filter((u) => {
53899
53950
  return u.entry < (reverse_position.entry || focus_position.entry);
53900
53951
  }) : entries.filter((u) => {
@@ -53920,19 +53971,21 @@ class Strategy {
53920
53971
  price: reverse_position.entry,
53921
53972
  quantity: reverse_position.quantity
53922
53973
  }
53923
- ]), rest.decimal_places, rest.price_places);
53924
- const reverse_pnl = this.to_f((reverse_avg.price - second_payload.stop) * reverse_avg.quantity);
53974
+ ]), this.config.global_config.decimal_places, this.config.global_config.price_places);
53975
+ const sell_quantity = this.to_df(reverse_avg.quantity * sell_factor);
53976
+ const reverse_pnl = this.to_f(Math.abs(reverse_avg.price - second_payload.stop) * sell_quantity);
53925
53977
  const fee_to_pay = this.calculate_fee({
53926
53978
  price: avg.entry,
53927
53979
  quantity: avg.quantity
53928
53980
  }) + this.calculate_fee({
53929
53981
  price: reverse_avg.entry,
53930
- quantity: reverse_avg.quantity
53982
+ quantity: sell_quantity
53931
53983
  });
53932
53984
  const net_reverse_pnl = reverse_pnl - fee_to_pay;
53933
- const ratio = net_reverse_pnl / focus_loss;
53985
+ const ratio = net_reverse_pnl * reduce_ratio / focus_loss;
53934
53986
  const quantity_to_sell = this.to_df(ratio * avg.quantity);
53935
53987
  const remaining_quantity = this.to_df(avg.quantity - quantity_to_sell);
53988
+ const incurred_loss = this.to_f((avg.price - second_payload.stop) * quantity_to_sell);
53936
53989
  return {
53937
53990
  risk,
53938
53991
  risk_reward: this.config.risk_reward,
@@ -53944,7 +53997,8 @@ class Strategy {
53944
53997
  stop_quantity: quantity_to_sell,
53945
53998
  re_entry_quantity: remaining_quantity,
53946
53999
  initial_pnl: this.pnl(kind),
53947
- tp: second_payload.entry
54000
+ tp: second_payload.entry,
54001
+ incurred_loss
53948
54002
  },
53949
54003
  [reverse_kind]: {
53950
54004
  avg_entry: reverse_avg.entry,
@@ -53952,32 +54006,45 @@ class Strategy {
53952
54006
  pnl: reverse_pnl,
53953
54007
  tp: second_payload.stop,
53954
54008
  re_entry_quantity: remaining_quantity,
53955
- initial_pnl: this.pnl(reverse_kind)
54009
+ initial_pnl: this.pnl(reverse_kind),
54010
+ remaining_quantity: this.to_df(reverse_avg.quantity - sell_quantity)
53956
54011
  },
53957
- last_entry: rest.last_value.entry,
53958
- first_entry: entries.at(-1).entry,
53959
- threshold
54012
+ spread: Math.abs(avg.entry - reverse_avg.entry),
54013
+ gap_loss: to_f2(Math.abs(avg.entry - reverse_avg.entry) * reverse_avg.quantity, "%.2f"),
54014
+ net_profit: incurred_loss + reverse_pnl
53960
54015
  };
53961
54016
  }
53962
54017
  runIterations(payload) {
53963
- const { kind, iterations } = payload;
54018
+ const {
54019
+ kind,
54020
+ iterations,
54021
+ ignore_entries = false,
54022
+ reduce_ratio = 1,
54023
+ sell_factor = 1
54024
+ } = payload;
53964
54025
  const reverse_kind = kind == "long" ? "short" : "long";
53965
54026
  const result = [];
53966
54027
  let position2 = {
53967
54028
  long: this.position.long,
53968
54029
  short: this.position.short
53969
54030
  };
54031
+ let tp_percent_multiplier = 1;
54032
+ let short_tp_factor_multiplier = 1;
53970
54033
  for (let i2 = 0;i2 < iterations; i2++) {
53971
54034
  const instance = new Strategy({
53972
54035
  long: position2.long,
53973
54036
  short: position2.short,
53974
54037
  config: {
53975
54038
  ...this.config,
53976
- tp_percent: this.config.tp_percent + i2 * 4
54039
+ tp_percent: this.config.tp_percent * tp_percent_multiplier,
54040
+ short_tp_factor: this.config.short_tp_factor * short_tp_factor_multiplier
53977
54041
  }
53978
54042
  });
53979
54043
  const algorithm = instance.generateGapClosingAlgorithm({
53980
- kind
54044
+ kind,
54045
+ ignore_entries,
54046
+ reduce_ratio,
54047
+ sell_factor
53981
54048
  });
53982
54049
  if (!algorithm) {
53983
54050
  console.log("No algorithm found");
@@ -53989,9 +54056,31 @@ class Strategy {
53989
54056
  entry: algorithm[kind].avg_entry,
53990
54057
  quantity: algorithm[kind].re_entry_quantity
53991
54058
  };
54059
+ let reverse_entry = algorithm[reverse_kind].tp;
54060
+ let reverse_quantity = algorithm[reverse_kind].re_entry_quantity;
54061
+ if (algorithm[reverse_kind].remaining_quantity > 0) {
54062
+ const purchase_to_occur = {
54063
+ price: reverse_entry,
54064
+ quantity: algorithm[reverse_kind].remaining_quantity
54065
+ };
54066
+ const avg = determine_average_entry_and_size([
54067
+ purchase_to_occur,
54068
+ {
54069
+ price: algorithm[reverse_kind].avg_entry,
54070
+ quantity: reverse_quantity - algorithm[reverse_kind].remaining_quantity
54071
+ }
54072
+ ], this.config.global_config.decimal_places, this.config.global_config.price_places);
54073
+ reverse_entry = avg.entry;
54074
+ reverse_quantity = avg.quantity;
54075
+ if (reverse_kind === "short") {
54076
+ short_tp_factor_multiplier = 2;
54077
+ } else {
54078
+ tp_percent_multiplier = 2;
54079
+ }
54080
+ }
53992
54081
  position2[reverse_kind] = {
53993
- entry: algorithm[reverse_kind].tp,
53994
- quantity: algorithm[reverse_kind].re_entry_quantity
54082
+ entry: reverse_entry,
54083
+ quantity: reverse_quantity
53995
54084
  };
53996
54085
  }
53997
54086
  return result;
@@ -57624,6 +57713,9 @@ class ExchangeAccount {
57624
57713
  if (threshold_qty > 0 && track_position.quantity >= threshold_qty) {
57625
57714
  should_place_order = true;
57626
57715
  }
57716
+ if (focus_position.quantity === 0) {
57717
+ should_place_order = true;
57718
+ }
57627
57719
  if (track_position.quantity !== focus_position.quantity && focus_position.quantity < track_position.quantity && should_place_order) {
57628
57720
  const remaining_quantity = Math.abs(track_position.quantity - focus_position.quantity);
57629
57721
  await this.placeMarketOrder({
@@ -60581,16 +60581,18 @@ class Strategy {
60581
60581
  return this.tp("short");
60582
60582
  }
60583
60583
  generateGapClosingAlgorithm(payload) {
60584
- const { kind } = payload;
60584
+ const {
60585
+ kind,
60586
+ ignore_entries = false,
60587
+ reduce_ratio = 1,
60588
+ sell_factor = 1
60589
+ } = payload;
60585
60590
  const { entry, quantity } = this.position[kind];
60586
60591
  const focus_position = this.position[kind];
60587
60592
  const reverse_kind = kind == "long" ? "short" : "long";
60588
60593
  const reverse_position = this.position[reverse_kind];
60589
60594
  let _entry = this.tp(kind);
60590
60595
  let _stop = this.tp(reverse_kind);
60591
- if (!_entry && !_stop) {
60592
- return null;
60593
- }
60594
60596
  const second_payload = {
60595
60597
  entry: _entry,
60596
60598
  stop: _stop,
@@ -60605,11 +60607,60 @@ class Strategy {
60605
60607
  };
60606
60608
  console.log({ second_payload, third_payload });
60607
60609
  const app_config = generateOptimumAppConfig(this.config.global_config, second_payload, third_payload);
60608
- if (!app_config) {
60609
- return null;
60610
- }
60611
- const { entries, ...rest } = app_config;
60612
- const risk = this.to_f(rest.risk_per_trade);
60610
+ let entries = [];
60611
+ let risk_per_trade = this.config.budget;
60612
+ let last_value = null;
60613
+ if (app_config) {
60614
+ let { entries: _entries, ...rest } = app_config;
60615
+ entries = _entries;
60616
+ risk_per_trade = rest.risk_per_trade;
60617
+ last_value = rest.last_value;
60618
+ if (ignore_entries) {
60619
+ entries = [];
60620
+ }
60621
+ }
60622
+ const risk = this.to_f(risk_per_trade);
60623
+ let below_reverse_entries = kind === "long" ? entries.filter((u) => {
60624
+ return u.entry < (reverse_position.entry || focus_position.entry);
60625
+ }) : entries.filter((u) => {
60626
+ return u.entry > (reverse_position.entry || focus_position.entry);
60627
+ });
60628
+ const threshold = below_reverse_entries.at(-1);
60629
+ const result = this.gapCloserHelper({
60630
+ risk,
60631
+ entries,
60632
+ kind,
60633
+ sell_factor,
60634
+ reduce_ratio
60635
+ });
60636
+ return {
60637
+ ...result,
60638
+ last_entry: last_value?.entry,
60639
+ first_entry: entries.at(-1)?.entry,
60640
+ threshold
60641
+ };
60642
+ }
60643
+ gapCloserHelper(payload) {
60644
+ const {
60645
+ risk,
60646
+ entries = [],
60647
+ kind,
60648
+ sell_factor = 1,
60649
+ reduce_ratio = 1
60650
+ } = payload;
60651
+ const { entry, quantity } = this.position[kind];
60652
+ const focus_position = this.position[kind];
60653
+ const reverse_kind = kind == "long" ? "short" : "long";
60654
+ const reverse_position = this.position[reverse_kind];
60655
+ let _entry = this.tp(kind);
60656
+ let _stop = this.tp(reverse_kind);
60657
+ const second_payload = {
60658
+ entry: _entry,
60659
+ stop: _stop,
60660
+ risk_reward: this.config.risk_reward,
60661
+ start_risk: this.pnl(reverse_kind),
60662
+ max_risk: this.config.budget
60663
+ };
60613
60664
  const adjusted_focus_entries = entries.map((entry2) => {
60614
60665
  let adjusted_price = entry2.price;
60615
60666
  if (focus_position.quantity > 0) {
@@ -60629,8 +60680,8 @@ class Strategy {
60629
60680
  price: entry,
60630
60681
  quantity
60631
60682
  }
60632
- ]), rest.decimal_places, rest.price_places);
60633
- const focus_loss = this.to_f((avg.price - second_payload.stop) * avg.quantity);
60683
+ ]), this.config.global_config.decimal_places, this.config.global_config.price_places);
60684
+ const focus_loss = this.to_f(Math.abs(avg.price - second_payload.stop) * avg.quantity);
60634
60685
  let below_reverse_entries = kind === "long" ? entries.filter((u) => {
60635
60686
  return u.entry < (reverse_position.entry || focus_position.entry);
60636
60687
  }) : entries.filter((u) => {
@@ -60656,19 +60707,21 @@ class Strategy {
60656
60707
  price: reverse_position.entry,
60657
60708
  quantity: reverse_position.quantity
60658
60709
  }
60659
- ]), rest.decimal_places, rest.price_places);
60660
- const reverse_pnl = this.to_f((reverse_avg.price - second_payload.stop) * reverse_avg.quantity);
60710
+ ]), this.config.global_config.decimal_places, this.config.global_config.price_places);
60711
+ const sell_quantity = this.to_df(reverse_avg.quantity * sell_factor);
60712
+ const reverse_pnl = this.to_f(Math.abs(reverse_avg.price - second_payload.stop) * sell_quantity);
60661
60713
  const fee_to_pay = this.calculate_fee({
60662
60714
  price: avg.entry,
60663
60715
  quantity: avg.quantity
60664
60716
  }) + this.calculate_fee({
60665
60717
  price: reverse_avg.entry,
60666
- quantity: reverse_avg.quantity
60718
+ quantity: sell_quantity
60667
60719
  });
60668
60720
  const net_reverse_pnl = reverse_pnl - fee_to_pay;
60669
- const ratio = net_reverse_pnl / focus_loss;
60721
+ const ratio = net_reverse_pnl * reduce_ratio / focus_loss;
60670
60722
  const quantity_to_sell = this.to_df(ratio * avg.quantity);
60671
60723
  const remaining_quantity = this.to_df(avg.quantity - quantity_to_sell);
60724
+ const incurred_loss = this.to_f((avg.price - second_payload.stop) * quantity_to_sell);
60672
60725
  return {
60673
60726
  risk,
60674
60727
  risk_reward: this.config.risk_reward,
@@ -60680,7 +60733,8 @@ class Strategy {
60680
60733
  stop_quantity: quantity_to_sell,
60681
60734
  re_entry_quantity: remaining_quantity,
60682
60735
  initial_pnl: this.pnl(kind),
60683
- tp: second_payload.entry
60736
+ tp: second_payload.entry,
60737
+ incurred_loss
60684
60738
  },
60685
60739
  [reverse_kind]: {
60686
60740
  avg_entry: reverse_avg.entry,
@@ -60688,32 +60742,45 @@ class Strategy {
60688
60742
  pnl: reverse_pnl,
60689
60743
  tp: second_payload.stop,
60690
60744
  re_entry_quantity: remaining_quantity,
60691
- initial_pnl: this.pnl(reverse_kind)
60745
+ initial_pnl: this.pnl(reverse_kind),
60746
+ remaining_quantity: this.to_df(reverse_avg.quantity - sell_quantity)
60692
60747
  },
60693
- last_entry: rest.last_value.entry,
60694
- first_entry: entries.at(-1).entry,
60695
- threshold
60748
+ spread: Math.abs(avg.entry - reverse_avg.entry),
60749
+ gap_loss: to_f2(Math.abs(avg.entry - reverse_avg.entry) * reverse_avg.quantity, "%.2f"),
60750
+ net_profit: incurred_loss + reverse_pnl
60696
60751
  };
60697
60752
  }
60698
60753
  runIterations(payload) {
60699
- const { kind, iterations } = payload;
60754
+ const {
60755
+ kind,
60756
+ iterations,
60757
+ ignore_entries = false,
60758
+ reduce_ratio = 1,
60759
+ sell_factor = 1
60760
+ } = payload;
60700
60761
  const reverse_kind = kind == "long" ? "short" : "long";
60701
60762
  const result = [];
60702
60763
  let position2 = {
60703
60764
  long: this.position.long,
60704
60765
  short: this.position.short
60705
60766
  };
60767
+ let tp_percent_multiplier = 1;
60768
+ let short_tp_factor_multiplier = 1;
60706
60769
  for (let i2 = 0;i2 < iterations; i2++) {
60707
60770
  const instance = new Strategy({
60708
60771
  long: position2.long,
60709
60772
  short: position2.short,
60710
60773
  config: {
60711
60774
  ...this.config,
60712
- tp_percent: this.config.tp_percent + i2 * 4
60775
+ tp_percent: this.config.tp_percent * tp_percent_multiplier,
60776
+ short_tp_factor: this.config.short_tp_factor * short_tp_factor_multiplier
60713
60777
  }
60714
60778
  });
60715
60779
  const algorithm = instance.generateGapClosingAlgorithm({
60716
- kind
60780
+ kind,
60781
+ ignore_entries,
60782
+ reduce_ratio,
60783
+ sell_factor
60717
60784
  });
60718
60785
  if (!algorithm) {
60719
60786
  console.log("No algorithm found");
@@ -60725,9 +60792,31 @@ class Strategy {
60725
60792
  entry: algorithm[kind].avg_entry,
60726
60793
  quantity: algorithm[kind].re_entry_quantity
60727
60794
  };
60795
+ let reverse_entry = algorithm[reverse_kind].tp;
60796
+ let reverse_quantity = algorithm[reverse_kind].re_entry_quantity;
60797
+ if (algorithm[reverse_kind].remaining_quantity > 0) {
60798
+ const purchase_to_occur = {
60799
+ price: reverse_entry,
60800
+ quantity: algorithm[reverse_kind].remaining_quantity
60801
+ };
60802
+ const avg = determine_average_entry_and_size([
60803
+ purchase_to_occur,
60804
+ {
60805
+ price: algorithm[reverse_kind].avg_entry,
60806
+ quantity: reverse_quantity - algorithm[reverse_kind].remaining_quantity
60807
+ }
60808
+ ], this.config.global_config.decimal_places, this.config.global_config.price_places);
60809
+ reverse_entry = avg.entry;
60810
+ reverse_quantity = avg.quantity;
60811
+ if (reverse_kind === "short") {
60812
+ short_tp_factor_multiplier = 2;
60813
+ } else {
60814
+ tp_percent_multiplier = 2;
60815
+ }
60816
+ }
60728
60817
  position2[reverse_kind] = {
60729
- entry: algorithm[reverse_kind].tp,
60730
- quantity: algorithm[reverse_kind].re_entry_quantity
60818
+ entry: reverse_entry,
60819
+ quantity: reverse_quantity
60731
60820
  };
60732
60821
  }
60733
60822
  return result;
@@ -64360,6 +64449,9 @@ class ExchangeAccount {
64360
64449
  if (threshold_qty > 0 && track_position.quantity >= threshold_qty) {
64361
64450
  should_place_order = true;
64362
64451
  }
64452
+ if (focus_position.quantity === 0) {
64453
+ should_place_order = true;
64454
+ }
64363
64455
  if (track_position.quantity !== focus_position.quantity && focus_position.quantity < track_position.quantity && should_place_order) {
64364
64456
  const remaining_quantity = Math.abs(track_position.quantity - focus_position.quantity);
64365
64457
  await this.placeMarketOrder({
@@ -60558,16 +60558,18 @@ class Strategy {
60558
60558
  return this.tp("short");
60559
60559
  }
60560
60560
  generateGapClosingAlgorithm(payload) {
60561
- const { kind } = payload;
60561
+ const {
60562
+ kind,
60563
+ ignore_entries = false,
60564
+ reduce_ratio = 1,
60565
+ sell_factor = 1
60566
+ } = payload;
60562
60567
  const { entry, quantity } = this.position[kind];
60563
60568
  const focus_position = this.position[kind];
60564
60569
  const reverse_kind = kind == "long" ? "short" : "long";
60565
60570
  const reverse_position = this.position[reverse_kind];
60566
60571
  let _entry = this.tp(kind);
60567
60572
  let _stop = this.tp(reverse_kind);
60568
- if (!_entry && !_stop) {
60569
- return null;
60570
- }
60571
60573
  const second_payload = {
60572
60574
  entry: _entry,
60573
60575
  stop: _stop,
@@ -60582,11 +60584,60 @@ class Strategy {
60582
60584
  };
60583
60585
  console.log({ second_payload, third_payload });
60584
60586
  const app_config = generateOptimumAppConfig(this.config.global_config, second_payload, third_payload);
60585
- if (!app_config) {
60586
- return null;
60587
- }
60588
- const { entries, ...rest } = app_config;
60589
- const risk = this.to_f(rest.risk_per_trade);
60587
+ let entries = [];
60588
+ let risk_per_trade = this.config.budget;
60589
+ let last_value = null;
60590
+ if (app_config) {
60591
+ let { entries: _entries, ...rest } = app_config;
60592
+ entries = _entries;
60593
+ risk_per_trade = rest.risk_per_trade;
60594
+ last_value = rest.last_value;
60595
+ if (ignore_entries) {
60596
+ entries = [];
60597
+ }
60598
+ }
60599
+ const risk = this.to_f(risk_per_trade);
60600
+ let below_reverse_entries = kind === "long" ? entries.filter((u) => {
60601
+ return u.entry < (reverse_position.entry || focus_position.entry);
60602
+ }) : entries.filter((u) => {
60603
+ return u.entry > (reverse_position.entry || focus_position.entry);
60604
+ });
60605
+ const threshold = below_reverse_entries.at(-1);
60606
+ const result = this.gapCloserHelper({
60607
+ risk,
60608
+ entries,
60609
+ kind,
60610
+ sell_factor,
60611
+ reduce_ratio
60612
+ });
60613
+ return {
60614
+ ...result,
60615
+ last_entry: last_value?.entry,
60616
+ first_entry: entries.at(-1)?.entry,
60617
+ threshold
60618
+ };
60619
+ }
60620
+ gapCloserHelper(payload) {
60621
+ const {
60622
+ risk,
60623
+ entries = [],
60624
+ kind,
60625
+ sell_factor = 1,
60626
+ reduce_ratio = 1
60627
+ } = payload;
60628
+ const { entry, quantity } = this.position[kind];
60629
+ const focus_position = this.position[kind];
60630
+ const reverse_kind = kind == "long" ? "short" : "long";
60631
+ const reverse_position = this.position[reverse_kind];
60632
+ let _entry = this.tp(kind);
60633
+ let _stop = this.tp(reverse_kind);
60634
+ const second_payload = {
60635
+ entry: _entry,
60636
+ stop: _stop,
60637
+ risk_reward: this.config.risk_reward,
60638
+ start_risk: this.pnl(reverse_kind),
60639
+ max_risk: this.config.budget
60640
+ };
60590
60641
  const adjusted_focus_entries = entries.map((entry2) => {
60591
60642
  let adjusted_price = entry2.price;
60592
60643
  if (focus_position.quantity > 0) {
@@ -60606,8 +60657,8 @@ class Strategy {
60606
60657
  price: entry,
60607
60658
  quantity
60608
60659
  }
60609
- ]), rest.decimal_places, rest.price_places);
60610
- const focus_loss = this.to_f((avg.price - second_payload.stop) * avg.quantity);
60660
+ ]), this.config.global_config.decimal_places, this.config.global_config.price_places);
60661
+ const focus_loss = this.to_f(Math.abs(avg.price - second_payload.stop) * avg.quantity);
60611
60662
  let below_reverse_entries = kind === "long" ? entries.filter((u) => {
60612
60663
  return u.entry < (reverse_position.entry || focus_position.entry);
60613
60664
  }) : entries.filter((u) => {
@@ -60633,19 +60684,21 @@ class Strategy {
60633
60684
  price: reverse_position.entry,
60634
60685
  quantity: reverse_position.quantity
60635
60686
  }
60636
- ]), rest.decimal_places, rest.price_places);
60637
- const reverse_pnl = this.to_f((reverse_avg.price - second_payload.stop) * reverse_avg.quantity);
60687
+ ]), this.config.global_config.decimal_places, this.config.global_config.price_places);
60688
+ const sell_quantity = this.to_df(reverse_avg.quantity * sell_factor);
60689
+ const reverse_pnl = this.to_f(Math.abs(reverse_avg.price - second_payload.stop) * sell_quantity);
60638
60690
  const fee_to_pay = this.calculate_fee({
60639
60691
  price: avg.entry,
60640
60692
  quantity: avg.quantity
60641
60693
  }) + this.calculate_fee({
60642
60694
  price: reverse_avg.entry,
60643
- quantity: reverse_avg.quantity
60695
+ quantity: sell_quantity
60644
60696
  });
60645
60697
  const net_reverse_pnl = reverse_pnl - fee_to_pay;
60646
- const ratio = net_reverse_pnl / focus_loss;
60698
+ const ratio = net_reverse_pnl * reduce_ratio / focus_loss;
60647
60699
  const quantity_to_sell = this.to_df(ratio * avg.quantity);
60648
60700
  const remaining_quantity = this.to_df(avg.quantity - quantity_to_sell);
60701
+ const incurred_loss = this.to_f((avg.price - second_payload.stop) * quantity_to_sell);
60649
60702
  return {
60650
60703
  risk,
60651
60704
  risk_reward: this.config.risk_reward,
@@ -60657,7 +60710,8 @@ class Strategy {
60657
60710
  stop_quantity: quantity_to_sell,
60658
60711
  re_entry_quantity: remaining_quantity,
60659
60712
  initial_pnl: this.pnl(kind),
60660
- tp: second_payload.entry
60713
+ tp: second_payload.entry,
60714
+ incurred_loss
60661
60715
  },
60662
60716
  [reverse_kind]: {
60663
60717
  avg_entry: reverse_avg.entry,
@@ -60665,32 +60719,45 @@ class Strategy {
60665
60719
  pnl: reverse_pnl,
60666
60720
  tp: second_payload.stop,
60667
60721
  re_entry_quantity: remaining_quantity,
60668
- initial_pnl: this.pnl(reverse_kind)
60722
+ initial_pnl: this.pnl(reverse_kind),
60723
+ remaining_quantity: this.to_df(reverse_avg.quantity - sell_quantity)
60669
60724
  },
60670
- last_entry: rest.last_value.entry,
60671
- first_entry: entries.at(-1).entry,
60672
- threshold
60725
+ spread: Math.abs(avg.entry - reverse_avg.entry),
60726
+ gap_loss: to_f2(Math.abs(avg.entry - reverse_avg.entry) * reverse_avg.quantity, "%.2f"),
60727
+ net_profit: incurred_loss + reverse_pnl
60673
60728
  };
60674
60729
  }
60675
60730
  runIterations(payload) {
60676
- const { kind, iterations } = payload;
60731
+ const {
60732
+ kind,
60733
+ iterations,
60734
+ ignore_entries = false,
60735
+ reduce_ratio = 1,
60736
+ sell_factor = 1
60737
+ } = payload;
60677
60738
  const reverse_kind = kind == "long" ? "short" : "long";
60678
60739
  const result = [];
60679
60740
  let position2 = {
60680
60741
  long: this.position.long,
60681
60742
  short: this.position.short
60682
60743
  };
60744
+ let tp_percent_multiplier = 1;
60745
+ let short_tp_factor_multiplier = 1;
60683
60746
  for (let i2 = 0;i2 < iterations; i2++) {
60684
60747
  const instance = new Strategy({
60685
60748
  long: position2.long,
60686
60749
  short: position2.short,
60687
60750
  config: {
60688
60751
  ...this.config,
60689
- tp_percent: this.config.tp_percent + i2 * 4
60752
+ tp_percent: this.config.tp_percent * tp_percent_multiplier,
60753
+ short_tp_factor: this.config.short_tp_factor * short_tp_factor_multiplier
60690
60754
  }
60691
60755
  });
60692
60756
  const algorithm = instance.generateGapClosingAlgorithm({
60693
- kind
60757
+ kind,
60758
+ ignore_entries,
60759
+ reduce_ratio,
60760
+ sell_factor
60694
60761
  });
60695
60762
  if (!algorithm) {
60696
60763
  console.log("No algorithm found");
@@ -60702,9 +60769,31 @@ class Strategy {
60702
60769
  entry: algorithm[kind].avg_entry,
60703
60770
  quantity: algorithm[kind].re_entry_quantity
60704
60771
  };
60772
+ let reverse_entry = algorithm[reverse_kind].tp;
60773
+ let reverse_quantity = algorithm[reverse_kind].re_entry_quantity;
60774
+ if (algorithm[reverse_kind].remaining_quantity > 0) {
60775
+ const purchase_to_occur = {
60776
+ price: reverse_entry,
60777
+ quantity: algorithm[reverse_kind].remaining_quantity
60778
+ };
60779
+ const avg = determine_average_entry_and_size([
60780
+ purchase_to_occur,
60781
+ {
60782
+ price: algorithm[reverse_kind].avg_entry,
60783
+ quantity: reverse_quantity - algorithm[reverse_kind].remaining_quantity
60784
+ }
60785
+ ], this.config.global_config.decimal_places, this.config.global_config.price_places);
60786
+ reverse_entry = avg.entry;
60787
+ reverse_quantity = avg.quantity;
60788
+ if (reverse_kind === "short") {
60789
+ short_tp_factor_multiplier = 2;
60790
+ } else {
60791
+ tp_percent_multiplier = 2;
60792
+ }
60793
+ }
60705
60794
  position2[reverse_kind] = {
60706
- entry: algorithm[reverse_kind].tp,
60707
- quantity: algorithm[reverse_kind].re_entry_quantity
60795
+ entry: reverse_entry,
60796
+ quantity: reverse_quantity
60708
60797
  };
60709
60798
  }
60710
60799
  return result;
@@ -64337,6 +64426,9 @@ class ExchangeAccount {
64337
64426
  if (threshold_qty > 0 && track_position.quantity >= threshold_qty) {
64338
64427
  should_place_order = true;
64339
64428
  }
64429
+ if (focus_position.quantity === 0) {
64430
+ should_place_order = true;
64431
+ }
64340
64432
  if (track_position.quantity !== focus_position.quantity && focus_position.quantity < track_position.quantity && should_place_order) {
64341
64433
  const remaining_quantity = Math.abs(track_position.quantity - focus_position.quantity);
64342
64434
  await this.placeMarketOrder({
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gbozee/ultimate",
3
3
  "type": "module",
4
- "version": "0.0.2-73",
4
+ "version": "0.0.2-75",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",