def __init__(self, ticker, start_date, start_value, strategy): """Initialize for a certain ticker and start value. - Ticker is a Yahoo ticker. - Start value is assumed to be in the same currency as the underlying ticker. - Strategy should be a subclass of Strategy. Note: this involves a HTTP call to Yahoo, so you need an internet connection. """ self._strategy = strategy if not isinstance(strategy, AbstractStrategy): raise Exception("strategy argument should be a subclass of " "AbstractStrategy class.") self._state = State(ticker, start_date, start_value)
class StockMarketStrategySim: _strategy = None _state = None _silent = False def __init__(self, ticker, start_date, start_value, strategy): """Initialize for a certain ticker and start value. - Ticker is a Yahoo ticker. - Start value is assumed to be in the same currency as the underlying ticker. - Strategy should be a subclass of Strategy. Note: this involves a HTTP call to Yahoo, so you need an internet connection. """ self._strategy = strategy if not isinstance(strategy, AbstractStrategy): raise Exception("strategy argument should be a subclass of " "AbstractStrategy class.") self._state = State(ticker, start_date, start_value) def set_silent(self): self._silent = True return self def get_current_value(self): """Get the current value.""" return self._state.get_current_value() def run(self): """Run the simulation from the given startdate until latest available date in data. """ state = self._state state.reset() while True: df_today = state.get_df_for_current_date() self._evaluate_day(df_today) if not state.increment_current_date(): self._log(( "Done! Reached date {}. Total trades: {}. " "Total transactioncosts: {:0.2f}.") .format(state.get_current_date(), state.get_num_trades(), state.get_total_transactioncosts())) break if not self._silent: plot = Plot() plot.plot_results(state, self._strategy.__class__.__name__) def _evaluate_day(self, df_day): """Process actions for a single day and update the portfolio value.""" open = df_day['Open'][0] close = df_day['Close'][0] prev_close = df_day['PrevClose'][0] avg = (open + close) / 2.0 ticker_proc_change = (close - prev_close) / prev_close prevclose_value = self._state.get_current_value() if self._state.position_is_long(): if self._strategy.sell_trigger(self._state): # Selling at halfway point today. # Add perc. diff from Open to avg and sell. self._state.apply_percentual_change((1 + (avg - open) / open)) self._sell() else: # Had a full day exposure to the market. # Add perc. diff from Open to Close self._state.apply_percentual_change(1 + ticker_proc_change) else: # Position is not long, so it's cash if self._strategy.buy_trigger(self._state): self._buy() # Buying at halfway point today. # Add perc. diff from avg to Close self._state.apply_percentual_change((1 + (close - avg) / avg)) self._state.save_value_in_df() self._strategy.update(self._state) value_proc_change = (self._state.get_current_value() - prevclose_value) / prevclose_value self._log(( "{}. Your value at Close: {:8.2f} ({:5.2f} %). " "Ticker at Close: {:8.2f} " "({:5.2f} %). {}") .format(df_day.index[0].date(), self._state.get_current_value(), value_proc_change * 100, close, ticker_proc_change * 100, self._strategy.status_update_suffix())) def _buy(self): """Perform a buy action using all available cash.""" tc = self._get_transaction_cost(self._state.get_current_value()) self._state.set_long_position() self._log("Bought long position for {0} (transaction cost: {1})" .format(self._state.get_current_value(), tc)) self._state.apply_transaction_cost(tc) def _sell(self): """Sell everything.""" tc = self._get_transaction_cost(self._state.get_current_value()) self._state.set_cash_position() self._log("Sold long position for {0} (transaction cost: {1})" .format(self._state.get_current_value(), tc)) self._state.apply_transaction_cost(tc) def _get_transaction_cost(self, amount): """Return transaction costs for a given amount. This is for a Dutch bank (in euro). """ return min(max(amount * 0.001 + 6.50, 10.0), 150.0) def _log(self, s): if not self._silent: print(s)