Compare commits
4 Commits
90295d21e6
...
c9e39b07f0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9e39b07f0 | ||
|
|
d17065bb56 | ||
|
|
2a971e81b7 | ||
|
|
196caea4a1 |
30
.github/workflows/rebalance.yml
vendored
Normal file
30
.github/workflows/rebalance.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
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
|
||||||
|
environment: tinvest
|
||||||
|
|
||||||
|
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 app/main.py
|
||||||
@@ -17,4 +17,4 @@ TARGET_WEIGHTS = {
|
|||||||
"ROSN": Decimal("0.05"),
|
"ROSN": Decimal("0.05"),
|
||||||
}
|
}
|
||||||
|
|
||||||
CORRIDOR = Decimal("0.02") # 3% коридор отклонения от целевой доли
|
CORRIDOR = Decimal("0.02") # 2% коридор отклонения от целевой доли
|
||||||
|
|||||||
21
app/load_config.py
Normal file
21
app/load_config.py
Normal file
@@ -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
|
||||||
@@ -10,13 +10,13 @@ load_dotenv()
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
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(
|
bot = RebalanceBot(
|
||||||
client=client,
|
client=client,
|
||||||
account_id="bb29bb79-8cf1-42ba-843f-47ef76c5b7c0",
|
account_id="2235046505",
|
||||||
target_weights=TARGET_WEIGHTS,
|
target_weights=TARGET_WEIGHTS,
|
||||||
corridor=CORRIDOR,
|
corridor=CORRIDOR,
|
||||||
dry_run=True,
|
dry_run=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
plan = bot.calculate_rebalance(bot.fetch_portfolio())
|
plan = bot.calculate_rebalance(bot.fetch_portfolio())
|
||||||
@@ -30,3 +30,5 @@ def main():
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
# with Client(token=os.getenv("TINVEST_TOKEN")) as client:
|
||||||
|
# print(client.users.get_accounts())
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ class RebalanceBot:
|
|||||||
if lots != 0:
|
if lots != 0:
|
||||||
plan.append(
|
plan.append(
|
||||||
{
|
{
|
||||||
|
"figi": pos.figi,
|
||||||
"ticker": ticker,
|
"ticker": ticker,
|
||||||
"uid": uid,
|
"uid": uid,
|
||||||
"action": "BUY" if lots > 0 else "SELL",
|
"action": "BUY" if lots > 0 else "SELL",
|
||||||
|
|||||||
Reference in New Issue
Block a user