@gbozee/ultimate 0.0.2-64 → 0.0.2-67
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 +25 -0
- package/dist/frontend-index.js +109 -0
- package/dist/index.cjs +178 -1
- package/dist/index.d.ts +42 -0
- package/dist/index.js +178 -1
- package/package.json +1 -1
package/dist/frontend-index.d.ts
CHANGED
|
@@ -367,6 +367,31 @@ export declare function generateOptimumAppConfig(config: GlobalConfig, payload:
|
|
|
367
367
|
quantity: number;
|
|
368
368
|
kind: "long" | "short";
|
|
369
369
|
}): AppConfig | null;
|
|
370
|
+
export declare function determineOptimumReward(app_config: AppConfig, increase?: boolean, low_range?: number, high_range?: number): number | {
|
|
371
|
+
result: any[];
|
|
372
|
+
value: number;
|
|
373
|
+
total: number;
|
|
374
|
+
risk_per_trade: number;
|
|
375
|
+
max: number;
|
|
376
|
+
min: number;
|
|
377
|
+
neg_pnl: any;
|
|
378
|
+
entry: any;
|
|
379
|
+
};
|
|
380
|
+
export declare function computeRiskReward(payload: {
|
|
381
|
+
app_config: AppConfig;
|
|
382
|
+
entry: number;
|
|
383
|
+
stop: number;
|
|
384
|
+
risk_per_trade: number;
|
|
385
|
+
}): number | {
|
|
386
|
+
result: any[];
|
|
387
|
+
value: number;
|
|
388
|
+
total: number;
|
|
389
|
+
risk_per_trade: number;
|
|
390
|
+
max: number;
|
|
391
|
+
min: number;
|
|
392
|
+
neg_pnl: any;
|
|
393
|
+
entry: any;
|
|
394
|
+
};
|
|
370
395
|
export type StrategyPosition = {
|
|
371
396
|
entry: number;
|
|
372
397
|
quantity: number;
|
package/dist/frontend-index.js
CHANGED
|
@@ -1644,6 +1644,113 @@ function generateOptimumAppConfig(config, payload, position2) {
|
|
|
1644
1644
|
});
|
|
1645
1645
|
return best_app_config;
|
|
1646
1646
|
}
|
|
1647
|
+
function determineOptimumReward(app_config, increase = true, low_range = 30, high_range = 199) {
|
|
1648
|
+
const criterion = app_config.strategy || "quantity";
|
|
1649
|
+
const risk_rewards = createArray(low_range, high_range, 1);
|
|
1650
|
+
let func = risk_rewards.map((trade_no) => {
|
|
1651
|
+
let trades = sortedBuildConfig(app_config, {
|
|
1652
|
+
take_profit: app_config.take_profit,
|
|
1653
|
+
entry: app_config.entry,
|
|
1654
|
+
stop: app_config.stop,
|
|
1655
|
+
no_of_trades: trade_no,
|
|
1656
|
+
risk_reward: trade_no,
|
|
1657
|
+
increase,
|
|
1658
|
+
kind: app_config.kind,
|
|
1659
|
+
gap: app_config.gap,
|
|
1660
|
+
decimal_places: app_config.decimal_places
|
|
1661
|
+
});
|
|
1662
|
+
let total = 0;
|
|
1663
|
+
let max = -Infinity;
|
|
1664
|
+
let min = Infinity;
|
|
1665
|
+
let neg_pnl = trades[0]?.neg_pnl || 0;
|
|
1666
|
+
let entry = trades.at(-1)?.entry;
|
|
1667
|
+
if (!entry) {
|
|
1668
|
+
return null;
|
|
1669
|
+
}
|
|
1670
|
+
for (let trade of trades) {
|
|
1671
|
+
total += trade.quantity;
|
|
1672
|
+
max = Math.max(max, trade.quantity);
|
|
1673
|
+
min = Math.min(min, trade.quantity);
|
|
1674
|
+
entry = app_config.kind === "long" ? Math.max(entry, trade.entry) : Math.min(entry, trade.entry);
|
|
1675
|
+
}
|
|
1676
|
+
return {
|
|
1677
|
+
result: trades,
|
|
1678
|
+
value: trade_no,
|
|
1679
|
+
total,
|
|
1680
|
+
risk_per_trade: app_config.risk_per_trade,
|
|
1681
|
+
max,
|
|
1682
|
+
min,
|
|
1683
|
+
neg_pnl,
|
|
1684
|
+
entry
|
|
1685
|
+
};
|
|
1686
|
+
});
|
|
1687
|
+
func = func.filter((r) => Boolean(r)).filter((r) => {
|
|
1688
|
+
let foundIndex = r?.result.findIndex((e) => e.quantity === r.max);
|
|
1689
|
+
return criterion === "quantity" ? foundIndex === 0 : true;
|
|
1690
|
+
});
|
|
1691
|
+
const highest = criterion === "quantity" ? Math.max(...func.map((o) => o.max)) : Math.min(...func.map((o) => o.entry));
|
|
1692
|
+
const key = criterion === "quantity" ? "max" : "entry";
|
|
1693
|
+
const index = findIndexByCondition(func, app_config.kind, (x) => x[key] == highest, criterion);
|
|
1694
|
+
if (app_config.raw) {
|
|
1695
|
+
return func[index];
|
|
1696
|
+
}
|
|
1697
|
+
return func[index]?.value;
|
|
1698
|
+
}
|
|
1699
|
+
function findIndexByCondition(lst, kind, condition, defaultKey = "neg_pnl") {
|
|
1700
|
+
const found = [];
|
|
1701
|
+
let max_new_diff = 0;
|
|
1702
|
+
let new_lst = lst.map((i, j) => ({
|
|
1703
|
+
...i,
|
|
1704
|
+
net_diff: lst[j].neg_pnl + lst[j].risk_per_trade
|
|
1705
|
+
}));
|
|
1706
|
+
new_lst.forEach((item, index) => {
|
|
1707
|
+
if (item.net_diff > 0) {
|
|
1708
|
+
found.push(index);
|
|
1709
|
+
}
|
|
1710
|
+
});
|
|
1711
|
+
if (found.length === 0) {
|
|
1712
|
+
max_new_diff = Math.max(...new_lst.map((e) => e.net_diff));
|
|
1713
|
+
found.push(new_lst.findIndex((e) => e.net_diff === max_new_diff));
|
|
1714
|
+
}
|
|
1715
|
+
const sortedFound = found.map((o, index) => ({ ...new_lst[o], index: o })).filter((j) => max_new_diff === 0 ? j.net_diff > 0 : j.net_diff >= max_new_diff).sort((a, b) => {
|
|
1716
|
+
if (a.total !== b.total) {
|
|
1717
|
+
return b.total - a.total;
|
|
1718
|
+
return b.net_diff - a.net_diff;
|
|
1719
|
+
return a.net_diff - b.net_diff;
|
|
1720
|
+
} else {
|
|
1721
|
+
return b.net_diff - a.net_diff;
|
|
1722
|
+
}
|
|
1723
|
+
});
|
|
1724
|
+
if (defaultKey === "quantity") {
|
|
1725
|
+
return sortedFound[0].index;
|
|
1726
|
+
}
|
|
1727
|
+
if (found.length === 1) {
|
|
1728
|
+
return found[0];
|
|
1729
|
+
}
|
|
1730
|
+
if (found.length === 0) {
|
|
1731
|
+
return -1;
|
|
1732
|
+
}
|
|
1733
|
+
const entryCondition = (a, b) => {
|
|
1734
|
+
if (kind == "long") {
|
|
1735
|
+
return a.entry > b.entry;
|
|
1736
|
+
}
|
|
1737
|
+
return a.entry < b.entry;
|
|
1738
|
+
};
|
|
1739
|
+
const maximum = found.reduce((maxIndex, currentIndex) => {
|
|
1740
|
+
return new_lst[currentIndex]["net_diff"] < new_lst[maxIndex]["net_diff"] && entryCondition(new_lst[currentIndex], new_lst[maxIndex]) ? currentIndex : maxIndex;
|
|
1741
|
+
}, found[0]);
|
|
1742
|
+
return maximum;
|
|
1743
|
+
}
|
|
1744
|
+
function computeRiskReward(payload) {
|
|
1745
|
+
const { app_config, entry, stop, risk_per_trade } = payload;
|
|
1746
|
+
const kind = entry > stop ? "long" : "short";
|
|
1747
|
+
app_config.kind = kind;
|
|
1748
|
+
app_config.entry = entry;
|
|
1749
|
+
app_config.stop = stop;
|
|
1750
|
+
app_config.risk_per_trade = risk_per_trade;
|
|
1751
|
+
const result = determineOptimumReward(app_config);
|
|
1752
|
+
return result;
|
|
1753
|
+
}
|
|
1647
1754
|
// src/helpers/strategy.ts
|
|
1648
1755
|
class Strategy {
|
|
1649
1756
|
position;
|
|
@@ -1940,10 +2047,12 @@ export {
|
|
|
1940
2047
|
determine_average_entry_and_size,
|
|
1941
2048
|
determine_amount_to_sell2 as determine_amount_to_sell,
|
|
1942
2049
|
determine_amount_to_buy,
|
|
2050
|
+
determineOptimumReward,
|
|
1943
2051
|
createGapPairs,
|
|
1944
2052
|
createArray,
|
|
1945
2053
|
computeTotalAverageForEachTrade,
|
|
1946
2054
|
computeSellZones,
|
|
2055
|
+
computeRiskReward,
|
|
1947
2056
|
buildConfig,
|
|
1948
2057
|
buildAvg,
|
|
1949
2058
|
buildAppConfig,
|
package/dist/index.cjs
CHANGED
|
@@ -41746,8 +41746,10 @@ __export(exports_src, {
|
|
|
41746
41746
|
determine_break_even_price: () => determine_break_even_price,
|
|
41747
41747
|
determine_average_entry_and_size: () => determine_average_entry_and_size,
|
|
41748
41748
|
determine_amount_to_buy: () => determine_amount_to_buy,
|
|
41749
|
+
determineOptimumReward: () => determineOptimumReward,
|
|
41749
41750
|
database: () => exports_database,
|
|
41750
41751
|
createArray: () => createArray,
|
|
41752
|
+
computeRiskReward: () => computeRiskReward,
|
|
41751
41753
|
buildConfig: () => buildConfig,
|
|
41752
41754
|
buildAvg: () => buildAvg,
|
|
41753
41755
|
buildAppConfig: () => buildAppConfig,
|
|
@@ -53543,6 +53545,113 @@ function generateOptimumAppConfig(config2, payload, position2) {
|
|
|
53543
53545
|
});
|
|
53544
53546
|
return best_app_config;
|
|
53545
53547
|
}
|
|
53548
|
+
function determineOptimumReward(app_config, increase = true, low_range = 30, high_range = 199) {
|
|
53549
|
+
const criterion = app_config.strategy || "quantity";
|
|
53550
|
+
const risk_rewards = createArray(low_range, high_range, 1);
|
|
53551
|
+
let func = risk_rewards.map((trade_no) => {
|
|
53552
|
+
let trades = sortedBuildConfig(app_config, {
|
|
53553
|
+
take_profit: app_config.take_profit,
|
|
53554
|
+
entry: app_config.entry,
|
|
53555
|
+
stop: app_config.stop,
|
|
53556
|
+
no_of_trades: trade_no,
|
|
53557
|
+
risk_reward: trade_no,
|
|
53558
|
+
increase,
|
|
53559
|
+
kind: app_config.kind,
|
|
53560
|
+
gap: app_config.gap,
|
|
53561
|
+
decimal_places: app_config.decimal_places
|
|
53562
|
+
});
|
|
53563
|
+
let total = 0;
|
|
53564
|
+
let max = -Infinity;
|
|
53565
|
+
let min = Infinity;
|
|
53566
|
+
let neg_pnl = trades[0]?.neg_pnl || 0;
|
|
53567
|
+
let entry = trades.at(-1)?.entry;
|
|
53568
|
+
if (!entry) {
|
|
53569
|
+
return null;
|
|
53570
|
+
}
|
|
53571
|
+
for (let trade of trades) {
|
|
53572
|
+
total += trade.quantity;
|
|
53573
|
+
max = Math.max(max, trade.quantity);
|
|
53574
|
+
min = Math.min(min, trade.quantity);
|
|
53575
|
+
entry = app_config.kind === "long" ? Math.max(entry, trade.entry) : Math.min(entry, trade.entry);
|
|
53576
|
+
}
|
|
53577
|
+
return {
|
|
53578
|
+
result: trades,
|
|
53579
|
+
value: trade_no,
|
|
53580
|
+
total,
|
|
53581
|
+
risk_per_trade: app_config.risk_per_trade,
|
|
53582
|
+
max,
|
|
53583
|
+
min,
|
|
53584
|
+
neg_pnl,
|
|
53585
|
+
entry
|
|
53586
|
+
};
|
|
53587
|
+
});
|
|
53588
|
+
func = func.filter((r2) => Boolean(r2)).filter((r2) => {
|
|
53589
|
+
let foundIndex = r2?.result.findIndex((e2) => e2.quantity === r2.max);
|
|
53590
|
+
return criterion === "quantity" ? foundIndex === 0 : true;
|
|
53591
|
+
});
|
|
53592
|
+
const highest = criterion === "quantity" ? Math.max(...func.map((o) => o.max)) : Math.min(...func.map((o) => o.entry));
|
|
53593
|
+
const key = criterion === "quantity" ? "max" : "entry";
|
|
53594
|
+
const index = findIndexByCondition(func, app_config.kind, (x) => x[key] == highest, criterion);
|
|
53595
|
+
if (app_config.raw) {
|
|
53596
|
+
return func[index];
|
|
53597
|
+
}
|
|
53598
|
+
return func[index]?.value;
|
|
53599
|
+
}
|
|
53600
|
+
function findIndexByCondition(lst, kind, condition, defaultKey = "neg_pnl") {
|
|
53601
|
+
const found = [];
|
|
53602
|
+
let max_new_diff = 0;
|
|
53603
|
+
let new_lst = lst.map((i2, j) => ({
|
|
53604
|
+
...i2,
|
|
53605
|
+
net_diff: lst[j].neg_pnl + lst[j].risk_per_trade
|
|
53606
|
+
}));
|
|
53607
|
+
new_lst.forEach((item, index) => {
|
|
53608
|
+
if (item.net_diff > 0) {
|
|
53609
|
+
found.push(index);
|
|
53610
|
+
}
|
|
53611
|
+
});
|
|
53612
|
+
if (found.length === 0) {
|
|
53613
|
+
max_new_diff = Math.max(...new_lst.map((e2) => e2.net_diff));
|
|
53614
|
+
found.push(new_lst.findIndex((e2) => e2.net_diff === max_new_diff));
|
|
53615
|
+
}
|
|
53616
|
+
const sortedFound = found.map((o, index) => ({ ...new_lst[o], index: o })).filter((j) => max_new_diff === 0 ? j.net_diff > 0 : j.net_diff >= max_new_diff).sort((a, b) => {
|
|
53617
|
+
if (a.total !== b.total) {
|
|
53618
|
+
return b.total - a.total;
|
|
53619
|
+
return b.net_diff - a.net_diff;
|
|
53620
|
+
return a.net_diff - b.net_diff;
|
|
53621
|
+
} else {
|
|
53622
|
+
return b.net_diff - a.net_diff;
|
|
53623
|
+
}
|
|
53624
|
+
});
|
|
53625
|
+
if (defaultKey === "quantity") {
|
|
53626
|
+
return sortedFound[0].index;
|
|
53627
|
+
}
|
|
53628
|
+
if (found.length === 1) {
|
|
53629
|
+
return found[0];
|
|
53630
|
+
}
|
|
53631
|
+
if (found.length === 0) {
|
|
53632
|
+
return -1;
|
|
53633
|
+
}
|
|
53634
|
+
const entryCondition = (a, b) => {
|
|
53635
|
+
if (kind == "long") {
|
|
53636
|
+
return a.entry > b.entry;
|
|
53637
|
+
}
|
|
53638
|
+
return a.entry < b.entry;
|
|
53639
|
+
};
|
|
53640
|
+
const maximum = found.reduce((maxIndex, currentIndex) => {
|
|
53641
|
+
return new_lst[currentIndex]["net_diff"] < new_lst[maxIndex]["net_diff"] && entryCondition(new_lst[currentIndex], new_lst[maxIndex]) ? currentIndex : maxIndex;
|
|
53642
|
+
}, found[0]);
|
|
53643
|
+
return maximum;
|
|
53644
|
+
}
|
|
53645
|
+
function computeRiskReward(payload) {
|
|
53646
|
+
const { app_config, entry, stop, risk_per_trade } = payload;
|
|
53647
|
+
const kind = entry > stop ? "long" : "short";
|
|
53648
|
+
app_config.kind = kind;
|
|
53649
|
+
app_config.entry = entry;
|
|
53650
|
+
app_config.stop = stop;
|
|
53651
|
+
app_config.risk_per_trade = risk_per_trade;
|
|
53652
|
+
const result = determineOptimumReward(app_config);
|
|
53653
|
+
return result;
|
|
53654
|
+
}
|
|
53546
53655
|
|
|
53547
53656
|
// src/helpers/strategy.ts
|
|
53548
53657
|
class Strategy {
|
|
@@ -56603,6 +56712,64 @@ class ExchangeAccount {
|
|
|
56603
56712
|
});
|
|
56604
56713
|
return app_config;
|
|
56605
56714
|
}
|
|
56715
|
+
async justInTimeProfit(payload) {
|
|
56716
|
+
const { symbol, target_pnl, kind, refresh, place, take_profit, pause_tp } = payload;
|
|
56717
|
+
if (refresh) {
|
|
56718
|
+
await this.syncAccount({
|
|
56719
|
+
symbol,
|
|
56720
|
+
live_refresh: true,
|
|
56721
|
+
update: true
|
|
56722
|
+
});
|
|
56723
|
+
}
|
|
56724
|
+
const position2 = await this.syncAccount({
|
|
56725
|
+
symbol,
|
|
56726
|
+
kind,
|
|
56727
|
+
as_view: true
|
|
56728
|
+
});
|
|
56729
|
+
const current_price = take_profit || position2.current_price;
|
|
56730
|
+
const notional_value = position2.quantity * position2.entry;
|
|
56731
|
+
const current_pnl = to_f2(Math.abs(current_price - position2.entry) * position2.quantity, "%.2f");
|
|
56732
|
+
const profit_percent = current_pnl * 100 / notional_value;
|
|
56733
|
+
const sell_ratio = target_pnl / current_pnl;
|
|
56734
|
+
if (place) {
|
|
56735
|
+
const existing_config = await this.getPositionConfig({
|
|
56736
|
+
symbol,
|
|
56737
|
+
kind
|
|
56738
|
+
});
|
|
56739
|
+
if (existing_config) {
|
|
56740
|
+
const trigger2 = place;
|
|
56741
|
+
const config2 = await this.buildReduceConfig({
|
|
56742
|
+
symbol,
|
|
56743
|
+
as_dict: true,
|
|
56744
|
+
trigger: {
|
|
56745
|
+
long: trigger2 || false,
|
|
56746
|
+
short: trigger2 || false
|
|
56747
|
+
},
|
|
56748
|
+
kind,
|
|
56749
|
+
target_pnl: current_pnl
|
|
56750
|
+
});
|
|
56751
|
+
config2[kind].sell_ratio = sell_ratio;
|
|
56752
|
+
if (trigger2) {
|
|
56753
|
+
await this.app_db.updateScheduledTrade(existing_config.id, {
|
|
56754
|
+
pause_tp
|
|
56755
|
+
});
|
|
56756
|
+
await this.reduceMajorPositionEntry({
|
|
56757
|
+
symbol,
|
|
56758
|
+
long: config2.long,
|
|
56759
|
+
short: config2.short,
|
|
56760
|
+
trigger: config2.trigger
|
|
56761
|
+
});
|
|
56762
|
+
}
|
|
56763
|
+
}
|
|
56764
|
+
}
|
|
56765
|
+
return {
|
|
56766
|
+
sell_ratio,
|
|
56767
|
+
current_pnl,
|
|
56768
|
+
profit_percent,
|
|
56769
|
+
current_price,
|
|
56770
|
+
notional_value
|
|
56771
|
+
};
|
|
56772
|
+
}
|
|
56606
56773
|
async buildTrades(payload) {
|
|
56607
56774
|
const { symbol, kind, risk } = payload;
|
|
56608
56775
|
const config2 = await this.getPositionConfig({
|
|
@@ -57061,7 +57228,17 @@ class ExchangeAccount {
|
|
|
57061
57228
|
kind
|
|
57062
57229
|
});
|
|
57063
57230
|
}
|
|
57064
|
-
|
|
57231
|
+
const long_config = await this.getPositionConfig({
|
|
57232
|
+
symbol,
|
|
57233
|
+
kind: "long"
|
|
57234
|
+
});
|
|
57235
|
+
const short_config = await this.getPositionConfig({
|
|
57236
|
+
symbol,
|
|
57237
|
+
kind: "short"
|
|
57238
|
+
});
|
|
57239
|
+
const long_pause_tp = long_config?.pause_tp;
|
|
57240
|
+
const short_pause_tp = short_config?.pause_tp;
|
|
57241
|
+
if (payload.trigger && !long_pause_tp && !short_pause_tp) {
|
|
57065
57242
|
return await this.reduceMajorPositionEntry({
|
|
57066
57243
|
symbol,
|
|
57067
57244
|
long: config2.long,
|
package/dist/index.d.ts
CHANGED
|
@@ -70,6 +70,7 @@ export interface ScheduledTrade extends BaseSystemFields {
|
|
|
70
70
|
reduce_ratio?: number;
|
|
71
71
|
sell_ratio?: number;
|
|
72
72
|
threshold_qty?: number;
|
|
73
|
+
pause_tp?: boolean;
|
|
73
74
|
}
|
|
74
75
|
export interface AccountStrategy extends BaseSystemFields {
|
|
75
76
|
account: string;
|
|
@@ -117,6 +118,7 @@ export interface PositionsView {
|
|
|
117
118
|
sell_ratio?: number;
|
|
118
119
|
threshold_qty?: number;
|
|
119
120
|
follow?: boolean | 1 | 0;
|
|
121
|
+
current_price?: number;
|
|
120
122
|
}
|
|
121
123
|
export interface BullishMarket extends RecordModel {
|
|
122
124
|
id: string;
|
|
@@ -898,6 +900,31 @@ export declare function generateOptimumAppConfig(config: GlobalConfig, payload:
|
|
|
898
900
|
quantity: number;
|
|
899
901
|
kind: "long" | "short";
|
|
900
902
|
}): AppConfig | null;
|
|
903
|
+
export declare function determineOptimumReward(app_config: AppConfig, increase?: boolean, low_range?: number, high_range?: number): number | {
|
|
904
|
+
result: any[];
|
|
905
|
+
value: number;
|
|
906
|
+
total: number;
|
|
907
|
+
risk_per_trade: number;
|
|
908
|
+
max: number;
|
|
909
|
+
min: number;
|
|
910
|
+
neg_pnl: any;
|
|
911
|
+
entry: any;
|
|
912
|
+
};
|
|
913
|
+
export declare function computeRiskReward(payload: {
|
|
914
|
+
app_config: AppConfig;
|
|
915
|
+
entry: number;
|
|
916
|
+
stop: number;
|
|
917
|
+
risk_per_trade: number;
|
|
918
|
+
}): number | {
|
|
919
|
+
result: any[];
|
|
920
|
+
value: number;
|
|
921
|
+
total: number;
|
|
922
|
+
risk_per_trade: number;
|
|
923
|
+
max: number;
|
|
924
|
+
min: number;
|
|
925
|
+
neg_pnl: any;
|
|
926
|
+
entry: any;
|
|
927
|
+
};
|
|
901
928
|
declare class ExchangePosition {
|
|
902
929
|
exchange: BaseExchange;
|
|
903
930
|
exchange_account: ExchangeAccount$1;
|
|
@@ -1147,6 +1174,21 @@ declare class ExchangeAccount$1 {
|
|
|
1147
1174
|
symbol: string;
|
|
1148
1175
|
kind: "long" | "short";
|
|
1149
1176
|
}): Promise<AppConfig>;
|
|
1177
|
+
justInTimeProfit(payload: {
|
|
1178
|
+
symbol: string;
|
|
1179
|
+
target_pnl: number;
|
|
1180
|
+
kind: "long" | "short";
|
|
1181
|
+
refresh?: boolean;
|
|
1182
|
+
place?: boolean;
|
|
1183
|
+
take_profit?: number;
|
|
1184
|
+
pause_tp?: boolean;
|
|
1185
|
+
}): Promise<{
|
|
1186
|
+
sell_ratio: number;
|
|
1187
|
+
current_pnl: number;
|
|
1188
|
+
profit_percent: number;
|
|
1189
|
+
current_price: number;
|
|
1190
|
+
notional_value: number;
|
|
1191
|
+
}>;
|
|
1150
1192
|
buildTrades(payload: {
|
|
1151
1193
|
symbol: string;
|
|
1152
1194
|
kind: "long" | "short";
|
package/dist/index.js
CHANGED
|
@@ -53501,6 +53501,113 @@ function generateOptimumAppConfig(config2, payload, position2) {
|
|
|
53501
53501
|
});
|
|
53502
53502
|
return best_app_config;
|
|
53503
53503
|
}
|
|
53504
|
+
function determineOptimumReward(app_config, increase = true, low_range = 30, high_range = 199) {
|
|
53505
|
+
const criterion = app_config.strategy || "quantity";
|
|
53506
|
+
const risk_rewards = createArray(low_range, high_range, 1);
|
|
53507
|
+
let func = risk_rewards.map((trade_no) => {
|
|
53508
|
+
let trades = sortedBuildConfig(app_config, {
|
|
53509
|
+
take_profit: app_config.take_profit,
|
|
53510
|
+
entry: app_config.entry,
|
|
53511
|
+
stop: app_config.stop,
|
|
53512
|
+
no_of_trades: trade_no,
|
|
53513
|
+
risk_reward: trade_no,
|
|
53514
|
+
increase,
|
|
53515
|
+
kind: app_config.kind,
|
|
53516
|
+
gap: app_config.gap,
|
|
53517
|
+
decimal_places: app_config.decimal_places
|
|
53518
|
+
});
|
|
53519
|
+
let total = 0;
|
|
53520
|
+
let max = -Infinity;
|
|
53521
|
+
let min = Infinity;
|
|
53522
|
+
let neg_pnl = trades[0]?.neg_pnl || 0;
|
|
53523
|
+
let entry = trades.at(-1)?.entry;
|
|
53524
|
+
if (!entry) {
|
|
53525
|
+
return null;
|
|
53526
|
+
}
|
|
53527
|
+
for (let trade of trades) {
|
|
53528
|
+
total += trade.quantity;
|
|
53529
|
+
max = Math.max(max, trade.quantity);
|
|
53530
|
+
min = Math.min(min, trade.quantity);
|
|
53531
|
+
entry = app_config.kind === "long" ? Math.max(entry, trade.entry) : Math.min(entry, trade.entry);
|
|
53532
|
+
}
|
|
53533
|
+
return {
|
|
53534
|
+
result: trades,
|
|
53535
|
+
value: trade_no,
|
|
53536
|
+
total,
|
|
53537
|
+
risk_per_trade: app_config.risk_per_trade,
|
|
53538
|
+
max,
|
|
53539
|
+
min,
|
|
53540
|
+
neg_pnl,
|
|
53541
|
+
entry
|
|
53542
|
+
};
|
|
53543
|
+
});
|
|
53544
|
+
func = func.filter((r2) => Boolean(r2)).filter((r2) => {
|
|
53545
|
+
let foundIndex = r2?.result.findIndex((e2) => e2.quantity === r2.max);
|
|
53546
|
+
return criterion === "quantity" ? foundIndex === 0 : true;
|
|
53547
|
+
});
|
|
53548
|
+
const highest = criterion === "quantity" ? Math.max(...func.map((o) => o.max)) : Math.min(...func.map((o) => o.entry));
|
|
53549
|
+
const key = criterion === "quantity" ? "max" : "entry";
|
|
53550
|
+
const index = findIndexByCondition(func, app_config.kind, (x) => x[key] == highest, criterion);
|
|
53551
|
+
if (app_config.raw) {
|
|
53552
|
+
return func[index];
|
|
53553
|
+
}
|
|
53554
|
+
return func[index]?.value;
|
|
53555
|
+
}
|
|
53556
|
+
function findIndexByCondition(lst, kind, condition, defaultKey = "neg_pnl") {
|
|
53557
|
+
const found = [];
|
|
53558
|
+
let max_new_diff = 0;
|
|
53559
|
+
let new_lst = lst.map((i2, j) => ({
|
|
53560
|
+
...i2,
|
|
53561
|
+
net_diff: lst[j].neg_pnl + lst[j].risk_per_trade
|
|
53562
|
+
}));
|
|
53563
|
+
new_lst.forEach((item, index) => {
|
|
53564
|
+
if (item.net_diff > 0) {
|
|
53565
|
+
found.push(index);
|
|
53566
|
+
}
|
|
53567
|
+
});
|
|
53568
|
+
if (found.length === 0) {
|
|
53569
|
+
max_new_diff = Math.max(...new_lst.map((e2) => e2.net_diff));
|
|
53570
|
+
found.push(new_lst.findIndex((e2) => e2.net_diff === max_new_diff));
|
|
53571
|
+
}
|
|
53572
|
+
const sortedFound = found.map((o, index) => ({ ...new_lst[o], index: o })).filter((j) => max_new_diff === 0 ? j.net_diff > 0 : j.net_diff >= max_new_diff).sort((a, b) => {
|
|
53573
|
+
if (a.total !== b.total) {
|
|
53574
|
+
return b.total - a.total;
|
|
53575
|
+
return b.net_diff - a.net_diff;
|
|
53576
|
+
return a.net_diff - b.net_diff;
|
|
53577
|
+
} else {
|
|
53578
|
+
return b.net_diff - a.net_diff;
|
|
53579
|
+
}
|
|
53580
|
+
});
|
|
53581
|
+
if (defaultKey === "quantity") {
|
|
53582
|
+
return sortedFound[0].index;
|
|
53583
|
+
}
|
|
53584
|
+
if (found.length === 1) {
|
|
53585
|
+
return found[0];
|
|
53586
|
+
}
|
|
53587
|
+
if (found.length === 0) {
|
|
53588
|
+
return -1;
|
|
53589
|
+
}
|
|
53590
|
+
const entryCondition = (a, b) => {
|
|
53591
|
+
if (kind == "long") {
|
|
53592
|
+
return a.entry > b.entry;
|
|
53593
|
+
}
|
|
53594
|
+
return a.entry < b.entry;
|
|
53595
|
+
};
|
|
53596
|
+
const maximum = found.reduce((maxIndex, currentIndex) => {
|
|
53597
|
+
return new_lst[currentIndex]["net_diff"] < new_lst[maxIndex]["net_diff"] && entryCondition(new_lst[currentIndex], new_lst[maxIndex]) ? currentIndex : maxIndex;
|
|
53598
|
+
}, found[0]);
|
|
53599
|
+
return maximum;
|
|
53600
|
+
}
|
|
53601
|
+
function computeRiskReward(payload) {
|
|
53602
|
+
const { app_config, entry, stop, risk_per_trade } = payload;
|
|
53603
|
+
const kind = entry > stop ? "long" : "short";
|
|
53604
|
+
app_config.kind = kind;
|
|
53605
|
+
app_config.entry = entry;
|
|
53606
|
+
app_config.stop = stop;
|
|
53607
|
+
app_config.risk_per_trade = risk_per_trade;
|
|
53608
|
+
const result = determineOptimumReward(app_config);
|
|
53609
|
+
return result;
|
|
53610
|
+
}
|
|
53504
53611
|
|
|
53505
53612
|
// src/helpers/strategy.ts
|
|
53506
53613
|
class Strategy {
|
|
@@ -56561,6 +56668,64 @@ class ExchangeAccount {
|
|
|
56561
56668
|
});
|
|
56562
56669
|
return app_config;
|
|
56563
56670
|
}
|
|
56671
|
+
async justInTimeProfit(payload) {
|
|
56672
|
+
const { symbol, target_pnl, kind, refresh, place, take_profit, pause_tp } = payload;
|
|
56673
|
+
if (refresh) {
|
|
56674
|
+
await this.syncAccount({
|
|
56675
|
+
symbol,
|
|
56676
|
+
live_refresh: true,
|
|
56677
|
+
update: true
|
|
56678
|
+
});
|
|
56679
|
+
}
|
|
56680
|
+
const position2 = await this.syncAccount({
|
|
56681
|
+
symbol,
|
|
56682
|
+
kind,
|
|
56683
|
+
as_view: true
|
|
56684
|
+
});
|
|
56685
|
+
const current_price = take_profit || position2.current_price;
|
|
56686
|
+
const notional_value = position2.quantity * position2.entry;
|
|
56687
|
+
const current_pnl = to_f2(Math.abs(current_price - position2.entry) * position2.quantity, "%.2f");
|
|
56688
|
+
const profit_percent = current_pnl * 100 / notional_value;
|
|
56689
|
+
const sell_ratio = target_pnl / current_pnl;
|
|
56690
|
+
if (place) {
|
|
56691
|
+
const existing_config = await this.getPositionConfig({
|
|
56692
|
+
symbol,
|
|
56693
|
+
kind
|
|
56694
|
+
});
|
|
56695
|
+
if (existing_config) {
|
|
56696
|
+
const trigger2 = place;
|
|
56697
|
+
const config2 = await this.buildReduceConfig({
|
|
56698
|
+
symbol,
|
|
56699
|
+
as_dict: true,
|
|
56700
|
+
trigger: {
|
|
56701
|
+
long: trigger2 || false,
|
|
56702
|
+
short: trigger2 || false
|
|
56703
|
+
},
|
|
56704
|
+
kind,
|
|
56705
|
+
target_pnl: current_pnl
|
|
56706
|
+
});
|
|
56707
|
+
config2[kind].sell_ratio = sell_ratio;
|
|
56708
|
+
if (trigger2) {
|
|
56709
|
+
await this.app_db.updateScheduledTrade(existing_config.id, {
|
|
56710
|
+
pause_tp
|
|
56711
|
+
});
|
|
56712
|
+
await this.reduceMajorPositionEntry({
|
|
56713
|
+
symbol,
|
|
56714
|
+
long: config2.long,
|
|
56715
|
+
short: config2.short,
|
|
56716
|
+
trigger: config2.trigger
|
|
56717
|
+
});
|
|
56718
|
+
}
|
|
56719
|
+
}
|
|
56720
|
+
}
|
|
56721
|
+
return {
|
|
56722
|
+
sell_ratio,
|
|
56723
|
+
current_pnl,
|
|
56724
|
+
profit_percent,
|
|
56725
|
+
current_price,
|
|
56726
|
+
notional_value
|
|
56727
|
+
};
|
|
56728
|
+
}
|
|
56564
56729
|
async buildTrades(payload) {
|
|
56565
56730
|
const { symbol, kind, risk } = payload;
|
|
56566
56731
|
const config2 = await this.getPositionConfig({
|
|
@@ -57019,7 +57184,17 @@ class ExchangeAccount {
|
|
|
57019
57184
|
kind
|
|
57020
57185
|
});
|
|
57021
57186
|
}
|
|
57022
|
-
|
|
57187
|
+
const long_config = await this.getPositionConfig({
|
|
57188
|
+
symbol,
|
|
57189
|
+
kind: "long"
|
|
57190
|
+
});
|
|
57191
|
+
const short_config = await this.getPositionConfig({
|
|
57192
|
+
symbol,
|
|
57193
|
+
kind: "short"
|
|
57194
|
+
});
|
|
57195
|
+
const long_pause_tp = long_config?.pause_tp;
|
|
57196
|
+
const short_pause_tp = short_config?.pause_tp;
|
|
57197
|
+
if (payload.trigger && !long_pause_tp && !short_pause_tp) {
|
|
57023
57198
|
return await this.reduceMajorPositionEntry({
|
|
57024
57199
|
symbol,
|
|
57025
57200
|
long: config2.long,
|
|
@@ -58581,8 +58756,10 @@ export {
|
|
|
58581
58756
|
determine_break_even_price,
|
|
58582
58757
|
determine_average_entry_and_size,
|
|
58583
58758
|
determine_amount_to_buy,
|
|
58759
|
+
determineOptimumReward,
|
|
58584
58760
|
exports_database as database,
|
|
58585
58761
|
createArray,
|
|
58762
|
+
computeRiskReward,
|
|
58586
58763
|
buildConfig,
|
|
58587
58764
|
buildAvg,
|
|
58588
58765
|
buildAppConfig,
|