My code shown below has been modified to mask the original values, I'll explain the functions of the mentioned code and ultimately I'd like to have this be ported to QuantConnect to be traded using Interactive Brokers
The strategy is a Sole Short Selling strategy, in the code just for the sake of proving a point a backtest was initiated on 09/08/2017 utilizing only market orders. This was important to determine the ability to #1 get filled and #2 see if the most unreasonable execution could yield a profit.
The strategy also relies on there being shares available for shorting at Interactive Brokers
Code Function Breakdown -
Call Open, High, Low, Close, Volume Data
Calculate last day's momentum - Unreasonable, however it has proven profitable
Compute corporate values (book,sales,free cash flow) morning star - sort worst to best
With corporate values determine quality gross profit, balance sheet totals - sort worst to best
Using pandas create a custom volatility factor generated using the last 3 trading day closes.
- Startup
Rank results based on momentum and utilize only those with a momentum below 0.7 pick the top 5
Universe filter - Average volume above N for liquidity
call the last price to be used in the universe filter
calculate average volume on as a 5 day sma
calculate RSI in a percentage
OH Range - 5 day HIGH SMA MINUS 5 DAY OPEN SMA
mean_range = OH Range divided by last price * 100
call value
call momentum
call volality
filter by vol_range is average volume > greater than a set value to ensure liquidity
filter by rsi_range = rsi percent greater than and less than set values
filter by OH_range_percent - mean range between two predefined values
define dollar volume = averagedollarvolume(5 day sma)
filter by high_volume = dollar_volume percentile between 20,90
define price = usequity.close.latest
filter by at most price
filter by at least price
initiate pipe screen to include
high_volume, at most price, at least price, volume range, rsi_range, OH range
utilize the S&P as a bench mark
for an orderly market when managing larger sums implement slippage
the strategy rebalances every day on market open
open orders cancelled at market close
positions are closed out once the momentum factor exceeds the universe filter
In exchange for your assistance you will learn a new strategy and will receive the original quantopian code which yielded the above results exceeding 300% in less than one year.
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.factors import CustomFactor, AverageDollarVolume
from quantopian.pipeline.factors import SimpleMovingAverage
from quantopian.pipeline.factors import RSI
from quantopian.pipeline.data import morningstar
from zipline.finance.asset_restrictions import HistoricalRestrictions, Restriction, RESTRICTION_STATES as states
from datetime import timedelta
import pandas as pd
import numpy as np
class Value(CustomFactor):
inputs = [morningstar.valuation_ratios.book_value_yield,
morningstar.valuation_ratios.sales_yield,
morningstar.valuation_ratios.fcf_yield]
window_length = 1
def compute(self, today, assets, out, book_value, sales, fcf):
value_table = pd.DataFrame(index=assets)
value_table["book_value"] = book_value[-1]
value_table["sales"] = sales[-1]
value_table["fcf"] = fcf[-1]
out[:] = value_table.rank().mean(axis=1)
class Momentum(CustomFactor):
inputs = [USEquityPricing.close]
window_length = 1
def compute(self, today, assets, out, close):
out[:] = close[-1] / close[0]
class Quality(CustomFactor):
inputs = [morningstar.income_statement.gross_profit, morningstar.balance_sheet.total_assets]
window_length = 1
def compute(self, today, assets, out, gross_profit, total_assets):
out[:] = gross_profit[-1] / total_assets[-1]
class Volatility(CustomFactor):
inputs = [USEquityPricing.close]
window_length = 3
def compute(self, today, assets, out, close):
close = pd.DataFrame(data=close, columns=assets)
# Since we are going to rank largest is best we need to invert the sdev.
out[:] = 1 / np.log(close).diff().std()
# Compute final rank and assign long and short baskets.
def before_trading_start(context, data):
results = pipeline_output('factors').dropna()
ranks = results.rank().mean(axis=1).order()
context.shorts = 1 / ranks[results["momentum"] < .7].head(5)
context.shorts /= context.shorts.sum()
context.security_list = context.shorts.index.tolist()
# Put any initialization logic here. The context object will be passed to
# the other methods in your algorithm.
def initialize(context):
pipe = Pipeline()
pipe = attach_pipeline(pipe, name='factors')
last_price = USEquityPricing.close.latest
#factors
avg_vol = SimpleMovingAverage(inputs=[USEquityPricing.volume],window_length=5)
# RSI Relative Strength Indicator would give stocks that are most
rsi_percentage = RSI(inputs=[USEquityPricing.close], window_length=5)
#Open to High range.
OH_range = (SimpleMovingAverage(inputs=[USEquityPricing.high],window_length=5)
- SimpleMovingAverage(inputs=[USEquityPricing.open],window_length=5))
mean_range = (OH_range / last_price) * 100
#filters
value = Value()
momentum = Momentum()
quality = Quality()
volatility = Volatility()
vol_range = avg_vol > 7000
rsi_range = ((rsi_percentage < 100) & (rsi_percentage > 50))
OH_range_pct = ((mean_range > 1) & (mean_range < 100))
pipe.add(value, "value")
pipe.add(momentum, "momentum")
pipe.add(quality, "quality")
pipe.add(volatility, "volatility")
pipe.add(avg_vol, 'Volume')
pipe.add(rsi_percentage, 'RSI')
pipe.add(mean_range, 'Volatility')
dollar_volume = AverageDollarVolume(window_length=5)
high_volume = dollar_volume.percentile_between(20,90)
price = USEquityPricing.close.latest
AtMostPrice = (price <= 35)
AtLeastPrice = (price >= 2)
# Screen out low liquidity securities.
pipe.set_screen(
high_volume
& AtMostPrice
& AtLeastPrice
& vol_range
& rsi_range
& OH_range_pct
)
context.spy = sid(8554)
context.shorts = None
context.longs = None
#set_commission(commission.PerShare(cost=0.008, min_trade_cost=1))
#set_commission(commission.PerTrade(cost=0.00))
set_slippage(slippage.VolumeShareSlippage(volume_limit=0.15,
price_impact=0.00))
schedule_function(rebalance,
date_rules.every_day(),
time_rules.market_open())
#schedule_function(noovernight, date_rules.every_day(), time_rules.market_close(minutes=30))
schedule_function(cancel_open_orders,
date_rules.every_day(),
time_rules.market_close())
schedule_function(record_vars,
date_rules.every_day(),
time_rules.market_close())
schedule_function(screen_time,
date_rules.every_day(),
time_rules.market_open(minutes=1))
# Will be called on every trade event for the securities you specify.
def record_vars(context, data):
record(lvg=context.account.leverage,
exposure=context.account.net_leverage,
pos_count=len(context.portfolio.positions),
oo=len(get_open_orders()))
def screen_time(context,data):
results = pipeline_output('factors')
context.pipeline_output = results.sort_values('momentum', ascending = True).iloc[:5]
#Total number of securities that the fit the strategy
print 'Total Securities in the Universe: %d' % len(results)
#Stocks to be traded
log.info("\n" + str(context.pipeline_output.head(5)))
log.info("\n" + str(len(context.pipeline_output.index)))
def cancel_open_orders(context, data):
open_orders = get_open_orders()
for security in open_orders:
for order in open_orders[security]:
cancel_order(order)
def rebalance(context, data):
for security in context.shorts.index:
if get_open_orders(security):
continue
if data.can_trade(security):
order_target_percent(security, -context.shorts[security])
for security in context.portfolio.positions:
for order in context.portfolio.positions:
continue
if data.can_trade(security) and security not in context.security_list:
order_target_percent(security, 0)
def noovernight(context,data):
for security in context.portfolio.positions:
order_target_percent(security, 0)
def handle_data(context, data):
pass