@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.
@@ -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 {};
@@ -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
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gbozee/ultimate",
3
3
  "type": "module",
4
- "version": "0.0.2-48",
4
+ "version": "0.0.2-51",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",