@gbozee/ultimate 0.0.2-74 → 0.0.2-76
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 +37 -6
- package/dist/frontend-index.js +55 -15
- package/dist/index.cjs +101 -19
- package/dist/index.d.ts +102 -9
- package/dist/index.js +101 -19
- package/dist/mcp-server.cjs +101 -19
- package/dist/mcp-server.js +101 -19
- package/package.json +1 -1
package/dist/frontend-index.d.ts
CHANGED
|
@@ -401,6 +401,20 @@ export declare function getRiskReward(payload: {
|
|
|
401
401
|
export type StrategyPosition = {
|
|
402
402
|
entry: number;
|
|
403
403
|
quantity: number;
|
|
404
|
+
avg_price?: number;
|
|
405
|
+
};
|
|
406
|
+
export type GapCloserResult = {
|
|
407
|
+
avg_entry: number;
|
|
408
|
+
avg_size: number;
|
|
409
|
+
loss: number;
|
|
410
|
+
stop: number;
|
|
411
|
+
stop_quantity: number;
|
|
412
|
+
re_entry_quantity: number;
|
|
413
|
+
initial_pnl: number;
|
|
414
|
+
tp: number;
|
|
415
|
+
incurred_loss: number;
|
|
416
|
+
pnl: number;
|
|
417
|
+
remaining_quantity: number;
|
|
404
418
|
};
|
|
405
419
|
export type Config = {
|
|
406
420
|
tp_percent: number;
|
|
@@ -442,15 +456,31 @@ export declare class Strategy {
|
|
|
442
456
|
reduce_ratio?: number;
|
|
443
457
|
sell_factor?: number;
|
|
444
458
|
}): {
|
|
445
|
-
[x: string]: any;
|
|
446
|
-
risk: number;
|
|
447
|
-
risk_reward: number;
|
|
448
459
|
last_entry: any;
|
|
449
460
|
first_entry: any;
|
|
450
461
|
threshold: any;
|
|
462
|
+
risk: number;
|
|
463
|
+
risk_reward: number;
|
|
451
464
|
spread: number;
|
|
452
465
|
gap_loss: number;
|
|
453
466
|
net_profit: number;
|
|
467
|
+
long: GapCloserResult;
|
|
468
|
+
short: GapCloserResult;
|
|
469
|
+
};
|
|
470
|
+
gapCloserHelper(payload: {
|
|
471
|
+
risk: number;
|
|
472
|
+
entries?: any[];
|
|
473
|
+
kind: "long" | "short";
|
|
474
|
+
sell_factor?: number;
|
|
475
|
+
reduce_ratio?: number;
|
|
476
|
+
}): {
|
|
477
|
+
risk: number;
|
|
478
|
+
risk_reward: number;
|
|
479
|
+
spread: number;
|
|
480
|
+
gap_loss: number;
|
|
481
|
+
net_profit: number;
|
|
482
|
+
long: GapCloserResult;
|
|
483
|
+
short: GapCloserResult;
|
|
454
484
|
};
|
|
455
485
|
runIterations(payload: {
|
|
456
486
|
kind: "long" | "short";
|
|
@@ -460,15 +490,16 @@ export declare class Strategy {
|
|
|
460
490
|
reduce_ratio?: number;
|
|
461
491
|
sell_factor?: number;
|
|
462
492
|
}): {
|
|
463
|
-
[x: string]: any;
|
|
464
|
-
risk: number;
|
|
465
|
-
risk_reward: number;
|
|
466
493
|
last_entry: any;
|
|
467
494
|
first_entry: any;
|
|
468
495
|
threshold: any;
|
|
496
|
+
risk: number;
|
|
497
|
+
risk_reward: number;
|
|
469
498
|
spread: number;
|
|
470
499
|
gap_loss: number;
|
|
471
500
|
net_profit: number;
|
|
501
|
+
long: GapCloserResult;
|
|
502
|
+
short: GapCloserResult;
|
|
472
503
|
}[];
|
|
473
504
|
getPositionAfterTp(payload: {
|
|
474
505
|
kind: "long" | "short";
|
package/dist/frontend-index.js
CHANGED
|
@@ -1842,9 +1842,6 @@ class Strategy {
|
|
|
1842
1842
|
const reverse_position = this.position[reverse_kind];
|
|
1843
1843
|
let _entry = this.tp(kind);
|
|
1844
1844
|
let _stop = this.tp(reverse_kind);
|
|
1845
|
-
if (!_entry && !_stop) {
|
|
1846
|
-
return null;
|
|
1847
|
-
}
|
|
1848
1845
|
const second_payload = {
|
|
1849
1846
|
entry: _entry,
|
|
1850
1847
|
stop: _stop,
|
|
@@ -1859,14 +1856,60 @@ class Strategy {
|
|
|
1859
1856
|
};
|
|
1860
1857
|
console.log({ second_payload, third_payload });
|
|
1861
1858
|
const app_config = generateOptimumAppConfig(this.config.global_config, second_payload, third_payload);
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
entries =
|
|
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
|
+
}
|
|
1868
1870
|
}
|
|
1869
|
-
const risk = this.to_f(
|
|
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
|
+
};
|
|
1870
1913
|
const adjusted_focus_entries = entries.map((entry2) => {
|
|
1871
1914
|
let adjusted_price = entry2.price;
|
|
1872
1915
|
if (focus_position.quantity > 0) {
|
|
@@ -1886,7 +1929,7 @@ class Strategy {
|
|
|
1886
1929
|
price: entry,
|
|
1887
1930
|
quantity
|
|
1888
1931
|
}
|
|
1889
|
-
]),
|
|
1932
|
+
]), this.config.global_config.decimal_places, this.config.global_config.price_places);
|
|
1890
1933
|
const focus_loss = this.to_f(Math.abs(avg.price - second_payload.stop) * avg.quantity);
|
|
1891
1934
|
let below_reverse_entries = kind === "long" ? entries.filter((u) => {
|
|
1892
1935
|
return u.entry < (reverse_position.entry || focus_position.entry);
|
|
@@ -1913,7 +1956,7 @@ class Strategy {
|
|
|
1913
1956
|
price: reverse_position.entry,
|
|
1914
1957
|
quantity: reverse_position.quantity
|
|
1915
1958
|
}
|
|
1916
|
-
]),
|
|
1959
|
+
]), this.config.global_config.decimal_places, this.config.global_config.price_places);
|
|
1917
1960
|
const sell_quantity = this.to_df(reverse_avg.quantity * sell_factor);
|
|
1918
1961
|
const reverse_pnl = this.to_f(Math.abs(reverse_avg.price - second_payload.stop) * sell_quantity);
|
|
1919
1962
|
const fee_to_pay = this.calculate_fee({
|
|
@@ -1951,9 +1994,6 @@ class Strategy {
|
|
|
1951
1994
|
initial_pnl: this.pnl(reverse_kind),
|
|
1952
1995
|
remaining_quantity: this.to_df(reverse_avg.quantity - sell_quantity)
|
|
1953
1996
|
},
|
|
1954
|
-
last_entry: rest.last_value.entry,
|
|
1955
|
-
first_entry: entries.at(-1)?.entry,
|
|
1956
|
-
threshold,
|
|
1957
1997
|
spread: Math.abs(avg.entry - reverse_avg.entry),
|
|
1958
1998
|
gap_loss: to_f(Math.abs(avg.entry - reverse_avg.entry) * reverse_avg.quantity, "%.2f"),
|
|
1959
1999
|
net_profit: incurred_loss + reverse_pnl
|
package/dist/index.cjs
CHANGED
|
@@ -53902,9 +53902,6 @@ class Strategy {
|
|
|
53902
53902
|
const reverse_position = this.position[reverse_kind];
|
|
53903
53903
|
let _entry = this.tp(kind);
|
|
53904
53904
|
let _stop = this.tp(reverse_kind);
|
|
53905
|
-
if (!_entry && !_stop) {
|
|
53906
|
-
return null;
|
|
53907
|
-
}
|
|
53908
53905
|
const second_payload = {
|
|
53909
53906
|
entry: _entry,
|
|
53910
53907
|
stop: _stop,
|
|
@@ -53919,14 +53916,60 @@ class Strategy {
|
|
|
53919
53916
|
};
|
|
53920
53917
|
console.log({ second_payload, third_payload });
|
|
53921
53918
|
const app_config = generateOptimumAppConfig(this.config.global_config, second_payload, third_payload);
|
|
53922
|
-
|
|
53923
|
-
|
|
53924
|
-
|
|
53925
|
-
|
|
53926
|
-
|
|
53927
|
-
entries =
|
|
53928
|
-
|
|
53929
|
-
|
|
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
|
+
};
|
|
53930
53973
|
const adjusted_focus_entries = entries.map((entry2) => {
|
|
53931
53974
|
let adjusted_price = entry2.price;
|
|
53932
53975
|
if (focus_position.quantity > 0) {
|
|
@@ -53946,7 +53989,7 @@ class Strategy {
|
|
|
53946
53989
|
price: entry,
|
|
53947
53990
|
quantity
|
|
53948
53991
|
}
|
|
53949
|
-
]),
|
|
53992
|
+
]), this.config.global_config.decimal_places, this.config.global_config.price_places);
|
|
53950
53993
|
const focus_loss = this.to_f(Math.abs(avg.price - second_payload.stop) * avg.quantity);
|
|
53951
53994
|
let below_reverse_entries = kind === "long" ? entries.filter((u) => {
|
|
53952
53995
|
return u.entry < (reverse_position.entry || focus_position.entry);
|
|
@@ -53973,7 +54016,7 @@ class Strategy {
|
|
|
53973
54016
|
price: reverse_position.entry,
|
|
53974
54017
|
quantity: reverse_position.quantity
|
|
53975
54018
|
}
|
|
53976
|
-
]),
|
|
54019
|
+
]), this.config.global_config.decimal_places, this.config.global_config.price_places);
|
|
53977
54020
|
const sell_quantity = this.to_df(reverse_avg.quantity * sell_factor);
|
|
53978
54021
|
const reverse_pnl = this.to_f(Math.abs(reverse_avg.price - second_payload.stop) * sell_quantity);
|
|
53979
54022
|
const fee_to_pay = this.calculate_fee({
|
|
@@ -54011,9 +54054,6 @@ class Strategy {
|
|
|
54011
54054
|
initial_pnl: this.pnl(reverse_kind),
|
|
54012
54055
|
remaining_quantity: this.to_df(reverse_avg.quantity - sell_quantity)
|
|
54013
54056
|
},
|
|
54014
|
-
last_entry: rest.last_value.entry,
|
|
54015
|
-
first_entry: entries.at(-1)?.entry,
|
|
54016
|
-
threshold,
|
|
54017
54057
|
spread: Math.abs(avg.entry - reverse_avg.entry),
|
|
54018
54058
|
gap_loss: to_f2(Math.abs(avg.entry - reverse_avg.entry) * reverse_avg.quantity, "%.2f"),
|
|
54019
54059
|
net_profit: incurred_loss + reverse_pnl
|
|
@@ -57961,10 +58001,50 @@ class ExchangeAccount {
|
|
|
57961
58001
|
}
|
|
57962
58002
|
return 0;
|
|
57963
58003
|
}
|
|
58004
|
+
async buildOppositeTrades(payload) {
|
|
58005
|
+
const { symbol, kind, place } = payload;
|
|
58006
|
+
const strategy = await this.runSimulation({
|
|
58007
|
+
symbol,
|
|
58008
|
+
kind,
|
|
58009
|
+
raw: true
|
|
58010
|
+
});
|
|
58011
|
+
const result = strategy.generateOppositeTrades({
|
|
58012
|
+
kind,
|
|
58013
|
+
avg_entry: strategy.position[kind].avg_price
|
|
58014
|
+
});
|
|
58015
|
+
if (place && result?.kind) {
|
|
58016
|
+
const config2 = await this.getPositionConfig({
|
|
58017
|
+
symbol,
|
|
58018
|
+
kind: result.kind
|
|
58019
|
+
});
|
|
58020
|
+
await this.app_db.updateScheduledTrade(config2.id, {
|
|
58021
|
+
entry: result.entry,
|
|
58022
|
+
stop: result.stop,
|
|
58023
|
+
risk: result.risk_per_trade,
|
|
58024
|
+
profit_percent: result.profit_percent,
|
|
58025
|
+
risk_reward: result.risk_reward
|
|
58026
|
+
});
|
|
58027
|
+
await this.placeTrade({
|
|
58028
|
+
symbol,
|
|
58029
|
+
kind: result.kind,
|
|
58030
|
+
place: true,
|
|
58031
|
+
ignore_config: true
|
|
58032
|
+
});
|
|
58033
|
+
await this.placeTrade({
|
|
58034
|
+
symbol,
|
|
58035
|
+
kind: result.kind,
|
|
58036
|
+
place: true,
|
|
58037
|
+
stop: true,
|
|
58038
|
+
ignore_config: true
|
|
58039
|
+
});
|
|
58040
|
+
}
|
|
58041
|
+
return result;
|
|
58042
|
+
}
|
|
57964
58043
|
async runSimulation(payload) {
|
|
57965
58044
|
const { symbol, kind, iterations = 2, raw = false } = payload;
|
|
57966
58045
|
const positions = await this.syncAccount({
|
|
57967
|
-
symbol
|
|
58046
|
+
symbol,
|
|
58047
|
+
as_view: true
|
|
57968
58048
|
});
|
|
57969
58049
|
const symbol_config = await this.recomputeSymbolConfig({
|
|
57970
58050
|
symbol
|
|
@@ -57998,11 +58078,13 @@ class ExchangeAccount {
|
|
|
57998
58078
|
const strategy = new Strategy({
|
|
57999
58079
|
long: {
|
|
58000
58080
|
entry: long_position.entry,
|
|
58001
|
-
quantity: long_position.quantity
|
|
58081
|
+
quantity: long_position.quantity,
|
|
58082
|
+
avg_price: long_position.avg_price
|
|
58002
58083
|
},
|
|
58003
58084
|
short: {
|
|
58004
58085
|
entry: short_position.entry,
|
|
58005
|
-
quantity: short_position.quantity
|
|
58086
|
+
quantity: short_position.quantity,
|
|
58087
|
+
avg_price: short_position.avg_price
|
|
58006
58088
|
},
|
|
58007
58089
|
config: strategy_config
|
|
58008
58090
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -527,6 +527,20 @@ export declare class AppDatabase {
|
|
|
527
527
|
export type StrategyPosition = {
|
|
528
528
|
entry: number;
|
|
529
529
|
quantity: number;
|
|
530
|
+
avg_price?: number;
|
|
531
|
+
};
|
|
532
|
+
export type GapCloserResult = {
|
|
533
|
+
avg_entry: number;
|
|
534
|
+
avg_size: number;
|
|
535
|
+
loss: number;
|
|
536
|
+
stop: number;
|
|
537
|
+
stop_quantity: number;
|
|
538
|
+
re_entry_quantity: number;
|
|
539
|
+
initial_pnl: number;
|
|
540
|
+
tp: number;
|
|
541
|
+
incurred_loss: number;
|
|
542
|
+
pnl: number;
|
|
543
|
+
remaining_quantity: number;
|
|
530
544
|
};
|
|
531
545
|
export type Config = {
|
|
532
546
|
tp_percent: number;
|
|
@@ -568,15 +582,31 @@ export declare class Strategy {
|
|
|
568
582
|
reduce_ratio?: number;
|
|
569
583
|
sell_factor?: number;
|
|
570
584
|
}): {
|
|
571
|
-
[x: string]: any;
|
|
572
|
-
risk: number;
|
|
573
|
-
risk_reward: number;
|
|
574
585
|
last_entry: any;
|
|
575
586
|
first_entry: any;
|
|
576
587
|
threshold: any;
|
|
588
|
+
risk: number;
|
|
589
|
+
risk_reward: number;
|
|
577
590
|
spread: number;
|
|
578
591
|
gap_loss: number;
|
|
579
592
|
net_profit: number;
|
|
593
|
+
long: GapCloserResult;
|
|
594
|
+
short: GapCloserResult;
|
|
595
|
+
};
|
|
596
|
+
gapCloserHelper(payload: {
|
|
597
|
+
risk: number;
|
|
598
|
+
entries?: any[];
|
|
599
|
+
kind: "long" | "short";
|
|
600
|
+
sell_factor?: number;
|
|
601
|
+
reduce_ratio?: number;
|
|
602
|
+
}): {
|
|
603
|
+
risk: number;
|
|
604
|
+
risk_reward: number;
|
|
605
|
+
spread: number;
|
|
606
|
+
gap_loss: number;
|
|
607
|
+
net_profit: number;
|
|
608
|
+
long: GapCloserResult;
|
|
609
|
+
short: GapCloserResult;
|
|
580
610
|
};
|
|
581
611
|
runIterations(payload: {
|
|
582
612
|
kind: "long" | "short";
|
|
@@ -586,15 +616,16 @@ export declare class Strategy {
|
|
|
586
616
|
reduce_ratio?: number;
|
|
587
617
|
sell_factor?: number;
|
|
588
618
|
}): {
|
|
589
|
-
[x: string]: any;
|
|
590
|
-
risk: number;
|
|
591
|
-
risk_reward: number;
|
|
592
619
|
last_entry: any;
|
|
593
620
|
first_entry: any;
|
|
594
621
|
threshold: any;
|
|
622
|
+
risk: number;
|
|
623
|
+
risk_reward: number;
|
|
595
624
|
spread: number;
|
|
596
625
|
gap_loss: number;
|
|
597
626
|
net_profit: number;
|
|
627
|
+
long: GapCloserResult;
|
|
628
|
+
short: GapCloserResult;
|
|
598
629
|
}[];
|
|
599
630
|
getPositionAfterTp(payload: {
|
|
600
631
|
kind: "long" | "short";
|
|
@@ -1463,21 +1494,83 @@ declare class ExchangeAccount$1 {
|
|
|
1463
1494
|
symbol: string;
|
|
1464
1495
|
kind: "long" | "short";
|
|
1465
1496
|
}): Promise<number>;
|
|
1497
|
+
buildOppositeTrades(payload: {
|
|
1498
|
+
symbol: string;
|
|
1499
|
+
kind: "long" | "short";
|
|
1500
|
+
place?: boolean;
|
|
1501
|
+
}): Promise<{
|
|
1502
|
+
avg: {
|
|
1503
|
+
entry: number;
|
|
1504
|
+
price: number;
|
|
1505
|
+
quantity: number;
|
|
1506
|
+
};
|
|
1507
|
+
loss: number;
|
|
1508
|
+
profit_percent: number;
|
|
1509
|
+
fee: number;
|
|
1510
|
+
risk_per_trade: number;
|
|
1511
|
+
risk_reward: number;
|
|
1512
|
+
symbol?: string;
|
|
1513
|
+
focus: number;
|
|
1514
|
+
budget: number;
|
|
1515
|
+
support: number;
|
|
1516
|
+
resistance: number;
|
|
1517
|
+
percent_change: number;
|
|
1518
|
+
tradeSplit?: number;
|
|
1519
|
+
take_profit?: number;
|
|
1520
|
+
kind: "long" | "short";
|
|
1521
|
+
entry: number;
|
|
1522
|
+
stop: number;
|
|
1523
|
+
min_size: number;
|
|
1524
|
+
price_places?: string;
|
|
1525
|
+
strategy?: "quantity" | "entry";
|
|
1526
|
+
as_array?: boolean;
|
|
1527
|
+
decimal_places?: string;
|
|
1528
|
+
min_profit?: number;
|
|
1529
|
+
raw?: boolean;
|
|
1530
|
+
gap?: number;
|
|
1531
|
+
rr?: number;
|
|
1532
|
+
max_size?: number;
|
|
1533
|
+
}>;
|
|
1466
1534
|
runSimulation(payload: {
|
|
1467
1535
|
symbol: string;
|
|
1468
1536
|
kind: "long" | "short";
|
|
1469
1537
|
iterations?: number;
|
|
1470
1538
|
raw?: boolean;
|
|
1471
1539
|
}): Promise<Strategy | {
|
|
1472
|
-
[x: string]: any;
|
|
1473
|
-
risk: number;
|
|
1474
|
-
risk_reward: number;
|
|
1475
1540
|
last_entry: any;
|
|
1476
1541
|
first_entry: any;
|
|
1477
1542
|
threshold: any;
|
|
1543
|
+
risk: number;
|
|
1544
|
+
risk_reward: number;
|
|
1478
1545
|
spread: number;
|
|
1479
1546
|
gap_loss: number;
|
|
1480
1547
|
net_profit: number;
|
|
1548
|
+
long: {
|
|
1549
|
+
avg_entry: number;
|
|
1550
|
+
avg_size: number;
|
|
1551
|
+
loss: number;
|
|
1552
|
+
stop: number;
|
|
1553
|
+
stop_quantity: number;
|
|
1554
|
+
re_entry_quantity: number;
|
|
1555
|
+
initial_pnl: number;
|
|
1556
|
+
tp: number;
|
|
1557
|
+
incurred_loss: number;
|
|
1558
|
+
pnl: number;
|
|
1559
|
+
remaining_quantity: number;
|
|
1560
|
+
};
|
|
1561
|
+
short: {
|
|
1562
|
+
avg_entry: number;
|
|
1563
|
+
avg_size: number;
|
|
1564
|
+
loss: number;
|
|
1565
|
+
stop: number;
|
|
1566
|
+
stop_quantity: number;
|
|
1567
|
+
re_entry_quantity: number;
|
|
1568
|
+
initial_pnl: number;
|
|
1569
|
+
tp: number;
|
|
1570
|
+
incurred_loss: number;
|
|
1571
|
+
pnl: number;
|
|
1572
|
+
remaining_quantity: number;
|
|
1573
|
+
};
|
|
1481
1574
|
}[]>;
|
|
1482
1575
|
getCurrentRun(payload: {
|
|
1483
1576
|
symbol: string;
|
package/dist/index.js
CHANGED
|
@@ -53857,9 +53857,6 @@ class Strategy {
|
|
|
53857
53857
|
const reverse_position = this.position[reverse_kind];
|
|
53858
53858
|
let _entry = this.tp(kind);
|
|
53859
53859
|
let _stop = this.tp(reverse_kind);
|
|
53860
|
-
if (!_entry && !_stop) {
|
|
53861
|
-
return null;
|
|
53862
|
-
}
|
|
53863
53860
|
const second_payload = {
|
|
53864
53861
|
entry: _entry,
|
|
53865
53862
|
stop: _stop,
|
|
@@ -53874,14 +53871,60 @@ class Strategy {
|
|
|
53874
53871
|
};
|
|
53875
53872
|
console.log({ second_payload, third_payload });
|
|
53876
53873
|
const app_config = generateOptimumAppConfig(this.config.global_config, second_payload, third_payload);
|
|
53877
|
-
|
|
53878
|
-
|
|
53879
|
-
|
|
53880
|
-
|
|
53881
|
-
|
|
53882
|
-
entries =
|
|
53883
|
-
|
|
53884
|
-
|
|
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
|
+
};
|
|
53885
53928
|
const adjusted_focus_entries = entries.map((entry2) => {
|
|
53886
53929
|
let adjusted_price = entry2.price;
|
|
53887
53930
|
if (focus_position.quantity > 0) {
|
|
@@ -53901,7 +53944,7 @@ class Strategy {
|
|
|
53901
53944
|
price: entry,
|
|
53902
53945
|
quantity
|
|
53903
53946
|
}
|
|
53904
|
-
]),
|
|
53947
|
+
]), this.config.global_config.decimal_places, this.config.global_config.price_places);
|
|
53905
53948
|
const focus_loss = this.to_f(Math.abs(avg.price - second_payload.stop) * avg.quantity);
|
|
53906
53949
|
let below_reverse_entries = kind === "long" ? entries.filter((u) => {
|
|
53907
53950
|
return u.entry < (reverse_position.entry || focus_position.entry);
|
|
@@ -53928,7 +53971,7 @@ class Strategy {
|
|
|
53928
53971
|
price: reverse_position.entry,
|
|
53929
53972
|
quantity: reverse_position.quantity
|
|
53930
53973
|
}
|
|
53931
|
-
]),
|
|
53974
|
+
]), this.config.global_config.decimal_places, this.config.global_config.price_places);
|
|
53932
53975
|
const sell_quantity = this.to_df(reverse_avg.quantity * sell_factor);
|
|
53933
53976
|
const reverse_pnl = this.to_f(Math.abs(reverse_avg.price - second_payload.stop) * sell_quantity);
|
|
53934
53977
|
const fee_to_pay = this.calculate_fee({
|
|
@@ -53966,9 +54009,6 @@ class Strategy {
|
|
|
53966
54009
|
initial_pnl: this.pnl(reverse_kind),
|
|
53967
54010
|
remaining_quantity: this.to_df(reverse_avg.quantity - sell_quantity)
|
|
53968
54011
|
},
|
|
53969
|
-
last_entry: rest.last_value.entry,
|
|
53970
|
-
first_entry: entries.at(-1)?.entry,
|
|
53971
|
-
threshold,
|
|
53972
54012
|
spread: Math.abs(avg.entry - reverse_avg.entry),
|
|
53973
54013
|
gap_loss: to_f2(Math.abs(avg.entry - reverse_avg.entry) * reverse_avg.quantity, "%.2f"),
|
|
53974
54014
|
net_profit: incurred_loss + reverse_pnl
|
|
@@ -57916,10 +57956,50 @@ class ExchangeAccount {
|
|
|
57916
57956
|
}
|
|
57917
57957
|
return 0;
|
|
57918
57958
|
}
|
|
57959
|
+
async buildOppositeTrades(payload) {
|
|
57960
|
+
const { symbol, kind, place } = payload;
|
|
57961
|
+
const strategy = await this.runSimulation({
|
|
57962
|
+
symbol,
|
|
57963
|
+
kind,
|
|
57964
|
+
raw: true
|
|
57965
|
+
});
|
|
57966
|
+
const result = strategy.generateOppositeTrades({
|
|
57967
|
+
kind,
|
|
57968
|
+
avg_entry: strategy.position[kind].avg_price
|
|
57969
|
+
});
|
|
57970
|
+
if (place && result?.kind) {
|
|
57971
|
+
const config2 = await this.getPositionConfig({
|
|
57972
|
+
symbol,
|
|
57973
|
+
kind: result.kind
|
|
57974
|
+
});
|
|
57975
|
+
await this.app_db.updateScheduledTrade(config2.id, {
|
|
57976
|
+
entry: result.entry,
|
|
57977
|
+
stop: result.stop,
|
|
57978
|
+
risk: result.risk_per_trade,
|
|
57979
|
+
profit_percent: result.profit_percent,
|
|
57980
|
+
risk_reward: result.risk_reward
|
|
57981
|
+
});
|
|
57982
|
+
await this.placeTrade({
|
|
57983
|
+
symbol,
|
|
57984
|
+
kind: result.kind,
|
|
57985
|
+
place: true,
|
|
57986
|
+
ignore_config: true
|
|
57987
|
+
});
|
|
57988
|
+
await this.placeTrade({
|
|
57989
|
+
symbol,
|
|
57990
|
+
kind: result.kind,
|
|
57991
|
+
place: true,
|
|
57992
|
+
stop: true,
|
|
57993
|
+
ignore_config: true
|
|
57994
|
+
});
|
|
57995
|
+
}
|
|
57996
|
+
return result;
|
|
57997
|
+
}
|
|
57919
57998
|
async runSimulation(payload) {
|
|
57920
57999
|
const { symbol, kind, iterations = 2, raw = false } = payload;
|
|
57921
58000
|
const positions = await this.syncAccount({
|
|
57922
|
-
symbol
|
|
58001
|
+
symbol,
|
|
58002
|
+
as_view: true
|
|
57923
58003
|
});
|
|
57924
58004
|
const symbol_config = await this.recomputeSymbolConfig({
|
|
57925
58005
|
symbol
|
|
@@ -57953,11 +58033,13 @@ class ExchangeAccount {
|
|
|
57953
58033
|
const strategy = new Strategy({
|
|
57954
58034
|
long: {
|
|
57955
58035
|
entry: long_position.entry,
|
|
57956
|
-
quantity: long_position.quantity
|
|
58036
|
+
quantity: long_position.quantity,
|
|
58037
|
+
avg_price: long_position.avg_price
|
|
57957
58038
|
},
|
|
57958
58039
|
short: {
|
|
57959
58040
|
entry: short_position.entry,
|
|
57960
|
-
quantity: short_position.quantity
|
|
58041
|
+
quantity: short_position.quantity,
|
|
58042
|
+
avg_price: short_position.avg_price
|
|
57961
58043
|
},
|
|
57962
58044
|
config: strategy_config
|
|
57963
58045
|
});
|
package/dist/mcp-server.cjs
CHANGED
|
@@ -60593,9 +60593,6 @@ class Strategy {
|
|
|
60593
60593
|
const reverse_position = this.position[reverse_kind];
|
|
60594
60594
|
let _entry = this.tp(kind);
|
|
60595
60595
|
let _stop = this.tp(reverse_kind);
|
|
60596
|
-
if (!_entry && !_stop) {
|
|
60597
|
-
return null;
|
|
60598
|
-
}
|
|
60599
60596
|
const second_payload = {
|
|
60600
60597
|
entry: _entry,
|
|
60601
60598
|
stop: _stop,
|
|
@@ -60610,14 +60607,60 @@ class Strategy {
|
|
|
60610
60607
|
};
|
|
60611
60608
|
console.log({ second_payload, third_payload });
|
|
60612
60609
|
const app_config = generateOptimumAppConfig(this.config.global_config, second_payload, third_payload);
|
|
60613
|
-
|
|
60614
|
-
|
|
60615
|
-
|
|
60616
|
-
|
|
60617
|
-
|
|
60618
|
-
entries =
|
|
60619
|
-
|
|
60620
|
-
|
|
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
|
+
};
|
|
60621
60664
|
const adjusted_focus_entries = entries.map((entry2) => {
|
|
60622
60665
|
let adjusted_price = entry2.price;
|
|
60623
60666
|
if (focus_position.quantity > 0) {
|
|
@@ -60637,7 +60680,7 @@ class Strategy {
|
|
|
60637
60680
|
price: entry,
|
|
60638
60681
|
quantity
|
|
60639
60682
|
}
|
|
60640
|
-
]),
|
|
60683
|
+
]), this.config.global_config.decimal_places, this.config.global_config.price_places);
|
|
60641
60684
|
const focus_loss = this.to_f(Math.abs(avg.price - second_payload.stop) * avg.quantity);
|
|
60642
60685
|
let below_reverse_entries = kind === "long" ? entries.filter((u) => {
|
|
60643
60686
|
return u.entry < (reverse_position.entry || focus_position.entry);
|
|
@@ -60664,7 +60707,7 @@ class Strategy {
|
|
|
60664
60707
|
price: reverse_position.entry,
|
|
60665
60708
|
quantity: reverse_position.quantity
|
|
60666
60709
|
}
|
|
60667
|
-
]),
|
|
60710
|
+
]), this.config.global_config.decimal_places, this.config.global_config.price_places);
|
|
60668
60711
|
const sell_quantity = this.to_df(reverse_avg.quantity * sell_factor);
|
|
60669
60712
|
const reverse_pnl = this.to_f(Math.abs(reverse_avg.price - second_payload.stop) * sell_quantity);
|
|
60670
60713
|
const fee_to_pay = this.calculate_fee({
|
|
@@ -60702,9 +60745,6 @@ class Strategy {
|
|
|
60702
60745
|
initial_pnl: this.pnl(reverse_kind),
|
|
60703
60746
|
remaining_quantity: this.to_df(reverse_avg.quantity - sell_quantity)
|
|
60704
60747
|
},
|
|
60705
|
-
last_entry: rest.last_value.entry,
|
|
60706
|
-
first_entry: entries.at(-1)?.entry,
|
|
60707
|
-
threshold,
|
|
60708
60748
|
spread: Math.abs(avg.entry - reverse_avg.entry),
|
|
60709
60749
|
gap_loss: to_f2(Math.abs(avg.entry - reverse_avg.entry) * reverse_avg.quantity, "%.2f"),
|
|
60710
60750
|
net_profit: incurred_loss + reverse_pnl
|
|
@@ -64652,10 +64692,50 @@ class ExchangeAccount {
|
|
|
64652
64692
|
}
|
|
64653
64693
|
return 0;
|
|
64654
64694
|
}
|
|
64695
|
+
async buildOppositeTrades(payload) {
|
|
64696
|
+
const { symbol, kind, place } = payload;
|
|
64697
|
+
const strategy = await this.runSimulation({
|
|
64698
|
+
symbol,
|
|
64699
|
+
kind,
|
|
64700
|
+
raw: true
|
|
64701
|
+
});
|
|
64702
|
+
const result = strategy.generateOppositeTrades({
|
|
64703
|
+
kind,
|
|
64704
|
+
avg_entry: strategy.position[kind].avg_price
|
|
64705
|
+
});
|
|
64706
|
+
if (place && result?.kind) {
|
|
64707
|
+
const config2 = await this.getPositionConfig({
|
|
64708
|
+
symbol,
|
|
64709
|
+
kind: result.kind
|
|
64710
|
+
});
|
|
64711
|
+
await this.app_db.updateScheduledTrade(config2.id, {
|
|
64712
|
+
entry: result.entry,
|
|
64713
|
+
stop: result.stop,
|
|
64714
|
+
risk: result.risk_per_trade,
|
|
64715
|
+
profit_percent: result.profit_percent,
|
|
64716
|
+
risk_reward: result.risk_reward
|
|
64717
|
+
});
|
|
64718
|
+
await this.placeTrade({
|
|
64719
|
+
symbol,
|
|
64720
|
+
kind: result.kind,
|
|
64721
|
+
place: true,
|
|
64722
|
+
ignore_config: true
|
|
64723
|
+
});
|
|
64724
|
+
await this.placeTrade({
|
|
64725
|
+
symbol,
|
|
64726
|
+
kind: result.kind,
|
|
64727
|
+
place: true,
|
|
64728
|
+
stop: true,
|
|
64729
|
+
ignore_config: true
|
|
64730
|
+
});
|
|
64731
|
+
}
|
|
64732
|
+
return result;
|
|
64733
|
+
}
|
|
64655
64734
|
async runSimulation(payload) {
|
|
64656
64735
|
const { symbol, kind, iterations = 2, raw = false } = payload;
|
|
64657
64736
|
const positions = await this.syncAccount({
|
|
64658
|
-
symbol
|
|
64737
|
+
symbol,
|
|
64738
|
+
as_view: true
|
|
64659
64739
|
});
|
|
64660
64740
|
const symbol_config = await this.recomputeSymbolConfig({
|
|
64661
64741
|
symbol
|
|
@@ -64689,11 +64769,13 @@ class ExchangeAccount {
|
|
|
64689
64769
|
const strategy = new Strategy({
|
|
64690
64770
|
long: {
|
|
64691
64771
|
entry: long_position.entry,
|
|
64692
|
-
quantity: long_position.quantity
|
|
64772
|
+
quantity: long_position.quantity,
|
|
64773
|
+
avg_price: long_position.avg_price
|
|
64693
64774
|
},
|
|
64694
64775
|
short: {
|
|
64695
64776
|
entry: short_position.entry,
|
|
64696
|
-
quantity: short_position.quantity
|
|
64777
|
+
quantity: short_position.quantity,
|
|
64778
|
+
avg_price: short_position.avg_price
|
|
64697
64779
|
},
|
|
64698
64780
|
config: strategy_config
|
|
64699
64781
|
});
|
package/dist/mcp-server.js
CHANGED
|
@@ -60570,9 +60570,6 @@ class Strategy {
|
|
|
60570
60570
|
const reverse_position = this.position[reverse_kind];
|
|
60571
60571
|
let _entry = this.tp(kind);
|
|
60572
60572
|
let _stop = this.tp(reverse_kind);
|
|
60573
|
-
if (!_entry && !_stop) {
|
|
60574
|
-
return null;
|
|
60575
|
-
}
|
|
60576
60573
|
const second_payload = {
|
|
60577
60574
|
entry: _entry,
|
|
60578
60575
|
stop: _stop,
|
|
@@ -60587,14 +60584,60 @@ class Strategy {
|
|
|
60587
60584
|
};
|
|
60588
60585
|
console.log({ second_payload, third_payload });
|
|
60589
60586
|
const app_config = generateOptimumAppConfig(this.config.global_config, second_payload, third_payload);
|
|
60590
|
-
|
|
60591
|
-
|
|
60592
|
-
|
|
60593
|
-
|
|
60594
|
-
|
|
60595
|
-
entries =
|
|
60596
|
-
|
|
60597
|
-
|
|
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
|
+
};
|
|
60598
60641
|
const adjusted_focus_entries = entries.map((entry2) => {
|
|
60599
60642
|
let adjusted_price = entry2.price;
|
|
60600
60643
|
if (focus_position.quantity > 0) {
|
|
@@ -60614,7 +60657,7 @@ class Strategy {
|
|
|
60614
60657
|
price: entry,
|
|
60615
60658
|
quantity
|
|
60616
60659
|
}
|
|
60617
|
-
]),
|
|
60660
|
+
]), this.config.global_config.decimal_places, this.config.global_config.price_places);
|
|
60618
60661
|
const focus_loss = this.to_f(Math.abs(avg.price - second_payload.stop) * avg.quantity);
|
|
60619
60662
|
let below_reverse_entries = kind === "long" ? entries.filter((u) => {
|
|
60620
60663
|
return u.entry < (reverse_position.entry || focus_position.entry);
|
|
@@ -60641,7 +60684,7 @@ class Strategy {
|
|
|
60641
60684
|
price: reverse_position.entry,
|
|
60642
60685
|
quantity: reverse_position.quantity
|
|
60643
60686
|
}
|
|
60644
|
-
]),
|
|
60687
|
+
]), this.config.global_config.decimal_places, this.config.global_config.price_places);
|
|
60645
60688
|
const sell_quantity = this.to_df(reverse_avg.quantity * sell_factor);
|
|
60646
60689
|
const reverse_pnl = this.to_f(Math.abs(reverse_avg.price - second_payload.stop) * sell_quantity);
|
|
60647
60690
|
const fee_to_pay = this.calculate_fee({
|
|
@@ -60679,9 +60722,6 @@ class Strategy {
|
|
|
60679
60722
|
initial_pnl: this.pnl(reverse_kind),
|
|
60680
60723
|
remaining_quantity: this.to_df(reverse_avg.quantity - sell_quantity)
|
|
60681
60724
|
},
|
|
60682
|
-
last_entry: rest.last_value.entry,
|
|
60683
|
-
first_entry: entries.at(-1)?.entry,
|
|
60684
|
-
threshold,
|
|
60685
60725
|
spread: Math.abs(avg.entry - reverse_avg.entry),
|
|
60686
60726
|
gap_loss: to_f2(Math.abs(avg.entry - reverse_avg.entry) * reverse_avg.quantity, "%.2f"),
|
|
60687
60727
|
net_profit: incurred_loss + reverse_pnl
|
|
@@ -64629,10 +64669,50 @@ class ExchangeAccount {
|
|
|
64629
64669
|
}
|
|
64630
64670
|
return 0;
|
|
64631
64671
|
}
|
|
64672
|
+
async buildOppositeTrades(payload) {
|
|
64673
|
+
const { symbol, kind, place } = payload;
|
|
64674
|
+
const strategy = await this.runSimulation({
|
|
64675
|
+
symbol,
|
|
64676
|
+
kind,
|
|
64677
|
+
raw: true
|
|
64678
|
+
});
|
|
64679
|
+
const result = strategy.generateOppositeTrades({
|
|
64680
|
+
kind,
|
|
64681
|
+
avg_entry: strategy.position[kind].avg_price
|
|
64682
|
+
});
|
|
64683
|
+
if (place && result?.kind) {
|
|
64684
|
+
const config2 = await this.getPositionConfig({
|
|
64685
|
+
symbol,
|
|
64686
|
+
kind: result.kind
|
|
64687
|
+
});
|
|
64688
|
+
await this.app_db.updateScheduledTrade(config2.id, {
|
|
64689
|
+
entry: result.entry,
|
|
64690
|
+
stop: result.stop,
|
|
64691
|
+
risk: result.risk_per_trade,
|
|
64692
|
+
profit_percent: result.profit_percent,
|
|
64693
|
+
risk_reward: result.risk_reward
|
|
64694
|
+
});
|
|
64695
|
+
await this.placeTrade({
|
|
64696
|
+
symbol,
|
|
64697
|
+
kind: result.kind,
|
|
64698
|
+
place: true,
|
|
64699
|
+
ignore_config: true
|
|
64700
|
+
});
|
|
64701
|
+
await this.placeTrade({
|
|
64702
|
+
symbol,
|
|
64703
|
+
kind: result.kind,
|
|
64704
|
+
place: true,
|
|
64705
|
+
stop: true,
|
|
64706
|
+
ignore_config: true
|
|
64707
|
+
});
|
|
64708
|
+
}
|
|
64709
|
+
return result;
|
|
64710
|
+
}
|
|
64632
64711
|
async runSimulation(payload) {
|
|
64633
64712
|
const { symbol, kind, iterations = 2, raw = false } = payload;
|
|
64634
64713
|
const positions = await this.syncAccount({
|
|
64635
|
-
symbol
|
|
64714
|
+
symbol,
|
|
64715
|
+
as_view: true
|
|
64636
64716
|
});
|
|
64637
64717
|
const symbol_config = await this.recomputeSymbolConfig({
|
|
64638
64718
|
symbol
|
|
@@ -64666,11 +64746,13 @@ class ExchangeAccount {
|
|
|
64666
64746
|
const strategy = new Strategy({
|
|
64667
64747
|
long: {
|
|
64668
64748
|
entry: long_position.entry,
|
|
64669
|
-
quantity: long_position.quantity
|
|
64749
|
+
quantity: long_position.quantity,
|
|
64750
|
+
avg_price: long_position.avg_price
|
|
64670
64751
|
},
|
|
64671
64752
|
short: {
|
|
64672
64753
|
entry: short_position.entry,
|
|
64673
|
-
quantity: short_position.quantity
|
|
64754
|
+
quantity: short_position.quantity,
|
|
64755
|
+
avg_price: short_position.avg_price
|
|
64674
64756
|
},
|
|
64675
64757
|
config: strategy_config
|
|
64676
64758
|
});
|