@gbozee/ultimate 0.0.2-48 → 0.0.2-51
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 +69 -0
- package/dist/frontend-index.js +317 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.js +317 -0
- package/package.json +1 -1
package/dist/frontend-index.d.ts
CHANGED
|
@@ -221,6 +221,7 @@ export type GlobalConfig = {
|
|
|
221
221
|
}[];
|
|
222
222
|
risk_reward: number;
|
|
223
223
|
reverse_factor: number;
|
|
224
|
+
leverage?: number;
|
|
224
225
|
};
|
|
225
226
|
export type AppConfig = {
|
|
226
227
|
fee: number;
|
|
@@ -350,5 +351,73 @@ export declare function determine_amount_to_buy(payload: {
|
|
|
350
351
|
position: any;
|
|
351
352
|
existingOrders: any[];
|
|
352
353
|
}): any[];
|
|
354
|
+
export declare function generateOptimumAppConfig(config: GlobalConfig, payload: {
|
|
355
|
+
entry: number;
|
|
356
|
+
stop: number;
|
|
357
|
+
risk_reward: number;
|
|
358
|
+
start_risk: number;
|
|
359
|
+
max_risk?: number;
|
|
360
|
+
}, position: {
|
|
361
|
+
entry: number;
|
|
362
|
+
quantity: number;
|
|
363
|
+
kind: "long" | "short";
|
|
364
|
+
}): AppConfig | null;
|
|
365
|
+
export type StrategyPosition = {
|
|
366
|
+
entry: number;
|
|
367
|
+
quantity: number;
|
|
368
|
+
};
|
|
369
|
+
export type Config = {
|
|
370
|
+
leverage: number;
|
|
371
|
+
tp_percent: number;
|
|
372
|
+
short_tp_factor: number;
|
|
373
|
+
price_places: string;
|
|
374
|
+
decimal_places: string;
|
|
375
|
+
fee_percent?: number;
|
|
376
|
+
budget: number;
|
|
377
|
+
global_config: GlobalConfig;
|
|
378
|
+
};
|
|
379
|
+
export declare class Strategy {
|
|
380
|
+
position: {
|
|
381
|
+
long: StrategyPosition;
|
|
382
|
+
short: StrategyPosition;
|
|
383
|
+
};
|
|
384
|
+
config: Config;
|
|
385
|
+
constructor(payload: {
|
|
386
|
+
long: StrategyPosition;
|
|
387
|
+
short: StrategyPosition;
|
|
388
|
+
config: Config;
|
|
389
|
+
});
|
|
390
|
+
to_f(price: number): number;
|
|
391
|
+
to_df(quantity: number): number;
|
|
392
|
+
pnl(kind: "long" | "short"): number;
|
|
393
|
+
tp(kind: "long" | "short"): number;
|
|
394
|
+
calculate_fee(position: {
|
|
395
|
+
price: number;
|
|
396
|
+
quantity: number;
|
|
397
|
+
}): number;
|
|
398
|
+
get long_tp(): number;
|
|
399
|
+
get short_tp(): number;
|
|
400
|
+
generateGapClosingAlgorithm(payload: {
|
|
401
|
+
kind: "long" | "short";
|
|
402
|
+
risk_reward?: number;
|
|
403
|
+
}): {
|
|
404
|
+
[x: string]: any;
|
|
405
|
+
risk: number;
|
|
406
|
+
last_entry: any;
|
|
407
|
+
first_entry: any;
|
|
408
|
+
threshold: any;
|
|
409
|
+
};
|
|
410
|
+
runIterations(payload: {
|
|
411
|
+
kind: "long" | "short";
|
|
412
|
+
iterations: number;
|
|
413
|
+
risk_reward?: number;
|
|
414
|
+
}): {
|
|
415
|
+
[x: string]: any;
|
|
416
|
+
risk: number;
|
|
417
|
+
last_entry: any;
|
|
418
|
+
first_entry: any;
|
|
419
|
+
threshold: any;
|
|
420
|
+
}[];
|
|
421
|
+
}
|
|
353
422
|
|
|
354
423
|
export {};
|
package/dist/frontend-index.js
CHANGED
|
@@ -1519,6 +1519,321 @@ function determine_amount_to_buy(payload) {
|
|
|
1519
1519
|
filteredOrders = filteredOrders.filter((k) => !existingOrders.map((j) => j.price).includes(k.price));
|
|
1520
1520
|
return filteredOrders;
|
|
1521
1521
|
}
|
|
1522
|
+
function generateOptimumAppConfig(config, payload, position2) {
|
|
1523
|
+
let low_risk = payload.start_risk;
|
|
1524
|
+
let high_risk = payload.max_risk || 50000;
|
|
1525
|
+
let best_risk = null;
|
|
1526
|
+
let best_app_config = null;
|
|
1527
|
+
const tolerance = 0.1;
|
|
1528
|
+
const max_iterations = 150;
|
|
1529
|
+
let iterations = 0;
|
|
1530
|
+
console.log(`Starting risk search for ${position2.kind} position. Target Entry: ${position2.entry}, Initial Risk Range: [${low_risk}, ${high_risk}]`);
|
|
1531
|
+
while (high_risk - low_risk > tolerance && iterations < max_iterations) {
|
|
1532
|
+
iterations++;
|
|
1533
|
+
const mid_risk = (low_risk + high_risk) / 2;
|
|
1534
|
+
const {
|
|
1535
|
+
app_config: current_app_config,
|
|
1536
|
+
max_size,
|
|
1537
|
+
last_value,
|
|
1538
|
+
entries
|
|
1539
|
+
} = get_app_config_and_max_size({
|
|
1540
|
+
...config,
|
|
1541
|
+
risk: mid_risk,
|
|
1542
|
+
profit_percent: config.profit_percent || 0,
|
|
1543
|
+
profit: config.profit || 500,
|
|
1544
|
+
risk_reward: payload.risk_reward,
|
|
1545
|
+
kind: position2.kind,
|
|
1546
|
+
price_places: config.price_places,
|
|
1547
|
+
decimal_places: config.decimal_places,
|
|
1548
|
+
accounts: config.accounts || [],
|
|
1549
|
+
reduce_percent: config.reduce_percent || 90,
|
|
1550
|
+
reverse_factor: config.reverse_factor || 1,
|
|
1551
|
+
symbol: config.symbol || "",
|
|
1552
|
+
support: config.support || 0,
|
|
1553
|
+
resistance: config.resistance || 0,
|
|
1554
|
+
min_size: config.min_size || 0,
|
|
1555
|
+
stop_percent: config.stop_percent || 0
|
|
1556
|
+
}, {
|
|
1557
|
+
entry: payload.entry,
|
|
1558
|
+
stop: payload.stop,
|
|
1559
|
+
kind: position2.kind
|
|
1560
|
+
});
|
|
1561
|
+
current_app_config.max_size = max_size;
|
|
1562
|
+
current_app_config.last_value = last_value;
|
|
1563
|
+
current_app_config.entries = entries;
|
|
1564
|
+
current_app_config.risk_reward = payload.risk_reward;
|
|
1565
|
+
const full_trades = sortedBuildConfig(current_app_config, {
|
|
1566
|
+
entry: current_app_config.entry,
|
|
1567
|
+
stop: current_app_config.stop,
|
|
1568
|
+
kind: current_app_config.kind,
|
|
1569
|
+
risk: current_app_config.risk_per_trade,
|
|
1570
|
+
risk_reward: current_app_config.risk_reward,
|
|
1571
|
+
increase: true,
|
|
1572
|
+
gap: current_app_config.gap,
|
|
1573
|
+
price_places: current_app_config.price_places,
|
|
1574
|
+
decimal_places: current_app_config.decimal_places
|
|
1575
|
+
});
|
|
1576
|
+
console.log(`Iteration ${iterations}: Risk=${mid_risk.toFixed(2)}, Low=${low_risk.toFixed(2)}, High=${high_risk.toFixed(2)}`);
|
|
1577
|
+
if (full_trades.length === 0) {
|
|
1578
|
+
console.log(` -> No trades generated by sortedBuildConfig at Risk=${mid_risk.toFixed(2)}. Adjusting high_risk down.`);
|
|
1579
|
+
high_risk = mid_risk;
|
|
1580
|
+
continue;
|
|
1581
|
+
}
|
|
1582
|
+
const trades = determine_amount_to_buy({
|
|
1583
|
+
orders: full_trades,
|
|
1584
|
+
kind: position2.kind,
|
|
1585
|
+
decimal_places: current_app_config.decimal_places,
|
|
1586
|
+
price_places: current_app_config.price_places,
|
|
1587
|
+
position: position2,
|
|
1588
|
+
existingOrders: []
|
|
1589
|
+
});
|
|
1590
|
+
if (trades.length === 0) {
|
|
1591
|
+
console.log(` -> No trades met quantity requirement after filtering at Risk=${mid_risk.toFixed(2)}. Adjusting low_risk up.`);
|
|
1592
|
+
low_risk = mid_risk;
|
|
1593
|
+
continue;
|
|
1594
|
+
}
|
|
1595
|
+
const last_trade = trades[trades.length - 1];
|
|
1596
|
+
const last_entry = last_trade.entry;
|
|
1597
|
+
console.log(` -> Last Trade Entry (Filtered): ${last_entry.toFixed(4)}`);
|
|
1598
|
+
if (position2.kind === "long") {
|
|
1599
|
+
if (last_entry > position2.entry) {
|
|
1600
|
+
console.log(` -> Constraint VIOLATED (Long): last_entry (${last_entry.toFixed(4)}) > position.entry (${position2.entry.toFixed(4)}). Reducing high_risk.`);
|
|
1601
|
+
high_risk = mid_risk;
|
|
1602
|
+
} else {
|
|
1603
|
+
console.log(` -> Constraint MET (Long): last_entry (${last_entry.toFixed(4)}) <= position.entry (${position2.entry.toFixed(4)}). Storing as best, increasing low_risk.`);
|
|
1604
|
+
best_risk = mid_risk;
|
|
1605
|
+
best_app_config = current_app_config;
|
|
1606
|
+
low_risk = mid_risk;
|
|
1607
|
+
}
|
|
1608
|
+
} else {
|
|
1609
|
+
if (last_entry < position2.entry) {
|
|
1610
|
+
console.log(` -> Constraint VIOLATED (Short): last_entry (${last_entry.toFixed(4)}) < position.entry (${position2.entry.toFixed(4)}). Reducing high_risk.`);
|
|
1611
|
+
high_risk = mid_risk;
|
|
1612
|
+
} else {
|
|
1613
|
+
console.log(` -> Constraint MET (Short): last_entry (${last_entry.toFixed(4)}) >= position.entry (${position2.entry.toFixed(4)}). Storing as best, increasing low_risk.`);
|
|
1614
|
+
best_risk = mid_risk;
|
|
1615
|
+
best_app_config = current_app_config;
|
|
1616
|
+
low_risk = mid_risk;
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
if (iterations >= max_iterations) {
|
|
1621
|
+
console.warn(`generateAppConfig: Reached max iterations (${max_iterations}) without converging. Returning best found result.`);
|
|
1622
|
+
} else if (best_app_config) {
|
|
1623
|
+
console.log(`Search finished. Best Risk: ${best_risk?.toFixed(2)}, Final Last Entry: ${best_app_config.last_value?.entry?.toFixed(4)}`);
|
|
1624
|
+
} else {
|
|
1625
|
+
console.warn(`generateAppConfig: Could not find a valid risk configuration.`);
|
|
1626
|
+
}
|
|
1627
|
+
if (!best_app_config) {
|
|
1628
|
+
return null;
|
|
1629
|
+
}
|
|
1630
|
+
best_app_config.entries = determine_amount_to_buy({
|
|
1631
|
+
orders: best_app_config.entries,
|
|
1632
|
+
kind: position2.kind,
|
|
1633
|
+
decimal_places: best_app_config.decimal_places,
|
|
1634
|
+
price_places: best_app_config.price_places,
|
|
1635
|
+
position: position2,
|
|
1636
|
+
existingOrders: []
|
|
1637
|
+
});
|
|
1638
|
+
return best_app_config;
|
|
1639
|
+
}
|
|
1640
|
+
// src/helpers/strategy.ts
|
|
1641
|
+
class Strategy {
|
|
1642
|
+
position;
|
|
1643
|
+
config;
|
|
1644
|
+
constructor(payload) {
|
|
1645
|
+
this.position = {
|
|
1646
|
+
long: payload.long,
|
|
1647
|
+
short: payload.short
|
|
1648
|
+
};
|
|
1649
|
+
this.config = payload.config;
|
|
1650
|
+
if (!this.config.fee_percent) {
|
|
1651
|
+
this.config.fee_percent = 0.05;
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
to_f(price) {
|
|
1655
|
+
return to_f(price, this.config.price_places);
|
|
1656
|
+
}
|
|
1657
|
+
to_df(quantity) {
|
|
1658
|
+
return to_f(quantity, this.config.decimal_places);
|
|
1659
|
+
}
|
|
1660
|
+
pnl(kind) {
|
|
1661
|
+
const position2 = this.position[kind];
|
|
1662
|
+
const { entry, quantity } = position2;
|
|
1663
|
+
const notional = entry * quantity;
|
|
1664
|
+
let tp_percent = this.config.tp_percent;
|
|
1665
|
+
if (kind == "short") {
|
|
1666
|
+
tp_percent = tp_percent * this.config.short_tp_factor;
|
|
1667
|
+
}
|
|
1668
|
+
const profit = notional * (tp_percent / 100);
|
|
1669
|
+
return this.to_f(profit);
|
|
1670
|
+
}
|
|
1671
|
+
tp(kind) {
|
|
1672
|
+
const position2 = this.position[kind];
|
|
1673
|
+
const { entry, quantity } = position2;
|
|
1674
|
+
const profit = this.pnl(kind);
|
|
1675
|
+
const diff = profit / quantity;
|
|
1676
|
+
return this.to_f(kind == "long" ? entry + diff : entry - diff);
|
|
1677
|
+
}
|
|
1678
|
+
calculate_fee(position2) {
|
|
1679
|
+
const { price, quantity } = position2;
|
|
1680
|
+
const fee = price * quantity * this.config.fee_percent / 100;
|
|
1681
|
+
return this.to_f(fee);
|
|
1682
|
+
}
|
|
1683
|
+
get long_tp() {
|
|
1684
|
+
return this.tp("long");
|
|
1685
|
+
}
|
|
1686
|
+
get short_tp() {
|
|
1687
|
+
return this.tp("short");
|
|
1688
|
+
}
|
|
1689
|
+
generateGapClosingAlgorithm(payload) {
|
|
1690
|
+
const { kind, risk_reward = 199 } = payload;
|
|
1691
|
+
const { entry, quantity } = this.position[kind];
|
|
1692
|
+
const focus_position = this.position[kind];
|
|
1693
|
+
const reverse_kind = kind == "long" ? "short" : "long";
|
|
1694
|
+
const reverse_position = this.position[reverse_kind];
|
|
1695
|
+
const second_payload = {
|
|
1696
|
+
entry: this.tp(kind),
|
|
1697
|
+
stop: this.tp(reverse_kind),
|
|
1698
|
+
risk_reward,
|
|
1699
|
+
start_risk: this.pnl(reverse_kind),
|
|
1700
|
+
max_risk: this.config.budget
|
|
1701
|
+
};
|
|
1702
|
+
const third_payload = {
|
|
1703
|
+
entry,
|
|
1704
|
+
quantity,
|
|
1705
|
+
kind
|
|
1706
|
+
};
|
|
1707
|
+
console.log({ second_payload, third_payload });
|
|
1708
|
+
const app_config = generateOptimumAppConfig(this.config.global_config, second_payload, third_payload);
|
|
1709
|
+
if (!app_config) {
|
|
1710
|
+
return null;
|
|
1711
|
+
}
|
|
1712
|
+
const { entries, ...rest } = app_config;
|
|
1713
|
+
const risk = this.to_f(rest.risk_per_trade);
|
|
1714
|
+
const adjusted_focus_entries = entries.map((entry2) => {
|
|
1715
|
+
let adjusted_price = entry2.price;
|
|
1716
|
+
if (focus_position.quantity > 0) {
|
|
1717
|
+
if (kind === "long" && entry2.price >= focus_position.entry) {
|
|
1718
|
+
adjusted_price = focus_position.entry;
|
|
1719
|
+
} else if (kind === "short" && entry2.price <= focus_position.entry) {
|
|
1720
|
+
adjusted_price = focus_position.entry;
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
return {
|
|
1724
|
+
price: adjusted_price,
|
|
1725
|
+
quantity: entry2.quantity
|
|
1726
|
+
};
|
|
1727
|
+
});
|
|
1728
|
+
const avg = determine_average_entry_and_size(adjusted_focus_entries.concat([
|
|
1729
|
+
{
|
|
1730
|
+
price: entry,
|
|
1731
|
+
quantity
|
|
1732
|
+
}
|
|
1733
|
+
]), rest.decimal_places, rest.price_places);
|
|
1734
|
+
const focus_loss = this.to_f((avg.price - second_payload.stop) * avg.quantity);
|
|
1735
|
+
let below_reverse_entries = kind === "long" ? entries.filter((u) => {
|
|
1736
|
+
return u.entry < reverse_position.entry;
|
|
1737
|
+
}) : entries.filter((u) => {
|
|
1738
|
+
return u.entry > reverse_position.entry;
|
|
1739
|
+
});
|
|
1740
|
+
const threshold = below_reverse_entries.at(-1);
|
|
1741
|
+
let adjusted_reverse_entries = entries.map((entry2) => {
|
|
1742
|
+
let adjusted_price = entry2.price;
|
|
1743
|
+
if (threshold) {
|
|
1744
|
+
if (reverse_kind === "short" && entry2.price > threshold.entry) {
|
|
1745
|
+
adjusted_price = threshold.entry;
|
|
1746
|
+
} else if (reverse_kind === "long" && entry2.price < threshold.entry) {
|
|
1747
|
+
adjusted_price = threshold.entry;
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
return {
|
|
1751
|
+
price: adjusted_price,
|
|
1752
|
+
quantity: entry2.quantity
|
|
1753
|
+
};
|
|
1754
|
+
});
|
|
1755
|
+
const reverse_avg = determine_average_entry_and_size(adjusted_reverse_entries.concat([
|
|
1756
|
+
{
|
|
1757
|
+
price: reverse_position.entry,
|
|
1758
|
+
quantity: reverse_position.quantity
|
|
1759
|
+
}
|
|
1760
|
+
]), rest.decimal_places, rest.price_places);
|
|
1761
|
+
const reverse_pnl = this.to_f((reverse_avg.price - second_payload.stop) * reverse_avg.quantity);
|
|
1762
|
+
const fee_to_pay = this.calculate_fee({
|
|
1763
|
+
price: avg.entry,
|
|
1764
|
+
quantity: avg.quantity
|
|
1765
|
+
}) + this.calculate_fee({
|
|
1766
|
+
price: reverse_avg.entry,
|
|
1767
|
+
quantity: reverse_avg.quantity
|
|
1768
|
+
});
|
|
1769
|
+
const net_reverse_pnl = reverse_pnl - fee_to_pay;
|
|
1770
|
+
const ratio = net_reverse_pnl / focus_loss;
|
|
1771
|
+
const quantity_to_sell = this.to_df(ratio * avg.quantity);
|
|
1772
|
+
const remaining_quantity = this.to_df(avg.quantity - quantity_to_sell);
|
|
1773
|
+
return {
|
|
1774
|
+
risk,
|
|
1775
|
+
[kind]: {
|
|
1776
|
+
avg_entry: avg.entry,
|
|
1777
|
+
avg_size: avg.quantity,
|
|
1778
|
+
loss: focus_loss,
|
|
1779
|
+
stop: second_payload.stop,
|
|
1780
|
+
stop_quantity: quantity_to_sell,
|
|
1781
|
+
re_entry_quantity: remaining_quantity,
|
|
1782
|
+
initial_pnl: this.pnl(kind),
|
|
1783
|
+
tp: second_payload.entry
|
|
1784
|
+
},
|
|
1785
|
+
[reverse_kind]: {
|
|
1786
|
+
avg_entry: reverse_avg.entry,
|
|
1787
|
+
avg_size: reverse_avg.quantity,
|
|
1788
|
+
pnl: reverse_pnl,
|
|
1789
|
+
tp: second_payload.stop,
|
|
1790
|
+
re_entry_quantity: remaining_quantity,
|
|
1791
|
+
initial_pnl: this.pnl(reverse_kind)
|
|
1792
|
+
},
|
|
1793
|
+
last_entry: rest.last_value.entry,
|
|
1794
|
+
first_entry: entries.at(-1).entry,
|
|
1795
|
+
threshold
|
|
1796
|
+
};
|
|
1797
|
+
}
|
|
1798
|
+
runIterations(payload) {
|
|
1799
|
+
const { kind, iterations, risk_reward = 199 } = payload;
|
|
1800
|
+
const reverse_kind = kind == "long" ? "short" : "long";
|
|
1801
|
+
const result = [];
|
|
1802
|
+
let position2 = {
|
|
1803
|
+
long: this.position.long,
|
|
1804
|
+
short: this.position.short
|
|
1805
|
+
};
|
|
1806
|
+
for (let i = 0;i < iterations; i++) {
|
|
1807
|
+
const instance = new Strategy({
|
|
1808
|
+
long: position2.long,
|
|
1809
|
+
short: position2.short,
|
|
1810
|
+
config: {
|
|
1811
|
+
...this.config,
|
|
1812
|
+
tp_percent: this.config.tp_percent + i * 4
|
|
1813
|
+
}
|
|
1814
|
+
});
|
|
1815
|
+
const algorithm = instance.generateGapClosingAlgorithm({
|
|
1816
|
+
kind,
|
|
1817
|
+
risk_reward
|
|
1818
|
+
});
|
|
1819
|
+
if (!algorithm) {
|
|
1820
|
+
console.log("No algorithm found");
|
|
1821
|
+
return result;
|
|
1822
|
+
break;
|
|
1823
|
+
}
|
|
1824
|
+
result.push(algorithm);
|
|
1825
|
+
position2[kind] = {
|
|
1826
|
+
entry: algorithm[kind].avg_entry,
|
|
1827
|
+
quantity: algorithm[kind].re_entry_quantity
|
|
1828
|
+
};
|
|
1829
|
+
position2[reverse_kind] = {
|
|
1830
|
+
entry: algorithm[reverse_kind].tp,
|
|
1831
|
+
quantity: algorithm[reverse_kind].re_entry_quantity
|
|
1832
|
+
};
|
|
1833
|
+
}
|
|
1834
|
+
return result;
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1522
1837
|
export {
|
|
1523
1838
|
to_f,
|
|
1524
1839
|
sortedBuildConfig,
|
|
@@ -1534,6 +1849,7 @@ export {
|
|
|
1534
1849
|
getOptimumStopAndRisk,
|
|
1535
1850
|
getDecimalPlaces,
|
|
1536
1851
|
generate_config_params,
|
|
1852
|
+
generateOptimumAppConfig,
|
|
1537
1853
|
formatPrice,
|
|
1538
1854
|
fibonacci_analysis,
|
|
1539
1855
|
extractValue,
|
|
@@ -1552,5 +1868,6 @@ export {
|
|
|
1552
1868
|
buildAppConfig,
|
|
1553
1869
|
asCoins,
|
|
1554
1870
|
allCoins,
|
|
1871
|
+
Strategy,
|
|
1555
1872
|
SpecialCoins
|
|
1556
1873
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -123,6 +123,7 @@ export type GlobalConfig = {
|
|
|
123
123
|
}[];
|
|
124
124
|
risk_reward: number;
|
|
125
125
|
reverse_factor: number;
|
|
126
|
+
leverage?: number;
|
|
126
127
|
};
|
|
127
128
|
interface Position$1 {
|
|
128
129
|
id: number;
|
|
@@ -755,6 +756,17 @@ export declare function determine_amount_to_buy(payload: {
|
|
|
755
756
|
position: any;
|
|
756
757
|
existingOrders: any[];
|
|
757
758
|
}): any[];
|
|
759
|
+
export declare function generateOptimumAppConfig(config: GlobalConfig, payload: {
|
|
760
|
+
entry: number;
|
|
761
|
+
stop: number;
|
|
762
|
+
risk_reward: number;
|
|
763
|
+
start_risk: number;
|
|
764
|
+
max_risk?: number;
|
|
765
|
+
}, position: {
|
|
766
|
+
entry: number;
|
|
767
|
+
quantity: number;
|
|
768
|
+
kind: "long" | "short";
|
|
769
|
+
}): AppConfig | null;
|
|
758
770
|
declare class ExchangePosition {
|
|
759
771
|
exchange: BaseExchange;
|
|
760
772
|
exchange_account: ExchangeAccount$1;
|
|
@@ -1418,6 +1430,63 @@ export declare function initialize(payload: {
|
|
|
1418
1430
|
ignore_proxy?: boolean;
|
|
1419
1431
|
canWithdraw?: boolean;
|
|
1420
1432
|
}): Promise<App>;
|
|
1433
|
+
export type StrategyPosition = {
|
|
1434
|
+
entry: number;
|
|
1435
|
+
quantity: number;
|
|
1436
|
+
};
|
|
1437
|
+
export type Config = {
|
|
1438
|
+
leverage: number;
|
|
1439
|
+
tp_percent: number;
|
|
1440
|
+
short_tp_factor: number;
|
|
1441
|
+
price_places: string;
|
|
1442
|
+
decimal_places: string;
|
|
1443
|
+
fee_percent?: number;
|
|
1444
|
+
budget: number;
|
|
1445
|
+
global_config: GlobalConfig;
|
|
1446
|
+
};
|
|
1447
|
+
export declare class Strategy {
|
|
1448
|
+
position: {
|
|
1449
|
+
long: StrategyPosition;
|
|
1450
|
+
short: StrategyPosition;
|
|
1451
|
+
};
|
|
1452
|
+
config: Config;
|
|
1453
|
+
constructor(payload: {
|
|
1454
|
+
long: StrategyPosition;
|
|
1455
|
+
short: StrategyPosition;
|
|
1456
|
+
config: Config;
|
|
1457
|
+
});
|
|
1458
|
+
to_f(price: number): number;
|
|
1459
|
+
to_df(quantity: number): number;
|
|
1460
|
+
pnl(kind: "long" | "short"): number;
|
|
1461
|
+
tp(kind: "long" | "short"): number;
|
|
1462
|
+
calculate_fee(position: {
|
|
1463
|
+
price: number;
|
|
1464
|
+
quantity: number;
|
|
1465
|
+
}): number;
|
|
1466
|
+
get long_tp(): number;
|
|
1467
|
+
get short_tp(): number;
|
|
1468
|
+
generateGapClosingAlgorithm(payload: {
|
|
1469
|
+
kind: "long" | "short";
|
|
1470
|
+
risk_reward?: number;
|
|
1471
|
+
}): {
|
|
1472
|
+
[x: string]: any;
|
|
1473
|
+
risk: number;
|
|
1474
|
+
last_entry: any;
|
|
1475
|
+
first_entry: any;
|
|
1476
|
+
threshold: any;
|
|
1477
|
+
};
|
|
1478
|
+
runIterations(payload: {
|
|
1479
|
+
kind: "long" | "short";
|
|
1480
|
+
iterations: number;
|
|
1481
|
+
risk_reward?: number;
|
|
1482
|
+
}): {
|
|
1483
|
+
[x: string]: any;
|
|
1484
|
+
risk: number;
|
|
1485
|
+
last_entry: any;
|
|
1486
|
+
first_entry: any;
|
|
1487
|
+
threshold: any;
|
|
1488
|
+
}[];
|
|
1489
|
+
}
|
|
1421
1490
|
|
|
1422
1491
|
export {
|
|
1423
1492
|
ExchangeAccount$1 as ExchangeAccount,
|
package/dist/index.js
CHANGED
|
@@ -55418,6 +55418,124 @@ function determine_amount_to_buy(payload) {
|
|
|
55418
55418
|
filteredOrders = filteredOrders.filter((k) => !existingOrders.map((j) => j.price).includes(k.price));
|
|
55419
55419
|
return filteredOrders;
|
|
55420
55420
|
}
|
|
55421
|
+
function generateOptimumAppConfig(config2, payload, position2) {
|
|
55422
|
+
let low_risk = payload.start_risk;
|
|
55423
|
+
let high_risk = payload.max_risk || 50000;
|
|
55424
|
+
let best_risk = null;
|
|
55425
|
+
let best_app_config = null;
|
|
55426
|
+
const tolerance = 0.1;
|
|
55427
|
+
const max_iterations = 150;
|
|
55428
|
+
let iterations = 0;
|
|
55429
|
+
console.log(`Starting risk search for ${position2.kind} position. Target Entry: ${position2.entry}, Initial Risk Range: [${low_risk}, ${high_risk}]`);
|
|
55430
|
+
while (high_risk - low_risk > tolerance && iterations < max_iterations) {
|
|
55431
|
+
iterations++;
|
|
55432
|
+
const mid_risk = (low_risk + high_risk) / 2;
|
|
55433
|
+
const {
|
|
55434
|
+
app_config: current_app_config,
|
|
55435
|
+
max_size,
|
|
55436
|
+
last_value,
|
|
55437
|
+
entries
|
|
55438
|
+
} = get_app_config_and_max_size({
|
|
55439
|
+
...config2,
|
|
55440
|
+
risk: mid_risk,
|
|
55441
|
+
profit_percent: config2.profit_percent || 0,
|
|
55442
|
+
profit: config2.profit || 500,
|
|
55443
|
+
risk_reward: payload.risk_reward,
|
|
55444
|
+
kind: position2.kind,
|
|
55445
|
+
price_places: config2.price_places,
|
|
55446
|
+
decimal_places: config2.decimal_places,
|
|
55447
|
+
accounts: config2.accounts || [],
|
|
55448
|
+
reduce_percent: config2.reduce_percent || 90,
|
|
55449
|
+
reverse_factor: config2.reverse_factor || 1,
|
|
55450
|
+
symbol: config2.symbol || "",
|
|
55451
|
+
support: config2.support || 0,
|
|
55452
|
+
resistance: config2.resistance || 0,
|
|
55453
|
+
min_size: config2.min_size || 0,
|
|
55454
|
+
stop_percent: config2.stop_percent || 0
|
|
55455
|
+
}, {
|
|
55456
|
+
entry: payload.entry,
|
|
55457
|
+
stop: payload.stop,
|
|
55458
|
+
kind: position2.kind
|
|
55459
|
+
});
|
|
55460
|
+
current_app_config.max_size = max_size;
|
|
55461
|
+
current_app_config.last_value = last_value;
|
|
55462
|
+
current_app_config.entries = entries;
|
|
55463
|
+
current_app_config.risk_reward = payload.risk_reward;
|
|
55464
|
+
const full_trades = sortedBuildConfig(current_app_config, {
|
|
55465
|
+
entry: current_app_config.entry,
|
|
55466
|
+
stop: current_app_config.stop,
|
|
55467
|
+
kind: current_app_config.kind,
|
|
55468
|
+
risk: current_app_config.risk_per_trade,
|
|
55469
|
+
risk_reward: current_app_config.risk_reward,
|
|
55470
|
+
increase: true,
|
|
55471
|
+
gap: current_app_config.gap,
|
|
55472
|
+
price_places: current_app_config.price_places,
|
|
55473
|
+
decimal_places: current_app_config.decimal_places
|
|
55474
|
+
});
|
|
55475
|
+
console.log(`Iteration ${iterations}: Risk=${mid_risk.toFixed(2)}, Low=${low_risk.toFixed(2)}, High=${high_risk.toFixed(2)}`);
|
|
55476
|
+
if (full_trades.length === 0) {
|
|
55477
|
+
console.log(` -> No trades generated by sortedBuildConfig at Risk=${mid_risk.toFixed(2)}. Adjusting high_risk down.`);
|
|
55478
|
+
high_risk = mid_risk;
|
|
55479
|
+
continue;
|
|
55480
|
+
}
|
|
55481
|
+
const trades = determine_amount_to_buy({
|
|
55482
|
+
orders: full_trades,
|
|
55483
|
+
kind: position2.kind,
|
|
55484
|
+
decimal_places: current_app_config.decimal_places,
|
|
55485
|
+
price_places: current_app_config.price_places,
|
|
55486
|
+
position: position2,
|
|
55487
|
+
existingOrders: []
|
|
55488
|
+
});
|
|
55489
|
+
if (trades.length === 0) {
|
|
55490
|
+
console.log(` -> No trades met quantity requirement after filtering at Risk=${mid_risk.toFixed(2)}. Adjusting low_risk up.`);
|
|
55491
|
+
low_risk = mid_risk;
|
|
55492
|
+
continue;
|
|
55493
|
+
}
|
|
55494
|
+
const last_trade = trades[trades.length - 1];
|
|
55495
|
+
const last_entry = last_trade.entry;
|
|
55496
|
+
console.log(` -> Last Trade Entry (Filtered): ${last_entry.toFixed(4)}`);
|
|
55497
|
+
if (position2.kind === "long") {
|
|
55498
|
+
if (last_entry > position2.entry) {
|
|
55499
|
+
console.log(` -> Constraint VIOLATED (Long): last_entry (${last_entry.toFixed(4)}) > position.entry (${position2.entry.toFixed(4)}). Reducing high_risk.`);
|
|
55500
|
+
high_risk = mid_risk;
|
|
55501
|
+
} else {
|
|
55502
|
+
console.log(` -> Constraint MET (Long): last_entry (${last_entry.toFixed(4)}) <= position.entry (${position2.entry.toFixed(4)}). Storing as best, increasing low_risk.`);
|
|
55503
|
+
best_risk = mid_risk;
|
|
55504
|
+
best_app_config = current_app_config;
|
|
55505
|
+
low_risk = mid_risk;
|
|
55506
|
+
}
|
|
55507
|
+
} else {
|
|
55508
|
+
if (last_entry < position2.entry) {
|
|
55509
|
+
console.log(` -> Constraint VIOLATED (Short): last_entry (${last_entry.toFixed(4)}) < position.entry (${position2.entry.toFixed(4)}). Reducing high_risk.`);
|
|
55510
|
+
high_risk = mid_risk;
|
|
55511
|
+
} else {
|
|
55512
|
+
console.log(` -> Constraint MET (Short): last_entry (${last_entry.toFixed(4)}) >= position.entry (${position2.entry.toFixed(4)}). Storing as best, increasing low_risk.`);
|
|
55513
|
+
best_risk = mid_risk;
|
|
55514
|
+
best_app_config = current_app_config;
|
|
55515
|
+
low_risk = mid_risk;
|
|
55516
|
+
}
|
|
55517
|
+
}
|
|
55518
|
+
}
|
|
55519
|
+
if (iterations >= max_iterations) {
|
|
55520
|
+
console.warn(`generateAppConfig: Reached max iterations (${max_iterations}) without converging. Returning best found result.`);
|
|
55521
|
+
} else if (best_app_config) {
|
|
55522
|
+
console.log(`Search finished. Best Risk: ${best_risk?.toFixed(2)}, Final Last Entry: ${best_app_config.last_value?.entry?.toFixed(4)}`);
|
|
55523
|
+
} else {
|
|
55524
|
+
console.warn(`generateAppConfig: Could not find a valid risk configuration.`);
|
|
55525
|
+
}
|
|
55526
|
+
if (!best_app_config) {
|
|
55527
|
+
return null;
|
|
55528
|
+
}
|
|
55529
|
+
best_app_config.entries = determine_amount_to_buy({
|
|
55530
|
+
orders: best_app_config.entries,
|
|
55531
|
+
kind: position2.kind,
|
|
55532
|
+
decimal_places: best_app_config.decimal_places,
|
|
55533
|
+
price_places: best_app_config.price_places,
|
|
55534
|
+
position: position2,
|
|
55535
|
+
existingOrders: []
|
|
55536
|
+
});
|
|
55537
|
+
return best_app_config;
|
|
55538
|
+
}
|
|
55421
55539
|
|
|
55422
55540
|
// src/position.ts
|
|
55423
55541
|
class ExchangePosition {
|
|
@@ -57952,6 +58070,203 @@ async function initialize(payload) {
|
|
|
57952
58070
|
});
|
|
57953
58071
|
return app;
|
|
57954
58072
|
}
|
|
58073
|
+
// src/helpers/strategy.ts
|
|
58074
|
+
class Strategy {
|
|
58075
|
+
position;
|
|
58076
|
+
config;
|
|
58077
|
+
constructor(payload) {
|
|
58078
|
+
this.position = {
|
|
58079
|
+
long: payload.long,
|
|
58080
|
+
short: payload.short
|
|
58081
|
+
};
|
|
58082
|
+
this.config = payload.config;
|
|
58083
|
+
if (!this.config.fee_percent) {
|
|
58084
|
+
this.config.fee_percent = 0.05;
|
|
58085
|
+
}
|
|
58086
|
+
}
|
|
58087
|
+
to_f(price) {
|
|
58088
|
+
return to_f(price, this.config.price_places);
|
|
58089
|
+
}
|
|
58090
|
+
to_df(quantity) {
|
|
58091
|
+
return to_f(quantity, this.config.decimal_places);
|
|
58092
|
+
}
|
|
58093
|
+
pnl(kind) {
|
|
58094
|
+
const position2 = this.position[kind];
|
|
58095
|
+
const { entry, quantity } = position2;
|
|
58096
|
+
const notional = entry * quantity;
|
|
58097
|
+
let tp_percent = this.config.tp_percent;
|
|
58098
|
+
if (kind == "short") {
|
|
58099
|
+
tp_percent = tp_percent * this.config.short_tp_factor;
|
|
58100
|
+
}
|
|
58101
|
+
const profit = notional * (tp_percent / 100);
|
|
58102
|
+
return this.to_f(profit);
|
|
58103
|
+
}
|
|
58104
|
+
tp(kind) {
|
|
58105
|
+
const position2 = this.position[kind];
|
|
58106
|
+
const { entry, quantity } = position2;
|
|
58107
|
+
const profit = this.pnl(kind);
|
|
58108
|
+
const diff = profit / quantity;
|
|
58109
|
+
return this.to_f(kind == "long" ? entry + diff : entry - diff);
|
|
58110
|
+
}
|
|
58111
|
+
calculate_fee(position2) {
|
|
58112
|
+
const { price, quantity } = position2;
|
|
58113
|
+
const fee = price * quantity * this.config.fee_percent / 100;
|
|
58114
|
+
return this.to_f(fee);
|
|
58115
|
+
}
|
|
58116
|
+
get long_tp() {
|
|
58117
|
+
return this.tp("long");
|
|
58118
|
+
}
|
|
58119
|
+
get short_tp() {
|
|
58120
|
+
return this.tp("short");
|
|
58121
|
+
}
|
|
58122
|
+
generateGapClosingAlgorithm(payload) {
|
|
58123
|
+
const { kind, risk_reward = 199 } = payload;
|
|
58124
|
+
const { entry, quantity } = this.position[kind];
|
|
58125
|
+
const focus_position = this.position[kind];
|
|
58126
|
+
const reverse_kind = kind == "long" ? "short" : "long";
|
|
58127
|
+
const reverse_position = this.position[reverse_kind];
|
|
58128
|
+
const second_payload = {
|
|
58129
|
+
entry: this.tp(kind),
|
|
58130
|
+
stop: this.tp(reverse_kind),
|
|
58131
|
+
risk_reward,
|
|
58132
|
+
start_risk: this.pnl(reverse_kind),
|
|
58133
|
+
max_risk: this.config.budget
|
|
58134
|
+
};
|
|
58135
|
+
const third_payload = {
|
|
58136
|
+
entry,
|
|
58137
|
+
quantity,
|
|
58138
|
+
kind
|
|
58139
|
+
};
|
|
58140
|
+
console.log({ second_payload, third_payload });
|
|
58141
|
+
const app_config = generateOptimumAppConfig(this.config.global_config, second_payload, third_payload);
|
|
58142
|
+
if (!app_config) {
|
|
58143
|
+
return null;
|
|
58144
|
+
}
|
|
58145
|
+
const { entries, ...rest } = app_config;
|
|
58146
|
+
const risk = this.to_f(rest.risk_per_trade);
|
|
58147
|
+
const adjusted_focus_entries = entries.map((entry2) => {
|
|
58148
|
+
let adjusted_price = entry2.price;
|
|
58149
|
+
if (focus_position.quantity > 0) {
|
|
58150
|
+
if (kind === "long" && entry2.price >= focus_position.entry) {
|
|
58151
|
+
adjusted_price = focus_position.entry;
|
|
58152
|
+
} else if (kind === "short" && entry2.price <= focus_position.entry) {
|
|
58153
|
+
adjusted_price = focus_position.entry;
|
|
58154
|
+
}
|
|
58155
|
+
}
|
|
58156
|
+
return {
|
|
58157
|
+
price: adjusted_price,
|
|
58158
|
+
quantity: entry2.quantity
|
|
58159
|
+
};
|
|
58160
|
+
});
|
|
58161
|
+
const avg = determine_average_entry_and_size(adjusted_focus_entries.concat([
|
|
58162
|
+
{
|
|
58163
|
+
price: entry,
|
|
58164
|
+
quantity
|
|
58165
|
+
}
|
|
58166
|
+
]), rest.decimal_places, rest.price_places);
|
|
58167
|
+
const focus_loss = this.to_f((avg.price - second_payload.stop) * avg.quantity);
|
|
58168
|
+
let below_reverse_entries = kind === "long" ? entries.filter((u) => {
|
|
58169
|
+
return u.entry < reverse_position.entry;
|
|
58170
|
+
}) : entries.filter((u) => {
|
|
58171
|
+
return u.entry > reverse_position.entry;
|
|
58172
|
+
});
|
|
58173
|
+
const threshold = below_reverse_entries.at(-1);
|
|
58174
|
+
let adjusted_reverse_entries = entries.map((entry2) => {
|
|
58175
|
+
let adjusted_price = entry2.price;
|
|
58176
|
+
if (threshold) {
|
|
58177
|
+
if (reverse_kind === "short" && entry2.price > threshold.entry) {
|
|
58178
|
+
adjusted_price = threshold.entry;
|
|
58179
|
+
} else if (reverse_kind === "long" && entry2.price < threshold.entry) {
|
|
58180
|
+
adjusted_price = threshold.entry;
|
|
58181
|
+
}
|
|
58182
|
+
}
|
|
58183
|
+
return {
|
|
58184
|
+
price: adjusted_price,
|
|
58185
|
+
quantity: entry2.quantity
|
|
58186
|
+
};
|
|
58187
|
+
});
|
|
58188
|
+
const reverse_avg = determine_average_entry_and_size(adjusted_reverse_entries.concat([
|
|
58189
|
+
{
|
|
58190
|
+
price: reverse_position.entry,
|
|
58191
|
+
quantity: reverse_position.quantity
|
|
58192
|
+
}
|
|
58193
|
+
]), rest.decimal_places, rest.price_places);
|
|
58194
|
+
const reverse_pnl = this.to_f((reverse_avg.price - second_payload.stop) * reverse_avg.quantity);
|
|
58195
|
+
const fee_to_pay = this.calculate_fee({
|
|
58196
|
+
price: avg.entry,
|
|
58197
|
+
quantity: avg.quantity
|
|
58198
|
+
}) + this.calculate_fee({
|
|
58199
|
+
price: reverse_avg.entry,
|
|
58200
|
+
quantity: reverse_avg.quantity
|
|
58201
|
+
});
|
|
58202
|
+
const net_reverse_pnl = reverse_pnl - fee_to_pay;
|
|
58203
|
+
const ratio = net_reverse_pnl / focus_loss;
|
|
58204
|
+
const quantity_to_sell = this.to_df(ratio * avg.quantity);
|
|
58205
|
+
const remaining_quantity = this.to_df(avg.quantity - quantity_to_sell);
|
|
58206
|
+
return {
|
|
58207
|
+
risk,
|
|
58208
|
+
[kind]: {
|
|
58209
|
+
avg_entry: avg.entry,
|
|
58210
|
+
avg_size: avg.quantity,
|
|
58211
|
+
loss: focus_loss,
|
|
58212
|
+
stop: second_payload.stop,
|
|
58213
|
+
stop_quantity: quantity_to_sell,
|
|
58214
|
+
re_entry_quantity: remaining_quantity,
|
|
58215
|
+
initial_pnl: this.pnl(kind),
|
|
58216
|
+
tp: second_payload.entry
|
|
58217
|
+
},
|
|
58218
|
+
[reverse_kind]: {
|
|
58219
|
+
avg_entry: reverse_avg.entry,
|
|
58220
|
+
avg_size: reverse_avg.quantity,
|
|
58221
|
+
pnl: reverse_pnl,
|
|
58222
|
+
tp: second_payload.stop,
|
|
58223
|
+
re_entry_quantity: remaining_quantity,
|
|
58224
|
+
initial_pnl: this.pnl(reverse_kind)
|
|
58225
|
+
},
|
|
58226
|
+
last_entry: rest.last_value.entry,
|
|
58227
|
+
first_entry: entries.at(-1).entry,
|
|
58228
|
+
threshold
|
|
58229
|
+
};
|
|
58230
|
+
}
|
|
58231
|
+
runIterations(payload) {
|
|
58232
|
+
const { kind, iterations, risk_reward = 199 } = payload;
|
|
58233
|
+
const reverse_kind = kind == "long" ? "short" : "long";
|
|
58234
|
+
const result = [];
|
|
58235
|
+
let position2 = {
|
|
58236
|
+
long: this.position.long,
|
|
58237
|
+
short: this.position.short
|
|
58238
|
+
};
|
|
58239
|
+
for (let i2 = 0;i2 < iterations; i2++) {
|
|
58240
|
+
const instance = new Strategy({
|
|
58241
|
+
long: position2.long,
|
|
58242
|
+
short: position2.short,
|
|
58243
|
+
config: {
|
|
58244
|
+
...this.config,
|
|
58245
|
+
tp_percent: this.config.tp_percent + i2 * 4
|
|
58246
|
+
}
|
|
58247
|
+
});
|
|
58248
|
+
const algorithm = instance.generateGapClosingAlgorithm({
|
|
58249
|
+
kind,
|
|
58250
|
+
risk_reward
|
|
58251
|
+
});
|
|
58252
|
+
if (!algorithm) {
|
|
58253
|
+
console.log("No algorithm found");
|
|
58254
|
+
return result;
|
|
58255
|
+
break;
|
|
58256
|
+
}
|
|
58257
|
+
result.push(algorithm);
|
|
58258
|
+
position2[kind] = {
|
|
58259
|
+
entry: algorithm[kind].avg_entry,
|
|
58260
|
+
quantity: algorithm[kind].re_entry_quantity
|
|
58261
|
+
};
|
|
58262
|
+
position2[reverse_kind] = {
|
|
58263
|
+
entry: algorithm[reverse_kind].tp,
|
|
58264
|
+
quantity: algorithm[reverse_kind].re_entry_quantity
|
|
58265
|
+
};
|
|
58266
|
+
}
|
|
58267
|
+
return result;
|
|
58268
|
+
}
|
|
58269
|
+
}
|
|
57955
58270
|
export {
|
|
57956
58271
|
sortedBuildConfig,
|
|
57957
58272
|
initialize,
|
|
@@ -57959,6 +58274,7 @@ export {
|
|
|
57959
58274
|
get_app_config_and_max_size,
|
|
57960
58275
|
getOptimumStopAndRisk,
|
|
57961
58276
|
generate_config_params,
|
|
58277
|
+
generateOptimumAppConfig,
|
|
57962
58278
|
determine_break_even_price,
|
|
57963
58279
|
determine_average_entry_and_size,
|
|
57964
58280
|
determine_amount_to_buy,
|
|
@@ -57966,6 +58282,7 @@ export {
|
|
|
57966
58282
|
buildConfig,
|
|
57967
58283
|
buildAvg,
|
|
57968
58284
|
buildAppConfig,
|
|
58285
|
+
Strategy,
|
|
57969
58286
|
ExchangeAccount,
|
|
57970
58287
|
AppDatabase
|
|
57971
58288
|
};
|