{"id":480545,"date":"2026-05-21T13:38:31","date_gmt":"2026-05-21T13:38:31","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=480545"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=480545","title":{"rendered":"\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u043c\u043e\u043d\u043e\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f \u0434\u043b\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0442\u043e\u0440\u0433\u043e\u0432\u044b\u0445 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0439"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<blockquote>\n<p>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434, \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043d\u043d\u044b\u0439 \u0432 \u0441\u0442\u0430\u0442\u044c\u0435, \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u043d\u00a0<a href=\"https:\/\/github.com\/backtest-kit\/backtest-monorepo-parallel\" rel=\"noopener noreferrer nofollow\">\u0432 \u044d\u0442\u043e\u043c GitHub \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438<\/a><\/p>\n<\/blockquote>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/0b4\/6f9\/0c1\/0b46f90c1264345aacd2b2d572967a57.png\" width=\"1248\" height=\"832\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/0b4\/6f9\/0c1\/0b46f90c1264345aacd2b2d572967a57.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/0b4\/6f9\/0c1\/0b46f90c1264345aacd2b2d572967a57.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/figure>\n<p>\u0418\u043c\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0432 \u043c\u0438\u0440\u0435 \u0418\u0418. \u0415\u0441\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043a\u043e\u0434\u043e\u0432\u0443\u044e \u0431\u0430\u0437\u0443 \u043d\u0430 God Object, \u0438\u0437 \u0442\u0440\u0451\u0445 \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u043e\u0432 \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043e\u0434 \u0441\u043c\u043e\u0436\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d, \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u0430\u043c\u0438 \u0441\u043b\u0438\u044f\u043d\u0438\u044f. \u0415\u0441\u043b\u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438, \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u043a\u0430\u043f\u043b\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0431\u0430\u0437\u0430 \u0437\u043d\u0430\u043d\u0438\u0439: \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u0430\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0442\u043e\u0440\u0433\u043e\u0432\u0430\u044f \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044f, \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0441\u0430\u043c\u043e\u043c \u043a\u043e\u0434\u0435. \u0422\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u0441\u0438\u043d\u0435\u0440\u0433\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u044d\u0444\u0444\u0435\u043a\u0442: Coding Agent \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u0442 \u0447\u0438\u0442\u0430\u0442\u044c \u043a\u043e\u0434 \u0438\u0437 \u043f\u0440\u043e\u0448\u043b\u044b\u0445 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438. \u041a\u0430\u0436\u0434\u0430\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044f &#8212; \u044d\u0442\u043e \u0440\u0430\u043d\u0434\u043e\u043c, \u0430 \u043d\u0435 \u0440\u0430\u0437\u0432\u0438\u0442\u0438\u0435 \u043c\u044b\u0441\u043b\u0438 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439<\/p>\n<h3>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430<\/h3>\n<p>OpenSource \u0434\u043b\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0442\u043e\u0440\u0433\u043e\u0432\u044b\u0445 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0439 \u0432\u0435\u0441\u044c \u043d\u0430 python. \u041d\u0430 python \u043d\u0435\u0443\u0434\u043e\u0431\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0434\u0435\u043a\u043b\u0430\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u043a\u043e\u0434 &#8212; \u0432\u0441\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u044d\u0442\u043e \u0440\u0430\u0437\u0434\u0443\u0442\u044b\u0435 \u043d\u0430 \u0442\u044b\u0441\u044f\u0447\u0438 \u0441\u0442\u0440\u043e\u043a \u0438\u043c\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u043a\u043b\u0430\u0441\u0441\u044b \u0430\u043d\u0442\u0438\u043f\u0430\u0442\u0442\u0435\u0440\u043d\u0430 God Object<\/p>\n<pre><code class=\"python\">class AwesomeStrategy(IStrategy):    position_adjustment_enable = True   # \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0439 switch, \u0431\u0435\u0437 \u043d\u0435\u0433\u043e DCA \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442    # 1. \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043b\u0435\u0441\u0435\u043d\u043a\u0438 - \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u043a\u043b\u0430\u0441\u0441\u0430    max_entry_position_adjustment = 9   # \u043c\u0430\u043a\u0441. 9 \u0434\u043e\u043b\u0438\u0432\u043e\u043a + 1 \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u0439 \u0432\u0445\u043e\u0434 = 10 \u0448\u0430\u0433\u043e\u0432    max_dca_multiplier = 5.5            # \u0440\u0435\u0437\u0435\u0440\u0432\u0438\u0440\u0443\u0435\u043c 5.5\u00d7 \u043e\u0442 \u0441\u0442\u0435\u0439\u043a\u0430 \u043f\u043e\u0434 \u0431\u0443\u0434\u0443\u0449\u0438\u0435 \u0434\u043e\u043b\u0438\u0432\u043a\u0438    stoploss = -0.30                    # \u0432\u044b\u0441\u043e\u043a\u0438\u0439 \u0445\u0430\u0440\u0434-\u0441\u0442\u043e\u043f \u043d\u0443\u0436\u0435\u043d, \u0438\u043d\u0430\u0447\u0435 DCA \u0432\u044b\u043b\u0435\u0442\u0430\u0435\u0442    # 2. \u041f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u0439 \u0432\u0445\u043e\u0434 - \u0443\u0440\u0435\u0437\u0430\u0435\u043c \u0441\u0442\u0435\u0439\u043a, \u0447\u0442\u043e\u0431\u044b \u0445\u0432\u0430\u0442\u0438\u043b\u043e \u043d\u0430 \u043b\u0435\u0441\u0435\u043d\u043a\u0443    def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,                            proposed_stake: float, min_stake: float, max_stake: float,                            entry_tag: Optional[str], side: str, **kwargs) -&gt; float:        # \u0417\u0430\u0440\u0435\u0437\u0435\u0440\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 DCA-\u0434\u043e\u043b\u0438\u0432\u043a\u0438        return proposed_stake \/ self.max_dca_multiplier    # 3. \u0421\u0430\u043c\u0430 \u043b\u0435\u0441\u0435\u043d\u043a\u0430 - \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 callback, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 Freqtrade \u0437\u043e\u0432\u0451\u0442 \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u043c \u0442\u0438\u043a\u0435    def adjust_trade_position(self, trade: Trade, current_time: datetime,                              current_rate: float, current_profit: float,                              min_stake: float, max_stake: float,                              current_entry_rate: float, current_exit_rate: float,                              current_entry_profit: float, current_exit_profit: float,                              **kwargs) -&gt; Optional[float]:        # \u0422\u0435\u043a\u0443\u0449\u0430\u044f \u043f\u0440\u043e\u0441\u0430\u0434\u043a\u0430 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0433\u043b\u0443\u0431\u043e\u043a\u0430\u044f - \u043d\u0435 \u0434\u043e\u043b\u0438\u0432\u0430\u0435\u043c        if current_profit &gt; -0.05 and trade.nr_of_successful_entries == 1:            return None        if current_profit &gt; -0.10 and trade.nr_of_successful_entries == 2:            return None        # ... \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u044c \u044d\u0442\u0443 \u043b\u0435\u0441\u0435\u043d\u043a\u0443 \u0443\u0441\u043b\u043e\u0432\u0438\u0439 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u0448\u0430\u0433\u0430        # \u0414\u043e\u0441\u0442\u0430\u0451\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0443\u0436\u0435 \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u044b\u0445 \u0432\u0445\u043e\u0434\u043e\u0432 \u0438\u0437 state \u0442\u0440\u0435\u0439\u0434\u0430        filled_entries = trade.select_filled_orders(trade.entry_side)        count_of_entries = trade.nr_of_successful_entries        try:            # \u0421\u0442\u0435\u0439\u043a \u043f\u0435\u0440\u0432\u043e\u0439 \u043f\u043e\u043a\u0443\u043f\u043a\u0438 \u00d7 (count_of_entries - \u0434\u043b\u044f \u0433\u0435\u043e\u043c\u0435\u0442\u0440\u0438\u0438 \u043b\u0435\u0441\u0435\u043d\u043a\u0438)            stake_amount = filled_entries[0].stake_amount            stake_amount = stake_amount * (1 + (count_of_entries * 0.25))            return stake_amount        except Exception:            return None    # 4. populate_indicators \/ populate_entry_trend \/ populate_exit_trend \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u043d\u0443\u0436\u043d\u044b,    #    \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0431\u0435\u0437 entry-\u0441\u0438\u0433\u043d\u0430\u043b\u0430 \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u0439 \u0432\u0445\u043e\u0434 \u043d\u0435 \u0441\u043b\u0443\u0447\u0438\u0442\u0441\u044f.    def populate_indicators(self, dataframe, metadata): ...    def populate_entry_trend(self, dataframe, metadata): ...    def populate_exit_trend(self, dataframe, metadata): ...<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u0442\u043e\u0442 boilerplate \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043e\u043a\u0443\u043f \u043b\u0435\u0441\u0435\u043d\u043a\u043e\u0439: \u0443\u043f\u0430\u043b\u043e &#8212; \u0434\u043e\u043a\u0443\u043f\u0438\u043b\u0438. \u0427\u0442\u043e\u0431\u044b \u043f\u043e\u043d\u044f\u0442\u044c, \u043a\u043e\u0433\u0434\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u0442\u044c \u043f\u043e\u043a\u0443\u043f\u0430\u0442\u044c \u0432 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u043e\u0431\u0430\u043d\u043a\u0440\u043e\u0442\u0438\u0442\u044c\u0441\u044f, \u0432 \u044d\u0442\u043e\u0442 \u0436\u0435 \u043a\u043b\u0430\u0441\u0441 \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u044b \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445. \u041f\u0440\u044f\u043c\u043e\u0435 \u043d\u0430\u0440\u0443\u0448\u0435\u043d\u0438\u0435 SOLID<\/p>\n<pre><code class=\"python\">class TelegramSignalStrategy(IStrategy):    custom_signals = {}  # nested dict: pair -&gt; list of signals    def bot_start(self) -&gt; None:        # \u0441\u0442\u0430\u0440\u0442\u0443\u0435\u0442 cron-\u0437\u0430\u0434\u0430\u0447\u0430 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u043f\u043e\u0442\u043e\u043a\u0435, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0445\u043e\u0434\u0438\u0442 \u0432 Telegram        # \u0438 \u043f\u0438\u0448\u0435\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0432 self.custom_signals        threading.Thread(target=self._poll_telegram_loop, daemon=True).start()    def _poll_telegram_loop(self):        while True:            try:                messages = telegram_client.iter_messages(\"crypto_yoda_channel\")                for msg in messages:                    parsed = self._parse_signal(msg.text)  # regex inline                    if parsed:                        self.custom_signals.setdefault(parsed['pair'], []).append(parsed)            except Exception:                pass            time.sleep(60)    def _parse_signal(self, text: str) -&gt; Optional[dict]:        m = re.search(r\"#([A-Z0-9]+)\/USDT.*?(\u041b\u041e\u041d\u0413|\u0428\u041e\u0420\u0422).*?\u0437\u043e\u043d\u0435\\s+\\$?([\\d.,]+)\\s*[-\u2013-]\\s*\\$?([\\d.,]+)\", text)        if not m:            return None        return {            \"pair\": m.group(1) + \"\/USDT\",            \"direction\": \"long\" if m.group(2) == \"\u041b\u041e\u041d\u0413\" else \"short\",            \"entry_from\": float(m.group(3).replace(\",\", \".\")),            \"entry_to\":   float(m.group(4).replace(\",\", \".\")),        }    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -&gt; DataFrame:        signals = self.custom_signals.get(metadata['pair'], [])        # ... \u0442\u0443\u0442 \u043a\u0430\u043a-\u0442\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c signals \u043a dataframe<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0442\u043e\u0447\u043a\u0430 \u0432\u0445\u043e\u0434\u0430: \u0432 freqtrade \u044d\u0442\u043e json \u0444\u0430\u0439\u043b. \u0422\u0443\u0434\u0430 \u043f\u043e\u043d\u0430\u043f\u0438\u0445\u0430\u043d\u044b \u0438 API \u043a\u043b\u044e\u0447\u0438, \u0438 \u043c\u0430\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b \u0432\u0438\u0434\u0430\u00a0<code>dry_run<\/code>. \u041d\u0435\u043f\u043e\u043d\u044f\u0442\u043d\u043e, \u043a\u043e\u043c\u043c\u0438\u0442\u0438\u0442\u044c \u044d\u0442\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u043d\u0435\u0442: \u0435\u0441\u043b\u0438 \u043a\u043e\u043c\u043c\u0438\u0442\u0438\u0442\u044c, \u0432\u0441\u0435\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442 \u0441\u043b\u0438\u044f\u043d\u0438\u044f \u043f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0435<\/p>\n<pre><code class=\"json\">{  \"dry_run\": true,  \"timeframe\": \"3m\",  \"stake_currency\": \"USDT\",  \"stake_amount\": 200,  \"max_open_trades\": 5,  \"exchange\": {    \"name\": \"binance\",    \"key\": \"af8ddd35195e9dc500b9a6f799f6f5c93d89193b\",    \"secret\": \"08a9dc6db3d7b53e1acebd9275677f4b0a04f1a5\",    \"pair_whitelist\": [\"BTC\/USDT\", \"ETH\/USDT\"]  },  \"timerange\": \"20260401-20260427\",  \"datadir\": \"user_data\/data\/binance\"}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u0440\u0438 \u0442\u0430\u043a\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435 \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c look ahead bias \u043d\u0435\u043b\u044c\u0437\u044f: \u0447\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u0432\u043e \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434, \u043d\u0443\u0436\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445, \u0430 \u0443\u0434\u0430\u043b\u044f\u0442\u044c \u0447\u0435\u0440\u0435\u0437 crontab. \u042d\u0442\u043e\u0442 \u0438\u043c\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u043a\u043e\u0434 \u043c\u0430\u0442\u0435\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435\u0434\u043e\u043a\u0430\u0437\u0443\u0435\u043c, \u0442\u0430\u043a \u043a\u0430\u043a \u0434\u0430\u0436\u0435 \u0431\u043b\u0438\u0437\u043a\u043e \u043d\u0435 \u043f\u043e\u0445\u043e\u0436 \u043d\u0430 \u0447\u0438\u0441\u0442\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e<\/p>\n<h3>\u0420\u0435\u0448\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b<\/h3>\n<p>\u0414\u043e \u043d\u0430\u0439\u043c\u0430 \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u043e\u0432 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043a\u043e\u043d\u0432\u0435\u0439\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u043a\u0443\u043c\u0443\u043b\u044f\u0442\u0438\u0432\u043d\u044b\u0439 \u044d\u0444\u0444\u0435\u043a\u0442 \u0437\u0430\u0442\u0440\u0430\u0447\u0435\u043d\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0432\u0438\u0442\u043e\u043a \u0434\u0438\u0430\u043b\u0435\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0441\u043f\u0438\u0440\u0430\u043b\u0438. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043f\u0430\u043f\u043e\u043a \u0441 \u043a\u043e\u0434\u043e\u043c<\/p>\n<pre><code class=\"javascript\">monorepo\/\u251c\u2500\u2500 content\/\u2502   \u251c\u2500\u2500 apr_2026.strategy\/ |       \u251c\u2500\u2500modules\u2502       \u2502   \u251c\u2500\u2500backtest.module.ts  # testnet paper\u2502       \u251c\u2500\u2500 apr_2026.strategy.ts   # strategy production code\u2502       \u251c\u2500\u2500 apr_2026.test.ts       # developer playground\u251c\u2500\u2500 modules\/\u2502       \u251c\u2500\u2500 backtest.module.ts     # mainnet paper\u2502       \u251c\u2500\u2500 live.module.ts         # mainnet live\u251c\u2500\u2500 packages\/\u2502   \u251c\u2500\u2500 core\/                      # database layer\u2502   \u251c\u2500\u2500 main\/                      # cli arguments parser<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h4>N \u0442\u043e\u0447\u0435\u043a \u0432\u0445\u043e\u0434\u0430 \u0432\u043c\u0435\u0441\u0442\u043e \u043e\u0434\u043d\u043e\u0439: \u0432\u0435\u0442\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 CLI-\u0444\u043b\u0430\u0433\u0435<\/h4>\n<p>\u041a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u0438\u043c\u043f\u043e\u0440\u0442 \u0432\u00a0<code>packages\/main\/src\/index.ts<\/code>\u00a0\u043f\u043e\u0434\u0442\u044f\u0433\u0438\u0432\u0430\u0435\u0442 \u0447\u0435\u0442\u044b\u0440\u0435 \u0444\u0430\u0439\u043b\u0430<\/p>\n<pre><code class=\"typescript\">import \".\/main\/session\";import \".\/main\/backtest\";import \".\/main\/live\";import \".\/main\/paper\";<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a\u0430\u0436\u0434\u044b\u0439 \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 &#8212; \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0432\u0445\u043e\u0434\u0430<\/p>\n<pre><code class=\"typescript\">const main = async () =&gt; {  const { values } = getArgs();  if (!values.entry) return;  if (!values.backtest) return;  await waitForReady(true);  const [strategySchema] = await listStrategySchema();  const [exchangeSchema] = await listExchangeSchema();  const [frameSchema]    = await listFrameSchema();  if (values.cache) {    await CACHE_CANDLES_FN();  }  for (const symbol of CC_SYMBOL_LIST) {    Backtest.background(symbol, {      exchangeName: exchangeSchema.exchangeName,      strategyName: strategySchema.strategyName,      frameName:    frameSchema.frameName,    });  }};main();<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h4>\u0420\u0430\u0437\u0434\u0435\u043b\u044f\u0435\u043c\u044b\u0439 \u0441\u043e\u0431\u044b\u0442\u0438\u0439\u043d\u044b\u0439 \u043a\u043e\u0434 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438<\/h4>\n<p>\u0422\u043e\u0447\u043a\u0443 \u0432\u0445\u043e\u0434\u0430, \u0443\u0441\u0440\u0435\u0434\u043d\u0435\u043d\u0438\u0435, stop loss \u0438 \u0440\u0430\u043d\u043d\u0438\u0439 \u0432\u044b\u0445\u043e\u0434 \u043d\u0443\u0436\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e \u0447\u0438\u0441\u0442\u043e, \u0432 \u0441\u0442\u0438\u043b\u0435 \u0434\u0435\u043a\u043b\u0430\u0440\u0430\u0446\u0438\u0438, \u0442\u0430\u043a \u0447\u0442\u043e\u0431\u044b \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0440\u0430\u0437\u043d\u0435\u0441\u0442\u0438 \u043f\u043e \u0440\u0430\u0437\u043d\u044b\u043c \u0444\u0430\u0439\u043b\u0430\u043c \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e<\/p>\n<pre><code class=\"typescript\">import {  addStrategySchema, listenActivePing, listenError,  Log, Position,  commitClosePending, commitAverageBuy,  getPositionPnlPercent, getPositionEntryOverlap, getPositionEntries,} from \"backtest-kit\";const HARD_STOP        = 10.0;const TARGET_PROFIT    = 3;const LADDER_STEP_COST = 100;const LADDER_UPPER_STEP = 5;const LADDER_LOWER_STEP = 1;const LADDER_MAX_STEPS  = 10;addStrategySchema({  strategyName: \"apr_2026_strategy\",  getSignal: async (symbol, when, currentPrice) =&gt; ({    ...Position.moonbag({         position: \"long\",         currentPrice,         percentStopLoss: HARD_STOP    }),    minuteEstimatedTime: Infinity,    cost: LADDER_STEP_COST,  }),});listenActivePing(async ({ symbol, currentPrice }) =&gt; {  const { length: steps } = await getPositionEntries(symbol);  if (steps &gt;= LADDER_MAX_STEPS) return;  const hasOverlap = await getPositionEntryOverlap(symbol, currentPrice, {    upperPercent: LADDER_UPPER_STEP,    lowerPercent: LADDER_LOWER_STEP,  });  if (hasOverlap) return;  await commitAverageBuy(symbol, LADDER_STEP_COST);});listenActivePing(async ({ symbol }) =&gt; {  const currentProfit = await getPositionPnlPercent(symbol);  if (currentProfit &lt; TARGET_PROFIT) {    return;  }  await commitClosePending(symbol, { id: \"unknown\", note: \"# closed by target pnl\" });});<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043a \u043b\u044e\u0431\u043e\u0439 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438 \u043c\u043e\u0436\u043d\u043e \u0438\u043d\u043a\u0440\u0435\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u043e \u043d\u0430\u043a\u0438\u043d\u0443\u0442\u044c trailing take \u043d\u0435 \u043c\u0435\u043d\u044f\u044f \u043a\u043e\u0434. \u041b\u044e\u0431\u043e\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043a\u043e\u0434\u0430 &#8212; \u044d\u0442\u043e side effect, \u0442\u0430\u043a \u043a\u0430\u043a \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0435\u0442 \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0445 \u0432\u0445\u043e\u0434\u043e\u0432 \u0438 \u0432\u044b\u0445\u043e\u0434\u043e\u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u043f\u0440\u0438\u0440\u043e\u0434\u0430 \u043c\u0443\u0442\u0430\u0446\u0438\u0438 \u0432\u044b\u0437\u0432\u0430\u043d\u0430 \u0433\u043e\u0432\u043d\u043e\u043a\u043e\u0434\u044f\u0449\u0435\u0439 \u043d\u0435\u0439\u0440\u043e\u043d\u043a\u043e\u0439, \u0430 \u043d\u0435 \u043e\u0431\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u0430\u043c\u0438 \u0432 runtime.<\/p>\n<pre><code class=\"typescript\">if (GLOBAL_CONFIG.ATTACH_TRAILING_TAKE) {    listenActivePing(async ({ symbol, data }) =&gt; {        const peakProfitDistance = await getPositionHighestProfitDistancePnlPercentage(symbol);        const currentProfit = await getPositionPnlPercent(symbol);        if (currentProfit &lt; 0) {            return;        }        if (peakProfitDistance &lt; TRAILING_TAKE) {            return;        }        Log.info(\"position closed due to the trailing take\", {            symbol,            data,        });        await commitClosePending(symbol, {            id: \"unknown\",            note: str.newline(                \"# closed by trailing take\",            ),        });    });}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u043e Freqtrade \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 custom_stoploss \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u00ab\u043d\u0430\u0434\u043e \u0432\u0441\u043f\u043e\u043c\u043d\u0438\u0442\u044c, \u0447\u0442\u043e \u0442\u0430\u043c \u0431\u044b\u043b\u043e \u0432\u0430\u0436\u043d\u043e\u0433\u043e\u00bb. \u041c\u044b \u043d\u0435 \u0442\u0435\u0440\u044f\u0435\u043c \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e, \u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u043a\u0430\u043f\u043b\u0438\u0432\u0430\u0435\u043c<\/p>\n<h4>\u041c\u043e\u0434\u0443\u043b\u044c\u043d\u0430\u044f plug-and-play \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u0434 \u0440\u0435\u0436\u0438\u043c<\/h4>\n<p>\u0420\u044f\u0434\u043e\u043c \u0441\u043e \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0435\u0439 \u043b\u0435\u0436\u0438\u0442\u00a0<code>modules\/backtest.module.ts<\/code>. \u042d\u0442\u043e \u0444\u0430\u0439\u043b-\u0441\u043e\u0441\u0435\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 CLI \u0433\u0440\u0443\u0437\u0438\u0442 \u0432\u043c\u0435\u0441\u0442\u0435 \u0441\u043e \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0435\u0439 \u0438 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u00ab\u0432\u0441\u0451, \u0447\u0442\u043e \u043d\u0435 \u0441\u0430\u043c\u0430 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044f\u00bb &#8212; \u0431\u0438\u0440\u0436\u0443, \u0438\u0441\u0442\u043e\u0440\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0444\u0440\u0435\u0439\u043c, \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b<\/p>\n<pre><code class=\"typescript\">addExchangeSchema({  exchangeName: \"ccxt-exchange\",  getCandles: async (symbol, interval, since, limit) =&gt; {    const exchange = await getExchange();    const candles  = await exchange.fetchOHLCV(symbol, interval, since.getTime(), limit);    return candles.map(([timestamp, open, high, low, close, volume]) =&gt; ({      timestamp, open, high, low, close, volume,    }));  },  getOrderBook: async (symbol, depth, _from, _to, backtest) =&gt; {    if (backtest) throw new Error(\"Order book not supported in backtest\");    const exchange = await getExchange();    const bookData = await exchange.fetchOrderBook(symbol, depth);    return {      symbol,      asks: bookData.asks.map(([price, quantity]) =&gt; ({ price: String(price), quantity: String(quantity) })),      bids: bookData.bids.map(([price, quantity]) =&gt; ({ price: String(price), quantity: String(quantity) })),    };  },  \/\/ ...formatPrice, formatQuantity, getAggregatedTrades});addFrameSchema({  frameName:  \"apr_2026_frame\",  interval:   \"1m\",  startDate:  new Date(\"2026-04-01T00:00:00Z\"),  endDate:    new Date(\"2026-04-27T00:00:00Z\"),});<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041d\u043e\u0432\u0430\u044f \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044f &#8212; \u044d\u0442\u043e \u043f\u0430\u043f\u043a\u0430 \u0432 \u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u0438 content. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0432\u043e \u0432\u043a\u043b\u0430\u0434\u043a\u0430\u0445 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0430 \u043c\u043e\u0436\u043d\u043e \u043a\u0440\u0443\u0442\u0438\u0442\u044c \u0441\u0440\u0430\u0437\u0443 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0439 \u043d\u0435 \u043c\u0435\u043d\u044f\u044f \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 json<\/p>\n<pre><code class=\"javascript\">content\/apr_2026.strategy\/\u251c\u2500\u2500 apr_2026.strategy.ts        # strategy entry file\u251c\u2500\u2500 apr_2026.test.ts            # developer playground\u2514\u2500\u2500 modules\/    \u2514\u2500\u2500 backtest.module.ts      # exchange + timeframe<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u0440\u0438\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u043e, \u0447\u0442\u043e \u0442\u0430\u0439\u043c\u0444\u0440\u0435\u0439\u043c, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u043b\u0430\u0441\u044c \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044f, \u043d\u0435 \u0442\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u0438 \u043a\u043e\u043c\u043c\u0438\u0442\u0438\u0442\u0441\u044f \u043a\u0430\u043a \u043a\u043e\u0434. \u041c\u043e\u0436\u043d\u043e \u043f\u043e\u043d\u044f\u0442\u044c, \u043f\u043e\u0447\u0435\u043c\u0443 \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u043b\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c, \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043a\u043e\u0433\u0434\u0430 \u0442\u043e\u0447\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e<\/p>\n<h4>MVC \u0434\u043b\u044f \u0441\u043b\u043e\u044f \u0434\u0430\u043d\u043d\u044b\u0445<\/h4>\n<p>\u0422\u0440\u0438\u0433\u0433\u0435\u0440\u043e\u043c \u043b\u044e\u0431\u043e\u0433\u043e \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f \u043d\u0430 \u0440\u044b\u043d\u043a\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u043e\u0432\u043e\u0441\u0442\u043d\u043e\u0439 \u0444\u043e\u043d. \u041d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 2026 \u0438\u043d\u0434\u0438\u043a\u0430\u0442\u043e\u0440\u044b \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442, \u0442\u0430\u043a \u043a\u0430\u043a \u0440\u043e\u0441\u0442 \u0446\u0435\u043d\u044b &#8212; \u043d\u0435 \u043d\u043e\u0432\u043e\u0441\u0442\u044c. \u0415\u0441\u043b\u0438 \u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043a\u043e\u0434 \u043f\u0430\u0440\u0441\u0435\u0440\u0430 \u043d\u043e\u0432\u043e\u0441\u0442\u0435\u0439 \u0432 \u043e\u0434\u043d\u043e\u043c \u043a\u043b\u0430\u0441\u0441\u0435 \u0441\u043e \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0435\u0439, \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043d\u0435\u043b\u044c\u0437\u044f \u043e\u0442\u043b\u0430\u0436\u0438\u0432\u0430\u0442\u044c. \u041d\u0443\u0436\u043d\u043e \u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043a\u043e\u0434 \u043f\u0430\u0440\u0441\u0435\u0440\u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e, \u0447\u0442\u043e\u0431\u044b \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0447\u0435\u0440\u0435\u0437 TDD<\/p>\n<pre><code class=\"typescript\">export class ScraperService {  private readonly loggerService = inject&lt;LoggerService&gt;(TYPES.loggerService);  public scrapeDay = async (channel: string, date: Date): Promise&lt;ScraperMessage[]&gt; =&gt; {    const client   = await getTelegram();    const dayStart = new Date(date); dayStart.setUTCHours(0, 0, 0, 0);    const dayEnd   = new Date(date); dayEnd.setUTCHours(23, 59, 59, 999);    const rows: ScraperMessage[] = [];    for await (const message of client.iterMessages(channel, {      offsetDate: Math.floor(dayEnd.getTime() \/ 1000) + 1,      reverse: false,    })) {      if (!message.message) continue;      const ts = message.date * 1000;      if (ts &lt; dayStart.getTime()) break;      rows.push({ id: message.id, content: message.message, channel, date: new Date(ts) });    }    return rows;  }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u043a\u0430\u0441\u0430\u0435\u0442\u0441\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445. \u041d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c SQL \u0437\u0430\u043f\u0440\u043e\u0441, \u043f\u0435\u0440\u0435\u0434 \u0435\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435\u043c \u0432 backtest \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c, \u043d\u0435 \u0431\u044b\u043b\u043e \u043b\u0438 \u0437\u0430\u0435\u0437\u0434\u0430 \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u0447\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c look ahead bias. \u0414\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c \u043a\u043b\u0430\u0441\u0441 \u0441 \u043c\u0435\u0442\u043e\u0434\u0430\u043c\u0438<\/p>\n<pre><code class=\"typescript\">class CandleDbService extends BaseCRUD(CandleModel) {  readonly loggerService = inject&lt;LoggerService&gt;(TYPES.loggerService);  public create = async (dto: ICandleDto): Promise&lt;ICandleRow&gt; =&gt; {    this.loggerService.log(\"candleDbService create\", { dto });    const filter = {      symbol: dto.symbol,      interval: dto.interval,      timestamp: dto.timestamp,    };    const insertOnly = {      exchangeName: EXCHANGE_NAME,      open: dto.open,      high: dto.high,      low: dto.low,      close: dto.close,      volume: dto.volume,    };    const document = await CandleModel.findOneAndUpdate(      filter,      { $setOnInsert: insertOnly },      { upsert: true, new: true, setDefaultsOnInsert: true },    );    const result = readTransform(document.toJSON()) as unknown as ICandleRow;    return result;  };  public hasCandle = async (symbol: string, interval: CandleInterval, timestamp: number): Promise&lt;boolean&gt; =&gt; {    this.loggerService.log(\"candleDbService hasCandle\", {       symbol,      interval,      timestamp,    });    const candle = await this.findBySymbolIntervalTimestamp(symbol, interval, timestamp);    return !!candle;  };  public findBySymbolIntervalTimestamp =     async (symbol: string, interval: CandleInterval, timestamp: number): Promise&lt;ICandleRow | null&gt; =&gt; {      this.loggerService.log(\"candleDbService findBySymbolIntervalTimestamp\", { symbol, interval, timestamp });      return await await super.findByFilter({ symbol, interval, exchangeName: EXCHANGE_NAME, timestamp });    };}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>\u041f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c<\/h3>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0432\u043d\u0435\u0434\u0440\u0435\u043d\u0438\u044f \u041f\u041e \u0432\u044b\u044f\u0441\u043d\u0438\u043b\u043e\u0441\u044c, \u0447\u0442\u043e \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0440\u0430\u0431\u043e\u0447\u0438\u0435 \u043c\u0435\u0441\u0442\u0430 \u0438 \u0443\u0441\u043a\u043e\u0440\u044f\u0442\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443, \u043d\u043e \u0438 \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0431\u043e\u043b\u0435\u0435 \u0432\u044b\u0441\u043e\u043a\u0443\u044e \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c.<\/p>\n<pre><code class=\"typescript\">class CandleCacheService extends BaseMap(REDIS_KEY, -1) {  readonly loggerService = inject&lt;LoggerService&gt;(TYPES.loggerService);  private _cacheKey(symbol: string, interval: CandleInterval, exchangeName: string, timestamp: number): string {    return `${exchangeName}:${symbol}:${interval}:${timestamp}`;  }  public async hasCandleId(symbol: string, interval: CandleInterval, exchangeName: string, timestamp: number) {    this.loggerService.log(\"candleCacheService getCandleId\", {         symbol,         interval,         exchangeName,         timestamp,    });    const key = this._cacheKey(symbol, interval, exchangeName, timestamp);    return await this.has(key);  }  public async getCandleId(symbol: string, interval: CandleInterval, exchangeName: string, timestamp: number): Promise&lt;string | null&gt; {    this.loggerService.log(\"candleCacheService getCandleId\", {         symbol,         interval,         exchangeName,         timestamp,    });    const key = this._cacheKey(symbol, interval, exchangeName, timestamp);    const id = &lt;string&gt;await super.get(key);    return id ?? null;  }  public async setCandleId(row: ICandleRow): Promise&lt;string&gt; {    this.loggerService.log(`candleCacheService setCandleId`, {         symbol: row.symbol,         interval: row.interval,         timestamp: row.timestamp    });    const key = this._cacheKey(row.symbol, row.interval, row.exchangeName, row.timestamp);    await super.set(key, row.id);    return row.id;  }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u0441\u0435 \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0447\u0430\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0435 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445\u00a0<code>Postgres<\/code>,\u00a0<code>MongoDB<\/code>,\u00a0<code>MySQL<\/code>\u00a0\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u00a0<code>B-Tree<\/code>\u00a0\u0434\u043b\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0441\u0442\u0440\u043e\u043a, \u044d\u0442\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u00a0<code>O(log n)<\/code>, \u0433\u0434\u0435\u00a0<code>n<\/code>\u00a0&#8212; \u044d\u0442\u043e \u0447\u0438\u0441\u043b\u043e \u0441\u0442\u0440\u043e\u043a \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435. Redis \u043a\u0435\u0448 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0443\u043c\u0435\u043d\u044c\u0448\u0438\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0434\u043e\u00a0<code>O(1)<\/code>\u00a0\u0447\u0435\u0440\u0435\u0437 \u043f\u043e\u0438\u0441\u043a id \u0441\u0442\u0440\u043e\u043a\u0438 \u043f\u043e \u0441\u043e\u0441\u0442\u0430\u0432\u043d\u043e\u043c\u0443 \u043a\u043b\u044e\u0447\u0443, \u043e\u0434\u043d\u0430\u043a\u043e \u043f\u0440\u0438 \u0438\u043c\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441\u0435 freqtrade \u0435\u0433\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435\u0433\u0434\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c. \u0415\u0441\u043b\u0438 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 redis \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u043b\u043e\u0432\u0430\u0440\u044c \u043a\u043b\u044e\u0447-id, \u0430 \u0441\u0430\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u0435, \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0442\u0435\u0440\u044f\u043d\u0430 \u043d\u0430 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\/\u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 json \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432. \u041d\u0438\u0436\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u0430 \u0441 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u043c\u0438 \u043c\u0435\u0442\u0440\u0438\u043a\u0430\u043c\u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">Metric<\/p>\n<\/th>\n<th>\n<p align=\"left\">Value<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Wall-clock span (first \u2192 last event)<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>1779292952202 \u2212 1779292949309<\/code>\u00a0=\u00a0<strong>2 893 ms<\/strong>\u00a0(~2.9 s)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Total events captured<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>297<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Symbols running in parallel<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>9<\/strong>\u00a0(BTC, POL, ZEC, HYPE, XAUT, DOGE, SOL, PENGU, HBAR)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Historical time advanced per symbol<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>1775003640000 \u2212 1775001600000<\/code>\u00a0=\u00a0<strong>2 040 000 ms<\/strong>\u00a0=\u00a0<strong>34 minutes<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>Per-symbol replay speed<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">34 min historical \u00f7 2.9 s wall =\u00a0<strong>\u2248 703\u00d7<\/strong>\u00a0real-time<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>Aggregate replay speed (9 symbols)<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">9 \u00d7 703 =\u00a0<strong>\u2248 6 326\u00d7<\/strong>\u00a0real-time<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Event throughput<\/p>\n<\/td>\n<td>\n<p align=\"left\">297 ev \/ 2.893 s =\u00a0<strong>\u2248 103 events\/sec<\/strong>\u00a0(one Node process)<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Frame coverage<\/p>\n<\/td>\n<td>\n<p align=\"left\"><code>2026-04-01 \u2192 2026-04-27<\/code>\u00a0= 27 days \u00d7 1m candles =\u00a0<strong>38 880 candles\/symbol \u00d7 9<\/strong>\u00a0=\u00a0<strong>~350 000 candle ticks<\/strong><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p><strong>\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c: ~700x \u0443\u0441\u043a\u043e\u0440\u0435\u043d\u0438\u0435 \u0438\u0441\u0442\u043e\u0440\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e<\/strong>, \u043f\u0440\u0438 9 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430\u0445 &#8212; \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u044b\u0435\u00a0<strong>~6300x<\/strong>\u00a0\u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0441 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0435\u043c \u0441\u0432\u0435\u0447\u0435\u0439 \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438<\/p>\n<h3>\u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435<\/h3>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1037822\/\">https:\/\/habr.com\/ru\/articles\/1037822\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434, \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043d\u043d\u044b\u0439 \u0432 \u0441\u0442\u0430\u0442\u044c\u0435, \u043e\u043f\u0443\u0431\u043b\u0438\u043a\u043e\u0432\u0430\u043d\u00a0\u0432 \u044d\u0442\u043e\u043c GitHub \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438\u0418\u043c\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0432 \u043c\u0438\u0440\u0435 \u0418\u0418. \u0415\u0441\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u043a\u043e\u0434\u043e\u0432\u0443\u044e \u0431\u0430\u0437\u0443 \u043d\u0430 God Object, \u0438\u0437 \u0442\u0440\u0451\u0445 \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u043e\u0432 \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u043e\u0434 \u0441\u043c\u043e\u0436\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d, \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u0430\u043c\u0438 \u0441\u043b\u0438\u044f\u043d\u0438\u044f. \u0415\u0441\u043b\u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u0442\u044c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438, \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u043a\u0430\u043f\u043b\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0431\u0430\u0437\u0430 \u0437\u043d\u0430\u043d\u0438\u0439: \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u0430\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0442\u043e\u0440\u0433\u043e\u0432\u0430\u044f \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044f, \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0441\u0430\u043c\u043e\u043c \u043a\u043e\u0434\u0435. \u0422\u0435\u0440\u044f\u0435\u0442\u0441\u044f \u0441\u0438\u043d\u0435\u0440\u0433\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u044d\u0444\u0444\u0435\u043a\u0442: Coding Agent \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u0442 \u0447\u0438\u0442\u0430\u0442\u044c \u043a\u043e\u0434 \u0438\u0437 \u043f\u0440\u043e\u0448\u043b\u044b\u0445 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438. \u041a\u0430\u0436\u0434\u0430\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044f &#8212; \u044d\u0442\u043e \u0440\u0430\u043d\u0434\u043e\u043c, \u0430 \u043d\u0435 \u0440\u0430\u0437\u0432\u0438\u0442\u0438\u0435 \u043c\u044b\u0441\u043b\u0438 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430OpenSource \u0434\u043b\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0442\u043e\u0440\u0433\u043e\u0432\u044b\u0445 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0439 \u0432\u0435\u0441\u044c \u043d\u0430 python. \u041d\u0430 python \u043d\u0435\u0443\u0434\u043e\u0431\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0434\u0435\u043a\u043b\u0430\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u043a\u043e\u0434 &#8212; \u0432\u0441\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u044d\u0442\u043e \u0440\u0430\u0437\u0434\u0443\u0442\u044b\u0435 \u043d\u0430 \u0442\u044b\u0441\u044f\u0447\u0438 \u0441\u0442\u0440\u043e\u043a \u0438\u043c\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0435 \u043a\u043b\u0430\u0441\u0441\u044b \u0430\u043d\u0442\u0438\u043f\u0430\u0442\u0442\u0435\u0440\u043d\u0430 God Objectclass AwesomeStrategy(IStrategy):    position_adjustment_enable = True   # \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0439 switch, \u0431\u0435\u0437 \u043d\u0435\u0433\u043e DCA \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442    # 1. \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u043b\u0435\u0441\u0435\u043d\u043a\u0438 &#8212; \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u043a\u043b\u0430\u0441\u0441\u0430    max_entry_position_adjustment = 9   # \u043c\u0430\u043a\u0441. 9 \u0434\u043e\u043b\u0438\u0432\u043e\u043a + 1 \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u0439 \u0432\u0445\u043e\u0434 = 10 \u0448\u0430\u0433\u043e\u0432    max_dca_multiplier = 5.5            # \u0440\u0435\u0437\u0435\u0440\u0432\u0438\u0440\u0443\u0435\u043c 5.5\u00d7 \u043e\u0442 \u0441\u0442\u0435\u0439\u043a\u0430 \u043f\u043e\u0434 \u0431\u0443\u0434\u0443\u0449\u0438\u0435 \u0434\u043e\u043b\u0438\u0432\u043a\u0438    stoploss = -0.30                    # \u0432\u044b\u0441\u043e\u043a\u0438\u0439 \u0445\u0430\u0440\u0434-\u0441\u0442\u043e\u043f \u043d\u0443\u0436\u0435\u043d, \u0438\u043d\u0430\u0447\u0435 DCA \u0432\u044b\u043b\u0435\u0442\u0430\u0435\u0442    # 2. \u041f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u0439 \u0432\u0445\u043e\u0434 &#8212; \u0443\u0440\u0435\u0437\u0430\u0435\u043c \u0441\u0442\u0435\u0439\u043a, \u0447\u0442\u043e\u0431\u044b \u0445\u0432\u0430\u0442\u0438\u043b\u043e \u043d\u0430 \u043b\u0435\u0441\u0435\u043d\u043a\u0443    def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,                            proposed_stake: float, min_stake: float, max_stake: float,                            entry_tag: Optional[str], side: str, **kwargs) -&gt; float:        # \u0417\u0430\u0440\u0435\u0437\u0435\u0440\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 DCA-\u0434\u043e\u043b\u0438\u0432\u043a\u0438        return proposed_stake \/ self.max_dca_multiplier    # 3. \u0421\u0430\u043c\u0430 \u043b\u0435\u0441\u0435\u043d\u043a\u0430 &#8212; \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 callback, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 Freqtrade \u0437\u043e\u0432\u0451\u0442 \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u043c \u0442\u0438\u043a\u0435    def adjust_trade_position(self, trade: Trade, current_time: datetime,                              current_rate: float, current_profit: float,                              min_stake: float, max_stake: float,                              current_entry_rate: float, current_exit_rate: float,                              current_entry_profit: float, current_exit_profit: float,                              **kwargs) -&gt; Optional[float]:        # \u0422\u0435\u043a\u0443\u0449\u0430\u044f \u043f\u0440\u043e\u0441\u0430\u0434\u043a\u0430 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0433\u043b\u0443\u0431\u043e\u043a\u0430\u044f &#8212; \u043d\u0435 \u0434\u043e\u043b\u0438\u0432\u0430\u0435\u043c        if current_profit &gt; -0.05 and trade.nr_of_successful_entries == 1:            return None        if current_profit &gt; -0.10 and trade.nr_of_successful_entries == 2:            return None        # &#8230; \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u044c \u044d\u0442\u0443 \u043b\u0435\u0441\u0435\u043d\u043a\u0443 \u0443\u0441\u043b\u043e\u0432\u0438\u0439 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u0448\u0430\u0433\u0430        # \u0414\u043e\u0441\u0442\u0430\u0451\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0443\u0436\u0435 \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u043d\u044b\u0445 \u0432\u0445\u043e\u0434\u043e\u0432 \u0438\u0437 state \u0442\u0440\u0435\u0439\u0434\u0430        filled_entries = trade.select_filled_orders(trade.entry_side)        count_of_entries = trade.nr_of_successful_entries        try:            # \u0421\u0442\u0435\u0439\u043a \u043f\u0435\u0440\u0432\u043e\u0439 \u043f\u043e\u043a\u0443\u043f\u043a\u0438 \u00d7 (count_of_entries &#8212; \u0434\u043b\u044f \u0433\u0435\u043e\u043c\u0435\u0442\u0440\u0438\u0438 \u043b\u0435\u0441\u0435\u043d\u043a\u0438)            stake_amount = filled_entries[0].stake_amount            stake_amount = stake_amount * (1 + (count_of_entries * 0.25))            return stake_amount        except Exception:            return None    # 4. populate_indicators \/ populate_entry_trend \/ populate_exit_trend \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u043d\u0443\u0436\u043d\u044b,    #    \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0431\u0435\u0437 entry-\u0441\u0438\u0433\u043d\u0430\u043b\u0430 \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u044b\u0439 \u0432\u0445\u043e\u0434 \u043d\u0435 \u0441\u043b\u0443\u0447\u0438\u0442\u0441\u044f.    def populate_indicators(self, dataframe, metadata): &#8230;    def populate_entry_trend(self, dataframe, metadata): &#8230;    def populate_exit_trend(self, dataframe, metadata): &#8230;\u042d\u0442\u043e\u0442 boilerplate \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043e\u043a\u0443\u043f \u043b\u0435\u0441\u0435\u043d\u043a\u043e\u0439: \u0443\u043f\u0430\u043b\u043e &#8212; \u0434\u043e\u043a\u0443\u043f\u0438\u043b\u0438. \u0427\u0442\u043e\u0431\u044b \u043f\u043e\u043d\u044f\u0442\u044c, \u043a\u043e\u0433\u0434\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u0442\u044c \u043f\u043e\u043a\u0443\u043f\u0430\u0442\u044c \u0432 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u043e\u0431\u0430\u043d\u043a\u0440\u043e\u0442\u0438\u0442\u044c\u0441\u044f, \u0432 \u044d\u0442\u043e\u0442 \u0436\u0435 \u043a\u043b\u0430\u0441\u0441 \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u044b \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0432 \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445. \u041f\u0440\u044f\u043c\u043e\u0435 \u043d\u0430\u0440\u0443\u0448\u0435\u043d\u0438\u0435 SOLIDclass TelegramSignalStrategy(IStrategy):    custom_signals = {}  # nested dict: pair -&gt; list of signals    def bot_start(self) -&gt; None:        # \u0441\u0442\u0430\u0440\u0442\u0443\u0435\u0442 cron-\u0437\u0430\u0434\u0430\u0447\u0430 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u043f\u043e\u0442\u043e\u043a\u0435, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0445\u043e\u0434\u0438\u0442 \u0432 Telegram        # \u0438 \u043f\u0438\u0448\u0435\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0432 self.custom_signals        threading.Thread(target=self._poll_telegram_loop, daemon=True).start()    def _poll_telegram_loop(self):        while True:            try:                messages = telegram_client.iter_messages(&#171;crypto_yoda_channel&#187;)                for msg in messages:                    parsed = self._parse_signal(msg.text)  # regex inline                    if parsed:                        self.custom_signals.setdefault(parsed[&#8216;pair&#8217;], []).append(parsed)            except Exception:                pass            time.sleep(60)    def _parse_signal(self, text: str) -&gt; Optional[dict]:        m = re.search(r&#187;#([A-Z0-9]+)\/USDT.*?(\u041b\u041e\u041d\u0413|\u0428\u041e\u0420\u0422).*?\u0437\u043e\u043d\u0435\\s+\\$?([\\d.,]+)\\s*[-\u2013-]\\s*\\$?([\\d.,]+)&#187;, text)        if not m:            return None        return {            &#171;pair&#187;: m.group(1) + &#171;\/USDT&#187;,            &#171;direction&#187;: &#171;long&#187; if m.group(2) == &#171;\u041b\u041e\u041d\u0413&#187; else &#171;short&#187;,            &#171;entry_from&#187;: float(m.group(3).replace(&#171;,&#187;, &#171;.&#187;)),            &#171;entry_to&#187;:   float(m.group(4).replace(&#171;,&#187;, &#171;.&#187;)),        }    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -&gt; DataFrame:        signals = self.custom_signals.get(metadata[&#8216;pair&#8217;], [])        # &#8230; \u0442\u0443\u0442 \u043a\u0430\u043a-\u0442\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c signals \u043a dataframe\u041e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0442\u043e\u0447\u043a\u0430 \u0432\u0445\u043e\u0434\u0430: \u0432 freqtrade \u044d\u0442\u043e json \u0444\u0430\u0439\u043b. \u0422\u0443\u0434\u0430 \u043f\u043e\u043d\u0430\u043f\u0438\u0445\u0430\u043d\u044b \u0438 API \u043a\u043b\u044e\u0447\u0438, \u0438 \u043c\u0430\u0433\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b \u0432\u0438\u0434\u0430\u00a0dry_run. \u041d\u0435\u043f\u043e\u043d\u044f\u0442\u043d\u043e, \u043a\u043e\u043c\u043c\u0438\u0442\u0438\u0442\u044c \u044d\u0442\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u043d\u0435\u0442: \u0435\u0441\u043b\u0438 \u043a\u043e\u043c\u043c\u0438\u0442\u0438\u0442\u044c, \u0432\u0441\u0435\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442 \u0441\u043b\u0438\u044f\u043d\u0438\u044f \u043f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0435{  &#171;dry_run&#187;: true,  &#171;timeframe&#187;: &#171;3m&#187;,  &#171;stake_currency&#187;: &#171;USDT&#187;,  &#171;stake_amount&#187;: 200,  &#171;max_open_trades&#187;: 5,  &#171;exchange&#187;: {    &#171;name&#187;: &#171;binance&#187;,    &#171;key&#187;: &#171;af8ddd35195e9dc500b9a6f799f6f5c93d89193b&#187;,    &#171;secret&#187;: &#171;08a9dc6db3d7b53e1acebd9275677f4b0a04f1a5&#187;,    &#171;pair_whitelist&#187;: [&#171;BTC\/USDT&#187;, &#171;ETH\/USDT&#187;]  },  &#171;timerange&#187;: &#171;20260401-20260427&#187;,  &#171;datadir&#187;: &#171;user_data\/data\/binance&#187;}\u041f\u0440\u0438 \u0442\u0430\u043a\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435 \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c look ahead bias \u043d\u0435\u043b\u044c\u0437\u044f: \u0447\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e \u0432\u043e \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434, \u043d\u0443\u0436\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u0443\u044e \u0431\u0430\u0437\u0443 \u0434\u0430\u043d\u043d\u044b\u0445, \u0430 \u0443\u0434\u0430\u043b\u044f\u0442\u044c \u0447\u0435\u0440\u0435\u0437 crontab. \u042d\u0442\u043e\u0442 \u0438\u043c\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u043a\u043e\u0434 \u043c\u0430\u0442\u0435\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435\u0434\u043e\u043a\u0430\u0437\u0443\u0435\u043c, \u0442\u0430\u043a \u043a\u0430\u043a \u0434\u0430\u0436\u0435 \u0431\u043b\u0438\u0437\u043a\u043e \u043d\u0435 \u043f\u043e\u0445\u043e\u0436 \u043d\u0430 \u0447\u0438\u0441\u0442\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e\u0420\u0435\u0448\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b\u0414\u043e \u043d\u0430\u0439\u043c\u0430 \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u043e\u0432 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043a\u043e\u043d\u0432\u0435\u0439\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u043a\u0443\u043c\u0443\u043b\u044f\u0442\u0438\u0432\u043d\u044b\u0439 \u044d\u0444\u0444\u0435\u043a\u0442 \u0437\u0430\u0442\u0440\u0430\u0447\u0435\u043d\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0432\u0438\u0442\u043e\u043a \u0434\u0438\u0430\u043b\u0435\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0441\u043f\u0438\u0440\u0430\u043b\u0438. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043f\u0430\u043f\u043e\u043a \u0441 \u043a\u043e\u0434\u043e\u043cmonorepo\/\u251c\u2500\u2500 content\/\u2502   \u251c\u2500\u2500 apr_2026.strategy\/ |       \u251c\u2500\u2500modules\u2502       \u2502   \u251c\u2500\u2500backtest.module.ts  # testnet paper\u2502       \u251c\u2500\u2500 apr_2026.strategy.ts   # strategy production code\u2502       \u251c\u2500\u2500 apr_2026.test.ts       # developer playground\u251c\u2500\u2500 modules\/\u2502       \u251c\u2500\u2500 backtest.module.ts     # mainnet paper\u2502       \u251c\u2500\u2500 live.module.ts         # mainnet live\u251c\u2500\u2500 packages\/\u2502   \u251c\u2500\u2500 core\/                      # database layer\u2502   \u251c\u2500\u2500 main\/                      # cli arguments parserN \u0442\u043e\u0447\u0435\u043a \u0432\u0445\u043e\u0434\u0430 \u0432\u043c\u0435\u0441\u0442\u043e \u043e\u0434\u043d\u043e\u0439: \u0432\u0435\u0442\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430 CLI-\u0444\u043b\u0430\u0433\u0435\u041a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u0438\u043c\u043f\u043e\u0440\u0442 \u0432\u00a0packages\/main\/src\/index.ts\u00a0\u043f\u043e\u0434\u0442\u044f\u0433\u0438\u0432\u0430\u0435\u0442 \u0447\u0435\u0442\u044b\u0440\u0435 \u0444\u0430\u0439\u043b\u0430import &#171;.\/main\/session&#187;;import &#171;.\/main\/backtest&#187;;import &#171;.\/main\/live&#187;;import &#171;.\/main\/paper&#187;;\u041a\u0430\u0436\u0434\u044b\u0439 \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 &#8212; \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0432\u0445\u043e\u0434\u0430const main = async () =&gt; {  const { values } = getArgs();  if (!values.entry) return;  if (!values.backtest) return;  await waitForReady(true);  const [strategySchema] = await listStrategySchema();  const [exchangeSchema] = await listExchangeSchema();  const [frameSchema]    = await listFrameSchema();  if (values.cache) {    await CACHE_CANDLES_FN();  }  for (const symbol of CC_SYMBOL_LIST) {    Backtest.background(symbol, {      exchangeName: exchangeSchema.exchangeName,      strategyName: strategySchema.strategyName,      frameName:    frameSchema.frameName,    });  }};main();\u0420\u0430\u0437\u0434\u0435\u043b\u044f\u0435\u043c\u044b\u0439 \u0441\u043e\u0431\u044b\u0442\u0438\u0439\u043d\u044b\u0439 \u043a\u043e\u0434 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438\u0422\u043e\u0447\u043a\u0443 \u0432\u0445\u043e\u0434\u0430, \u0443\u0441\u0440\u0435\u0434\u043d\u0435\u043d\u0438\u0435, stop loss \u0438 \u0440\u0430\u043d\u043d\u0438\u0439 \u0432\u044b\u0445\u043e\u0434 \u043d\u0443\u0436\u043d\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e \u0447\u0438\u0441\u0442\u043e, \u0432 \u0441\u0442\u0438\u043b\u0435 \u0434\u0435\u043a\u043b\u0430\u0440\u0430\u0446\u0438\u0438, \u0442\u0430\u043a \u0447\u0442\u043e\u0431\u044b \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0440\u0430\u0437\u043d\u0435\u0441\u0442\u0438 \u043f\u043e \u0440\u0430\u0437\u043d\u044b\u043c \u0444\u0430\u0439\u043b\u0430\u043c \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043eimport {  addStrategySchema, listenActivePing, listenError,  Log, Position,  commitClosePending, commitAverageBuy,  getPositionPnlPercent, getPositionEntryOverlap, getPositionEntries,} from &#171;backtest-kit&#187;;const HARD_STOP        = 10.0;const TARGET_PROFIT    = 3;const LADDER_STEP_COST = 100;const LADDER_UPPER_STEP = 5;const LADDER_LOWER_STEP = 1;const LADDER_MAX_STEPS  = 10;addStrategySchema({  strategyName: &#171;apr_2026_strategy&#187;,  getSignal: async (symbol, when, currentPrice) =&gt; ({    &#8230;Position.moonbag({         position: &#171;long&#187;,         currentPrice,         percentStopLoss: HARD_STOP    }),    minuteEstimatedTime: Infinity,    cost: LADDER_STEP_COST,  }),});listenActivePing(async ({ symbol, currentPrice }) =&gt; {  const { length: steps } = await getPositionEntries(symbol);  if (steps &gt;= LADDER_MAX_STEPS) return;  const hasOverlap = await getPositionEntryOverlap(symbol, currentPrice, {    upperPercent: LADDER_UPPER_STEP,    lowerPercent: LADDER_LOWER_STEP,  });  if (hasOverlap) return;  await commitAverageBuy(symbol, LADDER_STEP_COST);});listenActivePing(async ({ symbol }) =&gt; {  const currentProfit = await getPositionPnlPercent(symbol);  if (currentProfit &lt; TARGET_PROFIT) {    return;  }  await commitClosePending(symbol, { id: &#171;unknown&#187;, note: &#171;# closed by target pnl&#187; });});\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043a \u043b\u044e\u0431\u043e\u0439 \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u0438 \u043c\u043e\u0436\u043d\u043e \u0438\u043d\u043a\u0440\u0435\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u043e \u043d\u0430\u043a\u0438\u043d\u0443\u0442\u044c trailing take \u043d\u0435 \u043c\u0435\u043d\u044f\u044f \u043a\u043e\u0434. \u041b\u044e\u0431\u043e\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u043a\u043e\u0434\u0430 &#8212; \u044d\u0442\u043e side effect, \u0442\u0430\u043a \u043a\u0430\u043a \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u0435\u0442 \u0442\u0430\u0431\u043b\u0438\u0446\u0443 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0445 \u0432\u0445\u043e\u0434\u043e\u0432 \u0438 \u0432\u044b\u0445\u043e\u0434\u043e\u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u043f\u0440\u0438\u0440\u043e\u0434\u0430 \u043c\u0443\u0442\u0430\u0446\u0438\u0438 \u0432\u044b\u0437\u0432\u0430\u043d\u0430 \u0433\u043e\u0432\u043d\u043e\u043a\u043e\u0434\u044f\u0449\u0435\u0439 \u043d\u0435\u0439\u0440\u043e\u043d\u043a\u043e\u0439, \u0430 \u043d\u0435 \u043e\u0431\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u0430\u043c\u0438 \u0432 runtime.if (GLOBAL_CONFIG.ATTACH_TRAILING_TAKE) {    listenActivePing(async ({ symbol, data }) =&gt; {        const peakProfitDistance = await getPositionHighestProfitDistancePnlPercentage(symbol);        const currentProfit = await getPositionPnlPercent(symbol);        if (currentProfit &lt; 0) {            return;        }        if (peakProfitDistance &lt; TRAILING_TAKE) {            return;        }        Log.info(&#171;position closed due to the trailing take&#187;, {            symbol,            data,        });        await commitClosePending(symbol, {            id: &#171;unknown&#187;,            note: str.newline(                &#171;# closed by trailing take&#187;,            ),        });    });}\u0412\u043e Freqtrade \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-480545","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/480545","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=480545"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/480545\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=480545"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=480545"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=480545"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}