-
Notifications
You must be signed in to change notification settings - Fork 46
Home
Here you can find more in depth documentation regarding the inner workings of Titan. From a high level, Titan is a framework built on top of ccxt which aims to provide an interface and set of utilities for automating trading strategies. Titan handles the storage and feed of market data, upon which it provides a structure and interface for combining indicators, other analysis and conditional rules to define strategies. As it currently sits in its prototype stage, Titan supports strategies that utilize a single trading pair, exchange, and data resolution (i.e. 5m, 1h, or 1d candles). However, future plans include supporting different types of strategies including things like arbitrage, multiple data resolutions, and shifting to a different trading pair dynamically.
Titan's core module contains the basic functionality for keeping in touch with the markets as well as everything database related. This module will be able to run on its own as a headless version of the system once more hooks are added. The core module is able to listen and store market data for any exchange, pair, and time resolution available through ccxt. This is accomplished by the following:
The MarketWatcher class defines an object which is responsible for keeping in sync with market data of a specific resolution on a specific exchange, of a specific pair (i.e. GDAX, LTC/BTC, 1h). Each market watcher that is instantiated is an active object that will run on its own thread and keep the database in sync with data from the API for its respective pair/resolution. When it gets a new candle, it will pass it along to the strategies that are subscribed via pypubsub.
The ticker is simply a timer that runs for each data data resolution on its own thread, and notifies market watchers to pull a new candle from their API. Only one ticker for each data resolution will run. These tickers will be automatically started by MarketWatchers as they are created and will send out "tick" messages to market watchers via pypubsub.
The Market class provides functionality for interfacing with an exchange in the context of a strategy. It manages the API keys, provides methods for making orders, and facilitates TA indicators and other market data analysis.
A class inheriting from Market that allows for strategy simulations by keeping track of a mock balance and overriding the actual order methods. For historical simulations, this class will use OHLCV data for fill prices as Titan does not record historical order book data.
The Order class contains functionality for executing, canceling, and getting status of an order through ccxt, and writing that order to the database.
The Position class is a container for an order which allows custom stop-losses, and profit-target sell points to be applied.
Strategies tie together the functionality of Titan by providing an interface to program order behavior based on market data. Strategies can be created by inheriting from BaseStrategy, and implementing an init() method and the on_data() method. In addition strategies can make use of signal generators and indicators (explained below).
Current strategies inherit from BaseStrategy which is constructed with a data resolution (interval), a target market, and a simulated flag. When started, the strategy will subscribe (and create if necessary) to the correct MarketWatcher via pypubsub. The strategy times candles, TA calculations, and actions through its __update(candle) method which is called with each candle in a market data stream. This can be used on historical as well as live market data. After a strategy is instantiated it can go straight to listening for new candles, or it can be warmed up or simulated on historical data. Each time the __update() is called it will do the following things in order:
- Update TA calculations via the Market object
- Update any open positions to check if stop-loss or profit target has been met
- Call on_data() method (this is the method that is implemented by inheriting strategies)
The current BaseStrategy also provides long() method which will open a long position defined by the following parameters: order_quantity - The quantity of the base asset for the position profit_target_percent - The percent above the order price at which to sell off each position fixed_stoploss_percent - The percent below the order price at which to sell off each position trailing_stoploss_percent - The percent below the market price at which to sell off each position (updated on each new candle)
Indicators are implemented in Titan by inheriting from BaseIndicator and implementing a next_calculation(candle) method which saves to values defined in init(market, interval, periods). Indicators are constructed with a Market object, from which they can grab however much cached historical data they need to do each next calculation. In addition the constructor contains a parameter for what interval (data resolution it should use) and the number of historical periods needed for each calculation. Pyti (https://github.com/kylejusticemagnuson/pyti) can be used to easily implement different TA indicators in this format.
Signal generators are simply a place to contain logic that combines Indicators and other data into buy or sell signals. Signal generators in Titan are implemented by inheriting from BaseSignalGenerator and implementing check_condition(candle) which will be called with each new candle and return True or False depending on whether to buy/sell.
In practice, a signal generator is not exactly needed with Titan, and all the same logic can be coded in the on_data() method of a strategy. However, as more diverse strategies types will eventually be supported beyond the current BaseStrategy, it was decided a separation of concerns would be beneficial.
Currently Titan comes with a POC strategy (strategies/poc_strategy.py) which consists of the following:
- SMA Crossover Signal Generator (implemented in signal_generators/sma_crossover_signal.py)
- Custom signal generator which will return True when a long should be opened
- Monitors the behavior of a fast and slow moving average
- FMA and SMA intervals are set in the constructor of the strategy
- order_quantity - Amount of base currency for each order
- position_limit - Number of simultaneous long position which can be open at a time
- profit_target_percent - Profit target percent for all positions opened by the strategy
- fixed_stoploss_percent - Percent of fixed stop loss for all positions opened by the strategy
- trailing_stoploss_percent - Percent of all trailing stop loss for all positions opened by the strategy
def __init__(self, interval, exchange, base_currency, quote_currency, is_simulated, fma_periods, sma_periods, sim_balance=0):
super().__init__(interval, exchange, base_currency, quote_currency, is_simulated, sim_balance)
self.order_quantity = 1
self.position_limit = 1
self.buy_signal = sma_crossover_signal.SmaCrossoverSignal(self.market, self.interval, fma_periods, sma_periods, self)
self.profit_target_percent = 1.03
self.fixed_stoploss_percent = .90
self.trailing_stoploss_percent = .97
- When on_data() is called, the strategy will check the buy signal and if the position_limit has been reached. If the buy signal returns true, and the position limit has not been reached, the strategy will open a long position. (Remember, on each candle, the BaseStrategy will update all of the previously opened positions do all of the calculations before this method is called)
def on_data(self, candle):
buy_condition = self.buy_signal.check_condition(candle)
if self.get_open_position_count() >= self.position_limit:
pass
elif buy_condition:
self.long(self.order_quantity, self.fixed_stoploss_percent, self.trailing_stoploss_percent, self.profit_target_percent)