Browse Source

feat: 100% coverage

pull/14/head
QuentinN42 8 months ago
parent
commit
fe82d7fa0a
Signed by: number42 GPG Key ID: 2CD7D563712B3A50
  1. 9
      auto_trading/bot.py
  2. 1
      auto_trading/broker/backtest.py
  3. 2
      auto_trading/interfaces.py
  4. 15
      auto_trading/ptf/in_memory.py
  5. 5
      auto_trading/strat/hold.py
  6. 0
      tests/bot/__init__.py
  7. 32
      tests/bot/test_bot_NYSE.py
  8. 3
      tests/broker/test_backtest.py
  9. 2
      tests/indicators/test_dumb.py
  10. 2
      tests/ptf/test_in_memory.py
  11. 15
      tests/strat/test_hold.py

9
auto_trading/bot.py

@ -4,7 +4,7 @@
"""
import logging
from tqdm import tqdm # type: ignore
from typing import List
from typing import Dict
from pandas import DataFrame # type: ignore
@ -38,9 +38,10 @@ class Bot:
self.logger.debug("Get %d orders to execute", len(orders))
self.ptf.execute_multiples(orders)
def _get_error_msg(self, exception: BaseException):
return getattr(exception, "message", getattr(exception, "msg", str(exception)))
@property
def balance(self):
return self.ptf.balance
def total_balance(self, conversion_rate: Dict[str, float]) -> float:
"""Return the current total balance."""
return self.ptf.total_balance(conversion_rate)

1
auto_trading/broker/backtest.py

@ -65,4 +65,5 @@ class Backtest(DataBroker):
try:
return self.data.loc[(self._date0,) : (self.cursor,)].copy # type: ignore
except IndexError:
self._cursor -= 1
raise StopIteration

2
auto_trading/interfaces.py

@ -54,9 +54,9 @@ class DataBroker(ABC):
def current_change(self) -> DataFrame:
"""Return the current change for each money."""
@abstractmethod
def __iter__(self) -> "DataBroker":
"""Initialise the iterator."""
return self
@abstractmethod
def __next__(self) -> DataFrame:

15
auto_trading/ptf/in_memory.py

@ -11,9 +11,16 @@ class InMemoryPortfolio(PTF):
_balance: float
_stocks: Dict[str, float]
def __init__(self, change_rate_getter: Callable[[], Dict[str, float]], **kwargs):
def __init__(
self,
change_rate_getter: Callable[[], Dict[str, float]],
base_balance: float = 0,
**kwargs
):
"""Init the class with a pointer to the bot to retrieve the change rate."""
super().__init__(**kwargs)
self._balance = base_balance
self._stocks = {}
self.change_rate_getter = change_rate_getter
@property
@ -28,7 +35,7 @@ class InMemoryPortfolio(PTF):
"""Sell actions."""
if self._stocks[order.stock] < order.amount:
raise OrderFails("Not enough stock.")
if self.change_rate[order.stock] < order.price:
if self.change_rate.get(order.stock, 0) < order.price:
raise OrderFails("You shell it too high.")
self._balance += order.price * order.amount
@ -38,10 +45,10 @@ class InMemoryPortfolio(PTF):
"""Buy actions."""
if self.balance < order.amount * order.price:
raise OrderFails("Not enough money.")
if self.change_rate[order.stock] > order.price:
if self.change_rate.get(order.stock, 0) > order.price:
raise OrderFails("You buy it too low.")
self._balance -= order.price * order.amount
self._stocks[order.stock] += order.amount
self._stocks[order.stock] = order.amount + self._stocks.get(order.stock, 0)
executors = {Short: execute_short, Long: execute_long}

5
auto_trading/strat/hold.py

@ -33,7 +33,10 @@ class Hold(Strategy):
# if I have some money
if (balance := state.balance) > 0:
# I calculate the value of the stock
market_price = data.iloc[-1].to_dict()[self.to_hold]
market_price = data.loc[data.index[-1][0]].close.to_dict().get(self.to_hold)
if market_price is None:
# retry later
return []
amount = balance / market_price
# and I buy it all at market price
orders.append(Long(stock=self.to_hold, amount=amount, price=market_price))

0
tests/bot/__init__.py

32
tests/bot/test_bot_NYSE.py

@ -0,0 +1,32 @@
import pytest
from auto_trading.broker.backtest import Backtest
from auto_trading.strat.hold import Hold
from auto_trading.ptf.in_memory import InMemoryPortfolio
from auto_trading.bot import Bot
@pytest.fixture
def backtest():
return Backtest("./data/NYSE_smallest.csv")
@pytest.fixture
def ptf(backtest):
return InMemoryPortfolio(
base_balance=100, change_rate_getter=lambda: backtest.current_change
)
@pytest.fixture
def strategy():
return Hold("GOOGL")
def test_bot(ptf, strategy, backtest):
"""Test the bot over a simple csv."""
bot = Bot(ptf, strategy, backtest)
assert bot.balance == 100
bot.run()
assert 104.74 < bot.total_balance(bot.ptf.change_rate) < 104.75
assert bot.balance == 0

3
tests/broker/test_backtest.py

@ -1,8 +1,5 @@
import pytest
import datetime
import json
from pandas import DataFrame # type: ignore
from auto_trading.broker.backtest import Backtest

2
tests/indicators/test_dumb.py

@ -9,7 +9,7 @@ from auto_trading.indicators.dumb import Dumb
@pytest.mark.parametrize(
"value",
[
(Series()),
(Series(dtype=float)),
(Series({"AAPL": 0, "GOOG": 1, "GOOGL": 2})),
],
)

2
tests/ptf/test_in_memory.py

@ -20,7 +20,7 @@ from auto_trading.orders import Long, Short
def test_execute(
change_rate, start_stocks, start_balance, order, stop_stocks, stop_balance
):
ptf = InMemoryPortfolio(lambda: change_rate)
ptf = InMemoryPortfolio(change_rate_getter=lambda: change_rate)
ptf._stocks = start_stocks
ptf._balance = start_balance
ptf.execute_multiples([order])

15
tests/strat/test_hold.py

@ -1,4 +1,5 @@
import pytest
from datetime import datetime
from pandas import DataFrame # type: ignore
from auto_trading.strat.hold import Hold
@ -6,17 +7,25 @@ from auto_trading.interfaces import PTFState as State
from auto_trading.orders import Long
date = datetime.strptime("2015-03-31", "%Y-%m-%d")
@pytest.mark.parametrize(
"data, state, output",
[
(DataFrame(), State(balance=0, stocks={}), None),
(
DataFrame({"AAPL": [10]}),
DataFrame({"close": {(date, "AAPL"): None}}),
State(balance=10, stocks={}),
None,
),
(
DataFrame({"close": {(date, "AAPL"): 10}}),
State(balance=10, stocks={}),
Long(stock="AAPL", amount=1, price=10),
),
(
DataFrame({"AAPL": [10]}),
DataFrame({"close": {(date, "AAPL"): 10}}),
State(
balance=10,
stocks={"AAPL": 5},
@ -24,7 +33,7 @@ from auto_trading.orders import Long
Long(stock="AAPL", amount=1, price=10),
),
(
DataFrame({"AAPL": [10], "TSLA": [20]}),
DataFrame({"close": {(date, "AAPL"): 10, (date, "TSLA"): 10}}),
State(
balance=10,
stocks={"AAPL": 5, "TSLA": 5},

Loading…
Cancel
Save