import time, os, requests, json, urllib3 from datetime import datetime, timedelta, timezone, time as dtime from dotenv import load_dotenv urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) load_dotenv("tok.env") TOKEN = os.getenv("TINKOFF_TOKEN") TG_TOKEN = os.getenv("TG_TOKEN") TG_CHAT_ID = os.getenv("TG_CHAT_ID") ACC_ID = os.getenv("ACCOUNT_ID") INSTRUMENT_UID = "a78b8349-a1dc-447d-9277-1d75826d089a" BASE_URL = "https://invest-public-api.tbank.ru/rest" STATE_FILE = "bot_state_prod.json" s = requests.Session() s.headers.update({"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}) def get_price(): path = f"{BASE_URL}/tinkoff.public.invest.api.contract.v1.MarketDataService/GetLastPrices" try: r = s.post(path, json={"instrumentId": [INSTRUMENT_UID]}, timeout=5) if r.status_code != 200: return None res = r.json() if 'lastPrices' in res and res['lastPrices']: p = res['lastPrices'][0]['price'] return int(p.get('units', 0)) + int(p.get('nano', 0)) / 1e9 except: return None return None def get_atr_steps(): path = f"{BASE_URL}/tinkoff.public.invest.api.contract.v1.MarketDataService/GetCandles" try: now_utc = datetime.now(timezone.utc) payload = { "instrumentId": INSTRUMENT_UID, "from": (now_utc - timedelta(days=2)).strftime('%Y-%m-%dT%H:%M:%SZ'), "to": now_utc.strftime('%Y-%m-%dT%H:%M:%SZ'), "interval": "CANDLE_INTERVAL_HOUR" } r = s.post(path, json=payload, timeout=10) candles = r.json().get('candles', []) if len(candles) < 2: return 1.1, 1.5 def to_f(p): return int(p.get('units', 0)) + int(p.get('nano', 0)) / 1e9 trs = [to_f(c['high']) - to_f(c['low']) for c in candles[-14:]] atr = sum(trs) / len(trs) buy_step = max(round(atr * 0.45, 2), 0.5) return buy_step, round(buy_step * 1.2, 2) except: return 1.1, 1.5 def post_order(direction): path = f"{BASE_URL}/tinkoff.public.invest.api.contract.v1.OrdersService/PostOrder" payload = { "instrumentId": INSTRUMENT_UID, "quantity": 1, "direction": direction, "orderType": "ORDER_TYPE_MARKET", "accountId": ACC_ID, "orderId": f"p_{int(time.time())}" } try: r = s.post(path, json=payload, timeout=10) return r.status_code == 200 except: return False def run(): print("🚀 БОТ ЗАПУЩЕН") state = {"phase": "BUY", "base_price": None} if os.path.exists(STATE_FILE): try: with open(STATE_FILE, 'r') as f: state = json.load(f) except: pass buy_step, profit_step = get_atr_steps() while True: price = get_price() if price: if state['base_price'] is None: state['base_price'] = price with open(STATE_FILE, 'w') as f: json.dump(state, f) print(f"\n🎯 База установлена: {price}") if state['phase'] == "BUY": target = round(state['base_price'] - buy_step, 2) else: target = round(state['base_price'] + profit_step, 2) print(f"[{datetime.now().strftime('%H:%M:%S')}] Цена: {price:.2f} | Цель: {target:.2f} | {state['phase']} ", end='\r') if state['phase'] == "BUY" and price <= target: if post_order("ORDER_DIRECTION_BUY"): state.update({"phase": "SELL", "base_price": price}) with open(STATE_FILE, 'w') as f: json.dump(state, f) print(f"\n🛒 КУПЛЕНО по {price}") elif state['phase'] == "SELL" and price >= target: if post_order("ORDER_DIRECTION_SELL"): state.update({"phase": "BUY", "base_price": price}) with open(STATE_FILE, 'w') as f: json.dump(state, f) print(f"\n💰 ПРОДАНО по {price}") time.sleep(1) if __name__ == "__main__": run()