Path: blob/master/ invest-robot-contest_tinkoff-contest-python-main/src/service/trader.py
5935 views
import asyncio1import datetime2import uuid34import tinkoff.invest56from src import settings7from src.containers.config import TraderConfig8from src.containers.market import (9AccountBalance,10CancelOrder,11CreateOrder,12MarketData,13MarketState,14)15from src.service.errors import DecisionExecutionError16from src.traders.base import BaseTrader171819class TraderRunner:20@classmethod21async def start_trader_loop(cls, trader: BaseTrader) -> None:22"""Start the trade loop with the given trader."""23print("The trader has been started")24while True:25# fetch data from the market26market_data = await cls._fetch_current_market_state(trader.trader_config)2728# make decisions in accordance with the strategy29decisions = await trader.make_decisions(market_data)3031# execute decisions, if any32await cls._execute_trader_decisions(decisions, trader.trader_config)3334# wait for the next step35await asyncio.sleep(trader.trader_config.config["decision_interval_s"])3637@classmethod38async def _fetch_current_market_state(cls, trader_config: TraderConfig) -> MarketState:39async with tinkoff.invest.AsyncClient(40settings.INVEST_TOKEN, sandbox_token=settings.SANDBOX_TOKEN, app_name=settings.APP_NAME41) as services:42# orders book43order_book = await services.market_data.get_order_book(44figi=trader_config.instrument_figi, depth=trader_config.config["order_book_depth"]45)4647# candles for required period48now = datetime.datetime.utcnow()49candles = (50await services.market_data.get_candles(51figi=trader_config.instrument_figi,52from_=now - trader_config.candle_timedelta,53to=now,54interval=trader_config.candle_interval,55)56).candles5758# available balance for the account59positions = await services.operations.get_positions(account_id=trader_config.account_id)6061# orders62orders = (await services.orders.get_orders(account_id=trader_config.account_id)).orders6364return MarketState(65account_balance=AccountBalance(66money=positions.money,67securities=positions.securities,68),69market_data=MarketData(70bids=order_book.bids,71asks=order_book.asks,72candles=candles,73),74opened_orders=orders,75)7677@classmethod78async def _execute_trader_decisions(cls, decisions, trader_config):79async with tinkoff.invest.AsyncClient(80settings.INVEST_TOKEN, sandbox_token=settings.SANDBOX_TOKEN, app_name=settings.APP_NAME81) as services:82for decision in decisions:83# execute decision84response = None85try:86response = await cls._execute_decision(services, trader_config, decision)87except DecisionExecutionError:88print("error executing decision")8990# log the decision and its execution result91await cls._log_algorithm_decision(trader_config, decision, response)9293@classmethod94async def _execute_decision(cls, client, trader_config, decision):95if isinstance(decision, CreateOrder):96try:97return await client.orders.post_order(98order_id=str(uuid.uuid4()),99figi=trader_config.instrument_figi,100account_id=trader_config.account_id,101# decision102order_type=decision.order_type,103direction=decision.order_direction,104price=decision.price,105quantity=decision.quantity,106)107except Exception as exc:108print("unable to post the order", str(exc))109elif isinstance(decision, CancelOrder):110try:111return await client.orders.cancel_order(account_id=trader_config.account_id, order_id=decision.order_id)112except Exception as exc:113print("unable to cancel the order", str(exc))114else:115print("Unsupported decision type", type(decision))116117@classmethod118async def _log_algorithm_decision(cls, trader_config, decision, response):119with open(f"logs/{trader_config.account_id}.log", "a") as f:120f.write(str(decision) + str(response))121122123