Ejemplo n.º 1
0
class Market:
    """Main Class. Runs the Simulation as a whole"""
    def __init__(self, TAlgorithm, datahandler, Broker, Portfolio, name):
        """Initialize a new Stock Simulation
		
		Parameters:
	
		TAlgorithm - A Class which extends TradingAlgorithm. Will be run
			as Trader in this simulation. Look at TradingAlgorithm
			for information about what this class should contain
		DataHandler - An instance of Class extending DataHandler. Look
			at DataHandler class about how this classes should look like
		Broker - A Class which will extend the Broker Class. Look at the
			Broker class about how this classes should look like
		Portfolio - a Reference to the Portfolio class
		string name - name of the simulation. has no use yet
		"""

        self.name = name
        self.game_end = Signal()
        self.data = datahandler
        start_port = defaultdict(lambda: 0)
        start_port['EUR'] = 50000
        self.broker = Broker(self)

        self.talgo_port = Portfolio(start_port)
        self.talgo = TAlgorithm(self, self.broker, self.talgo_port)

        self.date = None

#		self.database_path = directory + name + '.db'
#		self.conn = lite.connect(database_path)

    def run(self, data_frequency):
        """Starts and runs the simulation. Will forward in time
			`data_frequency` seconds. The broker will check each tick in
			that time (check if limits are reached etc.). the trading
			algorithm only gets a notification at the end of the forward
			that means, `data_frequency` being 10 would mean, the
			talgorithm would get notificated about new data every 10
			seconds. he could then load all ticks of the last 10 secs
			Parameters:
			number data_frequency - the frequency in which the
				talgorithm gets new data and is able to take action
				upon that data
		"""
        log.info("Starting simulation %s", self.name)
        time_intervall = timedelta(seconds=data_frequency)
        while True == self.data.data_available:
            self.data.update_current_time(time_intervall)
            if self.data.get_current_time().date() != self.date:
                self.date = self.data.get_current_time().date()
                log.info("Today is the %s. Your Portfolio is worth "
                         "%f€", self.date,
                         self.talgo_port.get_portfolio_value_in_eur(self.data))

        log.info("Game ended, no more Data available")
        self.game_end.trigger()
        print(self.talgo_port.get_portfolio())
        log.info("In EUR your portfolio is worth %f",
                 self.talgo_port.get_portfolio_value_in_eur(self.data))
Ejemplo n.º 2
0
class TickBroker(Broker):
    """ Broker handling the Order, which are represented by python
		Dictionaries
		For TAlgos, if you pass an order to TickBroker.order_xchange,
		ALL ORDERS dicts have to include keys:
			string fxcode, int `amount`, `kind` ('buy'/'sell'),
			`type` ('market'/'limit')
			LIMIT ORDERS need to contain
			number `limit`, datetime `expires`
	"""

    def __init__(self, market, limit_storage_time=timedelta(days=10), lag_time=timedelta(milliseconds=500)):
        """Initialize the broker
			Parameters:
			Market market - a market instance
			timedelta limit_storage_time - a time span which limit
				orders will be saved maximum
			timdelta lag_time - the time which it will take to broker
				to actually set a market order
		"""
        log.debug("Initiating Broker Object")
        self.market = market

        self.market.data.tick.registerObserver(self.market_tick)
        self.market.data.time_change.registerObserver(self.market_tick)

        self._orders = {}
        self._limit_storage_time = limit_storage_time
        self._market_order_delay = lag_time

        self.order_fill = Signal()
        self.order_delete = Signal()
        self._newest_order_id = -1

    def get_max_limit_storage_time(self):
        return self._limit_storage_time

    def _make_transaction_xchange(self, portfolio, curr1, amount1, curr2, amount2):
        """Makes the actuall transaction in the portfolio
			ParametersL
			Portfolio portfolio - a portfolio instance on which the
				transaction will happen
			string curr1,2 - the currency codes (e.g. 'EUR') on which
				the holdings should change
			numer amount1,2 - amount`i` is the number which will be
				added to your holdings of curr`i`

			Normally either curr1 or curr2 will be negative and the
			other one positive, because this is normally a transaction
			from one currency into another
		"""
        try:
            portfolio.transact(curr1, amount1)
            portfolio.transact(curr2, amount2)
        except InDebt as e:
            log.info("You are in debt, you ow %f %s", e.amount, e.code)

    def _fill_order_xchange(self, order):
        """Fill an order. Check if it is sell or buy, calculate the
			price for the given amount that should be bought/sold
			and then make the transaction
			Parameters:
			dict order - a dicitonary representing an order
		"""
        tick = self.market.data.get_current_tick(order["fxcode"])
        curr = self._get_currencies_from_fxcode(order["fxcode"])
        if order["kind"] == "buy":
            if order["amount"] == 0:
                order["amount"] = order["portfolio"].get_amount(curr[1]) / tick["ask"]
            self._make_transaction_xchange(
                order["portfolio"], curr[0], order["amount"], curr[1], -1 * order["amount"] * tick["ask"]
            )
        elif order["kind"] == "sell":
            if order["amount"] == 0:
                order["amount"] = order["portfolio"].get_amount(curr[0])
            self._make_transaction_xchange(
                order["portfolio"], curr[0], -1 * order["amount"], curr[1], order["amount"] * tick["bid"]
            )
        else:
            raise ValueError("order needs to be either sellout or an" "amount has to be set")
        self.order_fill.trigger(order)

    def _get_currencies_from_fxcode(self, fxcode):
        if fxcode == "EURUSD":
            return ("EUR", "USD")
        else:
            raise CurrencyNotForTrade(
                "Currency %s isn't implemented "
                " in the Broker Class yet. No information to resolve "
                " the fxcode to the single currencies" % fxcode
            )

    def market_tick(self, market):
        """called on every new tick"""
        self._check_open_orders()

    def order_xchange(self, order, portfolio):
        """Make an Limit or Market order. Return the `order_id` which
			the TAlgo can use to recognize the order later.
			This method will only append the order to self._orders,
			the actuall execution is done in _check_open_orders later
		"""
        self._newest_order_id += 1
        order.update({"sector": "xchange", "portfolio": portfolio})
        order = self._check_order(order)
        if order["type"] == "market":
            order["execute_time"] = self.market.data.get_current_time() + self._market_order_delay
        self._orders[self._newest_order_id] = order
        return self._newest_order_id

    def _check_open_orders(self):
        """Run through all orders in self._orders and check if they
			expired or need to be executed. limit orders will be
			executed when `limit` is reachend and deleted if `expires`
			date is reached. Market orders will be executed after the
			market order delay (simulates delay of your internet
			connection) passed by. If order is executed or deleted, it
			extends the order with the order-id so that the TAlgo
			will recieve it in the event trigger and then deletes it
		"""
        for order_id, order in list(self._orders.items()):
            if order["type"] == "limit":
                if order["expires"] < self.market.data.get_time():
                    order["id"] = order_id
                    self.order_delete.trigger(order)
                    del self._orders[order_id]
                    continue
                if (
                    order["kind"] == "buy"
                    and self.market.data.get_current_tick(order["fxcode"])["bid"][0] >= order["limit"]
                    or order["kind"] == "sell"
                    and self.market.data.get_current_tick(order["fxcode"])["ask"][0] <= order["limit"]
                ):
                    order["id"] = order_id
                    self._fill_order_xchange(order)
                    del self._orders[order_id]
            else:
                if order["execute_time"] <= (self.market.data.get_current_time()):
                    order["id"] = order_id
                    self._fill_order_xchange(order)
                    del self._orders[order_id]

    def _check_order(self, order):
        """Checks a Order dict if it is a valid order"""
        if order["type"] not in ("limit", "market"):
            raise ValueError("Order key `type` must be set to either " "'market' or 'limit'")
        if order["kind"] not in ("buy", "sell"):
            raise ValueError("Order key `kind` must be set to either " "'buy' or 'sell'")

        if order["type"] == "limit":
            if order["expires"] == None:
                order["expires"] = self.market.data.get_current_time() + self._orders_storage_time
            elif (
                order["expires"] > (self._orders_storage_time + self.market.data.get_current_time())
                or order["expires"] < 0
            ):
                raise ValueError(
                    "Expires should be smaller then or equal " " to %s and not-negative, you passed '%s' by.",
                    self._orders_storage_time,
                    expires,
                )
            if not isinstance(order["limit"], Number):
                raise ValueError("If you pass by a limit order you have" "to set a limit")

        return order
Ejemplo n.º 3
0
class TickBroker(Broker):
    """ Broker handling the Order, which are represented by python
		Dictionaries
		For TAlgos, if you pass an order to TickBroker.order_xchange,
		ALL ORDERS dicts have to include keys:
			string fxcode, int `amount`, `kind` ('buy'/'sell'),
			`type` ('market'/'limit')
			LIMIT ORDERS need to contain
			number `limit`, datetime `expires`
	"""
    def __init__(self,
                 market,
                 limit_storage_time=timedelta(days=10),
                 lag_time=timedelta(milliseconds=500)):
        """Initialize the broker
			Parameters:
			Market market - a market instance
			timedelta limit_storage_time - a time span which limit
				orders will be saved maximum
			timdelta lag_time - the time which it will take to broker
				to actually set a market order
		"""
        log.debug("Initiating Broker Object")
        self.market = market

        self.market.data.tick.registerObserver(self.market_tick)
        self.market.data.time_change.registerObserver(self.market_tick)

        self._orders = {}
        self._limit_storage_time = limit_storage_time
        self._market_order_delay = lag_time

        self.order_fill = Signal()
        self.order_delete = Signal()
        self._newest_order_id = -1

    def get_max_limit_storage_time(self):
        return self._limit_storage_time

    def _make_transaction_xchange(self, portfolio, curr1, amount1, curr2,
                                  amount2):
        """Makes the actuall transaction in the portfolio
			ParametersL
			Portfolio portfolio - a portfolio instance on which the
				transaction will happen
			string curr1,2 - the currency codes (e.g. 'EUR') on which
				the holdings should change
			numer amount1,2 - amount`i` is the number which will be
				added to your holdings of curr`i`

			Normally either curr1 or curr2 will be negative and the
			other one positive, because this is normally a transaction
			from one currency into another
		"""
        try:
            portfolio.transact(curr1, amount1)
            portfolio.transact(curr2, amount2)
        except InDebt as e:
            log.info("You are in debt, you ow %f %s", e.amount, e.code)

    def _fill_order_xchange(self, order):
        """Fill an order. Check if it is sell or buy, calculate the
			price for the given amount that should be bought/sold
			and then make the transaction
			Parameters:
			dict order - a dicitonary representing an order
		"""
        tick = self.market.data.get_current_tick(order['fxcode'])
        curr = self._get_currencies_from_fxcode(order['fxcode'])
        if order['kind'] == 'buy':
            if order['amount'] == 0:
                order['amount'] = order['portfolio'].get_amount(
                    curr[1]) / tick['ask']
            self._make_transaction_xchange(order['portfolio'], curr[0],
                                           order['amount'], curr[1],
                                           -1 * order['amount'] * tick['ask'])
        elif order['kind'] == 'sell':
            if order['amount'] == 0:
                order['amount'] = order['portfolio'].get_amount(curr[0])
            self._make_transaction_xchange(order['portfolio'], curr[0],
                                           -1 * order['amount'], curr[1],
                                           order['amount'] * tick['bid'])
        else:
            raise ValueError("order needs to be either sellout or an"
                             "amount has to be set")
        self.order_fill.trigger(order)

    def _get_currencies_from_fxcode(self, fxcode):
        if fxcode == 'EURUSD':
            return ('EUR', 'USD')
        else:
            raise CurrencyNotForTrade(
                "Currency %s isn't implemented "
                " in the Broker Class yet. No information to resolve "
                " the fxcode to the single currencies" % fxcode)

    def market_tick(self, market):
        """called on every new tick"""
        self._check_open_orders()

    def order_xchange(self, order, portfolio):
        """Make an Limit or Market order. Return the `order_id` which
			the TAlgo can use to recognize the order later.
			This method will only append the order to self._orders,
			the actuall execution is done in _check_open_orders later
		"""
        self._newest_order_id += 1
        order.update({'sector': 'xchange', 'portfolio': portfolio})
        order = self._check_order(order)
        if order['type'] == 'market':
            order['execute_time'] = (self.market.data.get_current_time() +
                                     self._market_order_delay)
        self._orders[self._newest_order_id] = order
        return self._newest_order_id

    def _check_open_orders(self):
        """Run through all orders in self._orders and check if they
			expired or need to be executed. limit orders will be
			executed when `limit` is reachend and deleted if `expires`
			date is reached. Market orders will be executed after the
			market order delay (simulates delay of your internet
			connection) passed by. If order is executed or deleted, it
			extends the order with the order-id so that the TAlgo
			will recieve it in the event trigger and then deletes it
		"""
        for order_id, order in list(self._orders.items()):
            if order['type'] == 'limit':
                if order['expires'] < self.market.data.get_time():
                    order['id'] = order_id
                    self.order_delete.trigger(order)
                    del self._orders[order_id]
                    continue
                if (order['kind'] == 'buy'
                        and self.market.data.get_current_tick(
                            order['fxcode'])['bid'][0] >= order['limit']
                        or order['kind'] == 'sell'
                        and self.market.data.get_current_tick(
                            order['fxcode'])['ask'][0] <= order['limit']):
                    order['id'] = order_id
                    self._fill_order_xchange(order)
                    del self._orders[order_id]
            else:
                if order['execute_time'] <= (
                        self.market.data.get_current_time()):
                    order['id'] = order_id
                    self._fill_order_xchange(order)
                    del self._orders[order_id]

    def _check_order(self, order):
        """Checks a Order dict if it is a valid order"""
        if order['type'] not in ('limit', 'market'):
            raise ValueError("Order key `type` must be set to either "
                             "'market' or 'limit'")
        if order['kind'] not in ('buy', 'sell'):
            raise ValueError("Order key `kind` must be set to either "
                             "'buy' or 'sell'")

        if order['type'] == 'limit':
            if order['expires'] == None:
                order['expires'] = (self.market.data.get_current_time() +
                                    self._orders_storage_time)
            elif (order['expires'] > (self._orders_storage_time +
                                      self.market.data.get_current_time())
                  or order['expires'] < 0):
                raise ValueError(
                    "Expires should be smaller then or equal "
                    " to %s and not-negative, you passed '%s' by.",
                    self._orders_storage_time, expires)
            if not isinstance(order['limit'], Number):
                raise ValueError("If you pass by a limit order you have"
                                 "to set a limit")

        return order
Ejemplo n.º 4
0
class Market:
	"""Main Class. Runs the Simulation as a whole"""
	
	def __init__(self, TAlgorithm, 
		datahandler, Broker, Portfolio, name):
		"""Initialize a new Stock Simulation
		
		Parameters:
	
		TAlgorithm - A Class which extends TradingAlgorithm. Will be run
			as Trader in this simulation. Look at TradingAlgorithm
			for information about what this class should contain
		DataHandler - An instance of Class extending DataHandler. Look
			at DataHandler class about how this classes should look like
		Broker - A Class which will extend the Broker Class. Look at the
			Broker class about how this classes should look like
		Portfolio - a Reference to the Portfolio class
		string name - name of the simulation. has no use yet
		"""
		
		self.name = name
		self.game_end = Signal()
		self.data = datahandler
		start_port = defaultdict(lambda: 0)
		start_port['EUR'] = 50000
		self.broker = Broker(self)

		self.talgo_port = Portfolio(start_port)
		self.talgo = TAlgorithm(
			self, self.broker, self.talgo_port )

		self.date = None

#		self.database_path = directory + name + '.db'
#		self.conn = lite.connect(database_path)

	def run(self, data_frequency):
		"""Starts and runs the simulation. Will forward in time
			`data_frequency` seconds. The broker will check each tick in
			that time (check if limits are reached etc.). the trading
			algorithm only gets a notification at the end of the forward
			that means, `data_frequency` being 10 would mean, the
			talgorithm would get notificated about new data every 10
			seconds. he could then load all ticks of the last 10 secs
			Parameters:
			number data_frequency - the frequency in which the
				talgorithm gets new data and is able to take action
				upon that data
		"""
		log.info("Starting simulation %s", self.name)
		time_intervall = timedelta(seconds=data_frequency)
		while True == self.data.data_available:
			self.data.update_current_time(time_intervall)
			if self.data.get_current_time().date() != self.date:
				self.date = self.data.get_current_time().date()
				log.info("Today is the %s. Your Portfolio is worth "
				"%f€", self.date,
				self.talgo_port.get_portfolio_value_in_eur(self.data))

		log.info("Game ended, no more Data available")
		self.game_end.trigger()
		print(self.talgo_port.get_portfolio())
		log.info("In EUR your portfolio is worth %f",
			self.talgo_port.get_portfolio_value_in_eur(self.data))
Ejemplo n.º 5
0
class CSVForexTicksHandler(DataHandler):
	"""The first used DataHandler. It reads a CSV file in this format:
		time, ask, bid, where time is formated as %Y%m%d %H%M%S%f
		The Data which this Handler was written for is the CSV Tick
		Data from http://histdata.com
		__Important functions provided:__
		.update_current_time(time)
		.get_latest_data(time)
		.get_current_time()
		.get_current_tick()
		for detailed explainaition look at the functions

		__Important Signals__
		self.time_change
			when this is triggered, the handler has moved forward in
			time and new data is available. To be implemented by TAlgos
			so they know when new data is available, so they can read
			the data and take action upon it
		self.tick
			this is triggered on every new tick, that means every single
			update of a ask/bid price of any fxcode. this should not be
			implemented by a TAlgo, since it is unrealistic! It is used
			by the broker, e.g. to check if limits are reached
		
	"""

	
	def __init__(self, directory, fxcodes, time = None):
		"""Initialize the Handler.
			Parameters:
			string directory - the directory which the .csv files lay in
			list fxcodes - a list of strings which represent currency
				pairs, e.g. ['EURUSD', 'USDJPY']. the Handler will look
				into the directory provided by `directory`, if it can
				find files called `fxcode`.csv (where fxcode is an
				item in fxcodes), e.g. EURUSD.csv
			datetime time - the time at which the simulation should
				start at, if not provided, it just uses the first time
				available
		"""
	
		log.debug("Initiating CSVForexTicksHandler Object")
		self._directory = directory
		self._data = defaultdict(None)
		self.fxcodes = fxcodes
		self.data_available = True

		self.time_change = Signal()
		self.tick = Signal()

		self._comb_index = None
		self._load_files(self._directory, self.fxcodes)
		
		if None == time:
			self._time = self._comb_index[0]
		else:
			self._time = time
		self._start_time = self._time

	def _load_files(self, directory, fxcodes):
		"""load all files in directory which belong to a fxcode and
			saves them to self._data[fxcode] as panda DataFrame. e.g. if 
			fxcodes is [`EURUSD`, `USDJPY`] and directory 
			'~/Documents/data/' it would look for 
			'~/Documents/data/EURUSD.csv' and
			'~/Documents/data/USDJPY.csv', load them and save them to
			self._data['EURUSD'] and self._data['USDJPY'] as panda
			DataFrame.
			When everything is loaded, update each panda dataframe to
			use the combined index as a index.
		"""
		log.info("Start loading CSV...")
		for fxcode in self.fxcodes:
			self._load_file(fxcode,
				os.path.join(self._directory, '%s.csv' % fxcode)
			)
			log.info("Loaded %s", fxcode)
		log.info("Finished loading CSV")

		log.info("Sorting loaded data")
		for fxcode in self.fxcodes:
			self._data[fxcode] = ( self._data[fxcode]
				.reindex(index=self._comb_index, method='pad') )
		log.info("Finished sorting")

	def _load_file(self, fxcode, filepath):
		""" load a csv file and save it to self._data.
			then update the combined index self._comb_index which in
			the end will contain every singe datetime where a tick in
			any of the loaded currency pairs occured
		"""
		self._data[fxcode] = pd.io.parsers.read_csv(
			filepath,
			header=0, index_col=0,
			usecols = [0,1,2],
			names=['datetime','ask','bid'],
			parse_dates=True,
			date_parser = self.date_parser 
		)
		if self._comb_index is None:
			self._comb_index = self._data[fxcode].index
		else:
			self._comb_index.union(self._data[fxcode].index)	
		
	def date_parser(self, data):
		return datetime(int(data[0:4]), int(data[4:6]), int(data[6:8]),
						int(data[9:11]), int(data[11:13]), int(data[13:15]),
						int(data[15:18])*1000) #expects microsecond, we've mili

	def update_current_time(self, time):
		"""
			Forward the simulation in time. Look for every tick that
			has happened between now and the time you want to forward 
			to, update the time to that tick and trigger self.tick
			At the end trigger self.time_change when reached the point 
			in time you want to forward to
			updates
		Parameters:
			timedelta time - the time which you want to forward
		"""
		if self._time + time > self._comb_index[-1]:
			log.warning("Couldn't forward time by %s, data is only available "
				"until %s. Will forward %s", time, self._comb_index[-1],
				self._comb_index[-1] - self._time)
			time = self._comb_index[-1] - self._time
			self.data_available = False
		time_temp = self._time

		for timepoint in self._comb_index[self._comb_index
			.slice_indexer(time_temp, time_temp + time)]:
			self._time = timepoint
			self.tick.trigger(self)

		self._time = time_temp + time
		self.time_change.trigger(self)

			
	def get_latest_data(self, fxcode, time):
		"""Return all ticks that happened between now and now - time as
			a pandas DataFrame
		Paramters:
		timedelta time - if this would be 20 secs, you would get all
			data from the last 20 secs
		"""
		if time > self._time - self._start_time:
			raise DataNotAvailable("You tried to retrieve data for last" 
				" '%s'.Only data for last '%s' is available" % (time, 
				self._time - self._start_time) )
		return self._data[fxcode][self._time - time : self._time]
		
	def get_current_tick(self, fxcode):
		"""Return the freshest bid/ask price which is available. Go back
			in time from the current time. Find the freshest tick which
			affected the currency pair referenced by `fxcode`
		"""

		'''get the length of the DataFrame up to current moment
		 = the current row-number in the DataFrame of all data for
		fxcode which we are at.
		'''
		row_location = self._data[fxcode][:self._time].shape[0] - 1
		i = 0
		#go back until you find a column which actually contains data
		while isnan(self._data[fxcode]['ask'].irow(row_location - i)):
			i += 1
			if i > row_location:
				raise DataNotAvailable("No data available for '%s' "
					"the moment. It will probably be available later "
					"in time" % fxcode)

		return self._data[fxcode].irow(row_location - i)

	def get_current_time(self):
		return self._time