def rebalance(context, data): context.i += 1 # skip first LONG_WINDOW bars to fill windows if context.i < LONG_WINDOW: return # get pipeline data for asset of interest pipeline_data = context.pipeline_data pipeline_data = pipeline_data[pipeline_data.index == context.asset].iloc[0] # retrieve long and short moving averages from pipeline short_mavg = pipeline_data.short_mavg long_mavg = pipeline_data.long_mavg price = pipeline_data.price # check that order has not already been placed open_orders = get_open_orders() if context.asset not in open_orders: # check that the asset of interest can currently be traded if data.can_trade(context.asset): # adjust portfolio based on comparison of long and short vwap if short_mavg > long_mavg: order_target_percent(context.asset, TARGET_INVESTMENT_RATIO) elif short_mavg < long_mavg: order_target_percent(context.asset, 0.0) record( price=price, cash=context.portfolio.cash, leverage=context.account.leverage, short_mavg=short_mavg, long_mavg=long_mavg, )
def handle_data(context, data): cash = context.portfolio.cash target_hodl_value = TARGET_HODL_RATIO * context.portfolio.starting_cash reserve_value = RESERVE_RATIO * context.portfolio.starting_cash # Cancel any outstanding orders orders = get_open_orders(context.asset) or [] for order in orders: cancel_order(order) # Stop buying after passing the reserve threshold if cash <= reserve_value: context.is_buying = False # Retrieve current asset price from pricing data price = data[context.asset].price # Check if still buying and could (approximately) afford another purchase if context.is_buying and cash > price: # Place order to make position in asset equal to target_hodl_value order_target_value( context.asset, target_hodl_value, limit_price=price * 1.1, stop_price=price * 0.9, ) record( price=price, cash=cash, starting_cash=context.portfolio.starting_cash, leverage=context.account.leverage, )
def handle_data(context, data): # define the windows for the moving averages short_window = 50 long_window = 200 # Skip as many bars as long_window to properly compute the average context.i += 1 if context.i < long_window: return # Compute moving averages calling data.history() for each # moving average with the appropriate parameters. We choose to use # minute bars for this simulation -> freq="1m" # Returns a pandas dataframe. short_mavg = data.history(context.asset, 'price', bar_count=short_window, frequency="1m").mean() long_mavg = data.history(context.asset, 'price', bar_count=long_window, frequency="1m").mean() # Let's keep the price of our asset in a more handy variable price = data.current(context.asset, 'price') # If base_price is not set, we use the current value. This is the # price at the first bar which we reference to calculate price_change. if context.base_price is None: context.base_price = price price_change = (price - context.base_price) / context.base_price # Save values for later inspection record(price=price, cash=context.portfolio.cash, price_change=price_change, short_mavg=short_mavg, long_mavg=long_mavg) # Since we are using limit orders, some orders may not execute immediately # we wait until all orders are executed before considering more trades. orders = get_open_orders(context.asset) if len(orders) > 0: return # Exit if we cannot trade if not data.can_trade(context.asset): return # We check what's our position on our portfolio and trade accordingly pos_amount = context.portfolio.positions[context.asset].amount # Trading logic if short_mavg > long_mavg and pos_amount == 0: # we buy 100% of our portfolio for this asset order_target_percent(context.asset, 1) elif short_mavg < long_mavg and pos_amount > 0: # we sell all our positions for this asset order_target_percent(context.asset, 0)
def _handle_buy_sell_decision(context, data, signal, price): orders = get_open_orders(context.asset) if len(orders) > 0: log.info('skipping bar until all open orders execute') return positions = context.portfolio.positions if context.position is None and context.asset in positions: position = positions[context.asset] context.position = dict( cost_basis=position['cost_basis'], amount=position['amount'], stop=None ) # action = None if context.position is not None: cost_basis = context.position['cost_basis'] amount = context.position['amount'] log.info( 'found {amount} positions with cost basis {cost_basis}'.format( amount=amount, cost_basis=cost_basis ) ) stop = context.position['stop'] target = cost_basis * (1 + context.TARGET) if price >= target: context.position['cost_basis'] = price context.position['stop'] = context.STOP stop_target = context.STOP_LOSS if stop is None else context.STOP if price < cost_basis * (1 - stop_target): log.info('executing stop loss') order( asset=context.asset, amount=-amount, limit_price=price * (1 - context.SLIPPAGE_ALLOWED), ) # action = 0 context.position = None else: if signal == 'long': log.info('opening position') buy_amount = context.MAX_HOLDINGS / price order( asset=context.asset, amount=buy_amount, limit_price=price * (1 + context.SLIPPAGE_ALLOWED), ) context.position = dict( cost_basis=price, amount=buy_amount, stop=None )
def rebalance(context, data): context.i += 1 # Skip first LONG_WINDOW bars to fill windows if context.i < context.WIN DOW: return # Get pipeline data for asset of interest pipeline_data = context.pipeline_data pipeline_data = pipeline_data[pipeline_data.index == context.asset].iloc[0] # Compute the necessary statistics sma = pipeline_data.sma std = pipeline_data.std() price = pipeline_data.price # Compute buy and sell thresholds # Buy threshold is the simple moving average value plus one standard dev. # Sell threshold is the simple moving average value minus one standard dev. buy_threshold = sma-0.3*std/math.sqrt(context.WINDOW) sell_threshold = sma+0.3*std/math.sqrt(context.WINDOW) # Check that the order has not already been placed open_orders = get_open_orders() if context.asset not in open_orders: # check that the asset of interest can currently be traded if data.can_trade(context.asset): # Trading logic: if price is less than the buy threshold, mean # reversion should drive price up. Algorithm invests 100% in the # asset. In the opposite case, mean reversion should drive price # down. Algorithm invests 50% in cash and 50% in the asset. If # price is between buy and sell thresholds, algorithm invests 25% # in cash and 75% in the asset. if price < buy_threshold: order_target_percent( context.asset, 1.0, ) elif price > sell_threshold: order_target_percent( context.asset, 0.5, ) else: order_target_percent( context.asset, 0.75, ) record( price=price, leverage=context.account.leverage, sma=sma, std=std, buy_threshold=buy_threshold, sell_threshold=sell_threshold, )
def _initialize(self): if self.initialized: return if self.is_live: exchange_orders = get_open_orders(self.asset) if exchange_orders is None: self.ignored_orderid = [] else: for o in exchange_orders: self.ignored_orderid.append(o.id) self.initialized = True
def _handle_buy_sell_decision(context, data, signal, price): orders = get_open_orders(context.asset) if len(orders) > 0: log.info('skipping bar until all open orders execute') return positions = context.portfolio.positions if context.position is None and context.asset in positions: position = positions[context.asset] context.position = dict(cost_basis=position['cost_basis'], amount=position['amount'], stop=None) action = None if context.position is not None: cost_basis = context.position['cost_basis'] amount = context.position['amount'] log.info( 'found {amount} positions with cost basis {cost_basis}'.format( amount=amount, cost_basis=cost_basis)) stop = context.position['stop'] target = cost_basis * (1 + context.TARGET) if price >= target: context.position['cost_basis'] = price context.position['stop'] = context.STOP stop_target = context.STOP_LOSS if stop is None else context.STOP if price < cost_basis * (1 - stop_target): log.info('executing stop loss') order( asset=context.asset, amount=-amount, limit_price=price * (1 - context.SLIPPAGE_ALLOWED), ) action = 0 context.position = None else: if signal == 'long': log.info('opening position') buy_amount = context.MAX_HOLDINGS / price order( asset=context.asset, amount=buy_amount, limit_price=price * (1 + context.SLIPPAGE_ALLOWED), ) context.position = dict(cost_basis=price, amount=buy_amount, stop=None) action = 0
def initialize(self): if self.initialized: return try: self.is_live = self.context.mode_name == 'live' except: self.is_live = False if self.is_live: exchange_orders = get_open_orders(self.context.asset) if exchange_orders is None: self.ignored_orderid = [] else: for o in exchange_orders: self.ignored_orderid.append(o.id) self.initialized = True
def handle_data(context, data): sym = symbol("XRP_BTC") #res = data.can_trade(sym) #history = data.history(symbol("bch_btc"), ['close'], # bar_count=11, # frequency='5T') #print('\nnow: %s\n%s' % (data.current_dt, history)) #if not hasattr(context, 'i'): # context.i = 0 #context.i += 1 #print(history) #get_order(5, return_price=True) print(get_open_orders(sym))
def rebalance(context, data): # To make market decisions, we're calculating the token's # moving average for the last 5 days. # We get the price history for the last 5 days. price_history = data.history(context.asset, fields='price', bar_count=5, frequency='1d') # Then we take an average of those 5 days. average_price = price_history.mean() # We also get the coin's current price. price = data.current(context.asset, 'price') # Cancel any outstanding orders orders = get_open_orders(context.asset) or [] for order in orders: cancel_order(order) # If our coin is currently listed on a major exchange if data.can_trade(context.asset): # If the current price is 1% above the 5-day average price, # we open a long position. If the current price is below the # average price, then we want to close our position to 0 shares. if price > (1.01 * average_price): # Place the buy order (positive means buy, negative means sell) order_target_percent(context.asset, .99) log.info("Buying %s" % (context.asset.symbol)) elif price < average_price: # Sell all of our shares by setting the target position to zero order_target_percent(context.asset, 0) log.info("Selling %s" % (context.asset.symbol)) # Use the record() method to track up to five custom signals. # Record Apple's current price and the average price over the last # five days. cash = context.portfolio.cash leverage = context.account.leverage record(price=price, average_price=average_price, cash=cash, leverage=leverage)
def handle_data(context, data): context.asset = symbol("btc_usd") current = data.history(context.asset, ['price', 'volume'], bar_count=1, frequency='1T') price = data.current(context.asset, 'price') volume = data.current(context.asset, 'volume') #pd.set_option("display.precision", 8) #print(history) context.pricing_data = context.pricing_data.append(current) context.csvwriter.writerow([ current.index[0], current.iloc[0]['price'], current.iloc[0]['volume'] ]) if context.index == 0 or context.index == 2 or context.index == 4: order(context.asset, 1) for item, orders in get_open_orders().items(): o = orders[0] get_order(o.sid) elif context.index == 6: order(context.asset, -1) elif context.index == 8: order(context.asset, -1) if context.portfolio.positions: position = context.portfolio.positions[context.asset] print("index={}".format(context.index)) print("amount={}, cost-basis={}".format(position.amount, position.cost_basis)) # # context.csvwriter.writerow([data.current_dt, # current.iloc[0]['price'], # current.iloc[0]['volume']]) #print(context.blotter.current_dt) #print(data.current_dt) context.index += 1
def handle_data(context, data): if context.index == 0: order(symbol("bat_btc"), -9.9, limit_price=0.00002630) print("blotter={}".format(context.blotter.orders)) #res = data.can_trade(sym) #history = data.history(symbol("bch_btc"), ['close'], # bar_count=11, # frequency='5T') #print('\nnow: %s\n%s' % (data.current_dt, history)) #if not hasattr(context, 'i'): # context.i = 0 #context.i += 1 #print(history) #get_order(5, return_price=True) print("open orders={}".format(get_open_orders(context.asset))) context.index += 1
def trade_logic(context, data): # Cancel any outstanding orders orders = get_open_orders(context.asset) or [] for order in orders: cancel_order(order) # Define base price and make initial trades to achieve target investment ratio of 0.5 order_target_percent(context.asset, 0.5) # Retrieve current asset price from pricing data price = data.current(context.asset, "price") # Compute portfolio cumulative return Portfolio_cumulative_return = (context.portfolio.portfolio_value / context.portfolio.starting_cash - 1) * 100 # Save values for later inspection record( price=price, cash=context.portfolio.cash, leverage=context.account.leverage, Portfolio_cumulative_return=Portfolio_cumulative_return, )
def handle_data(context, data): context.i += 1 starting_cash = context.portfolio.starting_cash target_hodl_value = context.TARGET_HODL_RATIO * starting_cash reserve_value = context.RESERVE_RATIO * starting_cash # Cancel any outstanding orders orders = get_open_orders(context.asset) or [] for order in orders: cancel_order(order) # Stop buying after passing the reserve threshold cash = context.portfolio.cash if cash <= reserve_value: context.is_buying = False # Retrieve current asset price from pricing data price = data.current(context.asset, 'price') # Check if still buying and could (approximately) afford another purchase if context.is_buying and cash > price: print('buying') # Place order to make position in asset equal to target_hodl_value order_target_value( context.asset, target_hodl_value, limit_price=price * 1.1, ) record( price=price, volume=data.current(context.asset, 'volume'), cash=cash, starting_cash=context.portfolio.starting_cash, leverage=context.account.leverage, )
def handle_data(context, data): today = data.current_dt.floor('1D') if today != context.current_day: context.current_day = today context.cmx_invent.cancel_all() if context.global_mode == 'live': context.cmx_account.update(data) context.cmx_risk.adjust_position_limits(data) context.cmx_risk.run(data) bid_price = 999 ask_price = 0 quantity = 100 print('{} @ {}'.format( context.portfolio.positions[context.asset].cost_basis, context.portfolio.positions[context.asset].amount)) if context.can_buy and context.cmx_risk.position < 1000: context.cmx_exec.send_orders({bid_price: quantity}, side.buy) #context.cmx_exec.send_orders({ask_price: -1 * quantity}, side.sell) else: context.can_sell = True context.can_buy = False if context.can_sell and context.cmx_risk.position > 0: context.cmx_exec.send_orders({ask_price: -1 * quantity}, side.sell) if context.cmx_risk.position != 0: logging.info('tester: risk.position = {} @ {}'.format( context.cmx_risk.position, context.cmx_risk.ts)) else: corders = context.blotter.open_orders[context.asset] eorders = get_open_orders(context.asset) logging.info('context orders: {}'.format(corders)) logging.info('exchange orders: {}'.format(eorders))
def _handle_data(context, data): price = data.current(context.asset, 'close') log.info('got price {price}'.format(price=price)) if price is None: log.warn('no pricing data') return prices = data.history(context.asset, fields='price', bar_count=1, frequency='1m') rsi = talib.RSI(prices.values, timeperiod=14)[-1] log.info('got rsi: {}'.format(rsi)) # Buying more when RSI is low, this should lower our cost basis if rsi <= 30: buy_increment = 1 elif rsi <= 40: buy_increment = 0.5 elif rsi <= 70: buy_increment = 0.1 else: buy_increment = None cash = context.portfolio.cash log.info('base currency available: {cash}'.format(cash=cash)) record(price=price) orders = get_open_orders(context.asset) if len(orders) > 0: log.info('skipping bar until all open orders execute') return is_buy = False cost_basis = None if context.asset in context.portfolio.positions: position = context.portfolio.positions[context.asset] cost_basis = position.cost_basis log.info( 'found {amount} positions with cost basis {cost_basis}'.format( amount=position.amount, cost_basis=cost_basis)) if position.amount >= context.TARGET_POSITIONS: log.info('reached positions target: {}'.format(position.amount)) return if price < cost_basis: is_buy = True elif position.amount > 0 and \ price > cost_basis * (1 + context.PROFIT_TARGET): profit = (price * position.amount) - (cost_basis * position.amount) log.info('closing position, taking profit: {}'.format(profit)) order_target_percent( asset=context.asset, target=0, limit_price=price * (1 - context.SLIPPAGE_ALLOWED), ) else: log.info('no buy or sell opportunity found') else: is_buy = True if is_buy: if buy_increment is None: return if price * buy_increment > cash: log.info('not enough base currency to consider buying') return log.info('buying position cheaper than cost basis {} < {}'.format( price, cost_basis)) limit_price = price * (1 + context.SLIPPAGE_ALLOWED) order(asset=context.asset, amount=buy_increment, limit_price=limit_price) pass
def handle_data(context, data): current_time = get_datetime().time() if current_time.hour == 0 and current_time.minute == 0: print('Current date is ' + str(get_datetime().date())) context.i += 1 if context.i < context.atr_time: return if not data.can_trade(context.asset): return starting_cash = context.portfolio.starting_cash current = data.current(context.asset, 'close') price = data.current(context.asset, 'price') last_price = data.history(context.asset, 'price', bar_count=context.atr_time - 1, frequency=context.tf) if order_vol == 0.1 and stop == 0.9975: if context.i % 60 == 0: ohlcv_data = data.history( context.asset, fields=['open', 'high', 'low', 'close', 'volume'], bar_count=1, frequency='H') get_ohlcv(database=db, exchange=exchange_name, pair='BTCUSDT', open=ohlcv_data.open, high=ohlcv_data.high, low=ohlcv_data.low, close=ohlcv_data.close, volume=ohlcv_data.volume, timestamp=datetime.timestamp(get_datetime())) bb_data = data.history(context.asset, 'close', bar_count=context.bb, frequency=context.tf) hlc_data = data.history(context.asset, fields=['high', 'low', 'close'], bar_count=context.atr_time, frequency=context.tf) upperband, middleband, lowerband = talib.BBANDS(bb_data, timeperiod=context.bb - 1, nbdevup=2, nbdevdn=2, matype=0) upperband, middleband, lowerband = upperband[-1], middleband[ -1], lowerband[-1] bb_range = upperband - lowerband record(price=price, starting_cash=starting_cash, cash=context.portfolio.cash, upperband=upperband, middleband=middleband, lowerband=lowerband, num_trades=context.num_trades, order_result=context.order_result) context.model = pyrenko.renko() optimal_brick = context.model.set_brick_size(HLC_history=hlc_data) context.model.build_history(prices=last_price) prev_dir = context.model.get_renko_directions() last_dir = prev_dir[-4:-1] if not context.is_open: if last_dir == context.open_trigger and bb_range > 500: order_target_percent(context.asset, order_vol, limit_price=current * 1.001) context.is_open = True context.started = get_open_orders(context.asset)[-1].dt context.order_price = get_open_orders(context.asset)[-1].limit context.amount = get_open_orders(context.asset)[-1].amount positions(db, type=algo_type, side='Buy', start=context.started, open_price=context.order_price, finish=None, close=None, amount=context.amount, status='Open', closed_by=None, exchange=exchange_name, timestamp=datetime.timestamp(get_datetime())) else: if current <= context.order_price * stop and stop != 0: close_id = order_target_percent(context.asset, 0, limit_price=current) context.is_open = False context.num_trades += 1 price_diff = current - context.order_price context.order_result.append(price_diff) context.finished = get_order(close_id).dt context.close_price = get_order(close_id).limit context.closed_by = 'Stop Loss' record(num_trades=context.num_trades, order_result=context.order_result) query = Position.select(fn.MAX(Position.id)) p = (Position.update({ 'finished': context.finished, 'closed_price': context.close_price, 'closed_by': context.closed_by, 'status': 'Closed' }).where(Position.id == query.scalar())) p.execute() else: if last_dir == context.close_trigger: close_id = order_target_percent(context.asset, 0, limit_price=current) context.model = pyrenko.renko() context.is_open = False price_diff = current - context.order_price context.order_result.append(price_diff) context.num_trades += 1 context.finished = get_order(close_id).dt context.close_price = get_order(close_id).limit context.closed_by = 'Algo' record(num_trades=context.num_trades, order_result=context.order_result) query = Position.select(fn.MAX(Position.id)) p = (Position.update({ 'finished': context.finished, 'closed_price': context.close_price, 'closed_by': context.closed_by, 'status': 'Closed' }).where(Position.id == query.scalar())) p.execute(db)
def handle_data(context, data): # define the windows for the moving averages short_window = 50 long_window = 200 # Skip as many bars as long_window to properly compute the average context.i += 1 if context.i < long_window: return # Compute moving averages calling data.history() for each # moving average with the appropriate parameters. We choose to use # minute bars for this simulation -> freq="1m" # Returns a pandas dataframe. short_data = data.history(context.asset, 'price', bar_count=short_window, frequency="1T", ) short_mavg = short_data.mean() long_data = data.history(context.asset, 'price', bar_count=long_window, frequency="1T", ) long_mavg = long_data.mean() # Let's keep the price of our asset in a more handy variable price = data.current(context.asset, 'price') # If base_price is not set, we use the current value. This is the # price at the first bar which we reference to calculate price_change. if context.base_price is None: context.base_price = price price_change = (price - context.base_price) / context.base_price # Save values for later inspection record(price=price, cash=context.portfolio.cash, price_change=price_change, short_mavg=short_mavg, long_mavg=long_mavg) # Since we are using limit orders, some orders may not execute immediately # we wait until all orders are executed before considering more trades. orders = get_open_orders(context.asset) if len(orders) > 0: return # Exit if we cannot trade if not data.can_trade(context.asset): return # We check what's our position on our portfolio and trade accordingly pos_amount = context.portfolio.positions[context.asset].amount # Trading logic if short_mavg > long_mavg and pos_amount == 0: # we buy 100% of our portfolio for this asset order_target_percent(context.asset, 1) elif short_mavg < long_mavg and pos_amount > 0: # we sell all our positions for this asset order_target_percent(context.asset, 0)
def handle_data(context, data): log.info('handling bar {}'.format(data.current_dt)) buying_price = data.current( context.trading_pairs[context.buying_exchange], 'price') log.info('price on buying exchange {exchange}: {price}'.format( exchange=context.buying_exchange.name.upper(), price=buying_price, )) selling_price = data.current( context.trading_pairs[context.selling_exchange], 'price') log.info('price on selling exchange {exchange}: {price}'.format( exchange=context.selling_exchange.name.upper(), price=selling_price, )) # If for example, # selling price = 50 # buying price = 25 # expected gap = 1 # If follows that, # selling price - buying price / buying price # 50 - 25 / 25 = 1 gap = (selling_price - buying_price) / buying_price log.info( 'the price gap: {gap} ({gap_percent}%)'.format( gap=gap, gap_percent=gap * 100 ) ) record(buying_price=buying_price, selling_price=selling_price, gap=gap) # Waiting for orders to close before initiating new ones for exchange in context.trading_pairs: asset = context.trading_pairs[exchange] orders = get_open_orders(asset) if orders: log.info( 'found {order_count} open orders on {exchange_name} ' 'skipping bar until all open orders execute'.format( order_count=len(orders), exchange_name=exchange.name ) ) return # Consider the least ambitious entry point first # Override of wider gap is found entry_points = sorted( context.entry_points, key=lambda point: point['gap'], ) buy_amount = None for entry_point in entry_points: if gap > entry_point['gap']: buy_amount = entry_point['amount'] if buy_amount: log.info('found buy trigger for amount: {}'.format(buy_amount)) place_orders( context=context, amount=buy_amount, buying_price=buying_price, selling_price=selling_price, action='enter' ) else: # Consider the narrowest exit gap first # Override of wider gap is found exit_points = sorted( context.exit_points, key=lambda point: point['gap'], reverse=True ) sell_amount = None for exit_point in exit_points: if gap < exit_point['gap']: sell_amount = exit_point['amount'] if sell_amount: log.info('found sell trigger for amount: {}'.format(sell_amount)) place_orders( context=context, amount=sell_amount, buying_price=buying_price, selling_price=selling_price, action='exit' )
def handle_data(context, data): # This handle_data function is where the real work is done. Our data is # minute-level tick data, and each minute is called a frame. This function # runs on each frame of the data. # We flag the first period of each day. # Since cryptocurrencies trade 24/7 the `before_trading_starts` handle # would only execute once. This method works with minute and daily # frequencies. today = data.current_dt.floor('1D') if today != context.current_day: context.traded_today = False context.current_day = today # We're computing the volume-weighted-average-price of the security # defined above, in the context.market variable. For this example, we're # using three bars on the 15 min bars. # The frequency attribute determine the bar size. We use this convention # for the frequency alias: # http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases prices = data.history(context.market, fields='close', bar_count=50, frequency=context.CANDLE_SIZE) # Ta-lib calculates various technical indicator based on price and # volume arrays. # In this example, we are comp rsi = talib.RSI(prices.values, timeperiod=14) # We need a variable for the current price of the security to compare to # the average. Since we are requesting two fields, data.current() # returns a DataFrame with current = data.current(context.market, fields=['close', 'volume']) price = current['close'] # If base_price is not set, we use the current value. This is the # price at the first bar which we reference to calculate price_change. if context.base_price is None: context.base_price = price price_change = (price - context.base_price) / context.base_price cash = context.portfolio.cash # Now that we've collected all current data for this frame, we use # the record() method to save it. This data will be available as # a parameter of the analyze() function for further analysis. record(volume=current['volume'], price=price, price_change=price_change, rsi=rsi[-1], cash=cash) # We are trying to avoid over-trading by limiting our trades to # one per day. if context.traded_today: return # TODO: retest with open orders # Since we are using limit orders, some orders may not execute immediately # we wait until all orders are executed before considering more trades. orders = get_open_orders(context.market) if len(orders) > 0: log.info('exiting because orders are open: {}'.format(orders)) return # Exit if we cannot trade if not data.can_trade(context.market): return # Another powerful built-in feature of the Catalyst backtester is the # portfolio object. The portfolio object tracks your positions, cash, # cost basis of specific holdings, and more. In this line, we calculate # how long or short our position is at this minute. pos_amount = context.portfolio.positions[context.market].amount if rsi[-1] <= context.RSI_OVERSOLD and pos_amount == 0: log.info('{}: buying - price: {}, rsi: {}'.format( data.current_dt, price, rsi[-1])) # Set a style for limit orders, limit_price = price * 1.005 order_target_percent(context.market, 1, limit_price=limit_price) context.traded_today = True elif rsi[-1] >= context.RSI_OVERBOUGHT and pos_amount > 0: log.info('{}: selling - price: {}, rsi: {}'.format( data.current_dt, price, rsi[-1])) limit_price = price * 0.995 order_target_percent(context.market, 0, limit_price=limit_price) context.traded_today = True
def handle_data(context, data): # This handle_data function is where the real work is done. Our data is # minute-level tick data, and each minute is called a frame. This function # runs on each frame of the data. # We flag the first period of each day. # Since cryptocurrencies trade 24/7 the `before_trading_starts` handle # would only execute once. This method works with minute and daily # frequencies. today = data.current_dt.floor('1D') if today != context.current_day: context.traded_today = False context.current_day = today # We're computing the volume-weighted-average-price of the security # defined above, in the context.market variable. For this example, we're # using three bars on the 15 min bars. # The frequency attribute determine the bar size. We use this convention # for the frequency alias: # http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases prices = data.history( context.market, fields='close', bar_count=50, frequency=context.CANDLE_SIZE ) # Ta-lib calculates various technical indicator based on price and # volume arrays. # In this example, we are comp rsi = talib.RSI(prices.values, timeperiod=14) # We need a variable for the current price of the security to compare to # the average. Since we are requesting two fields, data.current() # returns a DataFrame with current = data.current(context.market, fields=['close', 'volume']) price = current['close'] # If base_price is not set, we use the current value. This is the # price at the first bar which we reference to calculate price_change. if context.base_price is None: context.base_price = price price_change = (price - context.base_price) / context.base_price cash = context.portfolio.cash # Now that we've collected all current data for this frame, we use # the record() method to save it. This data will be available as # a parameter of the analyze() function for further analysis. record( volume=current['volume'], price=price, price_change=price_change, rsi=rsi[-1], cash=cash ) # We are trying to avoid over-trading by limiting our trades to # one per day. if context.traded_today: return # TODO: retest with open orders # Since we are using limit orders, some orders may not execute immediately # we wait until all orders are executed before considering more trades. orders = get_open_orders(context.market) if len(orders) > 0: log.info('exiting because orders are open: {}'.format(orders)) return # Exit if we cannot trade if not data.can_trade(context.market): return # Another powerful built-in feature of the Catalyst backtester is the # portfolio object. The portfolio object tracks your positions, cash, # cost basis of specific holdings, and more. In this line, we calculate # how long or short our position is at this minute. pos_amount = context.portfolio.positions[context.market].amount if rsi[-1] <= context.RSI_OVERSOLD and pos_amount == 0: log.info( '{}: buying - price: {}, rsi: {}'.format( data.current_dt, price, rsi[-1] ) ) # Set a style for limit orders, limit_price = price * 1.005 order_target_percent( context.market, 1, limit_price=limit_price ) context.traded_today = True elif rsi[-1] >= context.RSI_OVERBOUGHT and pos_amount > 0: log.info( '{}: selling - price: {}, rsi: {}'.format( data.current_dt, price, rsi[-1] ) ) limit_price = price * 0.995 order_target_percent( context.market, 0, limit_price=limit_price ) context.traded_today = True
def handle_data(context, data): # This handle_data function is where the real work is done. Our data is # minute-level tick data, and each minute is called a frame. This function # runs on each frame of the data. # We flag the first period of each day. # Since cryptocurrencies trade 24/7 the `before_trading_starts` handle # would only execute once. This method works with minute and daily # frequencies. today = data.current_dt.floor('1D') if today != context.current_day: context.traded_today = False context.current_day = today # We're computing the volume-weighted-average-price of the security # defined above, in the context.eth_btc variable. For this example, we're # using three bars on the 15 min bars. # The frequency attribute determine the bar size. We use this convention # for the frequency alias: # http://pandas.pydata.org/pandas-docs/stable/timeseries.html#offset-aliases prices = data.history(context.eth_btc, fields='close', bar_count=50, frequency='15T') # Ta-lib calculates various technical indicator based on price and # volume arrays. # In this example, we are comp rsi = talib.RSI(prices.values, timeperiod=14) upper, middle, lower = talib.BBANDS(prices.values, timeperiod=20, nbdevup=2, nbdevdn=2, matype=MA_Type.EMA) # We need a variable for the current price of the security to compare to # the average. Since we are requesting two fields, data.current() # returns a DataFrame with current = data.current(context.eth_btc, fields=['close', 'volume']) price = current['close'] # If base_price is not set, we use the current value. This is the # price at the first bar which we reference to calculate price_change. if context.base_price is None: context.base_price = price price_change = (price - context.base_price) / context.base_price cash = context.portfolio.cash # Now that we've collected all current data for this frame, we use # the record() method to save it. This data will be available as # a parameter of the analyze() function for further analysis. record(price=price, volume=current['volume'], upper_band=upper[-1], lower_band=lower[-1], price_change=price_change, rsi=rsi[-1], cash=cash) # We are trying to avoid over-trading by limiting our trades to # one per day. if context.traded_today: return # Since we are using limit orders, some orders may not execute immediately # we wait until all orders are executed before considering more trades. orders = get_open_orders(context.eth_btc) if len(orders) > 0: return # Exit if we cannot trade if not data.can_trade(context.eth_btc): return # Another powerful built-in feature of the Catalyst backtester is the # portfolio object. The portfolio object tracks your positions, cash, # cost basis of specific holdings, and more. In this line, we calculate # how long or short our position is at this minute. pos_amount = context.portfolio.positions[context.eth_btc].amount # In this example, we're using a trigger instead of buying directly after # a signal. Since this is mean reversion, our signals go against the # momentum. Using a trigger allow us to spot the opportunity but trade # only when a trade reversal begins. if context.trigger is not None: # The tread_direction() method determines the trend based on the last # two bars of the series. direction = trend_direction(rsi) if context.trigger[1] == 'buy' and direction == 'up': log.info('{}: buying - price: {}, rsi: {}, bband: {}'.format( data.current_dt, price, rsi[-1], lower[-1])) order_target_percent(context.eth_btc, 1) context.traded_today = True context.trigger = None elif context.trigger[1] == 'sell' and direction == 'down': log.info('{}: selling - price: {}, rsi: {}, bband: {}'.format( data.current_dt, price, rsi[-1], upper[-1])) order_target_percent(context.eth_btc, 0) context.traded_today = True context.trigger = None # If we found a signal but no trade reversal within two hours, we # reset the trigger. elif context.trigger[0] + timedelta(hours=2) < data.current_dt: context.trigger = None else: # Determining the entry and exit signals based on RSI and SMA if rsi[-1] <= 30 and pos_amount == 0: context.trigger = (data.current_dt, 'buy') elif rsi[-1] >= 80 and pos_amount > 0: context.trigger = (data.current_dt, 'sell')