@backtest-kit/cli 3.8.0 → 5.0.0

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/build/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import * as BacktestKit from 'backtest-kit';
3
- import { Storage, Notification, Markdown, Report, StorageLive, StorageBacktest, NotificationLive, NotificationBacktest, setConfig, Log, listExchangeSchema, addExchangeSchema, roundTicks, listFrameSchema, addFrameSchema, listenDoneLive, listenDoneBacktest, shutdown, listenSignal, listStrategySchema, overrideExchangeSchema, Backtest, Live, getCandles, checkCandles, warmCandles, listenRisk, listenStrategyCommit } from 'backtest-kit';
4
- import { getErrorMessage, errorData, singleshot, str, BehaviorSubject, compose, execpool, queued, sleep, randomString, createAwaiter, TIMEOUT_SYMBOL, typo, retry, memoize, trycatch } from 'functools-kit';
3
+ import { Storage, Notification, Markdown, Report, StorageLive, StorageBacktest, NotificationLive, NotificationBacktest, setConfig, Log, listExchangeSchema, addExchangeSchema, roundTicks, listFrameSchema, addFrameSchema, listenDoneLive, listenDoneBacktest, shutdown, listenSignal, listStrategySchema, overrideExchangeSchema, Backtest, Live, getCandles, checkCandles, warmCandles, listenRisk, listenStrategyCommit, listenSync } from 'backtest-kit';
4
+ import { getErrorMessage, errorData, singleshot, str, BehaviorSubject, compose, execpool, queued, sleep, randomString, createAwaiter, TIMEOUT_SYMBOL, typo, retry, trycatch, memoize } from 'functools-kit';
5
5
  import fs, { constants } from 'fs';
6
6
  import * as stackTrace from 'stack-trace';
7
7
  import { fileURLToPath, pathToFileURL } from 'url';
@@ -200,7 +200,6 @@ const schemaServices$1 = {
200
200
  const providerServices$1 = {
201
201
  frontendProviderService: Symbol('frontendProviderService'),
202
202
  telegramProviderService: Symbol('telegramProviderService'),
203
- liveProviderService: Symbol('liveProviderService'),
204
203
  };
205
204
  const webServices$1 = {
206
205
  telegramWebService: Symbol('telegramWebService'),
@@ -586,6 +585,7 @@ class BacktestMainService {
586
585
  this.cacheLogicService = inject(TYPES.cacheLogicService);
587
586
  this.frontendProviderService = inject(TYPES.frontendProviderService);
588
587
  this.telegramProviderService = inject(TYPES.telegramProviderService);
588
+ this.moduleConnectionService = inject(TYPES.moduleConnectionService);
589
589
  this.run = singleshot(async (payload) => {
590
590
  this.loggerService.log("backtestMainService run", {
591
591
  payload,
@@ -634,6 +634,7 @@ class BacktestMainService {
634
634
  });
635
635
  notifyVerbose();
636
636
  }
637
+ await this.moduleConnectionService.loadModule("./backtest.module");
637
638
  Backtest.background(symbol, {
638
639
  strategyName,
639
640
  frameName,
@@ -677,7 +678,7 @@ class LiveMainService {
677
678
  this.symbolSchemaService = inject(TYPES.symbolSchemaService);
678
679
  this.frontendProviderService = inject(TYPES.frontendProviderService);
679
680
  this.telegramProviderService = inject(TYPES.telegramProviderService);
680
- this.liveProviderService = inject(TYPES.liveProviderService);
681
+ this.moduleConnectionService = inject(TYPES.moduleConnectionService);
681
682
  this.run = singleshot(async (payload) => {
682
683
  this.loggerService.log("liveMainService run", {
683
684
  payload,
@@ -685,7 +686,6 @@ class LiveMainService {
685
686
  {
686
687
  this.frontendProviderService.connect();
687
688
  this.telegramProviderService.connect();
688
- this.liveProviderService.connect();
689
689
  }
690
690
  await this.resolveService.attachEntryPoint(payload.entryPoint);
691
691
  {
@@ -714,6 +714,7 @@ class LiveMainService {
714
714
  });
715
715
  notifyVerbose();
716
716
  }
717
+ await this.moduleConnectionService.loadModule("./live.module");
717
718
  Live.background(symbol, {
718
719
  strategyName,
719
720
  exchangeName,
@@ -752,6 +753,7 @@ class PaperMainService {
752
753
  this.symbolSchemaService = inject(TYPES.symbolSchemaService);
753
754
  this.frontendProviderService = inject(TYPES.frontendProviderService);
754
755
  this.telegramProviderService = inject(TYPES.telegramProviderService);
756
+ this.moduleConnectionService = inject(TYPES.moduleConnectionService);
755
757
  this.run = singleshot(async (payload) => {
756
758
  this.loggerService.log("paperMainService init");
757
759
  {
@@ -785,6 +787,7 @@ class PaperMainService {
785
787
  });
786
788
  notifyVerbose();
787
789
  }
790
+ await this.moduleConnectionService.loadModule("./paper.module");
788
791
  Live.background(symbol, {
789
792
  strategyName,
790
793
  exchangeName,
@@ -1487,6 +1490,26 @@ class TelegramLogicService {
1487
1490
  markdown,
1488
1491
  });
1489
1492
  };
1493
+ this.notifySignalOpen = trycatch(async (event) => {
1494
+ this.loggerService.log("telegramLogicService notifySignalOpen", {
1495
+ event,
1496
+ });
1497
+ const markdown = await this.telegramTemplateService.getSignalOpenMarkdown(event);
1498
+ await this.telegramWebService.publishNotify({
1499
+ symbol: event.symbol,
1500
+ markdown,
1501
+ });
1502
+ });
1503
+ this.notifySignalClose = trycatch(async (event) => {
1504
+ this.loggerService.log("telegramLogicService notifySignalClose", {
1505
+ event,
1506
+ });
1507
+ const markdown = await this.telegramTemplateService.getSignalCloseMarkdown(event);
1508
+ await this.telegramWebService.publishNotify({
1509
+ symbol: event.symbol,
1510
+ markdown,
1511
+ });
1512
+ });
1490
1513
  this.connect = singleshot(() => {
1491
1514
  this.loggerService.log("telegramLogicService connect");
1492
1515
  const unRisk = listenRisk(async (event) => {
@@ -1536,8 +1559,18 @@ class TelegramLogicService {
1536
1559
  return;
1537
1560
  }
1538
1561
  });
1562
+ const unSync = listenSync(async (event) => {
1563
+ if (event.action === "signal-open") {
1564
+ await this.notifySignalOpen(event);
1565
+ return;
1566
+ }
1567
+ if (event.action === "signal-close") {
1568
+ await this.notifySignalClose(event);
1569
+ return;
1570
+ }
1571
+ });
1539
1572
  const unConnect = () => this.connect.clear();
1540
- const unListen = compose(() => unRisk(), () => unSignal(), () => unCommit(), () => unConnect());
1573
+ const unListen = compose(() => unRisk(), () => unSignal(), () => unCommit(), () => unSync(), () => unConnect());
1541
1574
  return () => {
1542
1575
  STOP_BOT_FN();
1543
1576
  unListen();
@@ -1632,6 +1665,18 @@ class TelegramTemplateService {
1632
1665
  });
1633
1666
  return await RENDER_TEMPLATE_FN("average-buy.mustache", event, this);
1634
1667
  };
1668
+ this.getSignalOpenMarkdown = async (event) => {
1669
+ this.loggerService.log("telegramTemplateService getSignalOpenMarkdown", {
1670
+ event,
1671
+ });
1672
+ return await RENDER_TEMPLATE_FN("signal-open.mustache", event, this);
1673
+ };
1674
+ this.getSignalCloseMarkdown = async (event) => {
1675
+ this.loggerService.log("telegramTemplateService getSignalCloseMarkdown", {
1676
+ event,
1677
+ });
1678
+ return await RENDER_TEMPLATE_FN("signal-close.mustache", event, this);
1679
+ };
1635
1680
  }
1636
1681
  }
1637
1682
 
@@ -1650,39 +1695,36 @@ const getExtVariants = (fileName) => {
1650
1695
  };
1651
1696
  const REQUIRE_MODULE_FACTORY = (fileName) => {
1652
1697
  {
1653
- return null;
1698
+ return false;
1654
1699
  }
1655
1700
  };
1656
1701
  const IMPORT_MODULE_FACTORY = async (fileName) => {
1657
1702
  for (const variant of getExtVariants(fileName)) {
1658
1703
  try {
1659
- return await import(pathToFileURL(variant).href);
1704
+ await import(pathToFileURL(variant).href);
1705
+ return true;
1660
1706
  }
1661
1707
  catch {
1662
1708
  continue;
1663
1709
  }
1664
1710
  }
1665
- return null;
1711
+ return false;
1666
1712
  };
1667
1713
  const BABEL_MODULE_FACTORY = async (fileName, self) => {
1668
1714
  for (const variant of getExtVariants(fileName)) {
1669
1715
  try {
1670
1716
  const code = await fs$1.readFile(variant, "utf-8");
1671
- const { exports } = self.babelService.transpileAndRun(code);
1672
- if (exports.default) {
1673
- return exports.default;
1674
- }
1675
- return exports;
1717
+ self.babelService.transpileAndRun(code);
1718
+ return true;
1676
1719
  }
1677
1720
  catch (error) {
1678
1721
  console.log(getErrorMessage(error));
1679
1722
  continue;
1680
1723
  }
1681
1724
  }
1682
- return null;
1725
+ return false;
1683
1726
  };
1684
1727
  const LOAD_MODULE_MODULE_FN = async (fileName, self) => {
1685
- let Ctor = null;
1686
1728
  const overridePath = path.join(self.resolveService.OVERRIDE_MODULES_DIR, fileName);
1687
1729
  const targetPath = path.join(process.cwd(), "modules", fileName);
1688
1730
  const hasOverride = await fs$1
@@ -1690,212 +1732,29 @@ const LOAD_MODULE_MODULE_FN = async (fileName, self) => {
1690
1732
  .then(() => true)
1691
1733
  .catch(() => false);
1692
1734
  const resolvedFile = hasOverride ? overridePath : targetPath;
1693
- if ((Ctor = REQUIRE_MODULE_FACTORY())) {
1694
- return typeof Ctor === "function" ? new Ctor() : Ctor;
1735
+ if (REQUIRE_MODULE_FACTORY()) {
1736
+ return true;
1695
1737
  }
1696
- if ((Ctor = await IMPORT_MODULE_FACTORY(resolvedFile))) {
1697
- return typeof Ctor === "function" ? new Ctor() : Ctor;
1738
+ if (await IMPORT_MODULE_FACTORY(resolvedFile)) {
1739
+ return true;
1698
1740
  }
1699
- if ((Ctor = await BABEL_MODULE_FACTORY(resolvedFile, self))) {
1700
- return typeof Ctor === "function" ? new Ctor() : Ctor;
1741
+ if (await BABEL_MODULE_FACTORY(resolvedFile, self)) {
1742
+ return true;
1701
1743
  }
1702
- throw new Error(`Module module import failed for file: ${resolvedFile}`);
1744
+ console.warn(`Module module import failed for file: ${resolvedFile}`);
1745
+ return false;
1703
1746
  };
1704
1747
  class ModuleConnectionService {
1705
1748
  constructor() {
1706
1749
  this.loggerService = inject(TYPES.loggerService);
1707
1750
  this.resolveService = inject(TYPES.resolveService);
1708
1751
  this.babelService = inject(TYPES.babelService);
1709
- this.getInstance = memoize(([fileName]) => `${fileName}`, async (fileName) => {
1752
+ this.loadModule = async (fileName) => {
1710
1753
  this.loggerService.log("moduleConnectionService getInstance", {
1711
1754
  fileName,
1712
1755
  });
1713
1756
  return await LOAD_MODULE_MODULE_FN(fileName, this);
1714
- });
1715
- }
1716
- }
1717
-
1718
- const LOAD_INSTANCE_FN = singleshot(trycatch(async (self) => {
1719
- const module = (await self.moduleConnectionService.getInstance("./live.module"));
1720
- return module;
1721
- }, { defaultValue: null }));
1722
- class LiveProviderService {
1723
- constructor() {
1724
- this.loggerService = inject(TYPES.loggerService);
1725
- this.moduleConnectionService = inject(TYPES.moduleConnectionService);
1726
- this.handleTrailingTake = async (event) => {
1727
- this.loggerService.log("liveProviderService handleTrailingTake", {
1728
- event,
1729
- });
1730
- const instance = await LOAD_INSTANCE_FN(this);
1731
- if (instance?.onTrailingTake) {
1732
- await instance.onTrailingTake(event);
1733
- }
1734
- };
1735
- this.handleTrailingStop = async (event) => {
1736
- this.loggerService.log("liveProviderService handleTrailingStop", {
1737
- event,
1738
- });
1739
- const instance = await LOAD_INSTANCE_FN(this);
1740
- if (instance?.onTrailingStop) {
1741
- await instance.onTrailingStop(event);
1742
- }
1743
- };
1744
- this.handleBreakeven = async (event) => {
1745
- this.loggerService.log("liveProviderService handleBreakeven", {
1746
- event,
1747
- });
1748
- const instance = await LOAD_INSTANCE_FN(this);
1749
- if (instance?.onBreakeven) {
1750
- await instance.onBreakeven(event);
1751
- }
1752
1757
  };
1753
- this.handlePartialProfit = async (event) => {
1754
- this.loggerService.log("liveProviderService handlePartialProfit", {
1755
- event,
1756
- });
1757
- const instance = await LOAD_INSTANCE_FN(this);
1758
- if (instance?.onPartialProfit) {
1759
- await instance.onPartialProfit(event);
1760
- }
1761
- };
1762
- this.handlePartialLoss = async (event) => {
1763
- this.loggerService.log("liveProviderService handlePartialLoss", {
1764
- event,
1765
- });
1766
- const instance = await LOAD_INSTANCE_FN(this);
1767
- if (instance?.onPartialLoss) {
1768
- await instance.onPartialLoss(event);
1769
- }
1770
- };
1771
- this.handleScheduled = async (event) => {
1772
- this.loggerService.log("liveProviderService handleScheduled", {
1773
- event,
1774
- });
1775
- const instance = await LOAD_INSTANCE_FN(this);
1776
- if (instance?.onScheduled) {
1777
- await instance.onScheduled(event);
1778
- }
1779
- };
1780
- this.handleCancelled = async (event) => {
1781
- this.loggerService.log("liveProviderService handleCancelled", {
1782
- event,
1783
- });
1784
- const instance = await LOAD_INSTANCE_FN(this);
1785
- if (instance?.onCancelled) {
1786
- await instance.onCancelled(event);
1787
- }
1788
- };
1789
- this.handleOpened = async (event) => {
1790
- this.loggerService.log("liveProviderService handleOpened", {
1791
- event,
1792
- });
1793
- const instance = await LOAD_INSTANCE_FN(this);
1794
- if (instance?.onOpened) {
1795
- await instance.onOpened(event);
1796
- }
1797
- };
1798
- this.handleClosed = async (event) => {
1799
- this.loggerService.log("liveProviderService handleClosed", {
1800
- event,
1801
- });
1802
- const instance = await LOAD_INSTANCE_FN(this);
1803
- if (instance?.onClosed) {
1804
- await instance.onClosed(event);
1805
- }
1806
- };
1807
- this.handleRisk = async (event) => {
1808
- this.loggerService.log("liveProviderService handleClosed", {
1809
- event,
1810
- });
1811
- const instance = await LOAD_INSTANCE_FN(this);
1812
- if (instance?.onRisk) {
1813
- await instance.onRisk(event);
1814
- }
1815
- };
1816
- this.handleAverageBuy = async (event) => {
1817
- this.loggerService.log("liveProviderService handleAverageBuy", {
1818
- event,
1819
- });
1820
- const instance = await LOAD_INSTANCE_FN(this);
1821
- if (instance?.onAverageBuy) {
1822
- await instance.onAverageBuy(event);
1823
- }
1824
- };
1825
- this.enable = singleshot(() => {
1826
- this.loggerService.log("liveProviderService enable");
1827
- LOAD_INSTANCE_FN(this).then((module) => {
1828
- if (module) {
1829
- this.loggerService.log("Live trading initialized successfully with ./modules/live.module.mjs");
1830
- return;
1831
- }
1832
- console.log("No ./modules/live.module.mjs found, live trading failed to initialize");
1833
- process.exit(-1);
1834
- });
1835
- const unRisk = listenRisk(async (event) => {
1836
- await this.handleRisk(event);
1837
- });
1838
- const unSignal = listenSignal(async (event) => {
1839
- if (event.action === "scheduled") {
1840
- await this.handleScheduled(event);
1841
- return;
1842
- }
1843
- if (event.action === "cancelled") {
1844
- await this.handleCancelled(event);
1845
- return;
1846
- }
1847
- if (event.action === "opened") {
1848
- await this.handleOpened(event);
1849
- return;
1850
- }
1851
- if (event.action === "closed") {
1852
- await this.handleClosed(event);
1853
- return;
1854
- }
1855
- });
1856
- const unCommit = listenStrategyCommit(async (event) => {
1857
- if (event.action === "trailing-take") {
1858
- await this.handleTrailingTake(event);
1859
- return;
1860
- }
1861
- if (event.action === "trailing-stop") {
1862
- await this.handleTrailingStop(event);
1863
- return;
1864
- }
1865
- if (event.action === "breakeven") {
1866
- await this.handleBreakeven(event);
1867
- return;
1868
- }
1869
- if (event.action === "partial-profit") {
1870
- await this.handlePartialProfit(event);
1871
- return;
1872
- }
1873
- if (event.action === "partial-loss") {
1874
- await this.handlePartialLoss(event);
1875
- return;
1876
- }
1877
- if (event.action === "average-buy") {
1878
- await this.handleAverageBuy(event);
1879
- return;
1880
- }
1881
- });
1882
- const unConnect = () => this.enable.clear();
1883
- return compose(() => unRisk(), () => unSignal(), () => unCommit(), () => unConnect());
1884
- });
1885
- this.disable = () => {
1886
- this.loggerService.log("liveProviderService disable");
1887
- if (this.enable.hasValue()) {
1888
- const lastSubscription = this.enable();
1889
- lastSubscription();
1890
- }
1891
- };
1892
- this.connect = singleshot(async () => {
1893
- this.loggerService.log("liveProviderService connect");
1894
- if (!getArgs().values.live) {
1895
- return;
1896
- }
1897
- return entrySubject.subscribe(this.enable);
1898
- });
1899
1758
  }
1900
1759
  }
1901
1760
 
@@ -2018,7 +1877,6 @@ globalThis.BacktestKitSignals = BacktestKitSignals;
2018
1877
  {
2019
1878
  provide(TYPES.telegramProviderService, () => new TelegramProviderService());
2020
1879
  provide(TYPES.frontendProviderService, () => new FrontendProviderService());
2021
- provide(TYPES.liveProviderService, () => new LiveProviderService());
2022
1880
  }
2023
1881
  {
2024
1882
  provide(TYPES.telegramWebService, () => new TelegramWebService());
@@ -2057,7 +1915,6 @@ const schemaServices = {
2057
1915
  const providerServices = {
2058
1916
  frontendProviderService: inject(TYPES.frontendProviderService),
2059
1917
  telegramProviderService: inject(TYPES.telegramProviderService),
2060
- liveProviderService: inject(TYPES.liveProviderService),
2061
1918
  };
2062
1919
  const webServices = {
2063
1920
  telegramWebService: inject(TYPES.telegramWebService),
@@ -2162,7 +2019,6 @@ const BEFORE_EXIT_FN$2 = singleshot(async () => {
2162
2019
  exchangeName,
2163
2020
  strategyName,
2164
2021
  });
2165
- listenDoneLive(cli.liveProviderService.disable);
2166
2022
  });
2167
2023
  const listenGracefulShutdown$2 = singleshot(() => {
2168
2024
  process.on("SIGINT", BEFORE_EXIT_FN$2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backtest-kit/cli",
3
- "version": "3.8.0",
3
+ "version": "5.0.0",
4
4
  "description": "Zero-boilerplate CLI runner for backtest-kit strategies. Run backtests, paper trading, and live bots with candle cache warming, web dashboard, and Telegram notifications — no setup code required.",
5
5
  "author": {
6
6
  "name": "Petr Tripolsky",
@@ -60,11 +60,11 @@
60
60
  "devDependencies": {
61
61
  "@babel/plugin-transform-modules-umd": "7.27.1",
62
62
  "@babel/standalone": "7.29.1",
63
- "@backtest-kit/ui": "3.8.0",
64
- "@backtest-kit/graph": "3.8.0",
65
- "@backtest-kit/ollama": "3.8.0",
66
- "@backtest-kit/pinets": "3.8.0",
67
- "@backtest-kit/signals": "3.8.0",
63
+ "@backtest-kit/ui": "5.0.0",
64
+ "@backtest-kit/graph": "5.0.0",
65
+ "@backtest-kit/ollama": "5.0.0",
66
+ "@backtest-kit/pinets": "5.0.0",
67
+ "@backtest-kit/signals": "5.0.0",
68
68
  "@rollup/plugin-replace": "6.0.3",
69
69
  "@rollup/plugin-typescript": "11.1.6",
70
70
  "@types/image-size": "0.7.0",
@@ -72,7 +72,7 @@
72
72
  "@types/mustache": "4.2.6",
73
73
  "@types/node": "22.9.0",
74
74
  "@types/stack-trace": "0.0.33",
75
- "backtest-kit": "3.8.0",
75
+ "backtest-kit": "5.0.0",
76
76
  "glob": "11.0.1",
77
77
  "markdown-it": "14.1.1",
78
78
  "rimraf": "6.0.1",
@@ -87,12 +87,12 @@
87
87
  "peerDependencies": {
88
88
  "@babel/plugin-transform-modules-umd": "^7.27.1",
89
89
  "@babel/standalone": "^7.29.1",
90
- "@backtest-kit/ui": "^3.8.0",
91
- "@backtest-kit/graph": "^3.8.0",
92
- "@backtest-kit/ollama": "^3.8.0",
93
- "@backtest-kit/pinets": "^3.8.0",
94
- "@backtest-kit/signals": "^3.8.0",
95
- "backtest-kit": "^3.8.0",
90
+ "@backtest-kit/ui": "^5.0.0",
91
+ "@backtest-kit/graph": "^5.0.0",
92
+ "@backtest-kit/ollama": "^5.0.0",
93
+ "@backtest-kit/pinets": "^5.0.0",
94
+ "@backtest-kit/signals": "^5.0.0",
95
+ "backtest-kit": "^5.0.0",
96
96
  "markdown-it": "^14.1.1",
97
97
  "typescript": "^5.0.0"
98
98
  },
@@ -5,12 +5,17 @@
5
5
  **Current:** `{{currentPrice}}`
6
6
  **New Avg Entry:** `{{effectivePriceOpen}}`
7
7
  **Orig Entry:** `{{originalPriceOpen}}`
8
+ **Cost:** `{{cost}}`
8
9
  **DCA Entries:** `{{totalEntries}}`
10
+ **Partials Done:** `{{totalPartials}}`
9
11
  **Take Profit:** `{{priceTakeProfit}}`
10
12
  **Stop Loss:** `{{priceStopLoss}}`
13
+ **Orig TP:** `{{originalPriceTakeProfit}}`
14
+ **Orig SL:** `{{originalPriceStopLoss}}`
15
+ **PnL:** {{pnl.pnlPercentage}}% ({{pnl.pnlCost}} / {{pnl.pnlEntries}})
11
16
  **Signal ID:** `{{signalId}}`
12
17
  **Time:** `{{timestamp}}`
13
18
 
14
19
  {{#backtest}}
15
20
  _🧪 Backtest_
16
- {{/backtest}}
21
+ {{/backtest}}
@@ -4,13 +4,17 @@
4
4
 
5
5
  **Current:** `{{currentPrice}}`
6
6
  **Entry:** `{{priceOpen}}`
7
- **Take Profit:** `{{priceTakeProfit}}`
8
- **Stop Loss:** `{{priceStopLoss}}`
9
7
  **Orig Entry:** `{{originalPriceOpen}}`
10
8
  **DCA Entries:** `{{totalEntries}}`
9
+ **Partials Done:** `{{totalPartials}}`
10
+ **Take Profit:** `{{priceTakeProfit}}`
11
+ **Stop Loss:** `{{priceStopLoss}}`
12
+ **Orig TP:** `{{originalPriceTakeProfit}}`
13
+ **Orig SL:** `{{originalPriceStopLoss}}`
14
+ **PnL:** {{pnl.pnlPercentage}}% ({{pnl.pnlCost}} / {{pnl.pnlEntries}})
11
15
  **Signal ID:** `{{signalId}}`
12
16
  **Time:** `{{timestamp}}`
13
17
 
14
18
  {{#backtest}}
15
19
  _🧪 Backtest_
16
- {{/backtest}}
20
+ {{/backtest}}
@@ -6,11 +6,15 @@
6
6
  **Entry:** `{{signal.priceOpen}}`
7
7
  **Current:** `{{currentPrice}}`
8
8
  **Orig Entry:** `{{signal.originalPriceOpen}}`
9
+ **Orig TP:** `{{signal.originalPriceTakeProfit}}`
10
+ **Orig SL:** `{{signal.originalPriceStopLoss}}`
9
11
  **DCA Entries:** `{{signal.totalEntries}}`
12
+ **Partials Done:** `{{signal.totalPartials}}`
13
+ **Partial Executed:** {{signal.partialExecuted}}%
10
14
  **Signal ID:** `{{signal.id}}`
11
15
  {{#cancelId}}**Cancel ID:** `{{cancelId}}`
12
16
  {{/cancelId}}**Time:** `{{createdAt}}`
13
17
 
14
18
  {{#backtest}}
15
19
  _🧪 Backtest_
16
- {{/backtest}}
20
+ {{/backtest}}
@@ -3,15 +3,20 @@
3
3
  **Symbol:** `{{symbol}}` ({{signal.position}})
4
4
 
5
5
  **Reason:** `{{closeReason}}`
6
+ **Current:** `{{currentPrice}}`
6
7
  **Entry:** `{{pnl.priceOpen}}`
7
8
  **Exit:** `{{pnl.priceClose}}`
8
- **PnL:** {{pnl.pnlPercentage}}%
9
+ **PnL:** {{pnl.pnlPercentage}}% ({{pnl.pnlCost}} / {{pnl.pnlEntries}})
9
10
  **Orig Entry:** `{{signal.originalPriceOpen}}`
11
+ **Orig TP:** `{{signal.originalPriceTakeProfit}}`
12
+ **Orig SL:** `{{signal.originalPriceStopLoss}}`
10
13
  **DCA Entries:** `{{signal.totalEntries}}`
14
+ **Partials Done:** `{{signal.totalPartials}}`
15
+ **Partial Executed:** {{signal.partialExecuted}}%
11
16
  **Signal ID:** `{{signal.id}}`
12
17
  {{#closeId}}**Close ID:** `{{closeId}}`
13
18
  {{/closeId}}**Time:** `{{createdAt}}`
14
19
 
15
20
  {{#backtest}}
16
21
  _🧪 Backtest_
17
- {{/backtest}}
22
+ {{/backtest}}
@@ -7,11 +7,15 @@
7
7
  **Take Profit:** `{{signal.priceTakeProfit}}`
8
8
  **Stop Loss:** `{{signal.priceStopLoss}}`
9
9
  **Orig Entry:** `{{signal.originalPriceOpen}}`
10
+ **Orig TP:** `{{signal.originalPriceTakeProfit}}`
11
+ **Orig SL:** `{{signal.originalPriceStopLoss}}`
12
+ **Cost:** `{{signal.cost}}`
10
13
  **DCA Entries:** `{{signal.totalEntries}}`
14
+ **Partials Done:** `{{signal.totalPartials}}`
11
15
  **TTL:** {{signal.minuteEstimatedTime}} min
12
16
  **Signal ID:** `{{signal.id}}`
13
17
  **Time:** `{{createdAt}}`
14
18
 
15
19
  {{#backtest}}
16
20
  _🧪 Backtest_
17
- {{/backtest}}
21
+ {{/backtest}}
@@ -4,14 +4,18 @@
4
4
 
5
5
  **Current:** `{{currentPrice}}`
6
6
  **Entry:** `{{priceOpen}}`
7
+ **Orig Entry:** `{{originalPriceOpen}}`
7
8
  **Closed:** {{percentToClose}}%
8
9
  **Take Profit:** `{{priceTakeProfit}}`
9
10
  **Stop Loss:** `{{priceStopLoss}}`
10
- **Orig Entry:** `{{originalPriceOpen}}`
11
+ **Orig TP:** `{{originalPriceTakeProfit}}`
12
+ **Orig SL:** `{{originalPriceStopLoss}}`
11
13
  **DCA Entries:** `{{totalEntries}}`
14
+ **Partials Done:** `{{totalPartials}}`
15
+ **PnL:** {{pnl.pnlPercentage}}% ({{pnl.pnlCost}} / {{pnl.pnlEntries}})
12
16
  **Signal ID:** `{{signalId}}`
13
17
  **Time:** `{{timestamp}}`
14
18
 
15
19
  {{#backtest}}
16
20
  _🧪 Backtest_
17
- {{/backtest}}
21
+ {{/backtest}}
@@ -4,14 +4,18 @@
4
4
 
5
5
  **Current:** `{{currentPrice}}`
6
6
  **Entry:** `{{priceOpen}}`
7
+ **Orig Entry:** `{{originalPriceOpen}}`
7
8
  **Closed:** {{percentToClose}}%
8
9
  **Take Profit:** `{{priceTakeProfit}}`
9
10
  **Stop Loss:** `{{priceStopLoss}}`
10
- **Orig Entry:** `{{originalPriceOpen}}`
11
+ **Orig TP:** `{{originalPriceTakeProfit}}`
12
+ **Orig SL:** `{{originalPriceStopLoss}}`
11
13
  **DCA Entries:** `{{totalEntries}}`
14
+ **Partials Done:** `{{totalPartials}}`
15
+ **PnL:** {{pnl.pnlPercentage}}% ({{pnl.pnlCost}} / {{pnl.pnlEntries}})
12
16
  **Signal ID:** `{{signalId}}`
13
17
  **Time:** `{{timestamp}}`
14
18
 
15
19
  {{#backtest}}
16
20
  _🧪 Backtest_
17
- {{/backtest}}
21
+ {{/backtest}}
@@ -6,10 +6,10 @@
6
6
  **Entry:** `{{currentSignal.priceOpen}}`
7
7
  **Take Profit:** `{{currentSignal.priceTakeProfit}}`
8
8
  **Stop Loss:** `{{currentSignal.priceStopLoss}}`
9
- **Orig Entry:** `{{currentSignal.originalPriceOpen}}`
10
- **DCA Entries:** `{{currentSignal.totalEntries}}`
11
- **Active positions:** {{activePositionCount}}
12
- {{#rejectionId}}**Rejection ID:** `{{rejectionId}}`
9
+ **TTL:** {{currentSignal.minuteEstimatedTime}} min
10
+ **Active Positions:** {{activePositionCount}}
11
+ {{#currentSignal.note}}**Note:** {{currentSignal.note}}
12
+ {{/currentSignal.note}}{{#rejectionId}}**Rejection ID:** `{{rejectionId}}`
13
13
  {{/rejectionId}}**Time:** `{{timestamp}}`
14
14
 
15
15
  **Reason:**
@@ -17,4 +17,4 @@
17
17
 
18
18
  {{#backtest}}
19
19
  _🧪 Backtest_
20
- {{/backtest}}
20
+ {{/backtest}}
@@ -7,11 +7,15 @@
7
7
  **Take Profit:** `{{signal.priceTakeProfit}}`
8
8
  **Stop Loss:** `{{signal.priceStopLoss}}`
9
9
  **Orig Entry:** `{{signal.originalPriceOpen}}`
10
+ **Orig TP:** `{{signal.originalPriceTakeProfit}}`
11
+ **Orig SL:** `{{signal.originalPriceStopLoss}}`
12
+ **Cost:** `{{signal.cost}}`
10
13
  **DCA Entries:** `{{signal.totalEntries}}`
14
+ **Partials Done:** `{{signal.totalPartials}}`
11
15
  **TTL:** {{signal.minuteEstimatedTime}} min
12
16
  **Signal ID:** `{{signal.id}}`
13
17
  **Time:** `{{createdAt}}`
14
18
 
15
19
  {{#backtest}}
16
20
  _🧪 Backtest_
17
- {{/backtest}}
21
+ {{/backtest}}