Source code for curvesim.templates.trader

from abc import ABC, abstractmethod
from dataclasses import fields
from typing import Union

from curvesim.logging import get_logger
from curvesim.utils import dataclass

logger = get_logger(__name__)


@dataclass(frozen=True, slots=True)
class Trade:
    """Data container for a trade to be executed."""

    coin_in: Union[str, int]
    coin_out: Union[str, int]
    amount_in: int

    def __iter__(self):
        return (getattr(self, field.name) for field in fields(self))


@dataclass(frozen=True, slots=True)
class ArbTrade(Trade):
    """Trade object specifying an arbitrage trade."""

    price_target: float

    def replace_amount_in(self, new_amount_in):
        """Returns self, replacing amount_in."""
        coin_in, coin_out, _, price_target = self
        return ArbTrade(coin_in, coin_out, new_amount_in, price_target)


@dataclass(slots=True)
class TradeResult:
    """Data container for a trade execution."""

    coin_in: Union[str, int]
    coin_out: Union[str, int]
    amount_in: int
    amount_out: int
    fee: int

    def __iter__(self):
        return (getattr(self, field.name) for field in fields(self))

    def set_attrs(self, **kwargs):
        """Sets multiple attributes defined by keyword arguments."""
        for attr, value in kwargs.items():
            setattr(self, attr, value)

    @classmethod
    def from_trade(cls, trade, amount_out=None, fee=None):
        """Initializes a TradeResult object from a Trade object"""
        return cls(trade.coin_in, trade.coin_out, trade.amount_in, amount_out, fee)


[docs]class Trader(ABC): """ Computes, executes, and reports out arbitrage trades. """
[docs] def __init__(self, pool): """ Parameters ---------- pool : Simulation interface to a subclass of :class:`.Pool`. """ self.pool = pool
[docs] @abstractmethod def compute_trades(self, *args): """ Computes trades to execute on the pool. Returns ------- trades : list of :class:`Trade` objects List of trades to perform. additional_data: dict Dict of additional data to be passed to the state log as part of trade_data. """ raise NotImplementedError
[docs] def do_trades(self, trades): """ Executes a series of trades. Parameters ---------- trades : list of :class:`Trade` objects Trades to execute. Returns ------- trades: list of :class:`TradeResult` objects The results of the trades. """ trade_results = [] for trade in trades: dy, fee = self.pool.trade(trade.coin_in, trade.coin_out, trade.amount_in) trade_results.append(TradeResult.from_trade(trade, amount_out=dy, fee=fee)) return trade_results
[docs] def process_time_sample(self, *args): """ Process given tick data by computing and executing trades. The input args must be properly formed and fed by the parent `Strategy` object housing the trader class via its :meth:`~curvesim.pipelines.templates.Strategy._get_trader_inputs`. """ trades, additional_data = self.compute_trades(*args) trade_results = self.do_trades(trades) return {"trades": trade_results, **additional_data}