@gbozee/ultimate 0.0.2-73 → 0.0.2-74
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 +12 -0
- package/dist/frontend-index.js +64 -15
- package/dist/index.cjs +67 -15
- package/dist/index.d.ts +15 -0
- package/dist/index.js +67 -15
- package/dist/mcp-server.cjs +67 -15
- package/dist/mcp-server.js +67 -15
- package/package.json +1 -1
package/dist/frontend-index.d.ts
CHANGED
|
@@ -438,6 +438,9 @@ export declare class Strategy {
|
|
|
438
438
|
get short_tp(): number;
|
|
439
439
|
generateGapClosingAlgorithm(payload: {
|
|
440
440
|
kind: "long" | "short";
|
|
441
|
+
ignore_entries?: boolean;
|
|
442
|
+
reduce_ratio?: number;
|
|
443
|
+
sell_factor?: number;
|
|
441
444
|
}): {
|
|
442
445
|
[x: string]: any;
|
|
443
446
|
risk: number;
|
|
@@ -445,11 +448,17 @@ export declare class Strategy {
|
|
|
445
448
|
last_entry: any;
|
|
446
449
|
first_entry: any;
|
|
447
450
|
threshold: any;
|
|
451
|
+
spread: number;
|
|
452
|
+
gap_loss: number;
|
|
453
|
+
net_profit: number;
|
|
448
454
|
};
|
|
449
455
|
runIterations(payload: {
|
|
450
456
|
kind: "long" | "short";
|
|
451
457
|
iterations: number;
|
|
452
458
|
risk_reward?: number;
|
|
459
|
+
ignore_entries?: boolean;
|
|
460
|
+
reduce_ratio?: number;
|
|
461
|
+
sell_factor?: number;
|
|
453
462
|
}): {
|
|
454
463
|
[x: string]: any;
|
|
455
464
|
risk: number;
|
|
@@ -457,6 +466,9 @@ export declare class Strategy {
|
|
|
457
466
|
last_entry: any;
|
|
458
467
|
first_entry: any;
|
|
459
468
|
threshold: any;
|
|
469
|
+
spread: number;
|
|
470
|
+
gap_loss: number;
|
|
471
|
+
net_profit: number;
|
|
460
472
|
}[];
|
|
461
473
|
getPositionAfterTp(payload: {
|
|
462
474
|
kind: "long" | "short";
|
package/dist/frontend-index.js
CHANGED
|
@@ -1830,7 +1830,12 @@ 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";
|
|
@@ -1857,7 +1862,10 @@ class Strategy {
|
|
|
1857
1862
|
if (!app_config) {
|
|
1858
1863
|
return null;
|
|
1859
1864
|
}
|
|
1860
|
-
|
|
1865
|
+
let { entries, ...rest } = app_config;
|
|
1866
|
+
if (ignore_entries) {
|
|
1867
|
+
entries = [];
|
|
1868
|
+
}
|
|
1861
1869
|
const risk = this.to_f(rest.risk_per_trade);
|
|
1862
1870
|
const adjusted_focus_entries = entries.map((entry2) => {
|
|
1863
1871
|
let adjusted_price = entry2.price;
|
|
@@ -1879,7 +1887,7 @@ class Strategy {
|
|
|
1879
1887
|
quantity
|
|
1880
1888
|
}
|
|
1881
1889
|
]), rest.decimal_places, rest.price_places);
|
|
1882
|
-
const focus_loss = this.to_f((avg.price - second_payload.stop) * avg.quantity);
|
|
1890
|
+
const focus_loss = this.to_f(Math.abs(avg.price - second_payload.stop) * avg.quantity);
|
|
1883
1891
|
let below_reverse_entries = kind === "long" ? entries.filter((u) => {
|
|
1884
1892
|
return u.entry < (reverse_position.entry || focus_position.entry);
|
|
1885
1893
|
}) : entries.filter((u) => {
|
|
@@ -1906,18 +1914,20 @@ class Strategy {
|
|
|
1906
1914
|
quantity: reverse_position.quantity
|
|
1907
1915
|
}
|
|
1908
1916
|
]), rest.decimal_places, rest.price_places);
|
|
1909
|
-
const
|
|
1917
|
+
const sell_quantity = this.to_df(reverse_avg.quantity * sell_factor);
|
|
1918
|
+
const reverse_pnl = this.to_f(Math.abs(reverse_avg.price - second_payload.stop) * sell_quantity);
|
|
1910
1919
|
const fee_to_pay = this.calculate_fee({
|
|
1911
1920
|
price: avg.entry,
|
|
1912
1921
|
quantity: avg.quantity
|
|
1913
1922
|
}) + this.calculate_fee({
|
|
1914
1923
|
price: reverse_avg.entry,
|
|
1915
|
-
quantity:
|
|
1924
|
+
quantity: sell_quantity
|
|
1916
1925
|
});
|
|
1917
1926
|
const net_reverse_pnl = reverse_pnl - fee_to_pay;
|
|
1918
|
-
const ratio = net_reverse_pnl / focus_loss;
|
|
1927
|
+
const ratio = net_reverse_pnl * reduce_ratio / focus_loss;
|
|
1919
1928
|
const quantity_to_sell = this.to_df(ratio * avg.quantity);
|
|
1920
1929
|
const remaining_quantity = this.to_df(avg.quantity - quantity_to_sell);
|
|
1930
|
+
const incurred_loss = this.to_f((avg.price - second_payload.stop) * quantity_to_sell);
|
|
1921
1931
|
return {
|
|
1922
1932
|
risk,
|
|
1923
1933
|
risk_reward: this.config.risk_reward,
|
|
@@ -1929,7 +1939,8 @@ class Strategy {
|
|
|
1929
1939
|
stop_quantity: quantity_to_sell,
|
|
1930
1940
|
re_entry_quantity: remaining_quantity,
|
|
1931
1941
|
initial_pnl: this.pnl(kind),
|
|
1932
|
-
tp: second_payload.entry
|
|
1942
|
+
tp: second_payload.entry,
|
|
1943
|
+
incurred_loss
|
|
1933
1944
|
},
|
|
1934
1945
|
[reverse_kind]: {
|
|
1935
1946
|
avg_entry: reverse_avg.entry,
|
|
@@ -1937,32 +1948,48 @@ class Strategy {
|
|
|
1937
1948
|
pnl: reverse_pnl,
|
|
1938
1949
|
tp: second_payload.stop,
|
|
1939
1950
|
re_entry_quantity: remaining_quantity,
|
|
1940
|
-
initial_pnl: this.pnl(reverse_kind)
|
|
1951
|
+
initial_pnl: this.pnl(reverse_kind),
|
|
1952
|
+
remaining_quantity: this.to_df(reverse_avg.quantity - sell_quantity)
|
|
1941
1953
|
},
|
|
1942
1954
|
last_entry: rest.last_value.entry,
|
|
1943
|
-
first_entry: entries.at(-1)
|
|
1944
|
-
threshold
|
|
1955
|
+
first_entry: entries.at(-1)?.entry,
|
|
1956
|
+
threshold,
|
|
1957
|
+
spread: Math.abs(avg.entry - reverse_avg.entry),
|
|
1958
|
+
gap_loss: to_f(Math.abs(avg.entry - reverse_avg.entry) * reverse_avg.quantity, "%.2f"),
|
|
1959
|
+
net_profit: incurred_loss + reverse_pnl
|
|
1945
1960
|
};
|
|
1946
1961
|
}
|
|
1947
1962
|
runIterations(payload) {
|
|
1948
|
-
const {
|
|
1963
|
+
const {
|
|
1964
|
+
kind,
|
|
1965
|
+
iterations,
|
|
1966
|
+
ignore_entries = false,
|
|
1967
|
+
reduce_ratio = 1,
|
|
1968
|
+
sell_factor = 1
|
|
1969
|
+
} = payload;
|
|
1949
1970
|
const reverse_kind = kind == "long" ? "short" : "long";
|
|
1950
1971
|
const result = [];
|
|
1951
1972
|
let position2 = {
|
|
1952
1973
|
long: this.position.long,
|
|
1953
1974
|
short: this.position.short
|
|
1954
1975
|
};
|
|
1976
|
+
let tp_percent_multiplier = 1;
|
|
1977
|
+
let short_tp_factor_multiplier = 1;
|
|
1955
1978
|
for (let i = 0;i < iterations; i++) {
|
|
1956
1979
|
const instance = new Strategy({
|
|
1957
1980
|
long: position2.long,
|
|
1958
1981
|
short: position2.short,
|
|
1959
1982
|
config: {
|
|
1960
1983
|
...this.config,
|
|
1961
|
-
tp_percent: this.config.tp_percent
|
|
1984
|
+
tp_percent: this.config.tp_percent * tp_percent_multiplier,
|
|
1985
|
+
short_tp_factor: this.config.short_tp_factor * short_tp_factor_multiplier
|
|
1962
1986
|
}
|
|
1963
1987
|
});
|
|
1964
1988
|
const algorithm = instance.generateGapClosingAlgorithm({
|
|
1965
|
-
kind
|
|
1989
|
+
kind,
|
|
1990
|
+
ignore_entries,
|
|
1991
|
+
reduce_ratio,
|
|
1992
|
+
sell_factor
|
|
1966
1993
|
});
|
|
1967
1994
|
if (!algorithm) {
|
|
1968
1995
|
console.log("No algorithm found");
|
|
@@ -1974,9 +2001,31 @@ class Strategy {
|
|
|
1974
2001
|
entry: algorithm[kind].avg_entry,
|
|
1975
2002
|
quantity: algorithm[kind].re_entry_quantity
|
|
1976
2003
|
};
|
|
2004
|
+
let reverse_entry = algorithm[reverse_kind].tp;
|
|
2005
|
+
let reverse_quantity = algorithm[reverse_kind].re_entry_quantity;
|
|
2006
|
+
if (algorithm[reverse_kind].remaining_quantity > 0) {
|
|
2007
|
+
const purchase_to_occur = {
|
|
2008
|
+
price: reverse_entry,
|
|
2009
|
+
quantity: algorithm[reverse_kind].remaining_quantity
|
|
2010
|
+
};
|
|
2011
|
+
const avg = determine_average_entry_and_size([
|
|
2012
|
+
purchase_to_occur,
|
|
2013
|
+
{
|
|
2014
|
+
price: algorithm[reverse_kind].avg_entry,
|
|
2015
|
+
quantity: reverse_quantity - algorithm[reverse_kind].remaining_quantity
|
|
2016
|
+
}
|
|
2017
|
+
], this.config.global_config.decimal_places, this.config.global_config.price_places);
|
|
2018
|
+
reverse_entry = avg.entry;
|
|
2019
|
+
reverse_quantity = avg.quantity;
|
|
2020
|
+
if (reverse_kind === "short") {
|
|
2021
|
+
short_tp_factor_multiplier = 2;
|
|
2022
|
+
} else {
|
|
2023
|
+
tp_percent_multiplier = 2;
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
1977
2026
|
position2[reverse_kind] = {
|
|
1978
|
-
entry:
|
|
1979
|
-
quantity:
|
|
2027
|
+
entry: reverse_entry,
|
|
2028
|
+
quantity: reverse_quantity
|
|
1980
2029
|
};
|
|
1981
2030
|
}
|
|
1982
2031
|
return result;
|
package/dist/index.cjs
CHANGED
|
@@ -53890,7 +53890,12 @@ 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";
|
|
@@ -53917,7 +53922,10 @@ class Strategy {
|
|
|
53917
53922
|
if (!app_config) {
|
|
53918
53923
|
return null;
|
|
53919
53924
|
}
|
|
53920
|
-
|
|
53925
|
+
let { entries, ...rest } = app_config;
|
|
53926
|
+
if (ignore_entries) {
|
|
53927
|
+
entries = [];
|
|
53928
|
+
}
|
|
53921
53929
|
const risk = this.to_f(rest.risk_per_trade);
|
|
53922
53930
|
const adjusted_focus_entries = entries.map((entry2) => {
|
|
53923
53931
|
let adjusted_price = entry2.price;
|
|
@@ -53939,7 +53947,7 @@ class Strategy {
|
|
|
53939
53947
|
quantity
|
|
53940
53948
|
}
|
|
53941
53949
|
]), rest.decimal_places, rest.price_places);
|
|
53942
|
-
const focus_loss = this.to_f((avg.price - second_payload.stop) * avg.quantity);
|
|
53950
|
+
const focus_loss = this.to_f(Math.abs(avg.price - second_payload.stop) * avg.quantity);
|
|
53943
53951
|
let below_reverse_entries = kind === "long" ? entries.filter((u) => {
|
|
53944
53952
|
return u.entry < (reverse_position.entry || focus_position.entry);
|
|
53945
53953
|
}) : entries.filter((u) => {
|
|
@@ -53966,18 +53974,20 @@ class Strategy {
|
|
|
53966
53974
|
quantity: reverse_position.quantity
|
|
53967
53975
|
}
|
|
53968
53976
|
]), rest.decimal_places, rest.price_places);
|
|
53969
|
-
const
|
|
53977
|
+
const sell_quantity = this.to_df(reverse_avg.quantity * sell_factor);
|
|
53978
|
+
const reverse_pnl = this.to_f(Math.abs(reverse_avg.price - second_payload.stop) * sell_quantity);
|
|
53970
53979
|
const fee_to_pay = this.calculate_fee({
|
|
53971
53980
|
price: avg.entry,
|
|
53972
53981
|
quantity: avg.quantity
|
|
53973
53982
|
}) + this.calculate_fee({
|
|
53974
53983
|
price: reverse_avg.entry,
|
|
53975
|
-
quantity:
|
|
53984
|
+
quantity: sell_quantity
|
|
53976
53985
|
});
|
|
53977
53986
|
const net_reverse_pnl = reverse_pnl - fee_to_pay;
|
|
53978
|
-
const ratio = net_reverse_pnl / focus_loss;
|
|
53987
|
+
const ratio = net_reverse_pnl * reduce_ratio / focus_loss;
|
|
53979
53988
|
const quantity_to_sell = this.to_df(ratio * avg.quantity);
|
|
53980
53989
|
const remaining_quantity = this.to_df(avg.quantity - quantity_to_sell);
|
|
53990
|
+
const incurred_loss = this.to_f((avg.price - second_payload.stop) * quantity_to_sell);
|
|
53981
53991
|
return {
|
|
53982
53992
|
risk,
|
|
53983
53993
|
risk_reward: this.config.risk_reward,
|
|
@@ -53989,7 +53999,8 @@ class Strategy {
|
|
|
53989
53999
|
stop_quantity: quantity_to_sell,
|
|
53990
54000
|
re_entry_quantity: remaining_quantity,
|
|
53991
54001
|
initial_pnl: this.pnl(kind),
|
|
53992
|
-
tp: second_payload.entry
|
|
54002
|
+
tp: second_payload.entry,
|
|
54003
|
+
incurred_loss
|
|
53993
54004
|
},
|
|
53994
54005
|
[reverse_kind]: {
|
|
53995
54006
|
avg_entry: reverse_avg.entry,
|
|
@@ -53997,32 +54008,48 @@ class Strategy {
|
|
|
53997
54008
|
pnl: reverse_pnl,
|
|
53998
54009
|
tp: second_payload.stop,
|
|
53999
54010
|
re_entry_quantity: remaining_quantity,
|
|
54000
|
-
initial_pnl: this.pnl(reverse_kind)
|
|
54011
|
+
initial_pnl: this.pnl(reverse_kind),
|
|
54012
|
+
remaining_quantity: this.to_df(reverse_avg.quantity - sell_quantity)
|
|
54001
54013
|
},
|
|
54002
54014
|
last_entry: rest.last_value.entry,
|
|
54003
|
-
first_entry: entries.at(-1)
|
|
54004
|
-
threshold
|
|
54015
|
+
first_entry: entries.at(-1)?.entry,
|
|
54016
|
+
threshold,
|
|
54017
|
+
spread: Math.abs(avg.entry - reverse_avg.entry),
|
|
54018
|
+
gap_loss: to_f2(Math.abs(avg.entry - reverse_avg.entry) * reverse_avg.quantity, "%.2f"),
|
|
54019
|
+
net_profit: incurred_loss + reverse_pnl
|
|
54005
54020
|
};
|
|
54006
54021
|
}
|
|
54007
54022
|
runIterations(payload) {
|
|
54008
|
-
const {
|
|
54023
|
+
const {
|
|
54024
|
+
kind,
|
|
54025
|
+
iterations,
|
|
54026
|
+
ignore_entries = false,
|
|
54027
|
+
reduce_ratio = 1,
|
|
54028
|
+
sell_factor = 1
|
|
54029
|
+
} = payload;
|
|
54009
54030
|
const reverse_kind = kind == "long" ? "short" : "long";
|
|
54010
54031
|
const result = [];
|
|
54011
54032
|
let position2 = {
|
|
54012
54033
|
long: this.position.long,
|
|
54013
54034
|
short: this.position.short
|
|
54014
54035
|
};
|
|
54036
|
+
let tp_percent_multiplier = 1;
|
|
54037
|
+
let short_tp_factor_multiplier = 1;
|
|
54015
54038
|
for (let i2 = 0;i2 < iterations; i2++) {
|
|
54016
54039
|
const instance = new Strategy({
|
|
54017
54040
|
long: position2.long,
|
|
54018
54041
|
short: position2.short,
|
|
54019
54042
|
config: {
|
|
54020
54043
|
...this.config,
|
|
54021
|
-
tp_percent: this.config.tp_percent
|
|
54044
|
+
tp_percent: this.config.tp_percent * tp_percent_multiplier,
|
|
54045
|
+
short_tp_factor: this.config.short_tp_factor * short_tp_factor_multiplier
|
|
54022
54046
|
}
|
|
54023
54047
|
});
|
|
54024
54048
|
const algorithm = instance.generateGapClosingAlgorithm({
|
|
54025
|
-
kind
|
|
54049
|
+
kind,
|
|
54050
|
+
ignore_entries,
|
|
54051
|
+
reduce_ratio,
|
|
54052
|
+
sell_factor
|
|
54026
54053
|
});
|
|
54027
54054
|
if (!algorithm) {
|
|
54028
54055
|
console.log("No algorithm found");
|
|
@@ -54034,9 +54061,31 @@ class Strategy {
|
|
|
54034
54061
|
entry: algorithm[kind].avg_entry,
|
|
54035
54062
|
quantity: algorithm[kind].re_entry_quantity
|
|
54036
54063
|
};
|
|
54064
|
+
let reverse_entry = algorithm[reverse_kind].tp;
|
|
54065
|
+
let reverse_quantity = algorithm[reverse_kind].re_entry_quantity;
|
|
54066
|
+
if (algorithm[reverse_kind].remaining_quantity > 0) {
|
|
54067
|
+
const purchase_to_occur = {
|
|
54068
|
+
price: reverse_entry,
|
|
54069
|
+
quantity: algorithm[reverse_kind].remaining_quantity
|
|
54070
|
+
};
|
|
54071
|
+
const avg = determine_average_entry_and_size([
|
|
54072
|
+
purchase_to_occur,
|
|
54073
|
+
{
|
|
54074
|
+
price: algorithm[reverse_kind].avg_entry,
|
|
54075
|
+
quantity: reverse_quantity - algorithm[reverse_kind].remaining_quantity
|
|
54076
|
+
}
|
|
54077
|
+
], this.config.global_config.decimal_places, this.config.global_config.price_places);
|
|
54078
|
+
reverse_entry = avg.entry;
|
|
54079
|
+
reverse_quantity = avg.quantity;
|
|
54080
|
+
if (reverse_kind === "short") {
|
|
54081
|
+
short_tp_factor_multiplier = 2;
|
|
54082
|
+
} else {
|
|
54083
|
+
tp_percent_multiplier = 2;
|
|
54084
|
+
}
|
|
54085
|
+
}
|
|
54037
54086
|
position2[reverse_kind] = {
|
|
54038
|
-
entry:
|
|
54039
|
-
quantity:
|
|
54087
|
+
entry: reverse_entry,
|
|
54088
|
+
quantity: reverse_quantity
|
|
54040
54089
|
};
|
|
54041
54090
|
}
|
|
54042
54091
|
return result;
|
|
@@ -57669,6 +57718,9 @@ class ExchangeAccount {
|
|
|
57669
57718
|
if (threshold_qty > 0 && track_position.quantity >= threshold_qty) {
|
|
57670
57719
|
should_place_order = true;
|
|
57671
57720
|
}
|
|
57721
|
+
if (focus_position.quantity === 0) {
|
|
57722
|
+
should_place_order = true;
|
|
57723
|
+
}
|
|
57672
57724
|
if (track_position.quantity !== focus_position.quantity && focus_position.quantity < track_position.quantity && should_place_order) {
|
|
57673
57725
|
const remaining_quantity = Math.abs(track_position.quantity - focus_position.quantity);
|
|
57674
57726
|
await this.placeMarketOrder({
|
package/dist/index.d.ts
CHANGED
|
@@ -564,6 +564,9 @@ export declare class Strategy {
|
|
|
564
564
|
get short_tp(): number;
|
|
565
565
|
generateGapClosingAlgorithm(payload: {
|
|
566
566
|
kind: "long" | "short";
|
|
567
|
+
ignore_entries?: boolean;
|
|
568
|
+
reduce_ratio?: number;
|
|
569
|
+
sell_factor?: number;
|
|
567
570
|
}): {
|
|
568
571
|
[x: string]: any;
|
|
569
572
|
risk: number;
|
|
@@ -571,11 +574,17 @@ export declare class Strategy {
|
|
|
571
574
|
last_entry: any;
|
|
572
575
|
first_entry: any;
|
|
573
576
|
threshold: any;
|
|
577
|
+
spread: number;
|
|
578
|
+
gap_loss: number;
|
|
579
|
+
net_profit: number;
|
|
574
580
|
};
|
|
575
581
|
runIterations(payload: {
|
|
576
582
|
kind: "long" | "short";
|
|
577
583
|
iterations: number;
|
|
578
584
|
risk_reward?: number;
|
|
585
|
+
ignore_entries?: boolean;
|
|
586
|
+
reduce_ratio?: number;
|
|
587
|
+
sell_factor?: number;
|
|
579
588
|
}): {
|
|
580
589
|
[x: string]: any;
|
|
581
590
|
risk: number;
|
|
@@ -583,6 +592,9 @@ export declare class Strategy {
|
|
|
583
592
|
last_entry: any;
|
|
584
593
|
first_entry: any;
|
|
585
594
|
threshold: any;
|
|
595
|
+
spread: number;
|
|
596
|
+
gap_loss: number;
|
|
597
|
+
net_profit: number;
|
|
586
598
|
}[];
|
|
587
599
|
getPositionAfterTp(payload: {
|
|
588
600
|
kind: "long" | "short";
|
|
@@ -1463,6 +1475,9 @@ declare class ExchangeAccount$1 {
|
|
|
1463
1475
|
last_entry: any;
|
|
1464
1476
|
first_entry: any;
|
|
1465
1477
|
threshold: any;
|
|
1478
|
+
spread: number;
|
|
1479
|
+
gap_loss: number;
|
|
1480
|
+
net_profit: number;
|
|
1466
1481
|
}[]>;
|
|
1467
1482
|
getCurrentRun(payload: {
|
|
1468
1483
|
symbol: string;
|
package/dist/index.js
CHANGED
|
@@ -53845,7 +53845,12 @@ 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";
|
|
@@ -53872,7 +53877,10 @@ class Strategy {
|
|
|
53872
53877
|
if (!app_config) {
|
|
53873
53878
|
return null;
|
|
53874
53879
|
}
|
|
53875
|
-
|
|
53880
|
+
let { entries, ...rest } = app_config;
|
|
53881
|
+
if (ignore_entries) {
|
|
53882
|
+
entries = [];
|
|
53883
|
+
}
|
|
53876
53884
|
const risk = this.to_f(rest.risk_per_trade);
|
|
53877
53885
|
const adjusted_focus_entries = entries.map((entry2) => {
|
|
53878
53886
|
let adjusted_price = entry2.price;
|
|
@@ -53894,7 +53902,7 @@ class Strategy {
|
|
|
53894
53902
|
quantity
|
|
53895
53903
|
}
|
|
53896
53904
|
]), rest.decimal_places, rest.price_places);
|
|
53897
|
-
const focus_loss = this.to_f((avg.price - second_payload.stop) * avg.quantity);
|
|
53905
|
+
const focus_loss = this.to_f(Math.abs(avg.price - second_payload.stop) * avg.quantity);
|
|
53898
53906
|
let below_reverse_entries = kind === "long" ? entries.filter((u) => {
|
|
53899
53907
|
return u.entry < (reverse_position.entry || focus_position.entry);
|
|
53900
53908
|
}) : entries.filter((u) => {
|
|
@@ -53921,18 +53929,20 @@ class Strategy {
|
|
|
53921
53929
|
quantity: reverse_position.quantity
|
|
53922
53930
|
}
|
|
53923
53931
|
]), rest.decimal_places, rest.price_places);
|
|
53924
|
-
const
|
|
53932
|
+
const sell_quantity = this.to_df(reverse_avg.quantity * sell_factor);
|
|
53933
|
+
const reverse_pnl = this.to_f(Math.abs(reverse_avg.price - second_payload.stop) * sell_quantity);
|
|
53925
53934
|
const fee_to_pay = this.calculate_fee({
|
|
53926
53935
|
price: avg.entry,
|
|
53927
53936
|
quantity: avg.quantity
|
|
53928
53937
|
}) + this.calculate_fee({
|
|
53929
53938
|
price: reverse_avg.entry,
|
|
53930
|
-
quantity:
|
|
53939
|
+
quantity: sell_quantity
|
|
53931
53940
|
});
|
|
53932
53941
|
const net_reverse_pnl = reverse_pnl - fee_to_pay;
|
|
53933
|
-
const ratio = net_reverse_pnl / focus_loss;
|
|
53942
|
+
const ratio = net_reverse_pnl * reduce_ratio / focus_loss;
|
|
53934
53943
|
const quantity_to_sell = this.to_df(ratio * avg.quantity);
|
|
53935
53944
|
const remaining_quantity = this.to_df(avg.quantity - quantity_to_sell);
|
|
53945
|
+
const incurred_loss = this.to_f((avg.price - second_payload.stop) * quantity_to_sell);
|
|
53936
53946
|
return {
|
|
53937
53947
|
risk,
|
|
53938
53948
|
risk_reward: this.config.risk_reward,
|
|
@@ -53944,7 +53954,8 @@ class Strategy {
|
|
|
53944
53954
|
stop_quantity: quantity_to_sell,
|
|
53945
53955
|
re_entry_quantity: remaining_quantity,
|
|
53946
53956
|
initial_pnl: this.pnl(kind),
|
|
53947
|
-
tp: second_payload.entry
|
|
53957
|
+
tp: second_payload.entry,
|
|
53958
|
+
incurred_loss
|
|
53948
53959
|
},
|
|
53949
53960
|
[reverse_kind]: {
|
|
53950
53961
|
avg_entry: reverse_avg.entry,
|
|
@@ -53952,32 +53963,48 @@ class Strategy {
|
|
|
53952
53963
|
pnl: reverse_pnl,
|
|
53953
53964
|
tp: second_payload.stop,
|
|
53954
53965
|
re_entry_quantity: remaining_quantity,
|
|
53955
|
-
initial_pnl: this.pnl(reverse_kind)
|
|
53966
|
+
initial_pnl: this.pnl(reverse_kind),
|
|
53967
|
+
remaining_quantity: this.to_df(reverse_avg.quantity - sell_quantity)
|
|
53956
53968
|
},
|
|
53957
53969
|
last_entry: rest.last_value.entry,
|
|
53958
|
-
first_entry: entries.at(-1)
|
|
53959
|
-
threshold
|
|
53970
|
+
first_entry: entries.at(-1)?.entry,
|
|
53971
|
+
threshold,
|
|
53972
|
+
spread: Math.abs(avg.entry - reverse_avg.entry),
|
|
53973
|
+
gap_loss: to_f2(Math.abs(avg.entry - reverse_avg.entry) * reverse_avg.quantity, "%.2f"),
|
|
53974
|
+
net_profit: incurred_loss + reverse_pnl
|
|
53960
53975
|
};
|
|
53961
53976
|
}
|
|
53962
53977
|
runIterations(payload) {
|
|
53963
|
-
const {
|
|
53978
|
+
const {
|
|
53979
|
+
kind,
|
|
53980
|
+
iterations,
|
|
53981
|
+
ignore_entries = false,
|
|
53982
|
+
reduce_ratio = 1,
|
|
53983
|
+
sell_factor = 1
|
|
53984
|
+
} = payload;
|
|
53964
53985
|
const reverse_kind = kind == "long" ? "short" : "long";
|
|
53965
53986
|
const result = [];
|
|
53966
53987
|
let position2 = {
|
|
53967
53988
|
long: this.position.long,
|
|
53968
53989
|
short: this.position.short
|
|
53969
53990
|
};
|
|
53991
|
+
let tp_percent_multiplier = 1;
|
|
53992
|
+
let short_tp_factor_multiplier = 1;
|
|
53970
53993
|
for (let i2 = 0;i2 < iterations; i2++) {
|
|
53971
53994
|
const instance = new Strategy({
|
|
53972
53995
|
long: position2.long,
|
|
53973
53996
|
short: position2.short,
|
|
53974
53997
|
config: {
|
|
53975
53998
|
...this.config,
|
|
53976
|
-
tp_percent: this.config.tp_percent
|
|
53999
|
+
tp_percent: this.config.tp_percent * tp_percent_multiplier,
|
|
54000
|
+
short_tp_factor: this.config.short_tp_factor * short_tp_factor_multiplier
|
|
53977
54001
|
}
|
|
53978
54002
|
});
|
|
53979
54003
|
const algorithm = instance.generateGapClosingAlgorithm({
|
|
53980
|
-
kind
|
|
54004
|
+
kind,
|
|
54005
|
+
ignore_entries,
|
|
54006
|
+
reduce_ratio,
|
|
54007
|
+
sell_factor
|
|
53981
54008
|
});
|
|
53982
54009
|
if (!algorithm) {
|
|
53983
54010
|
console.log("No algorithm found");
|
|
@@ -53989,9 +54016,31 @@ class Strategy {
|
|
|
53989
54016
|
entry: algorithm[kind].avg_entry,
|
|
53990
54017
|
quantity: algorithm[kind].re_entry_quantity
|
|
53991
54018
|
};
|
|
54019
|
+
let reverse_entry = algorithm[reverse_kind].tp;
|
|
54020
|
+
let reverse_quantity = algorithm[reverse_kind].re_entry_quantity;
|
|
54021
|
+
if (algorithm[reverse_kind].remaining_quantity > 0) {
|
|
54022
|
+
const purchase_to_occur = {
|
|
54023
|
+
price: reverse_entry,
|
|
54024
|
+
quantity: algorithm[reverse_kind].remaining_quantity
|
|
54025
|
+
};
|
|
54026
|
+
const avg = determine_average_entry_and_size([
|
|
54027
|
+
purchase_to_occur,
|
|
54028
|
+
{
|
|
54029
|
+
price: algorithm[reverse_kind].avg_entry,
|
|
54030
|
+
quantity: reverse_quantity - algorithm[reverse_kind].remaining_quantity
|
|
54031
|
+
}
|
|
54032
|
+
], this.config.global_config.decimal_places, this.config.global_config.price_places);
|
|
54033
|
+
reverse_entry = avg.entry;
|
|
54034
|
+
reverse_quantity = avg.quantity;
|
|
54035
|
+
if (reverse_kind === "short") {
|
|
54036
|
+
short_tp_factor_multiplier = 2;
|
|
54037
|
+
} else {
|
|
54038
|
+
tp_percent_multiplier = 2;
|
|
54039
|
+
}
|
|
54040
|
+
}
|
|
53992
54041
|
position2[reverse_kind] = {
|
|
53993
|
-
entry:
|
|
53994
|
-
quantity:
|
|
54042
|
+
entry: reverse_entry,
|
|
54043
|
+
quantity: reverse_quantity
|
|
53995
54044
|
};
|
|
53996
54045
|
}
|
|
53997
54046
|
return result;
|
|
@@ -57624,6 +57673,9 @@ class ExchangeAccount {
|
|
|
57624
57673
|
if (threshold_qty > 0 && track_position.quantity >= threshold_qty) {
|
|
57625
57674
|
should_place_order = true;
|
|
57626
57675
|
}
|
|
57676
|
+
if (focus_position.quantity === 0) {
|
|
57677
|
+
should_place_order = true;
|
|
57678
|
+
}
|
|
57627
57679
|
if (track_position.quantity !== focus_position.quantity && focus_position.quantity < track_position.quantity && should_place_order) {
|
|
57628
57680
|
const remaining_quantity = Math.abs(track_position.quantity - focus_position.quantity);
|
|
57629
57681
|
await this.placeMarketOrder({
|
package/dist/mcp-server.cjs
CHANGED
|
@@ -60581,7 +60581,12 @@ 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";
|
|
@@ -60608,7 +60613,10 @@ class Strategy {
|
|
|
60608
60613
|
if (!app_config) {
|
|
60609
60614
|
return null;
|
|
60610
60615
|
}
|
|
60611
|
-
|
|
60616
|
+
let { entries, ...rest } = app_config;
|
|
60617
|
+
if (ignore_entries) {
|
|
60618
|
+
entries = [];
|
|
60619
|
+
}
|
|
60612
60620
|
const risk = this.to_f(rest.risk_per_trade);
|
|
60613
60621
|
const adjusted_focus_entries = entries.map((entry2) => {
|
|
60614
60622
|
let adjusted_price = entry2.price;
|
|
@@ -60630,7 +60638,7 @@ class Strategy {
|
|
|
60630
60638
|
quantity
|
|
60631
60639
|
}
|
|
60632
60640
|
]), rest.decimal_places, rest.price_places);
|
|
60633
|
-
const focus_loss = this.to_f((avg.price - second_payload.stop) * avg.quantity);
|
|
60641
|
+
const focus_loss = this.to_f(Math.abs(avg.price - second_payload.stop) * avg.quantity);
|
|
60634
60642
|
let below_reverse_entries = kind === "long" ? entries.filter((u) => {
|
|
60635
60643
|
return u.entry < (reverse_position.entry || focus_position.entry);
|
|
60636
60644
|
}) : entries.filter((u) => {
|
|
@@ -60657,18 +60665,20 @@ class Strategy {
|
|
|
60657
60665
|
quantity: reverse_position.quantity
|
|
60658
60666
|
}
|
|
60659
60667
|
]), rest.decimal_places, rest.price_places);
|
|
60660
|
-
const
|
|
60668
|
+
const sell_quantity = this.to_df(reverse_avg.quantity * sell_factor);
|
|
60669
|
+
const reverse_pnl = this.to_f(Math.abs(reverse_avg.price - second_payload.stop) * sell_quantity);
|
|
60661
60670
|
const fee_to_pay = this.calculate_fee({
|
|
60662
60671
|
price: avg.entry,
|
|
60663
60672
|
quantity: avg.quantity
|
|
60664
60673
|
}) + this.calculate_fee({
|
|
60665
60674
|
price: reverse_avg.entry,
|
|
60666
|
-
quantity:
|
|
60675
|
+
quantity: sell_quantity
|
|
60667
60676
|
});
|
|
60668
60677
|
const net_reverse_pnl = reverse_pnl - fee_to_pay;
|
|
60669
|
-
const ratio = net_reverse_pnl / focus_loss;
|
|
60678
|
+
const ratio = net_reverse_pnl * reduce_ratio / focus_loss;
|
|
60670
60679
|
const quantity_to_sell = this.to_df(ratio * avg.quantity);
|
|
60671
60680
|
const remaining_quantity = this.to_df(avg.quantity - quantity_to_sell);
|
|
60681
|
+
const incurred_loss = this.to_f((avg.price - second_payload.stop) * quantity_to_sell);
|
|
60672
60682
|
return {
|
|
60673
60683
|
risk,
|
|
60674
60684
|
risk_reward: this.config.risk_reward,
|
|
@@ -60680,7 +60690,8 @@ class Strategy {
|
|
|
60680
60690
|
stop_quantity: quantity_to_sell,
|
|
60681
60691
|
re_entry_quantity: remaining_quantity,
|
|
60682
60692
|
initial_pnl: this.pnl(kind),
|
|
60683
|
-
tp: second_payload.entry
|
|
60693
|
+
tp: second_payload.entry,
|
|
60694
|
+
incurred_loss
|
|
60684
60695
|
},
|
|
60685
60696
|
[reverse_kind]: {
|
|
60686
60697
|
avg_entry: reverse_avg.entry,
|
|
@@ -60688,32 +60699,48 @@ class Strategy {
|
|
|
60688
60699
|
pnl: reverse_pnl,
|
|
60689
60700
|
tp: second_payload.stop,
|
|
60690
60701
|
re_entry_quantity: remaining_quantity,
|
|
60691
|
-
initial_pnl: this.pnl(reverse_kind)
|
|
60702
|
+
initial_pnl: this.pnl(reverse_kind),
|
|
60703
|
+
remaining_quantity: this.to_df(reverse_avg.quantity - sell_quantity)
|
|
60692
60704
|
},
|
|
60693
60705
|
last_entry: rest.last_value.entry,
|
|
60694
|
-
first_entry: entries.at(-1)
|
|
60695
|
-
threshold
|
|
60706
|
+
first_entry: entries.at(-1)?.entry,
|
|
60707
|
+
threshold,
|
|
60708
|
+
spread: Math.abs(avg.entry - reverse_avg.entry),
|
|
60709
|
+
gap_loss: to_f2(Math.abs(avg.entry - reverse_avg.entry) * reverse_avg.quantity, "%.2f"),
|
|
60710
|
+
net_profit: incurred_loss + reverse_pnl
|
|
60696
60711
|
};
|
|
60697
60712
|
}
|
|
60698
60713
|
runIterations(payload) {
|
|
60699
|
-
const {
|
|
60714
|
+
const {
|
|
60715
|
+
kind,
|
|
60716
|
+
iterations,
|
|
60717
|
+
ignore_entries = false,
|
|
60718
|
+
reduce_ratio = 1,
|
|
60719
|
+
sell_factor = 1
|
|
60720
|
+
} = payload;
|
|
60700
60721
|
const reverse_kind = kind == "long" ? "short" : "long";
|
|
60701
60722
|
const result = [];
|
|
60702
60723
|
let position2 = {
|
|
60703
60724
|
long: this.position.long,
|
|
60704
60725
|
short: this.position.short
|
|
60705
60726
|
};
|
|
60727
|
+
let tp_percent_multiplier = 1;
|
|
60728
|
+
let short_tp_factor_multiplier = 1;
|
|
60706
60729
|
for (let i2 = 0;i2 < iterations; i2++) {
|
|
60707
60730
|
const instance = new Strategy({
|
|
60708
60731
|
long: position2.long,
|
|
60709
60732
|
short: position2.short,
|
|
60710
60733
|
config: {
|
|
60711
60734
|
...this.config,
|
|
60712
|
-
tp_percent: this.config.tp_percent
|
|
60735
|
+
tp_percent: this.config.tp_percent * tp_percent_multiplier,
|
|
60736
|
+
short_tp_factor: this.config.short_tp_factor * short_tp_factor_multiplier
|
|
60713
60737
|
}
|
|
60714
60738
|
});
|
|
60715
60739
|
const algorithm = instance.generateGapClosingAlgorithm({
|
|
60716
|
-
kind
|
|
60740
|
+
kind,
|
|
60741
|
+
ignore_entries,
|
|
60742
|
+
reduce_ratio,
|
|
60743
|
+
sell_factor
|
|
60717
60744
|
});
|
|
60718
60745
|
if (!algorithm) {
|
|
60719
60746
|
console.log("No algorithm found");
|
|
@@ -60725,9 +60752,31 @@ class Strategy {
|
|
|
60725
60752
|
entry: algorithm[kind].avg_entry,
|
|
60726
60753
|
quantity: algorithm[kind].re_entry_quantity
|
|
60727
60754
|
};
|
|
60755
|
+
let reverse_entry = algorithm[reverse_kind].tp;
|
|
60756
|
+
let reverse_quantity = algorithm[reverse_kind].re_entry_quantity;
|
|
60757
|
+
if (algorithm[reverse_kind].remaining_quantity > 0) {
|
|
60758
|
+
const purchase_to_occur = {
|
|
60759
|
+
price: reverse_entry,
|
|
60760
|
+
quantity: algorithm[reverse_kind].remaining_quantity
|
|
60761
|
+
};
|
|
60762
|
+
const avg = determine_average_entry_and_size([
|
|
60763
|
+
purchase_to_occur,
|
|
60764
|
+
{
|
|
60765
|
+
price: algorithm[reverse_kind].avg_entry,
|
|
60766
|
+
quantity: reverse_quantity - algorithm[reverse_kind].remaining_quantity
|
|
60767
|
+
}
|
|
60768
|
+
], this.config.global_config.decimal_places, this.config.global_config.price_places);
|
|
60769
|
+
reverse_entry = avg.entry;
|
|
60770
|
+
reverse_quantity = avg.quantity;
|
|
60771
|
+
if (reverse_kind === "short") {
|
|
60772
|
+
short_tp_factor_multiplier = 2;
|
|
60773
|
+
} else {
|
|
60774
|
+
tp_percent_multiplier = 2;
|
|
60775
|
+
}
|
|
60776
|
+
}
|
|
60728
60777
|
position2[reverse_kind] = {
|
|
60729
|
-
entry:
|
|
60730
|
-
quantity:
|
|
60778
|
+
entry: reverse_entry,
|
|
60779
|
+
quantity: reverse_quantity
|
|
60731
60780
|
};
|
|
60732
60781
|
}
|
|
60733
60782
|
return result;
|
|
@@ -64360,6 +64409,9 @@ class ExchangeAccount {
|
|
|
64360
64409
|
if (threshold_qty > 0 && track_position.quantity >= threshold_qty) {
|
|
64361
64410
|
should_place_order = true;
|
|
64362
64411
|
}
|
|
64412
|
+
if (focus_position.quantity === 0) {
|
|
64413
|
+
should_place_order = true;
|
|
64414
|
+
}
|
|
64363
64415
|
if (track_position.quantity !== focus_position.quantity && focus_position.quantity < track_position.quantity && should_place_order) {
|
|
64364
64416
|
const remaining_quantity = Math.abs(track_position.quantity - focus_position.quantity);
|
|
64365
64417
|
await this.placeMarketOrder({
|
package/dist/mcp-server.js
CHANGED
|
@@ -60558,7 +60558,12 @@ 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";
|
|
@@ -60585,7 +60590,10 @@ class Strategy {
|
|
|
60585
60590
|
if (!app_config) {
|
|
60586
60591
|
return null;
|
|
60587
60592
|
}
|
|
60588
|
-
|
|
60593
|
+
let { entries, ...rest } = app_config;
|
|
60594
|
+
if (ignore_entries) {
|
|
60595
|
+
entries = [];
|
|
60596
|
+
}
|
|
60589
60597
|
const risk = this.to_f(rest.risk_per_trade);
|
|
60590
60598
|
const adjusted_focus_entries = entries.map((entry2) => {
|
|
60591
60599
|
let adjusted_price = entry2.price;
|
|
@@ -60607,7 +60615,7 @@ class Strategy {
|
|
|
60607
60615
|
quantity
|
|
60608
60616
|
}
|
|
60609
60617
|
]), rest.decimal_places, rest.price_places);
|
|
60610
|
-
const focus_loss = this.to_f((avg.price - second_payload.stop) * avg.quantity);
|
|
60618
|
+
const focus_loss = this.to_f(Math.abs(avg.price - second_payload.stop) * avg.quantity);
|
|
60611
60619
|
let below_reverse_entries = kind === "long" ? entries.filter((u) => {
|
|
60612
60620
|
return u.entry < (reverse_position.entry || focus_position.entry);
|
|
60613
60621
|
}) : entries.filter((u) => {
|
|
@@ -60634,18 +60642,20 @@ class Strategy {
|
|
|
60634
60642
|
quantity: reverse_position.quantity
|
|
60635
60643
|
}
|
|
60636
60644
|
]), rest.decimal_places, rest.price_places);
|
|
60637
|
-
const
|
|
60645
|
+
const sell_quantity = this.to_df(reverse_avg.quantity * sell_factor);
|
|
60646
|
+
const reverse_pnl = this.to_f(Math.abs(reverse_avg.price - second_payload.stop) * sell_quantity);
|
|
60638
60647
|
const fee_to_pay = this.calculate_fee({
|
|
60639
60648
|
price: avg.entry,
|
|
60640
60649
|
quantity: avg.quantity
|
|
60641
60650
|
}) + this.calculate_fee({
|
|
60642
60651
|
price: reverse_avg.entry,
|
|
60643
|
-
quantity:
|
|
60652
|
+
quantity: sell_quantity
|
|
60644
60653
|
});
|
|
60645
60654
|
const net_reverse_pnl = reverse_pnl - fee_to_pay;
|
|
60646
|
-
const ratio = net_reverse_pnl / focus_loss;
|
|
60655
|
+
const ratio = net_reverse_pnl * reduce_ratio / focus_loss;
|
|
60647
60656
|
const quantity_to_sell = this.to_df(ratio * avg.quantity);
|
|
60648
60657
|
const remaining_quantity = this.to_df(avg.quantity - quantity_to_sell);
|
|
60658
|
+
const incurred_loss = this.to_f((avg.price - second_payload.stop) * quantity_to_sell);
|
|
60649
60659
|
return {
|
|
60650
60660
|
risk,
|
|
60651
60661
|
risk_reward: this.config.risk_reward,
|
|
@@ -60657,7 +60667,8 @@ class Strategy {
|
|
|
60657
60667
|
stop_quantity: quantity_to_sell,
|
|
60658
60668
|
re_entry_quantity: remaining_quantity,
|
|
60659
60669
|
initial_pnl: this.pnl(kind),
|
|
60660
|
-
tp: second_payload.entry
|
|
60670
|
+
tp: second_payload.entry,
|
|
60671
|
+
incurred_loss
|
|
60661
60672
|
},
|
|
60662
60673
|
[reverse_kind]: {
|
|
60663
60674
|
avg_entry: reverse_avg.entry,
|
|
@@ -60665,32 +60676,48 @@ class Strategy {
|
|
|
60665
60676
|
pnl: reverse_pnl,
|
|
60666
60677
|
tp: second_payload.stop,
|
|
60667
60678
|
re_entry_quantity: remaining_quantity,
|
|
60668
|
-
initial_pnl: this.pnl(reverse_kind)
|
|
60679
|
+
initial_pnl: this.pnl(reverse_kind),
|
|
60680
|
+
remaining_quantity: this.to_df(reverse_avg.quantity - sell_quantity)
|
|
60669
60681
|
},
|
|
60670
60682
|
last_entry: rest.last_value.entry,
|
|
60671
|
-
first_entry: entries.at(-1)
|
|
60672
|
-
threshold
|
|
60683
|
+
first_entry: entries.at(-1)?.entry,
|
|
60684
|
+
threshold,
|
|
60685
|
+
spread: Math.abs(avg.entry - reverse_avg.entry),
|
|
60686
|
+
gap_loss: to_f2(Math.abs(avg.entry - reverse_avg.entry) * reverse_avg.quantity, "%.2f"),
|
|
60687
|
+
net_profit: incurred_loss + reverse_pnl
|
|
60673
60688
|
};
|
|
60674
60689
|
}
|
|
60675
60690
|
runIterations(payload) {
|
|
60676
|
-
const {
|
|
60691
|
+
const {
|
|
60692
|
+
kind,
|
|
60693
|
+
iterations,
|
|
60694
|
+
ignore_entries = false,
|
|
60695
|
+
reduce_ratio = 1,
|
|
60696
|
+
sell_factor = 1
|
|
60697
|
+
} = payload;
|
|
60677
60698
|
const reverse_kind = kind == "long" ? "short" : "long";
|
|
60678
60699
|
const result = [];
|
|
60679
60700
|
let position2 = {
|
|
60680
60701
|
long: this.position.long,
|
|
60681
60702
|
short: this.position.short
|
|
60682
60703
|
};
|
|
60704
|
+
let tp_percent_multiplier = 1;
|
|
60705
|
+
let short_tp_factor_multiplier = 1;
|
|
60683
60706
|
for (let i2 = 0;i2 < iterations; i2++) {
|
|
60684
60707
|
const instance = new Strategy({
|
|
60685
60708
|
long: position2.long,
|
|
60686
60709
|
short: position2.short,
|
|
60687
60710
|
config: {
|
|
60688
60711
|
...this.config,
|
|
60689
|
-
tp_percent: this.config.tp_percent
|
|
60712
|
+
tp_percent: this.config.tp_percent * tp_percent_multiplier,
|
|
60713
|
+
short_tp_factor: this.config.short_tp_factor * short_tp_factor_multiplier
|
|
60690
60714
|
}
|
|
60691
60715
|
});
|
|
60692
60716
|
const algorithm = instance.generateGapClosingAlgorithm({
|
|
60693
|
-
kind
|
|
60717
|
+
kind,
|
|
60718
|
+
ignore_entries,
|
|
60719
|
+
reduce_ratio,
|
|
60720
|
+
sell_factor
|
|
60694
60721
|
});
|
|
60695
60722
|
if (!algorithm) {
|
|
60696
60723
|
console.log("No algorithm found");
|
|
@@ -60702,9 +60729,31 @@ class Strategy {
|
|
|
60702
60729
|
entry: algorithm[kind].avg_entry,
|
|
60703
60730
|
quantity: algorithm[kind].re_entry_quantity
|
|
60704
60731
|
};
|
|
60732
|
+
let reverse_entry = algorithm[reverse_kind].tp;
|
|
60733
|
+
let reverse_quantity = algorithm[reverse_kind].re_entry_quantity;
|
|
60734
|
+
if (algorithm[reverse_kind].remaining_quantity > 0) {
|
|
60735
|
+
const purchase_to_occur = {
|
|
60736
|
+
price: reverse_entry,
|
|
60737
|
+
quantity: algorithm[reverse_kind].remaining_quantity
|
|
60738
|
+
};
|
|
60739
|
+
const avg = determine_average_entry_and_size([
|
|
60740
|
+
purchase_to_occur,
|
|
60741
|
+
{
|
|
60742
|
+
price: algorithm[reverse_kind].avg_entry,
|
|
60743
|
+
quantity: reverse_quantity - algorithm[reverse_kind].remaining_quantity
|
|
60744
|
+
}
|
|
60745
|
+
], this.config.global_config.decimal_places, this.config.global_config.price_places);
|
|
60746
|
+
reverse_entry = avg.entry;
|
|
60747
|
+
reverse_quantity = avg.quantity;
|
|
60748
|
+
if (reverse_kind === "short") {
|
|
60749
|
+
short_tp_factor_multiplier = 2;
|
|
60750
|
+
} else {
|
|
60751
|
+
tp_percent_multiplier = 2;
|
|
60752
|
+
}
|
|
60753
|
+
}
|
|
60705
60754
|
position2[reverse_kind] = {
|
|
60706
|
-
entry:
|
|
60707
|
-
quantity:
|
|
60755
|
+
entry: reverse_entry,
|
|
60756
|
+
quantity: reverse_quantity
|
|
60708
60757
|
};
|
|
60709
60758
|
}
|
|
60710
60759
|
return result;
|
|
@@ -64337,6 +64386,9 @@ class ExchangeAccount {
|
|
|
64337
64386
|
if (threshold_qty > 0 && track_position.quantity >= threshold_qty) {
|
|
64338
64387
|
should_place_order = true;
|
|
64339
64388
|
}
|
|
64389
|
+
if (focus_position.quantity === 0) {
|
|
64390
|
+
should_place_order = true;
|
|
64391
|
+
}
|
|
64340
64392
|
if (track_position.quantity !== focus_position.quantity && focus_position.quantity < track_position.quantity && should_place_order) {
|
|
64341
64393
|
const remaining_quantity = Math.abs(track_position.quantity - focus_position.quantity);
|
|
64342
64394
|
await this.placeMarketOrder({
|