def submit_sell(stock, context, data): if get_open_orders(stock): return # We bought a stock but don't know it's age yet if stock not in context.age: context.age[stock] = 0 # Don't sell stuff that's less than 1 day old if stock in context.age and context.age[stock] < 1: return shares = context.portfolio.positions[stock].amount current_price = float(data.current([stock], 'price')) cost_basis = float(context.portfolio.positions[stock].cost_basis) if (context.age[stock] >= context.MyFireSaleAge and (current_price < context.MyFireSalePrice or current_price < cost_basis)): log.info("%s is in fire sale!" % stock.symbol) sell_price = float(make_div_by_05(.95 * current_price, buy=False)) order(stock, -shares, style=LimitOrder(sell_price)) else: sell_price = float( make_div_by_05(cost_basis * context.sell_factor, buy=False)) order(stock, -shares, style=LimitOrder(sell_price))
def my_rebalance(context, data): log.info("my_rebalance") BuyFactor = .99 SellFactor = 1.01 cash = context.portfolio.cash log.info(cash) cancel_open_buy_orders(context, data) # Order sell at profit target in hope that somebody actually buys it for stock in context.portfolio.positions: if not get_open_orders(stock): StockShares = context.portfolio.positions[stock].amount CurrPrice = float(data.current([stock], 'price')) CostBasis = float(context.portfolio.positions[stock].cost_basis) SellPrice = float(make_div_by_05(CostBasis * SellFactor, buy=False)) if np.isnan(SellPrice): pass # probably best to wait until nan goes away elif (stock in context.age and context.age[stock] == 1): pass elif (stock in context.age and context.MyFireSaleAge <= context.age[stock] and (context.MyFireSalePrice > CurrPrice or CostBasis > CurrPrice)): if (stock in context.age and context.age[stock] < 2): pass elif stock not in context.age: context.age[stock] = 1 else: SellPrice = float( make_div_by_05(.95 * CurrPrice, buy=False)) order(stock, -StockShares, style=LimitOrder(SellPrice)) else: if (stock in context.age and context.age[stock] < 2): pass elif stock not in context.age: context.age[stock] = 1 else: order(stock, -StockShares, style=LimitOrder(SellPrice)) WeightThisBuyOrder = float(1.00 / context.MaxBuyOrdersAtOnce) for ThisBuyOrder in range(context.MaxBuyOrdersAtOnce): stock = next(context.MyCandidate) PH = data.history([stock], 'price', 20, '1d') PH_Avg = float(PH.mean()) CurrPrice = float(data.current([stock], 'price')) if np.isnan(CurrPrice): pass # probably best to wait until nan goes away else: if CurrPrice > float(1.25 * PH_Avg): BuyPrice = float(CurrPrice) else: BuyPrice = float(CurrPrice * BuyFactor) BuyPrice = float(make_div_by_05(BuyPrice, buy=True)) StockShares = int(WeightThisBuyOrder * cash / BuyPrice) order(stock, StockShares, style=LimitOrder(BuyPrice))
def my_rebalance(context, data): BuyFactor = .99 SellFactor = 1.01 cash = context.portfolio.cash cancel_open_buy_orders(context, data) log.info("My Rebalance.") # Order sell at profit target in hope that somebody actually buys it open_orders = get_open_orders() for stock in context.portfolio.positions: print(stock.sid) print(get_open_orders(stock.sid)) if stock in open_orders: pass else: print("no orders") StockShares = context.portfolio.positions[stock].amount CurrPrice = float(data.current([stock], 'price')) CostBasis = float(context.portfolio.positions[stock].cost_basis) SellPrice = float(make_div_by_05(CostBasis * SellFactor, buy=False)) if np.isnan(SellPrice): log.info("My Rebalance: NaN") pass # probably best to wait until nan goes away elif (stock in context.age and context.age[stock] == 0): log.info("My Rebalance: Age") pass elif (stock in context.age and context.MyFireSaleAge <= context.age[stock] and (context.MyFireSalePrice > CurrPrice or CostBasis > CurrPrice)): log.info("My Rebalance: Losing") if (stock in context.age and context.age[stock] < 2): pass elif stock not in context.age: context.age[stock] = 1 else: SellPrice = float( make_div_by_05(.95 * CurrPrice, buy=False)) order(stock, -StockShares, style=LimitOrder(SellPrice)) else: if (stock in context.age and context.age[stock] < 1): log.info("My Rebalance: too young") pass elif stock not in context.age: log.info("My Rebalance: no age") context.age[stock] = 1 else: log.info("My Rebalance: Place order") order(stock, -StockShares, style=LimitOrder(SellPrice))
def my_rebalance(context, data): BuyFactor = .99 SellFactor = 1.1 cash = context.portfolio.cash cancel_open_buy_orders(context, data) log.info("My Rebalance: ") WeightThisBuyOrder = float(1.00 / context.MaxBuyOrdersAtOnce) for ThisBuyOrder in range(context.MaxBuyOrdersAtOnce): stock = next(context.MyCandidate) PH = data.history([stock], 'price', 20, '1d') PH_Avg = float(PH.mean()) CurrPrice = float(data.current([stock], 'price')) if np.isnan(CurrPrice): pass # probably best to wait until nan goes away else: if CurrPrice > float(1.25 * PH_Avg): BuyPrice = float(CurrPrice) else: BuyPrice = float(CurrPrice * BuyFactor) BuyPrice = float(make_div_by_05(BuyPrice, buy=True)) StockShares = int(WeightThisBuyOrder * cash / BuyPrice) order(stock, StockShares, style=LimitOrder(BuyPrice) ) place_sells(context,data)
def buy(context, data): cash_accum = 0 for security in context.candidates: if security not in context.portfolio.positions and data.can_trade(security): if len(context.portfolio.positions) < 10: price_share = data.current(security, 'close') cost = get_cost(context.my_output.get_value(security, 'atr'), context.portfolio.portfolio_value, price_share) if cost < (context.portfolio.cash - cash_accum) and is_trade(context, data): order_value(security, cost, style=LimitOrder(limit_price=price_share)) context.tmp_tracker[security.symbol] = context.my_output.get_value(security, 'atr') cash_accum = cash_accum + cost
def rebalance(context, data): """Rebalance the portfolio based on context.stocks""" cancel_all_orders(context, data) sell_stocks_not_in_portfolio(context, data) LOG.info("rebalancing") LOG.info(context.stocks) totals = calculate_totals(context, data) LOG.info("totals calculated: %s" % totals) for stock, info in totals.items(): order(stock, info["total"], style=LimitOrder(info["price"]))
def enter_sells(context, data): # get the current prices and calculate order values and shares update_stock_data(context, context.output, data) # If order shares is negative (ie a sell) and value is greater than our min adjust threshold rules = 'order_shares < 0 and order_value > @MIN_ADJUST_AMT' sells = context.output.query(rules).index.tolist() for stock in sells: order_id = order(stock, context.output.get_value(stock, 'order_shares'), style=LimitOrder(context.output.get_value(stock, 'latest_price')) ) # store the order id in case we need to retry the order context.todays_orders.append(order_id)
def enter_buys(context, data): # get the current prices and calculate order values and shares update_stock_data(context, context.output, data) adjust_buy_orders_per_available_cash(context, data) # Order shares is positive (ie a buy) and value greater than our min adjust threshold rules = 'order_shares > 0 and order_value > @MIN_ADJUST_AMT' buys = context.output.query(rules).index.tolist() for stock in buys: order_id = order(stock, context.output.get_value(stock, 'order_shares'), style=LimitOrder(context.output.get_value(stock, 'latest_price')) ) # store the order id in case we need to retry the order context.todays_orders.append(order_id)
def __convert_order_params_for_blotter(limit_price, stop_price, style): """ Helper method for converting deprecated limit_price and stop_price arguments into ExecutionStyle instances. This function assumes that either style == None or (limit_price, stop_price) == (None, None). """ if style: assert (limit_price, stop_price) == (None, None) return style if limit_price and stop_price: return StopLimitOrder(limit_price, stop_price) if limit_price: return LimitOrder(limit_price) if stop_price: return StopOrder(stop_price) else: return MarketOrder()
def retry_cancelled_order(context, data): ''' Every once in awhile Robinhood orders get mysteriously cancelled. This checks the order status and re-submits any cancelled orders. ''' for order_id in context.todays_orders[:]: original_order = get_order(order_id) if original_order and original_order.status == 2 : # The order was somehow cancelled so retry retry_id = order( original_order.sid, original_order.amount, style=LimitOrder(original_order.limit) ) log.info('order for %i shares of %s cancelled - retrying' % (original_order.amount, original_order.sid)) # Remove the original order (typically can't do but note the [:]) and store the new order context.todays_orders.remove(original_order) context.todays_orders.append(retry_id)
def submit_buy(stock, context, data, weight): cash = min( investment_limits(context)['remaining_to_invest'], context.portfolio.cash) price_history = data.history([stock], 'price', 20, '1d') average_price = float(price_history.mean()) current_price = float(data.current([stock], 'price')) if np.isnan(current_price): pass # probably best to wait until nan goes away else: if current_price > float( 1.25 * average_price): # if the price is 25% above the 20d avg buy_price = float(current_price) else: # Otherwise buy at a discount buy_price = float(current_price * context.buy_factor) buy_price = float(make_div_by_05(buy_price, buy=True)) shares_to_buy = int(weight * cash / buy_price) max_exposure = int(weight * context.portfolio.portfolio_value / buy_price) # Prevent over exposing to a particular stock, never own more than 1/max_buy_orders # of our account value positions = context.portfolio.positions if stock in positions and positions[stock].amount >= max_exposure: return # This cancels open sales that would prevent these buys from being submitted if running # up against the PDT rule open_orders = get_open_orders() if stock in open_orders: for open_order in open_orders[stock]: cancel_order(open_order) order(stock, shares_to_buy, style=LimitOrder(buy_price))
def test_orders(): backend = alpaca.Backend('key-id', 'secret-key') with patch.object(backend, '_api') as _api: _api.list_assets.return_value = [ Asset({ 'id': 'bcfdb21a-760c-44a6-a3af-6264851b5c1b', 'asset_class': 'us_equity', 'exchange': 'NYSE', 'symbol': 'X', 'status': 'inactive', 'tradable': True }), Asset({ 'id': '93f58d0b-6c53-432d-b8ce-2bad264dbd94', 'asset_class': 'us_equity', 'exchange': 'NASDAQ', 'symbol': 'AAPL', 'status': 'active', 'tradable': True }), Asset({ 'id': '8688f60a-04c9-4740-8468-c0b994499f41', 'asset_class': 'us_equity', 'exchange': 'NYSE', 'symbol': 'BAC', 'status': 'active', 'tradable': True }), ] res = backend._symbols2assets(['AAPL']) assert len(res) == 1 _api.get_account.return_value = Account({ 'account_blocked': False, 'buying_power': '43.38', 'cash': '35036.18', 'cash_withdrawable': '43.38', 'created_at': '2018-08-27T18:33:56.812574Z', 'currency': 'USD', 'id': 'da66e4e6-db7e-4c2e-83ae-2e0cce995cf2', 'pattern_day_trader': False, 'portfolio_value': '49723.85', 'status': 'ACTIVE', 'trading_blocked': False, 'transfers_blocked': False }) res = backend.account assert res.buying_power < 100 _api.list_positions.return_value = [ Position({ 'asset_class': 'us_equity', 'asset_id': '93f58d0b-6c53-432d-b8ce-2bad264dbd94', 'avg_entry_price': '198', 'change_today': '0', 'cost_basis': '200', 'current_price': '200', 'exchange': 'NASDAQ', 'lastday_price': '200', 'market_value': '200', 'qty': '1', 'side': 'long', 'symbol': 'AAPL', 'unrealized_intraday_pl': '0', 'unrealized_intraday_plpc': '0', 'unrealized_pl': '2', 'unrealized_plpc': '0.01111' }), ] algo = Mock() algo._backend = backend algo.symbol = lambda x: backend._symbols2assets([x])[0] with LiveTraderAPI(algo): res = backend.portfolio assert res.cash > 30e3 assert len(res.positions) == 1 _api.list_orders.return_value = [ Order({ 'asset_class': 'us_equity', 'asset_id': '93f58d0b-6c53-432d-b8ce-2bad264dbd94', 'canceled_at': None, 'client_order_id': 'my_id_open', 'created_at': '2018-08-29T13:31:02.779465Z', 'expired_at': None, 'failed_at': None, 'filled_at': None, 'filled_avg_price': None, 'filled_qty': '0', 'id': '6abca255-bc5a-4688-a547-4bfd2c33a979', 'limit_price': '1.3', 'order_type': 'limit', 'qty': '3846', 'side': 'buy', 'status': 'new', 'stop_price': None, 'submitted_at': '2018-08-29T13:31:02.779394Z', 'symbol': 'AAPL', 'time_in_force': 'day', 'type': 'limit', 'updated_at': '2018-08-30T19:59:00.737786Z' }), Order({ 'asset_class': 'us_equity', 'asset_id': '93f58d0b-6c53-432d-b8ce-2bad264dbd94', 'canceled_at': None, 'client_order_id': 'my_id_failed', 'created_at': '2018-08-29T13:31:02.779465Z', 'expired_at': None, 'failed_at': '2018-08-29T13:31:02.779465Z', 'filled_at': None, 'filled_avg_price': None, 'filled_qty': '0', 'id': '6abca255-bc5a-4688-a547-4bfd2c33a979', 'limit_price': '1.3', 'order_type': 'limit', 'qty': '3846', 'side': 'buy', 'status': 'new', 'stop_price': None, 'submitted_at': '2018-08-29T13:31:02.779394Z', 'symbol': 'AAPL', 'time_in_force': 'day', 'type': 'limit', 'updated_at': '2018-08-30T19:59:00.737786Z' }), Order({ 'asset_class': 'us_equity', 'asset_id': '93f58d0b-6c53-432d-b8ce-2bad264dbd94', 'canceled_at': None, 'client_order_id': 'my_id_filled', 'created_at': '2018-08-29T13:31:02.779465Z', 'expired_at': None, 'failed_at': None, 'filled_at': '2018-08-29T13:31:02.779465Z', 'filled_avg_price': '200', 'filled_qty': '0', 'id': '6abca255-bc5a-4688-a547-4bfd2c33a979', 'limit_price': '1.3', 'order_type': 'limit', 'qty': '3846', 'side': 'buy', 'status': 'new', 'stop_price': None, 'submitted_at': '2018-08-29T13:31:02.779394Z', 'symbol': 'AAPL', 'time_in_force': 'day', 'type': 'limit', 'updated_at': '2018-08-30T19:59:00.737786Z' }), ] res = backend.orders # make sure order status is set correctly assert res['my_id_open']._status == ZP_ORDER_STATUS.OPEN assert res['my_id_failed']._status == ZP_ORDER_STATUS.REJECTED assert res['my_id_filled']._status == ZP_ORDER_STATUS.FILLED _api.submit_order.return_value = Order({ 'asset_class': 'us_equity', 'asset_id': '93f58d0b-6c53-432d-b8ce-2bad264dbd94', 'canceled_at': None, 'client_order_id': '439dca01703b4674a61a72713a612d24', 'created_at': '2018-08-29T13:31:01.710698Z', 'expired_at': None, 'failed_at': None, 'filled_at': None, 'filled_avg_price': None, 'filled_qty': '0', 'id': '2c366657-fdbd-4554-a14d-b19df2bf430c', 'limit_price': '2.05', 'order_type': 'limit', 'qty': '1', 'side': 'buy', 'status': 'new', 'stop_price': None, 'submitted_at': '2018-08-29T13:31:01.710651Z', 'symbol': 'AAPL', 'time_in_force': 'day', 'type': 'limit', 'updated_at': '2018-08-30T19:59:00.553942Z' }) aapl = algo.symbol('AAPL') # this response is not correct logically, but fine for testing res = backend.order(aapl, 1, MarketOrder()) assert res.limit > 1 # different order types should go through backend.order(aapl, 1, LimitOrder(limit_price=100)) backend.order(aapl, 1, StopOrder(stop_price=200)) backend.order(aapl, 1, StopLimitOrder(limit_price=100, stop_price=200)) backend.cancel_order('some-id') # order submission fail _api.submit_order.side_effect = APIError({'message': 'test'}) res = backend.order(aapl, -1, MarketOrder()) assert res is None
def place_sells(context, data): SellFactor = 1.1 cash = context.portfolio.cash open_orders = get_open_orders() place_sell_orders = 1 # exit if we have open sell orders if len(open_orders) != 0: for stock, orders in open_orders.items(): for o in orders: if 0 > o.amount: # it is a sell order place_sell_orders = 0 if place_sell_orders == 1: log.info("Place sells") # Order sell at profit target in hope that somebody actually buys it for stock in context.portfolio.positions: if stock in open_orders: pass else: StockShares = context.portfolio.positions[stock].amount CurrPrice = float(data.current([stock], 'price')) CostBasis = float(context.portfolio.positions[stock].cost_basis) SellPrice = float( make_div_by_05( CostBasis * SellFactor, buy=False)) print(stock) print(StockShares) if np.isnan(SellPrice): pass # probably best to wait until nan goes away elif (stock in context.age and context.age[stock] < 1): print("age < 1.0") pass elif ( stock in context.age and context.MyFireSaleAge <= context.age[stock] and ( context.MyFireSalePrice > CurrPrice or CostBasis > CurrPrice ) ): if (stock in context.age and context.age[stock] < 1): print("age < 1.1") pass elif stock not in context.age: print("age = 1") context.age[stock] = 1 else: print("sell loss") SellPrice = float( make_div_by_05(.95 * CurrPrice, buy=False)) order(stock, -StockShares, style=LimitOrder(SellPrice) ) else: if (stock in context.age and context.age[stock] < 1): print("age < 1") pass elif stock not in context.age: print("age = 0") context.age[stock] = 1 else: print("sell win") order(stock, -StockShares, style=LimitOrder(SellPrice) )