Compare commits

...

No commits in common. 'master' and 'jonathan' have entirely different histories.

  1. 1
      .gitattributes
  2. 11
      .vscode/settings.json
  3. 22
      README.md
  4. 44
      auto_trading/bot.py
  5. 82
      auto_trading/broker/backtest.py
  6. 35
      auto_trading/errors.py
  7. 26
      auto_trading/indicators/dumb.py
  8. 28
      auto_trading/indicators/ema.py
  9. 45
      auto_trading/indicators/ema2.py
  10. 46
      auto_trading/indicators/slopy.py
  11. 34
      auto_trading/indicators/sma.py
  12. 43
      auto_trading/indicators/sma2.py
  13. 226
      auto_trading/interfaces.py
  14. 40
      auto_trading/main.py
  15. 56
      auto_trading/orders.py
  16. 0
      auto_trading/predictor/__init__.py
  17. 16
      auto_trading/predictor/mean_agg.py
  18. 11
      auto_trading/predictor/normalized.py
  19. 13
      auto_trading/predictor/random_predictor.py
  20. 13
      auto_trading/predictor/selector.py
  21. 76
      auto_trading/ptf/in_memory.py
  22. 27
      auto_trading/strat/all_in.py
  23. 78
      auto_trading/strat/buyupselldown.py
  24. 44
      auto_trading/strat/hold.py
  25. 91
      auto_trading/strat/prop.py
  26. 59
      auto_trading/strat/yoyo.py
  27. 851265
      data/NYSE.csv
  28. 367
      data/NYSE_small.csv
  29. 123
      data/NYSE_smallest.csv
  30. 2602
      data/cac40.csv
  31. 9772
      data/gold.csv
  32. 9772
      data/goldx.csv
  33. 2992
      data/price_history.csv
  34. 4
      docs/diagram.svg
  35. 75
      main.py
  36. 5
      pytest.ini
  37. 3
      requirements.txt
  38. 5
      scripts/install.sh
  39. 5
      scripts/test.sh
  40. 0
      tests/__init__.py
  41. 0
      tests/bot/__init__.py
  42. 32
      tests/bot/test_bot_NYSE.py
  43. 0
      tests/broker/__init__.py
  44. 34
      tests/broker/test_backtest.py
  45. 0
      tests/indicators/__init__.py
  46. 19
      tests/indicators/test_dumb.py
  47. 39
      tests/indicators/test_ema.py
  48. 36
      tests/indicators/test_ema2.py
  49. 57
      tests/indicators/test_slopy.py
  50. 34
      tests/indicators/test_sma.py
  51. 34
      tests/indicators/test_sma2.py
  52. 0
      tests/interfaces/__init__.py
  53. 85
      tests/interfaces/test_ptf.py
  54. 19
      tests/interfaces/test_strategy.py
  55. 0
      tests/orders/__init__.py
  56. 36
      tests/orders/test_orders.py
  57. 0
      tests/ptf/__init__.py
  58. 45
      tests/ptf/test_in_memory.py
  59. 0
      tests/strat/__init__.py
  60. 82
      tests/strat/test_buyUpSellDown.py
  61. 54
      tests/strat/test_hold.py
  62. 68
      tests/strat/test_prop.py
  63. 32
      tests/strat/test_yoyo.py

1
.gitattributes

@ -1 +0,0 @@
data filter=lfs diff=lfs merge=lfs -text

11
.vscode/settings.json

@ -1,11 +0,0 @@
{
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"testOnSave.enabled": true,
"testOnSave.testCommand": "./scripts/test.sh",
"python.testing.pytestArgs": [
"tests"
],
"python.formatting.provider": "black",
"editor.formatOnSave": true,
}

22
README.md

@ -1,23 +1,3 @@
# AutoTrading
Auto trading bot.
## Principe
![dia](docs/diagram.svg)
Il y a trois briques de base :
- Data broker : Recupere les données depuis un site web ou le simule via un CSV
- Strategie : Ce qui choisit si il faut acheter ou vendre des actions en fonction des données d'entrée
- Les indicateurs de la strategie sont calculés automatiquement sur les données d'entrée
- PTF : Ce qui permet d'acheter ou de vendre des actions, peut être interfacé avec une plateforme de trading ou tourner en local
## Use it
# install
./scripts/install.sh
# run
python main.py
# test
./scripts/test.sh
Used [this](https://www.kaggle.com/sudalairajkumar/cryptocurrencypricehistory) dataset but for now just making a mean of the values.

44
auto_trading/bot.py

@ -1,44 +0,0 @@
"""
One script to rule them all, One script to find them,
One script to bring them all and in the darkness bind them
"""
import logging
from tqdm import tqdm # type: ignore
from typing import Dict
from pandas import DataFrame # type: ignore
from .interfaces import DataBroker, Strategy, PTF
class Bot:
"""The class that wrap all the classes together.
For more information, read /documentation/Diagramme.svg
"""
def __init__(self, ptf: PTF, strategy: Strategy, broker: DataBroker):
"""Initialize the bot."""
self.logger = logging.getLogger(self.__class__.__name__)
self.ptf = ptf
self.strategy = strategy
self.broker = broker
def run(self):
"""run the bot"""
for data in tqdm(self.broker):
self.run_once(data)
def run_once(self, data: DataFrame):
"""run the bot once"""
orders = self.strategy.run(data, self.ptf.state)
self.logger.debug("Get %d orders to execute", len(orders))
self.ptf.execute_multiples(orders)
@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)

82
auto_trading/broker/backtest.py

@ -1,76 +1,22 @@
import datetime
import pandas as pd # type: ignore
from ..interfaces import DataBroker, CandlesProperties
from ..interfaces import Broker
class Backtest(DataBroker):
class Backtest(Broker):
"""
Backtest Broker
"""
def __init__(self, f_name: str, start: int = 0, **kwargs) -> None:
def __init__(self, f_name: str, start=0, **kwargs) -> None:
"""Read the csv file and store it into a dataframe"""
super().__init__()
self.data = self._prepare_data(f_name, **kwargs)
self.dates = self.data.index.levels[0]
self._date0 = self.dates[0]
self.step = self.calc_step()
self._cursor = start
self.start = start - 1
def calc_step(self) -> datetime.datetime:
return min(self.dates[i] - self.dates[i - 1] for i in range(1, 10))
@property
def cursor(self) -> datetime.datetime:
return self.dates[self._cursor]
@property
def change_rate_history(self) -> pd.DataFrame:
return self.data.close.unstack(level=1)
@staticmethod
def _prepare_data(f_name: str, **kwargs) -> pd.DataFrame:
data = pd.read_csv(f_name, index_col=[0, 1], parse_dates=True, **kwargs)
data.sort_index(inplace=True)
data.fillna(method="ffill", inplace=True)
return data
@property
def properties(self) -> CandlesProperties:
"""Return the properties of the candles for this broker."""
return CandlesProperties(period=self.step)
@property
def current_change(self) -> pd.DataFrame:
"""Return the current change for each money."""
return self.data.loc[(self.cursor,)]["close"].to_dict()
def __iter__(self) -> "DataBroker":
"""Initialise the iterator."""
self._cursor = self.start
return self
@property
def __next__(self) -> pd.DataFrame:
"""Next values.
Return the dataframe of all stock history for the strategy / indicators.
Returns:
DataFrame: Time-Stock valuated candlestick data.
For each time and each stock give (high, low, open, close).
"""
self._cursor += 1
try:
return self.data.loc[(self._date0,) : (self.cursor,)].copy # type: ignore
except IndexError:
self._cursor -= 1
raise StopIteration
def __len__(self) -> int:
return len(self.dates) - self.start
self.data = pd.read_csv(f_name, **kwargs).fillna(method="ffill")
self.curcor = start
def __bool__(self):
return self.curcor < len(self.data)
def next(self):
"""Return the next row of the dataframe"""
self.curcor += 1
return self.data.iloc[:self.curcor].copy()

35
auto_trading/errors.py

@ -1,35 +0,0 @@
"""Some errors to catch."""
class TradingException(Exception):
"""An error connected to the trading logic."""
message: str
def __init__(self, message):
super().__init__(message)
self.message = message
class OrderException(TradingException):
"""An order execution was unsuccessfull."""
class UnknowOrder(OrderException):
"""The exectuor can't process this order."""
class OrderFails(OrderException):
"""The order placement failed."""
class PTFException(TradingException):
"""An error occur inside the PTF."""
class StrategyError(TradingException):
"""An error linked to a strategy was encountered."""
class StrategyInitError(StrategyError):
"""An error occurred during the strategy initialization."""

26
auto_trading/indicators/dumb.py

@ -1,26 +0,0 @@
"""Dumb indicator, for testing purposes."""
import pandas as pd # type: ignore
from ..interfaces import Indicator
class Dumb(Indicator):
"""Replay the value."""
def __init__(self, value: pd.Series):
"""Save the value."""
super().__init__()
self.value = value
def __call__(self, data: pd.DataFrame) -> pd.Series:
"""Return a dataframe of valuation of each stock from the input data.
Args:
data (DataFrame): Time-Stock valuated candlestick data.
For each time and each stock give (high, low, open, close).
Returns:
DataFrame: Stock valuated float.
For each stock give -1 if realy bad and +1 if realy good.
"""
return self.value

28
auto_trading/indicators/ema.py

@ -1,28 +0,0 @@
"""EMA indicator."""
import pandas as pd # type: ignore
from ..interfaces import Indicator
class EMA(Indicator):
"""
https://stackoverflow.com/questions/48613151/simple-python-pandas-ema-ewma
"""
def __init__(self, nb_values: int):
"""Save the value."""
super().__init__()
self.nb_values = nb_values
def __call__(self, data: pd.DataFrame) -> pd.Series:
"""Return a dataframe of valuation of each stock from the input data.
Args:
data (DataFrame): Time-Stock valuated candlestick data.
For each time and each stock give (high, low, open, close).
Returns:
DataFrame: Stock valuated float.
For each stock give -1 if realy bad and +1 if realy good.
"""
return data.close.unstack().ewm(self.nb_values).mean().loc[data.index[-1][0]]

45
auto_trading/indicators/ema2.py

@ -1,45 +0,0 @@
"""EMA indicator, not ready yet."""
import logging
import pandas as pd # type: ignore
from ..interfaces import Indicator
class EMA(Indicator):
"""Replay the value."""
def __init__(self, alpha: float):
"""Save the value."""
super().__init__()
self.alpha = alpha
def __call__(self, data: pd.DataFrame) -> pd.Series:
"""Return a dataframe of valuation of each stock from the input data.
Args:
data (DataFrame): Time-Stock valuated candlestick data.
For each time and each stock give (high, low, open, close).
Returns:
DataFrame: Stock valuated float.
For each stock give -1 if realy bad and +1 if realy good.
"""
# only use date as index => actions become columns
data2 = data.unstack()
# select high prices for each action
data3 = data2.get("close")
if data3 is None or len(data3) <= 1:
return pd.Series([])
logging.info(data3)
# apply ema
ema = data3.ewm(alpha=self.alpha).mean().fillna(0)
# computes slope at each point of the sma
emaDiff = ema.diff().fillna(0)
res = emaDiff.iloc[-1]
return pd.Series(res)

46
auto_trading/indicators/slopy.py

@ -1,46 +0,0 @@
"""+1 when going up, -1 when going down, 0 when not moving enough."""
import pandas as pd # type: ignore
from ..interfaces import Indicator
class Slopy(Indicator):
"""Replay the value."""
def __init__(self):
"""Save the value."""
super().__init__()
def __call__(self, data: pd.DataFrame) -> pd.Series:
"""Return a dataframe of valuation of each stock from the input data.
Args:
data (DataFrame): Time-Stock valuated candlestick data.
For each time and each stock give (high, low, open, close).
Returns:
DataFrame: Stock valuated float.
For each stock give -1 if realy bad and +1 if realy good.
"""
# only use date as index => actions become columns
data2 = data.unstack()
# select high prices for each action
highData = data2["close"]
coeffs = highData.diff().fillna(0)
res = {}
for column in coeffs.columns:
# pour chaque type d'action
trust = coeffs[column].get(coeffs.index[-1])
# on clamp l'indicateur entre [-1, 1]
trust = min(trust, 1.0)
trust = max(trust, -1.0)
res[column] = trust
return pd.Series(res)

34
auto_trading/indicators/sma.py

@ -1,34 +0,0 @@
"""EMA indicator."""
import pandas as pd # type: ignore
from ..interfaces import Indicator
class SMA(Indicator):
"""
https://stackoverflow.com/questions/48613151/simple-python-pandas-ema-ewma
"""
def __init__(self, nb_values: int):
"""Save the value."""
super().__init__()
self.nb_values = nb_values
def __call__(self, data: pd.DataFrame) -> pd.Series:
"""Return a dataframe of valuation of each stock from the input data.
Args:
data (DataFrame): Time-Stock valuated candlestick data.
For each time and each stock give (high, low, open, close).
Returns:
DataFrame: Stock valuated float.
For each stock give -1 if realy bad and +1 if realy good.
"""
return (
data.close.unstack()
.rolling(self.nb_values)
.mean()
.fillna(0)
.loc[data.index[-1][0]]
)

43
auto_trading/indicators/sma2.py

@ -1,43 +0,0 @@
"""SMA indicator, not ready yet."""
import pandas as pd # type: ignore
from ..interfaces import Indicator
import logging
class SMA(Indicator):
"""Replay the value."""
def __init__(self, windowSize: int):
"""Save the value."""
super().__init__()
self.windowSize = windowSize
def __call__(self, data: pd.DataFrame) -> pd.Series:
"""Return a dataframe of valuation of each stock from the input data.
Args:
data (DataFrame): Time-Stock valuated candlestick data.
For each time and each stock give (high, low, open, close).
Returns:
DataFrame: Stock valuated float.
For each stock give -1 if realy bad and +1 if realy good.
"""
# only use date as index => actions become columns
data2 = data.unstack()
# select high prices for each action
data3 = data2.get("close")
if data3 is None or len(data3) <= 1:
return pd.Series([])
# apply sma
sma = data3.rolling(window=self.windowSize).mean().fillna(0)
# computes slope at each point of the sma
smaDiff = sma.diff().fillna(0)
res = smaDiff.iloc[-1]
#logging.info(res)
return pd.Series(res)

226
auto_trading/interfaces.py

@ -1,222 +1,54 @@
"""
Define basic interfaces for trading strategies.
"""
import logging
from abc import ABC, abstractmethod
from abc import ABC, abstractmethod, abstractproperty
from dataclasses import dataclass, field
from datetime import datetime
from typing import Dict, List, Optional, Any, Callable
import pandas as pd # type: ignore
from pandas import DataFrame, Timedelta, Series # type: ignore
from .errors import OrderException, UnknowOrder, PTFException
@dataclass
class PTFState:
"""A fixed state of a PTF (read only)."""
balance: float
stocks: Dict[str, float]
# TODO: stocker le conversion rate à l'instant t pour facilement calculer le total balance
# conversion_rate: Dict[str, float]
def total_balance(self, conversion_rate: Dict[str, float]) -> float:
return self.balance + sum(
conversion_rate[stock_name] * amount
for stock_name, amount in self.stocks.items()
)
def to_dict(self):
return {"USD": self.balance, **self.stocks}
@dataclass
class Order:
"""An order to execute on a market."""
successfull: Optional[bool] = field(default=None, init=False)
creation_date: datetime = field(default_factory=datetime.now, init=False)
@dataclass
class CandlesProperties:
period: Timedelta
class DataBroker(ABC):
"""Somethink that give you data."""
def __init__(self):
"""Init the class."""
self.logger = logging.getLogger(self.__class__.__name__)
@abstractproperty
def properties(self) -> CandlesProperties:
"""Return the properties of the candles for this broker."""
@abstractproperty
def current_change(self) -> DataFrame:
"""Return the current change for each money."""
class Portfolio(ABC):
"""How may of each money do I have ?"""
@abstractmethod
def __iter__(self) -> "DataBroker":
"""Initialise the iterator."""
def content(self) -> dict:
"""return the content in each currency"""
@abstractmethod
def __next__(self) -> DataFrame:
"""Next values.
Return the dataframe of all stock history for the strategy / indicators.
Returns:
DataFrame: Time-Stock valuated candlestick data.
For each time and each stock give (high, low, open, close).
"""
def widraw(self, amount: int, currency: str) -> None:
"""Withdraw money from the portfolio"""
@abstractmethod
def __len__(self) -> int:
"""Total number of values"""
class Indicator(ABC):
"""Somethink that give you an insight of the market."""
def __init__(self):
"""Init the class."""
self.logger = logging.getLogger(self.__class__.__name__)
def deposit(self, amount: int, currency: str) -> None:
"""Deposit money into the portfolio"""
@abstractmethod
def __call__(self, data: DataFrame) -> Series:
"""Return a dataframe of valuation of each stock from the input data.
def convert(self, amount: int, to_amount: int, currency: str, to_currency: str) -> None:
"""Convert money from one currency to another"""
Args:
data (DataFrame): Time-Stock valuated candlestick data.
For each time and each stock give (high, low, open, close).
Returns:
DataFrame: Stock valuated float.
For each stock give -1 if realy bad and +1 if realy good.
"""
class Strategy(ABC):
"""When do I buy and how many ?"""
@abstractmethod
def run(self, ptf: Portfolio, result: dict, current_conversion_rate: dict) -> None:
"""Run the strategy"""
class Strategy(ABC):
"""What order should you take on the market."""
indicators: Dict[str, Indicator]
class Broker(ABC):
"""Return the data"""
def __init__(self, indicators: Dict[str, Indicator] = None):
"""Init the class with some inticators."""
self.logger = logging.getLogger(self.__class__.__name__)
self.indicators = indicators or {}
@abstractmethod
def __bool__(self):
"""Return True if the broker has data to retrive"""
def run(self, data: DataFrame, state: PTFState) -> List[Order]:
"""Execute the strategy from the data.
@abstractmethod
def next(self) -> pd.DataFrame:
"""Return the next data to process"""
Args:
data (DataFrame): The Data broker output.
For each time and each stock give (high, low, open, close).
Returns:
List[Order]: A list of orders to execute.
"""
indicators_results = DataFrame(
{k: v(data) for k, v in self.indicators.items()}
).T
return self.execute(data, indicators_results, state)
class Predictor(ABC):
"""What is the future ?"""
@abstractmethod
def execute(
self, data: DataFrame, indicators_results: DataFrame, ptf_state: PTFState
) -> List[Order]:
"""Execute the strategy with the indicators insights.
Args:
data (DataFrame): The Data broker output.
For each time and each stock give (high, low, open, close).
indicators_results (DataFrame): Indicator-Stock valuated float.
For each indicator and each stock give -1 if realy bad and +1 if realy good.
Returns:
List[Order]: A list of orders to execute.
"""
class PTF(ABC):
"""Somethink that buy or sell stocks."""
executors: Dict[Any, Callable[[Any, Any], None]] = {}
orders_history: List[Order]
states_history: DataFrame
def __init__(self, skip_errors: bool = True, save_errors: bool = True):
"""Init the class.
Args:
skip_errors (bool, optional): Do we skip orders in failure ?
Defaults to True.
save_errors (bool, optional): Do we save orders in failure in the orders_history ?
Defaults to True.
"""
self.logger = logging.getLogger(self.__class__.__name__)
self.orders_history = []
self.states_history = DataFrame()
self.skip_errors = skip_errors
self.save_errors = save_errors
@abstractproperty
def state(self) -> PTFState:
"""Return the current state."""
@property
def balance(self) -> float:
"""Return the current total balance."""
return self.state.balance
def total_balance(self, conversion_rate: Dict[str, float]) -> float:
"""Return the current total balance."""
return self.state.total_balance(conversion_rate)
def execute_multiples(self, orders: List[Order]) -> None:
"""Execute all orders
Args:
orders (List[Order]): The list of all orders to execute.
"""
for order in orders:
self.logger.debug("Applying order %s...", order)
try:
self._execute(order)
order.successfull = True
except OrderException as e:
if not self.skip_errors:
raise PTFException(f"Got and order exception : {e.message}") from e
self.logger.warning("Got an order exception : %s", e.message)
order.successfull = False
if self.save_errors or order.successfull:
self.orders_history.append(order)
# self.states_history.append(self.state.to_dict(), ignore_index=True)
self.states_history = self.states_history.append(
self.state.to_dict(), ignore_index=True
)
def _execute(self, order: Order) -> None:
"""Execute one order.
Try to execute the order on the market.
Raises:
OrderError: if the execution was unsuccessfull.
Args:
order (Order): One order to execute.
"""
for order_type, executor in self.executors.items():
if isinstance(order, order_type):
executor(self, order)
return
raise UnknowOrder(f"Can not process order of type {type(order)}")
def predict(self, data: pd.DataFrame) -> dict:
"""Return the prediction"""

40
auto_trading/main.py

@ -0,0 +1,40 @@
"""
One script to rule them all, One script to find them,
One script to bring them all and in the darkness bind them
"""
from .interfaces import Portfolio, Strategy, Broker, Predictor
class Bot:
"""the main class"""
def __init__(self, ptf: Portfolio, strategy: Strategy, broker: Broker, predictor: Predictor):
"""initialize the bot"""
self.ptf = ptf
self.strategy = strategy
self.broker = broker
self.predictor = predictor
self._current_conversion_rate = None
self._balance = 0 # USD$
def run(self):
"""run the bot"""
while self.broker:
self.run_once()
def run_once(self):
"""run the bot once"""
data = self.broker.next()
self._current_conversion_rate = data.iloc[-1].fillna(0).to_dict()
self.strategy.run(self.ptf, self.predictor.predict(data), self._current_conversion_rate)
def print_results(self):
"""print the results"""
for k, v in self.ptf.content().items():
print(f"{k}: {v:e}")
def balance(self):
money = 0
for k, v in self.ptf.content().items():
money += self._current_conversion_rate[k] * v
return money

56
auto_trading/orders.py

@ -1,56 +0,0 @@
"""Some orders on the market."""
from dataclasses import dataclass
from .interfaces import Order
@dataclass
class Long(Order):
"""Buy stock."""
stock: str
amount: float
price: float
@property
def amount_usd(self) -> float:
"""The amount in $"""
return self.amount * self.price
def __str__(self) -> str:
return f"{self.__class__.__name__}({self.stock}: {self.amount_usd}$)"
def __eq__(self, other) -> bool:
if not isinstance(other, Long):
return False
return (
self.stock == other.stock
and self.amount == other.amount
and self.price == other.price
)
@dataclass
class Short(Order):
"""Buy stock."""
stock: str
amount: float
price: float
@property
def amount_usd(self) -> float:
"""The amount in $"""
return self.amount * self.price
def __str__(self) -> str:
return f"{self.__class__.__name__}({self.stock}: {self.amount_usd}$)"
def __eq__(self, other) -> bool:
if not isinstance(other, Short):
return False
return (
self.stock == other.stock
and self.amount == other.amount
and self.price == other.price
)

0
auto_trading/indicators/__init__.py → auto_trading/predictor/__init__.py

16
auto_trading/predictor/mean_agg.py

@ -0,0 +1,16 @@
import pandas as pd # type: ignore
from ..interfaces import Predictor
class MeanAggregator(Predictor):
"""Aggregate multiples predictors."""
def __init__(self, predictors):
"""Initialize MeanAggregator."""
self.predictors = predictors
def predict(self, data: pd.DataFrame) -> dict:
"""Predict from others predictors."""
preds = [cls.predict(data) for cls in self.predictors]
return pd.DataFrame(preds).mean().to_dict()

11
auto_trading/predictor/normalized.py

@ -0,0 +1,11 @@
import pandas as pd # type: ignore
from ..interfaces import Predictor
class NormalizedPredictor(Predictor):
def predict(self, data: pd.DataFrame) -> dict:
"""It's just random"""
df = data.iloc[-1]
df = df/df.sum()
return df.to_dict()

13
auto_trading/predictor/random_predictor.py

@ -0,0 +1,13 @@
from random import random
import pandas as pd # type: ignore
from ..interfaces import Predictor
class RandomPredictor(Predictor):
def predict(self, data: pd.DataFrame) -> dict:
"""It's just random"""
return {
k: 1
for k in data.columns
}

13
auto_trading/predictor/selector.py

@ -0,0 +1,13 @@
import pandas as pd # type: ignore
from ..interfaces import Predictor
class SelectorPredictor(Predictor):
def __init__(self, to_return) -> None:
self.to_return = to_return
def predict(self, data: pd.DataFrame) -> dict:
"""It's just random"""
return {k:0 for k in data.to_dict().keys()} | self.to_return

76
auto_trading/ptf/in_memory.py

@ -1,54 +1,28 @@
from typing import Dict, Callable
from ..interfaces import Portfolio
from ..interfaces import PTF, PTFState
from ..orders import Long, Short
from ..errors import OrderFails
class InMemoryPortfolio(PTF):
class InMemoryPortfolio(Portfolio):
"""Just store the value in memory."""
_balance: float
_stocks: Dict[str, float]
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
def state(self):
return PTFState(self._balance, self._stocks.copy())
@property
def change_rate(self) -> Dict[str, float]:
return self.change_rate_getter()
def execute_short(self, order: Short) -> None:
"""Sell actions."""
if (self._stocks.get(order.stock) or 0) < order.amount:
raise OrderFails("Not enough stock.")
if self.change_rate.get(order.stock, 0) < order.price:
raise OrderFails("You shell it too high.")
self._balance += order.price * order.amount
self._stocks[order.stock] -= order.amount
def execute_long(self, order: Long) -> None:
"""Buy actions."""
if self.balance < order.amount * order.price:
raise OrderFails("Not enough money.")
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.get(order.stock, 0)
executors = {Short: execute_short, Long: execute_long}
def __init__(self, currency: dict) -> None:
"""Define the ptf with some starting currency"""
self.currency = currency
def content(self) -> dict:
"""return the content in each currency"""
return self.currency
def widraw(self, amount: int, currency: str) -> None:
"""Withdraw money from the portfolio"""
if self.currency[currency] < amount:
raise ValueError("Not enough money")
self.currency[currency] -= amount
def deposit(self, amount: int, currency: str) -> None:
"""Deposit money into the portfolio"""
self.currency[currency] += amount
def convert(self, amount: int, to_amount: int, currency: str, to_currency: str) -> None:
"""Convert money from one currency to another"""
self.widraw(amount, currency)
self.deposit(to_amount, to_currency)

27
auto_trading/strat/all_in.py

@ -0,0 +1,27 @@
from ..interfaces import Strategy, Portfolio
import numpy as np
class AllIn(Strategy):
"""Just all in the greatest result"""
def __init__(self):
"""Initialise the anti strat"""
def run(self, ptf: Portfolio, result: dict, current_conversion_rate: dict) -> None:
"""Run the strategy"""
# first sell all the stocks
money = 0
for stock, amount in ptf.content().items():
#print(f"{stock}: {amount}")
if amount > 0:
money += current_conversion_rate[stock]*amount
ptf.widraw(amount, stock)
# after get the greatest result
result = {k:-1 if np.isnan(v) else v for k,v in result.items() }
epsilon = 1e-6
greatest = max(result, key=lambda k: result[k]-(np.inf if current_conversion_rate[k]<epsilon else 0))
# then buy all the greatest result
to_add = money/current_conversion_rate[greatest]
ptf.deposit(to_add, greatest)

78
auto_trading/strat/buyupselldown.py

@ -1,78 +0,0 @@
import logging
from typing import List
import pandas as pd # type: ignore
from ..indicators.ema2 import EMA
from ..indicators.sma2 import SMA
from ..orders import Long, Short
from ..interfaces import Strategy, Order, PTFState
class BuyUpSellDown(Strategy):
"""Buy when indicators are green, sell when red."""
def __init__(self):
"""Init the class"""
super().__init__()
self.indicators = {
"ema": EMA(alpha=0.6),
"sma": SMA(windowSize=5)
}
def execute(
self, data: pd.DataFrame, indicators_results: pd.DataFrame, state: PTFState
) -> List[Order]:
"""Just hold the value [to_hold].
Args:
data (DataFrame): The Data broker output.
For each time and each stock give (high, low, open, close).
indicators_results (DataFrame): Indicator-Stock valuated float.
For each indicator and each stock give -1 if realy bad and +1 if realy good.
Returns:
List[Order]: A list of orders to execute.
"""
nb_stock_type = len(indicators_results.columns)
orders = []
# if I have some money
for stock_name in indicators_results.columns:
# for each stock
# I calculate the value of the stock
market_price = data.loc[data.index[-1][0]].close.to_dict().get(stock_name)
### compute trust
trust = 0
for (index, row) in indicators_results.iterrows():
trust += row[stock_name]
if market_price is None:
# retry later
continue
if trust > 0.1:
if (balance := state.balance) > 0:
amount = balance / (market_price * nb_stock_type)
# and I buy it all at market price
orders.append(
Long(stock=stock_name, amount=amount, price=market_price)
)
else:
print("A PU DE THUNE")
elif trust < -0.1:
# and I sell it all at market price
if state.stocks.get(stock_name) is not None:
orders.append(
Short(
stock=stock_name,
amount=state.stocks[stock_name],
price=market_price,
)
)
return orders

44
auto_trading/strat/hold.py

@ -1,44 +0,0 @@
from typing import List
import pandas as pd # type: ignore
from ..orders import Long
from ..interfaces import Strategy, Order, PTFState
class Hold(Strategy):
"""Just hold some stock."""
def __init__(self, to_hold: str):
"""Init the class"""
super().__init__()
self.to_hold = to_hold
def execute(
self, data: pd.DataFrame, indicators_results: pd.DataFrame, state: PTFState
) -> List[Order]:
"""Just hold the value [to_hold].
Args:
data (DataFrame): The Data broker output.
For each time and each stock give (high, low, open, close).
indicators_results (DataFrame): Indicator-Stock valuated float.
For each indicator and each stock give -1 if realy bad and +1 if realy good.
Returns:
List[Order]: A list of orders to execute.
"""
orders = []
# if I have some money
if (balance := state.balance) > 0:
# I calculate the value of the stock
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))
return orders # type: ignore

91
auto_trading/strat/prop.py

@ -1,91 +0,0 @@
from typing import List, Dict, Union
import pandas as pd # type: ignore
from ..orders import Long, Short
from ..interfaces import Strategy, Order, PTFState, Indicator
from ..errors import StrategyInitError
class Prop(Strategy):
"""Proportional repartion from the indicator."""
def __init__(self, indicators: Dict[str, Indicator] = None, max_delta: float = 10):
"""Init the class.
Only have one indicator.
The max delta is the maximum difference between the actual and the desired state.
"""
if not indicators or len(indicators) != 1:
raise StrategyInitError(
"This strat must be initialized with one indicator."
)
super().__init__(indicators=indicators)
self.indicator = list(indicators.keys())[0]
self.max_delta = max_delta
def filter_order(self, order: Union[Long, Short]) -> bool:
"""Return if the order value is high enough."""
return order.amount_usd > self.max_delta
def execute(
self, data: pd.DataFrame, indicators_results: pd.DataFrame, state: PTFState
) -> List[Order]:
"""Just hold the value [to_hold].
Args:
data (DataFrame): The Data broker output.
For each time and each stock give (high, low, open, close).
indicators_results (DataFrame): Indicator-Stock valuated float.
For each indicator and each stock give -1 if realy bad and +1 if realy good.
Returns:
List[Order]: A list of orders to execute.
"""
orders: List[Union[Long, Short]] = []
conversion_rate = data.loc[data.index[-1][0]].close.to_dict()
results = indicators_results.T[self.indicator]
stock_to_sell = []
# Remove all action that the indicator place as negative.
for stock in results[results < 0].index:
order = Short(stock, state.stocks[stock], conversion_rate[stock])
if self.filter_order(order):
orders.append(order)
stock_to_sell.append(stock)
total_money = state.balance + sum(
conversion_rate[stock_name] * amount
for stock_name, amount in state.stocks.items()
if results[stock] > 0 or stock_name in stock_to_sell
)
# Desired state
desired_state_precent = results[results > 0] / results[results > 0].sum()
desired_state_dolards = total_money * desired_state_precent
# Create the new buy orders from with the delta between the actual and the desired state.
for stock, amount in desired_state_dolards.items():
if stock in state.stocks:
if amount > state.stocks[stock]:
orders.append(
Long(
stock,
amount / conversion_rate[stock] - state.stocks[stock],
conversion_rate[stock],
)
)
else:
orders.append(
Short(
stock,
state.stocks[stock] - amount / conversion_rate[stock],
conversion_rate[stock],
)
)
# Filter low orders
orders = list(filter(self.filter_order, orders))
return orders # type: ignore

59
auto_trading/strat/yoyo.py

@ -1,59 +0,0 @@
"""Yoyo strategy."""
from auto_trading.interfaces import Strategy, Order, PTFState
from auto_trading.orders import Long, Short
from typing import List, Dict, Optional
import pandas as pd # type: ignore
def is_positive(number: Optional[float]) -> bool:
"""Return True if the price is positive."""
return number is not None and number > 0
class Yoyo(Strategy):
"""A strat that buy a stock one time then sell it."""
def __init__(self, stock_name: str):
super().__init__()
self.stock_name = stock_name
def execute(
self,
data: pd.DataFrame,
indicators_results: pd.DataFrame,
ptf_state: PTFState,
) -> List[Order]:
try:
market_price = (
data.loc[data.index[-1][0]].close.to_dict().get(self.stock_name)
)
except:
return []
if not market_price:
return []
todo: Order
if self.as_stocks(ptf_state.stocks):
self.logger.info("sell")
todo = self.create_sell_order(ptf_state, market_price)
else:
self.logger.info("buy")
todo = self.create_buy_order(ptf_state, market_price)
return [todo]
def as_stocks(self, stocks: Dict[str, float]) -> bool:
"""Check if we currently have some stocks to sell."""
return is_positive(stocks.get(self.stock_name))
def create_sell_order(self, ptf: PTFState, current_price: float) -> Order:
"""Create a sell order."""
return Short(self.stock_name, ptf.stocks[self.stock_name], current_price)
def create_buy_order(self, ptf: PTFState, current_price: float) -> Order:
"""Create a buy order."""
return Long(self.stock_name, ptf.balance / current_price, current_price)

851265
data/NYSE.csv

File diff suppressed because it is too large

367
data/NYSE_small.csv

@ -1,367 +0,0 @@
date,symbol,open,close,low,high,volume
2015-01-02,AZO,623.97998,616.789978,613.48999,623.97998,224600.0
2015-01-02,CMG,686.0,678.400024,671.01001,687.469971,324800.0
2015-01-02,GOOG,529.012399,524.812404,524.102388,531.272382,1447500.0
2015-01-02,GOOGL,532.599976,529.549988,527.880005,535.799988,1324000.0
2015-01-02,ISRG,530.880005,525.570007,521.309998,535.799988,178200.0
2015-01-02,PCLN,1144.0,1142.060059,1131.51001,1149.439941,509300.0
2015-01-05,AZO,614.460022,608.48999,605.700012,616.109985,439800.0
2015-01-05,CMG,678.400024,667.690002,664.200012,679.919983,374400.0
2015-01-05,GOOG,523.262377,513.872306,513.062315,524.332389,2059800.0
2015-01-05,GOOGL,527.150024,519.460022,517.75,527.98999,2059100.0
2015-01-05,ISRG,522.780029,514.369995,513.789978,523.48999,430200.0
2015-01-05,PCLN,1138.369995,1097.579956,1090.73999,1138.369995,1051300.0
2015-01-06,AZO,610.77002,607.820007,599.809998,612.340027,388500.0
2015-01-06,CMG,666.789978,664.380005,653.77002,672.0,609400.0
2015-01-06,GOOG,515.002358,501.962262,501.052266,516.177334,2899900.0
2015-01-06,GOOGL,520.5,506.640015,505.549988,521.210022,2722800.0
2015-01-06,ISRG,523.400024,519.789978,514.390015,524.429993,281600.0
2015-01-06,PCLN,1103.410034,1079.959961,1072.5,1103.97998,1081500.0
2015-01-07,AZO,616.0,607.23999,605.590027,616.0,388200.0
2015-01-07,CMG,668.400024,694.26001,668.400024,694.849976,685600.0
2015-01-07,GOOG,507.002299,501.102268,499.652247,507.246285,2065000.0
2015-01-07,GOOGL,510.950012,505.149994,503.649994,511.48999,2345900.0
2015-01-07,ISRG,523.780029,522.640015,521.200012,532.900024,377200.0
2015-01-07,PCLN,1090.77002,1069.569946,1058.569946,1092.069946,955000.0
2015-01-08,AZO,610.359985,606.0,605.070007,614.969971,593300.0
2015-01-08,CMG,702.5,719.98999,702.5,727.969971,1169900.0
2015-01-08,GOOG,497.992268,502.682285,491.002212,503.48227,3353500.0
2015-01-08,GOOGL,501.51001,506.910004,495.019989,507.5,3652700.0
2015-01-08,ISRG,528.599976,533.0,523.780029,536.700012,406600.0
2015-01-08,PCLN,1056.949951,1082.849976,1047.119995,1083.170044,1045800.0
2015-01-09,AZO,608.330017,606.02002,600.039978,608.609985,473600.0
2015-01-09,CMG,721.849976,714.27002,711.960022,722.169983,404300.0
2015-01-09,GOOG,504.7623,496.172244,494.792239,504.922285,2071300.0
2015-01-09,GOOGL,508.179993,500.720001,498.649994,508.600006,2100000.0
2015-01-09,ISRG,530.969971,520.869995,520.47998,536.26001,327200.0
2015-01-09,PCLN,1083.5,1051.959961,1051.640015,1085.089966,974400.0
2015-01-12,AZO,606.840027,597.359985,596.559998,609.030029,163000.0
2015-01-12,CMG,718.890015,711.700012,709.47998,719.0,276800.0
2015-01-12,GOOG,494.942247,492.552239,487.562205,495.97823,2326700.0
2015-01-12,GOOGL,499.23999,497.059998,490.910004,500.279999,2856900.0
2015-01-12,ISRG,522.830017,525.51001,514.919983,526.299988,319900.0
2015-01-12,PCLN,1054.689941,1039.97998,1021.01001,1054.689941,1078500.0
2015-01-13,AZO,599.380005,588.950012,583.549988,603.219971,252800.0
2015-01-13,CMG,715.73999,714.059998,707.159973,724.47998,348100.0
2015-01-13,GOOG,498.842256,496.182251,492.392224,502.982272,2370400.0
2015-01-13,GOOGL,502.570007,501.799988,497.26001,508.600006,3047900.0
2015-01-13,ISRG,532.0,521.659973,516.73999,534.0,477400.0
2015-01-13,PCLN,1052.449951,1037.819946,1027.800049,1067.02002,871100.0
2015-01-14,AZO,584.590027,582.780029,577.929993,586.159973,316400.0
2015-01-14,CMG,703.210022,709.73999,700.0,713.02002,318600.0
2015-01-14,GOOG,494.652237,500.872267,493.002234,503.232286,2215500.0
2015-01-14,GOOGL,500.420013,505.929993,498.160004,508.26001,2566700.0
2015-01-14,ISRG,521.309998,535.359985,518.97998,536.849976,321900.0
2015-01-14,PCLN,1026.150024,1035.670044,1022.01001,1039.660034,747300.0
2015-01-15,AZO,584.080017,575.280029,574.919983,586.530029,196500.0
2015-01-15,CMG,709.0,700.780029,698.109985,715.099976,344600.0
2015-01-15,GOOG,505.572291,501.792271,497.762267,505.682303,2715800.0
2015-01-15,GOOGL,508.890015,504.01001,502.01001,509.75,2553400.0
2015-01-15,ISRG,535.119995,524.380005,522.440002,538.090027,247400.0
2015-01-15,PCLN,1041.060059,998.25,995.679993,1042.5,1451700.0
2015-01-16,AZO,575.190002,580.849976,569.539978,581.119995,340500.0
2015-01-16,CMG,696.570007,711.109985,696.570007,712.22998,316100.0
2015-01-16,GOOG,500.012273,508.082288,500.002267,508.1923,2298200.0
2015-01-16,GOOGL,503.149994,510.459991,503.089996,510.850006,2482900.0
2015-01-16,ISRG,522.960022,530.630005,519.390015,531.789978,265500.0
2015-01-16,PCLN,997.01001,1008.219971,990.690002,1012.309998,1004800.0
2015-01-20,AZO,582.799988,584.330017,578.049988,586.960022,201200.0
2015-01-20,CMG,714.890015,705.780029,702.5,716.0,340900.0
2015-01-20,GOOG,511.002313,506.902294,506.018277,512.502307,2232000.0
2015-01-20,GOOGL,512.77002,509.940002,509.369995,515.609985,2339800.0
2015-01-20,ISRG,534.190002,521.890015,518.590027,534.950012,271400.0
2015-01-20,PCLN,1016.450012,1024.949951,999.0,1028.01001,700300.0
2015-01-21,AZO,581.539978,586.059998,579.090027,588.440002,332500.0
2015-01-21,CMG,704.400024,703.890015,702.059998,710.52002,307500.0
2015-01-21,GOOG,507.252283,518.042373,506.202284,519.282346,2268700.0
2015-01-21,GOOGL,510.839996,520.390015,509.589996,521.849976,2317800.0
2015-01-21,ISRG,517.919983,518.119995,516.02002,527.130005,247200.0
2015-01-21,PCLN,1024.0,1045.530029,1020.0,1049.550049,732400.0
2015-01-22,AZO,587.73999,600.099976,586.409973,602.679993,253600.0
2015-01-22,CMG,706.809998,713.440002,703.539978,715.179993,301300.0
2015-01-22,GOOG,521.482349,534.392388,519.702382,536.332401,2676900.0
2015-01-22,GOOGL,523.0,537.299988,521.909973,538.840027,2803400.0
2015-01-22,ISRG,518.309998,525.380005,516.070007,526.539978,505500.0
2015-01-22,PCLN,1048.5,1049.680054,1036.060059,1053.0,913400.0
2015-01-23,AZO,603.349976,604.0,599.210022,605.950012,271600.0
2015-01-23,CMG,715.47998,713.690002,713.0,721.0,324600.0
2015-01-23,GOOG,535.592396,539.952437,533.002407,542.172453,2281700.0
2015-01-23,GOOGL,538.030029,541.950012,535.75,545.409973,2298300.0
2015-01-23,ISRG,512.340027,517.570007,508.019989,526.820007,504000.0
2015-01-23,PCLN,1047.949951,1037.98999,1033.280029,1047.949951,700400.0
2015-01-26,AZO,602.849976,612.140015,600.52002,612.609985,246700.0
2015-01-26,CMG,715.340027,723.429993,712.369995,725.859985,227700.0
2015-01-26,GOOG,538.532466,535.212448,529.672413,539.002444,1543700.0
2015-01-26,GOOGL,541.5,536.719971,532.070007,541.5,1546600.0
2015-01-26,ISRG,517.450012,521.01001,511.769989,521.210022,219500.0
2015-01-26,PCLN,1036.119995,1041.859985,1028.219971,1043.819946,507500.0
2015-01-27,AZO,608.440002,608.590027,605.130005,612.859985,271700.0
2015-01-27,CMG,718.380005,721.330017,716.0,724.830017,275600.0
2015-01-27,GOOG,529.972369,518.63237,518.19232,530.702398,1904000.0
2015-01-27,GOOGL,531.400024,521.190002,520.859985,532.780029,1957400.0
2015-01-27,ISRG,513.01001,509.209991,506.019989,517.159973,387000.0
2015-01-27,PCLN,1029.72998,1013.859985,1003.179993,1029.969971,942000.0
2015-01-28,AZO,610.25,610.570007,608.179993,617.690002,258200.0
2015-01-28,CMG,722.969971,711.98999,710.640015,726.97998,244500.0
2015-01-28,GOOG,522.782362,510.002318,510.002318,522.992349,1683800.0
2015-01-28,GOOGL,525.0,512.429993,512.349976,525.690002,1791100.0
2015-01-28,ISRG,505.0,502.559998,501.980011,513.73999,254600.0
2015-01-28,PCLN,1024.030029,1003.25,1001.940002,1024.410034,571300.0
2015-01-29,AZO,606.01001,611.130005,606.01001,612.900024,262200.0
2015-01-29,CMG,715.549988,714.530029,708.820007,717.150024,273700.0
2015-01-29,GOOG,511.002313,510.6623,501.202274,511.092313,4186300.0
2015-01-29,GOOGL,512.900024,513.22998,503.480011,515.190002,3950900.0
2015-01-29,ISRG,501.23999,504.980011,492.950012,507.519989,281200.0
2015-01-29,PCLN,1007.789978,1014.73999,992.130005,1015.51001,760500.0
2015-01-30,AZO,611.450012,596.960022,596.47998,614.630005,349000.0
2015-01-30,CMG,714.640015,709.840027,707.719971,716.97998,385100.0
2015-01-30,GOOG,515.862322,534.522445,515.522339,539.872444,5606300.0
2015-01-30,GOOGL,519.0,537.549988,518.179993,543.099976,6055400.0
2015-01-30,ISRG,500.950012,494.480011,493.640015,504.890015,283800.0
2015-01-30,PCLN,1011.73999,1009.47998,1008.409973,1022.200012,928400.0
2015-02-02,AZO,600.51001,593.75,586.25,601.590027,315900.0
2015-02-02,CMG,713.549988,712.549988,700.01001,716.440002,467900.0
2015-02-02,GOOG,531.732383,528.482381,518.552377,533.002407,2849800.0
2015-02-02,GOOGL,534.320007,532.200012,521.719971,536.5,3768900.0
2015-02-02,ISRG,499.850006,507.130005,494.660004,507.869995,409300.0
2015-02-02,PCLN,1012.0,1013.030029,993.5,1015.0,655300.0
2015-02-03,AZO,598.549988,607.840027,597.409973,608.130005,263900.0
2015-02-03,CMG,716.5,726.630005,710.0,726.630005,1364200.0
2015-02-03,GOOG,528.002366,529.2424,523.262377,533.40243,2038600.0
2015-02-03,GOOGL,529.940002,533.299988,526.809998,537.450012,2353100.0
2015-02-03,ISRG,508.269989,508.660004,502.519989,511.619995,225300.0
2015-02-03,PCLN,1019.690002,1037.550049,1012.359985,1039.98999,870600.0
2015-02-04,AZO,607.200012,601.630005,597.849976,612.809998,280900.0
2015-02-04,CMG,680.280029,676.0,667.150024,686.719971,2474700.0
2015-02-04,GOOG,529.2424,522.762349,521.272361,532.67442,1663600.0
2015-02-04,GOOGL,533.140015,526.099976,525.030029,536.75,1694800.0
2015-02-04,ISRG,507.410004,504.920013,504.100006,512.789978,171300.0
2015-02-04,PCLN,1036.0,1028.280029,1026.339966,1041.859985,557900.0
2015-02-05,AZO,605.960022,614.880005,596.640015,621.47998,363700.0
2015-02-05,CMG,679.799988,670.909973,670.030029,679.98999,830300.0
2015-02-05,GOOG,523.792395,527.582391,522.092359,528.502395,1849700.0
2015-02-05,GOOGL,527.929993,529.830017,525.640015,530.690002,1658800.0
2015-02-05,ISRG,508.160004,516.169983,507.100006,516.400024,334600.0
2015-02-05,PCLN,1033.52002,1044.599976,1031.300049,1049.439941,660300.0
2015-02-06,AZO,613.950012,611.340027,609.659973,618.099976,225600.0
2015-02-06,CMG,677.0,659.919983,657.97998,677.890015,870200.0
2015-02-06,GOOG,527.64237,531.002415,526.412373,537.202402,1763500.0
2015-02-06,GOOGL,531.01001,533.880005,528.650024,540.219971,2146900.0
2015-02-06,ISRG,514.390015,512.27002,509.76001,523.400024,288300.0
2015-02-06,PCLN,1027.560059,1022.419983,1007.109985,1030.02002,1307400.0
2015-02-09,AZO,607.140015,613.049988,606.280029,613.559998,223200.0
2015-02-09,CMG,655.780029,648.01001,647.280029,658.070007,646500.0
2015-02-09,GOOG,528.002366,527.832406,526.022388,532.002411,1267700.0
2015-02-09,GOOGL,531.059998,529.280029,527.549988,533.880005,1515700.0
2015-02-09,ISRG,510.0,501.290009,498.899994,513.01001,228800.0
2015-02-09,PCLN,1020.650024,1033.150024,1014.150024,1039.359985,492700.0
2015-02-10,AZO,620.219971,616.659973,609.25,620.219971,158700.0
2015-02-10,CMG,652.0,665.030029,648.929993,666.25,734500.0
2015-02-10,GOOG,529.302379,536.942412,526.922378,537.702431,1749800.0
2015-02-10,GOOGL,532.150024,540.159973,529.169983,541.0,2371500.0
2015-02-10,ISRG,502.730011,499.869995,495.51001,504.980011,310800.0
2015-02-10,PCLN,1040.800049,1057.619995,1039.430054,1060.800049,657600.0
2015-02-11,AZO,615.0,620.51001,614.0,621.98999,188100.0
2015-02-11,CMG,666.359985,669.640015,664.0,676.75,528200.0
2015-02-11,GOOG,535.302416,535.972405,533.380397,538.452412,1377700.0
2015-02-11,GOOGL,539.72998,538.0,536.0,541.950012,1915300.0
2015-02-11,ISRG,499.100006,498.929993,496.559998,504.0,134500.0
2015-02-11,PCLN,1064.23999,1060.060059,1051.050049,1067.930054,515700.0
2015-02-12,AZO,619.890015,617.51001,615.52002,621.75,160400.0
2015-02-12,CMG,671.080017,670.289978,665.26001,672.23999,288500.0
2015-02-12,GOOG,537.252405,542.932472,534.675391,544.822482,1620200.0
2015-02-12,GOOGL,539.659973,546.01001,537.0,548.340027,2429500.0
2015-02-12,ISRG,500.149994,507.709991,495.880005,508.279999,236700.0
2015-02-12,PCLN,1077.209961,1091.949951,1075.719971,1102.26001,1199100.0
2015-02-13,AZO,614.130005,618.5,611.679993,619.619995,164400.0
2015-02-13,CMG,671.669983,674.890015,667.130005,678.169983,377400.0
2015-02-13,GOOG,543.352447,549.012501,543.132484,549.912491,1900300.0
2015-02-13,GOOGL,547.51001,551.159973,546.599976,552.539978,2369100.0
2015-02-13,ISRG,506.220001,510.730011,505.690002,511.26001,139900.0
2015-02-13,PCLN,1100.150024,1103.369995,1094.589966,1108.5,814400.0
2015-02-17,AZO,615.549988,615.150024,612.950012,620.309998,205400.0
2015-02-17,CMG,674.460022,670.929993,669.299988,677.669983,280300.0
2015-02-17,GOOG,546.83245,542.842443,541.092465,550.00246,1616800.0
2015-02-17,GOOGL,551.159973,545.01001,543.26001,553.0,1958700.0
2015-02-17,ISRG,510.730011,514.0,507.200012,516.27002,190900.0
2015-02-17,PCLN,1115.0,1120.98999,1114.01001,1130.920044,900800.0
2015-02-18,AZO,613.719971,617.919983,612.679993,617.98999,167700.0
2015-02-18,CMG,669.960022,674.570007,669.0,674.950012,297000.0
2015-02-18,GOOG,541.402458,539.702422,537.512456,545.492471,1453000.0
2015-02-18,GOOGL,543.820007,542.650024,539.549988,547.549988,1558200.0
2015-02-18,ISRG,513.5,512.859985,510.140015,514.880005,111800.0
2015-02-18,PCLN,1116.439941,1122.98999,1112.0,1129.97998,819800.0
2015-02-19,AZO,621.47998,618.340027,612.76001,623.440002,238000.0
2015-02-19,CMG,674.599976,673.280029,672.23999,682.52002,295400.0
2015-02-19,GOOG,538.042413,542.872432,538.012424,543.11247,989100.0
2015-02-19,GOOGL,542.469971,546.450012,540.75,546.859985,1541700.0
2015-02-19,ISRG,509.51001,511.470001,505.420013,512.849976,128100.0
2015-02-19,PCLN,1219.0,1218.050049,1195.359985,1222.880005,2671500.0
2015-02-20,AZO,618.97998,623.309998,614.390015,625.0,236900.0
2015-02-20,CMG,670.590027,674.0,669.380005,674.679993,265600.0
2015-02-20,GOOG,543.132484,538.952441,535.802444,543.75247,1444300.0
2015-02-20,GOOGL,547.580017,541.799988,538.099976,547.580017,1911700.0
2015-02-20,ISRG,511.25,513.309998,506.279999,513.380005,153600.0
2015-02-20,PCLN,1217.0,1216.22998,1214.76001,1230.089966,1296800.0
2015-02-23,AZO,625.0,636.77002,620.48999,636.969971,281500.0
2015-02-23,CMG,673.469971,671.169983,670.0,679.719971,215200.0
2015-02-23,GOOG,536.052398,531.912381,529.412422,536.441404,1457800.0
2015-02-23,GOOGL,539.0,535.0,532.0,539.299988,1645300.0
2015-02-23,ISRG,509.630005,513.700012,508.619995,514.929993,116500.0
2015-02-23,PCLN,1210.469971,1207.579956,1202.829956,1216.5,869400.0
2015-02-24,AZO,638.580017,637.789978,634.150024,640.0,163800.0
2015-02-24,CMG,670.150024,667.909973,666.52002,673.219971,235800.0
2015-02-24,GOOG,530.002419,536.092424,528.252381,536.792403,1005000.0
2015-02-24,GOOGL,531.549988,538.650024,531.0,539.400024,1420300.0
2015-02-24,ISRG,512.76001,511.869995,509.519989,513.640015,119000.0
2015-02-24,PCLN,1204.099976,1219.790039,1201.099976,1220.359985,603500.0
2015-02-25,AZO,640.97998,637.119995,634.650024,642.169983,188600.0
2015-02-25,CMG,670.299988,675.52002,668.719971,678.940002,378800.0
2015-02-25,GOOG,535.90245,543.872489,535.447406,546.222501,1825900.0
2015-02-25,GOOGL,538.440002,547.330017,538.01001,549.570007,2041800.0
2015-02-25,ISRG,510.01001,507.660004,505.0,513.190002,115900.0
2015-02-25,PCLN,1219.140015,1250.859985,1217.959961,1259.859985,1615900.0
2015-02-26,AZO,633.299988,644.969971,633.299988,646.52002,231500.0
2015-02-26,CMG,675.0,670.289978,669.140015,678.890015,281900.0
2015-02-26,GOOG,543.212476,555.482516,541.502464,556.142529,2311500.0
2015-02-26,GOOGL,545.73999,559.289978,545.090027,560.130005,2701600.0
2015-02-26,ISRG,508.690002,506.329987,503.76001,512.51001,124500.0
2015-02-26,PCLN,1248.97998,1241.25,1238.969971,1264.0,935400.0
2015-02-27,AZO,647.23999,642.679993,640.52002,651.950012,319400.0
2015-02-27,CMG,670.5,664.969971,664.969971,673.590027,440600.0
2015-02-27,GOOG,554.242482,558.402511,552.902503,564.712541,2410100.0
2015-02-27,GOOGL,558.150024,562.630005,557.030029,569.419983,3416400.0
2015-02-27,ISRG,506.329987,500.0,497.540009,506.329987,281900.0
2015-02-27,PCLN,1240.75,1237.47998,1235.119995,1246.390015,645400.0
2015-03-02,AZO,642.419983,649.530029,640.659973,649.530029,326200.0
2015-03-02,CMG,664.969971,670.859985,663.049988,673.299988,398700.0
2015-03-02,GOOG,560.532559,571.342601,558.752531,572.152562,2129600.0
2015-03-02,GOOGL,567.0,575.02002,563.150024,575.98999,2520300.0
2015-03-02,ISRG,500.980011,503.26001,500.980011,504.98999,178200.0
2015-03-02,PCLN,1237.75,1249.400024,1232.030029,1262.0,691500.0
2015-03-03,AZO,671.140015,651.900024,648.98999,674.76001,666600.0
2015-03-03,CMG,670.380005,668.429993,664.559998,672.26001,264200.0
2015-03-03,GOOG,570.452587,573.64261,566.522559,575.392588,1704700.0
2015-03-03,GOOGL,576.349976,578.789978,570.5,580.849976,2526300.0
2015-03-03,ISRG,508.25,509.070007,502.049988,510.98999,277800.0
2015-03-03,PCLN,1245.0,1242.030029,1238.0,1254.640015,510900.0
2015-03-04,AZO,653.469971,652.47998,644.219971,654.76001,448400.0
2015-03-04,CMG,664.599976,664.159973,659.25,667.0,306000.0
2015-03-04,GOOG,571.872619,573.372583,568.012546,577.112637,1876800.0
2015-03-04,GOOGL,576.969971,578.330017,572.52002,581.630005,1898200.0
2015-03-04,ISRG,505.470001,500.559998,499.0,505.929993,264200.0
2015-03-04,PCLN,1233.5,1225.670044,1223.719971,1238.76001,777700.0
2015-03-05,AZO,655.849976,650.969971,646.849976,657.880005,345200.0
2015-03-05,CMG,667.159973,670.48999,666.200012,671.590027,249600.0
2015-03-05,GOOG,575.022616,575.332609,573.412609,577.912621,1389600.0
2015-03-05,GOOGL,579.619995,581.429993,578.22998,583.200012,1795900.0
2015-03-05,ISRG,501.459991,499.519989,498.049988,504.410004,204000.0
2015-03-05,PCLN,1225.300049,1231.900024,1224.540039,1242.0,502100.0
2015-03-06,AZO,647.460022,645.200012,644.390015,652.859985,277900.0
2015-03-06,CMG,666.919983,658.679993,657.609985,666.919983,381600.0
2015-03-06,GOOG,574.882583,567.687558,566.762536,576.682625,1659100.0
2015-03-06,GOOGL,582.0,572.900024,572.059998,582.780029,1879600.0
2015-03-06,ISRG,496.980011,494.040009,491.73999,499.519989,272300.0
2015-03-06,PCLN,1221.709961,1215.98999,1213.0,1228.380005,695500.0
2015-03-09,AZO,644.530029,650.890015,644.119995,651.140015,236900.0
2015-03-09,CMG,658.01001,660.340027,653.369995,662.97998,321300.0
2015-03-09,GOOG,566.862541,568.852557,563.537566,570.272589,1062100.0
2015-03-09,GOOGL,570.919983,574.099976,569.049988,575.450012,1173900.0
2015-03-09,ISRG,490.700012,492.589996,486.670013,494.0,299400.0
2015-03-09,PCLN,1215.189941,1217.0,1202.0,1220.890015,563800.0
2015-03-10,AZO,650.429993,643.049988,640.809998,650.429993,338800.0
2015-03-10,CMG,657.140015,654.73999,652.299988,658.330017,240400.0
2015-03-10,GOOG,564.252539,555.012538,554.732534,564.852573,1792300.0
2015-03-10,GOOGL,568.47998,559.849976,559.820007,569.76001,2004000.0
2015-03-10,ISRG,488.600006,487.519989,487.48999,492.609985,259000.0