@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.
- package/dist/frontend-index.d.ts +48 -6
- package/dist/frontend-index.js +113 -24
- package/dist/index.cjs +117 -25
- package/dist/index.d.ts +79 -9
- package/dist/index.js +117 -25
- package/dist/mcp-server.cjs +117 -25
- package/dist/mcp-server.js +117 -25
- package/package.json +1 -1
package/dist/frontend-index.d.ts
CHANGED
|
@@ -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";
|
package/dist/frontend-index.js
CHANGED
|
@@ -1830,16 +1830,18 @@ class Strategy {
|
|
|
1830
1830
|
return this.tp("short");
|
|
1831
1831
|
}
|
|
1832
1832
|
generateGapClosingAlgorithm(payload) {
|
|
1833
|
-
const {
|
|
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
|
-
|
|
1858
|
-
|
|
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
|
|
1861
|
-
|
|
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
|
-
]),
|
|
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
|
-
]),
|
|
1909
|
-
const
|
|
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:
|
|
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
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
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 {
|
|
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
|
|
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:
|
|
1979
|
-
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 {
|
|
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
|
-
|
|
53918
|
-
|
|
53919
|
-
|
|
53920
|
-
|
|
53921
|
-
|
|
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
|
-
]),
|
|
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
|
-
]),
|
|
53969
|
-
const
|
|
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:
|
|
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
|
-
|
|
54003
|
-
|
|
54004
|
-
|
|
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 {
|
|
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
|
|
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:
|
|
54039
|
-
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 {
|
|
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
|
-
|
|
53873
|
-
|
|
53874
|
-
|
|
53875
|
-
|
|
53876
|
-
|
|
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
|
-
]),
|
|
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
|
-
]),
|
|
53924
|
-
const
|
|
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:
|
|
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
|
-
|
|
53958
|
-
|
|
53959
|
-
|
|
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 {
|
|
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
|
|
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:
|
|
53994
|
-
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({
|
package/dist/mcp-server.cjs
CHANGED
|
@@ -60581,16 +60581,18 @@ class Strategy {
|
|
|
60581
60581
|
return this.tp("short");
|
|
60582
60582
|
}
|
|
60583
60583
|
generateGapClosingAlgorithm(payload) {
|
|
60584
|
-
const {
|
|
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
|
-
|
|
60609
|
-
|
|
60610
|
-
|
|
60611
|
-
|
|
60612
|
-
|
|
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
|
-
]),
|
|
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
|
-
]),
|
|
60660
|
-
const
|
|
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:
|
|
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
|
-
|
|
60694
|
-
|
|
60695
|
-
|
|
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 {
|
|
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
|
|
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:
|
|
60730
|
-
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({
|
package/dist/mcp-server.js
CHANGED
|
@@ -60558,16 +60558,18 @@ class Strategy {
|
|
|
60558
60558
|
return this.tp("short");
|
|
60559
60559
|
}
|
|
60560
60560
|
generateGapClosingAlgorithm(payload) {
|
|
60561
|
-
const {
|
|
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
|
-
|
|
60586
|
-
|
|
60587
|
-
|
|
60588
|
-
|
|
60589
|
-
|
|
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
|
-
]),
|
|
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
|
-
]),
|
|
60637
|
-
const
|
|
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:
|
|
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
|
-
|
|
60671
|
-
|
|
60672
|
-
|
|
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 {
|
|
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
|
|
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:
|
|
60707
|
-
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({
|