From 196caea4a11a63f7e855a125cae2c2ae7ab60350 Mon Sep 17 00:00:00 2001 From: Dmitry <124861781+ada-dmitry@users.noreply.github.com> Date: Tue, 6 Jan 2026 10:29:17 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20=D0=BA=D0=BE=D0=BD=D1=84=D0=B8=D0=B3=D1=83=D1=80=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D1=8E:=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=BA=D0=BE=D1=80=D0=B8=D0=B4=D0=BE=D1=80?= =?UTF-8?q?=20=D0=BE=D1=82=D0=BA=D0=BB=D0=BE=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?,=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B8=D1=82=D1=8C=20=D0=B8?= =?UTF-8?q?=D0=B4=D0=B5=D0=BD=D1=82=D0=B8=D1=84=D0=B8=D0=BA=D0=B0=D1=82?= =?UTF-8?q?=D0=BE=D1=80=20=D0=B0=D0=BA=D0=BA=D0=B0=D1=83=D0=BD=D1=82=D0=B0?= =?UTF-8?q?,=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D1=82=D1=8C=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D1=83=20=D0=BA=D0=BE=D0=BD?= =?UTF-8?q?=D1=84=D0=B8=D0=B3=D1=83=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B8?= =?UTF-8?q?=D0=B7=20YAML?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/rebalance.yml | 29 +++++++++++++++++++++++++++++ app/config.py | 2 +- app/load_config.py | 21 +++++++++++++++++++++ app/main.py | 6 ++++-- app/rebalance.py | 1 + 5 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/rebalance.yml create mode 100644 app/load_config.py diff --git a/.github/workflows/rebalance.yml b/.github/workflows/rebalance.yml new file mode 100644 index 0000000..cbe2858 --- /dev/null +++ b/.github/workflows/rebalance.yml @@ -0,0 +1,29 @@ +name: Weekly Portfolio Rebalance + +on: + # 1. Запуск по расписанию (каждый понедельник в 10:30 МСК) + schedule: + - cron: '30 7 * * 1' # Время в UTC (7:30 UTC = 10:30 МСК) + + # 2. Возможность запустить вручную кнопкой в интерфейсе GitHub + workflow_dispatch: + +jobs: + rebalance: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v3 + + - name: Set up Python + run: uv python install 3.13 + + - name: Run Rebalance Bot + env: + # Передаем секреты в переменные окружения скрипта + TINVEST_TOKEN: ${{ secrets.TINVEST_TOKEN }} + run: uv run main.py # Замени на имя своего главного файла \ No newline at end of file diff --git a/app/config.py b/app/config.py index f454de6..6d37dfa 100644 --- a/app/config.py +++ b/app/config.py @@ -17,4 +17,4 @@ TARGET_WEIGHTS = { "ROSN": Decimal("0.05"), } -CORRIDOR = Decimal("0.02") # 3% коридор отклонения от целевой доли +CORRIDOR = Decimal("0.02") # 2% коридор отклонения от целевой доли diff --git a/app/load_config.py b/app/load_config.py new file mode 100644 index 0000000..cc9610a --- /dev/null +++ b/app/load_config.py @@ -0,0 +1,21 @@ +import yaml +from decimal import Decimal + + +def load_config(config_path: str) -> dict: + with open(config_path, "r") as file: + data = yaml.safe_load(file) + + target_weights = { + ticker: Decimal(str(weight)) for ticker, weight in data["portfolio"].items + } + + config = { + "account_id": data["account"]["id"], + "is_sandbox": data["account"]["is_sandbox"], + "corridor": Decimal(str(data["settings"]["corridor"])), + "dry_run": data["settings"]["dry_run"], + "target_weights": target_weights, + } + + return config diff --git a/app/main.py b/app/main.py index aa40693..34288df 100644 --- a/app/main.py +++ b/app/main.py @@ -10,10 +10,10 @@ load_dotenv() def main(): - with SandboxClient(token=os.getenv("TINVEST_TOKEN")) as client: # type: ignore + with Client(token=os.getenv("TINVEST_TOKEN")) as client: # type: ignore bot = RebalanceBot( client=client, - account_id="bb29bb79-8cf1-42ba-843f-47ef76c5b7c0", + account_id="2235046505", target_weights=TARGET_WEIGHTS, corridor=CORRIDOR, dry_run=True, @@ -30,3 +30,5 @@ def main(): if __name__ == "__main__": main() + # with Client(token=os.getenv("TINVEST_TOKEN")) as client: + # print(client.users.get_accounts()) diff --git a/app/rebalance.py b/app/rebalance.py index f3d8fdd..f52315b 100644 --- a/app/rebalance.py +++ b/app/rebalance.py @@ -71,6 +71,7 @@ class RebalanceBot: if lots != 0: plan.append( { + "figi": pos.figi, "ticker": ticker, "uid": uid, "action": "BUY" if lots > 0 else "SELL",