pybottrader.traders

A collection of bottraders

Traders, these are bots that based on a data stream, a strategy, and a portfolio, run the trading operations. Currently only a basic Trader is offered, useful for back-testing.

 1"""
 2A collection of bottraders
 3
 4Traders, these are bots that based on a data stream, a strategy, and a
 5portfolio, run the trading operations. Currently only a basic Trader is offered,
 6useful for back-testing.
 7
 8"""
 9
10from typing import Union
11from attrs import define
12from .datastreamers import DataStreamer
13from .portfolios import Portfolio
14from .strategies import Strategy, Position, StrategySignal
15from .indicators import roi
16
17
18@define
19class TradingIteration:
20    """Used to report results from a trading iteration"""
21
22    signal: StrategySignal
23    data: dict
24    roi: Union[float, None]
25    portfolio_value: float
26    accumulated_roi: Union[float, None]
27
28
29class Trader:
30    """Base class"""
31
32    portfolio: Portfolio
33    datastream: DataStreamer
34    strategy: Strategy
35    last_result: Union[TradingIteration, None] = None
36    last_valuation: float = 0.0
37
38    def __init__(
39        self,
40        strategy: Strategy,
41        portfolio: Portfolio,
42        datastream: DataStreamer,
43    ):
44        """Init method"""
45        self.datastream = datastream
46        self.portfolio = portfolio
47        self.strategy = strategy
48
49    def next(self) -> bool:
50        """Perfoms a trading iteration"""
51        obs = self.datastream.next()
52        if obs is None:
53            return False
54        signal = self.strategy.evaluate(data=obs)
55        self.portfolio.process(signal)
56        self.last_result = TradingIteration(
57            signal=signal,
58            data=obs,
59            roi=roi(self.last_valuation, self.portfolio.valuation()),
60            portfolio_value=self.portfolio.valuation(),
61            accumulated_roi=self.portfolio.accumulated_return(),
62        )
63        self.last_valuation = self.portfolio.valuation()
64        return True
65
66    def status(self) -> TradingIteration:
67        """Trader last result"""
68        return self.last_result
69
70    def run(self):
71        """A default runner"""
72        # A nice header
73        print(
74            "{:25} {:4} {:>10} {:>10}  {:>10} {:>10}".format(  # pylint: disable=consider-using-f-string
75                "Time", "Pos.", "Price", "ROI", "Valuation", "Accum.ROI"
76            )
77        )
78        # Run the back-testing
79        while self.next():
80            status = self.status()
81            if status.signal.position != Position.STAY:
82                # A nice output
83                print(
84                    f"{status.signal.time} "
85                    + f"{status.signal.position.name:4} "
86                    + f"{status.data['close']:10.2f} "
87                    + f"{status.roi * 100.0:10.2f}% "
88                    + f"{status.portfolio_value:10.2f} "
89                    + f"{status.accumulated_roi * 100.0:10.2f}%"
90                )
@define
class TradingIteration:
19@define
20class TradingIteration:
21    """Used to report results from a trading iteration"""
22
23    signal: StrategySignal
24    data: dict
25    roi: Union[float, None]
26    portfolio_value: float
27    accumulated_roi: Union[float, None]

Used to report results from a trading iteration

TradingIteration( signal: pybottrader.strategies.StrategySignal, data: dict, roi: Optional[float], portfolio_value: float, accumulated_roi: Optional[float])
2def __init__(self, signal, data, roi, portfolio_value, accumulated_roi):
3    self.signal = signal
4    self.data = data
5    self.roi = roi
6    self.portfolio_value = portfolio_value
7    self.accumulated_roi = accumulated_roi

Method generated by attrs for class TradingIteration.

data: dict
roi: Optional[float]
portfolio_value: float
accumulated_roi: Optional[float]
class Trader:
30class Trader:
31    """Base class"""
32
33    portfolio: Portfolio
34    datastream: DataStreamer
35    strategy: Strategy
36    last_result: Union[TradingIteration, None] = None
37    last_valuation: float = 0.0
38
39    def __init__(
40        self,
41        strategy: Strategy,
42        portfolio: Portfolio,
43        datastream: DataStreamer,
44    ):
45        """Init method"""
46        self.datastream = datastream
47        self.portfolio = portfolio
48        self.strategy = strategy
49
50    def next(self) -> bool:
51        """Perfoms a trading iteration"""
52        obs = self.datastream.next()
53        if obs is None:
54            return False
55        signal = self.strategy.evaluate(data=obs)
56        self.portfolio.process(signal)
57        self.last_result = TradingIteration(
58            signal=signal,
59            data=obs,
60            roi=roi(self.last_valuation, self.portfolio.valuation()),
61            portfolio_value=self.portfolio.valuation(),
62            accumulated_roi=self.portfolio.accumulated_return(),
63        )
64        self.last_valuation = self.portfolio.valuation()
65        return True
66
67    def status(self) -> TradingIteration:
68        """Trader last result"""
69        return self.last_result
70
71    def run(self):
72        """A default runner"""
73        # A nice header
74        print(
75            "{:25} {:4} {:>10} {:>10}  {:>10} {:>10}".format(  # pylint: disable=consider-using-f-string
76                "Time", "Pos.", "Price", "ROI", "Valuation", "Accum.ROI"
77            )
78        )
79        # Run the back-testing
80        while self.next():
81            status = self.status()
82            if status.signal.position != Position.STAY:
83                # A nice output
84                print(
85                    f"{status.signal.time} "
86                    + f"{status.signal.position.name:4} "
87                    + f"{status.data['close']:10.2f} "
88                    + f"{status.roi * 100.0:10.2f}% "
89                    + f"{status.portfolio_value:10.2f} "
90                    + f"{status.accumulated_roi * 100.0:10.2f}%"
91                )

Base class

39    def __init__(
40        self,
41        strategy: Strategy,
42        portfolio: Portfolio,
43        datastream: DataStreamer,
44    ):
45        """Init method"""
46        self.datastream = datastream
47        self.portfolio = portfolio
48        self.strategy = strategy

Init method

last_result: Optional[TradingIteration] = None
last_valuation: float = 0.0
def next(self) -> bool:
50    def next(self) -> bool:
51        """Perfoms a trading iteration"""
52        obs = self.datastream.next()
53        if obs is None:
54            return False
55        signal = self.strategy.evaluate(data=obs)
56        self.portfolio.process(signal)
57        self.last_result = TradingIteration(
58            signal=signal,
59            data=obs,
60            roi=roi(self.last_valuation, self.portfolio.valuation()),
61            portfolio_value=self.portfolio.valuation(),
62            accumulated_roi=self.portfolio.accumulated_return(),
63        )
64        self.last_valuation = self.portfolio.valuation()
65        return True

Perfoms a trading iteration

def status(self) -> TradingIteration:
67    def status(self) -> TradingIteration:
68        """Trader last result"""
69        return self.last_result

Trader last result

def run(self):
71    def run(self):
72        """A default runner"""
73        # A nice header
74        print(
75            "{:25} {:4} {:>10} {:>10}  {:>10} {:>10}".format(  # pylint: disable=consider-using-f-string
76                "Time", "Pos.", "Price", "ROI", "Valuation", "Accum.ROI"
77            )
78        )
79        # Run the back-testing
80        while self.next():
81            status = self.status()
82            if status.signal.position != Position.STAY:
83                # A nice output
84                print(
85                    f"{status.signal.time} "
86                    + f"{status.signal.position.name:4} "
87                    + f"{status.data['close']:10.2f} "
88                    + f"{status.roi * 100.0:10.2f}% "
89                    + f"{status.portfolio_value:10.2f} "
90                    + f"{status.accumulated_roi * 100.0:10.2f}%"
91                )

A default runner