| Overall Statistics |
|
Total Orders 2407 Average Win 1.11% Average Loss -1.02% Compounding Annual Return -13.913% Drawdown 93.400% Expectancy -0.217 Start Equity 1000000 End Equity 94341.52 Net Profit -90.566% Sharpe Ratio -0.697 Sortino Ratio -0.611 Probabilistic Sharpe Ratio 0.000% Loss Rate 63% Win Rate 37% Profit-Loss Ratio 1.09 Alpha -0.124 Beta 0.197 Annual Standard Deviation 0.153 Annual Variance 0.023 Information Ratio -1.025 Tracking Error 0.189 Treynor Ratio -0.54 Total Fees $10960.68 Estimated Strategy Capacity $820000000.00 Lowest Capacity Asset MES YVXOP65RE0HT Portfolio Turnover 13.76% Drawdown Recovery 139 |
# region imports
from AlgorithmImports import *
# endregion
class BasicFutureAlgorithm(QCAlgorithm):
def initialize(self):
#self.universe_settings.asynchronous = True
#self.SetTimeZone("America/New_York")
self.set_start_date(2010, 1, 1)
self.set_cash(1_000_000)
self.set_security_initializer(
BrokerageModelSecurityInitializer(self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices))
)
#self.set_warm_up(210)
self.risk_factor = .0015
self.futures_raw_list = [
Futures.Grains.CORN, # /ZC
Futures.Grains.OATS, # /ZO
Futures.Grains.SOYBEANS, # /ZS
Futures.Grains.WHEAT, # /ZW
Futures.Grains.SOYBEAN_OIL, # /ZL
Futures.Grains.HRW_WHEAT, # /KE
Futures.Grains.SOYBEAN_MEAL, # /ZM
Futures.Grains.HRW_WHEAT, # /KE
Futures.Softs.SUGAR_11_CME, # /YO
Futures.Meats.LIVE_CATTLE, # /LE
Futures.Meats.FEEDER_CATTLE, # /GF
Futures.Meats.LEAN_HOGS, # /HE
Futures.Forestry.LUMBER, # /LBR
Futures.Dairy.CLASS_III_MILK, # /DC
Futures.Dairy.CASH_SETTLED_CHEESE, # /CSC
Futures.Energy.HEATING_OIL, # /HO
Futures.Energy.GASOLINE, # /RB
Futures.Metals.COPPER, # /HG
#Futures.Metals.PALLADIUM, # /PA
Futures.Metals.PLATINUM, # /PL
Futures.Metals.MICRO_SILVER, # /SIL
Futures.Currencies.AUD, # /6A
Futures.Currencies.GBP, # /6B
Futures.Currencies.MICRO_EUR, # /M6E
Futures.Currencies.JPY, # /6J
Futures.Currencies.NZD, # /6N
Futures.Currencies.CHF, # /6S
Futures.Currencies.CAD, # /6C
Futures.Currencies.MXN, # /6M
Futures.Indices.MICRO_NASDAQ_100_E_MINI, # /MNQ
Futures.Indices.NIKKEI_225_DOLLAR, # /NKD
Futures.Indices.MICRO_RUSSELL_2000_E_MINI, # /M2K
Futures.Indices.MICRO_DOW_30_E_MINI, # /MYM
Futures.Indices.MICRO_SP_500_E_MINI, # /MES
Futures.Financials.Y_2_TREASURY_NOTE, # /ZT
Futures.Financials.Y_10_TREASURY_NOTE, # /ZN
Futures.Financials.Y_5_TREASURY_NOTE # /ZF
]
self.futures_list = [
self.add_future(future,
resolution=Resolution.DAILY,
extended_market_hours=False,
data_mapping_mode=DataMappingMode.OPEN_INTEREST,
data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO,
contract_depth_offset=0
)
for future in self.futures_raw_list
]
self.old_symbol = None
self.new_symbol = None
self.quantity = None
self.futures_data = {}
for item in self.futures_list:
item.set_filter(0,182)
self.futures_data[item.symbol] = symbol_data(item.symbol, self)
def on_data(self, data):
if self.is_warming_up:
return
invested = [x.Symbol for x in self.Portfolio.Values if x.Invested]
for symbol, changed_event in data.symbol_changed_events.items():
old_symbol = changed_event.old_symbol
new_symbol = changed_event.new_symbol
quantity = self.portfolio[old_symbol].quantity
self.futures_data[symbol].pending_roll = (old_symbol, new_symbol, quantity)
for item in self.futures_list:
self.futures_data[item.symbol].update(self)
for mapped in invested:
symbol = mapped.Canonical
exit_signal = self.futures_data[symbol].exit_signal
if exit_signal:
tag = "Liquidated. Net Profit: " + str(self.portfolio[mapped].unrealized_profit) + "; Percent Profit: " + str(round(self.portfolio[mapped].unrealized_profit_percent, 2))
self.liquidate(mapped, tag=tag)
for item in self.futures_list:
if self.futures_data[item.symbol].pending_roll:
self.roll(item.symbol, data)
if item.mapped in invested:
continue
entry_signal = self.futures_data[item.symbol].entry_signal
if entry_signal:
atr20 = self.futures_data[item.symbol].atr20.current.value
point_value = item.symbol_properties.contract_multiplier
if atr20 > 0 and point_value > 0:
num_contracts = round((self.risk_factor * self.portfolio.total_portfolio_value) / (atr20 * point_value))
if data.bars.get(item.mapped):
self.market_order(item.mapped, num_contracts, tag="Long Entry")
else:
self.debug("Could not enter market order")
def roll(self, symbol, data):
pending_roll = self.futures_data[symbol].pending_roll
old_symbol = pending_roll[0]
new_symbol = pending_roll[1]
quantity = pending_roll[2]
if data.bars.get(old_symbol) and data.bars.get(new_symbol) and quantity != 0:
self.market_order(old_symbol, -quantity, tag="Rollover")
self.market_order(new_symbol, quantity, tag="Rollover")
self.futures_data[symbol].pending_roll = None
def on_order_event(self, order_event: OrderEvent):
if order_event.Status == OrderStatus.FILLED:
order_ticket = self.transactions.get_order_ticket(order_event.order_id)
symbol = order_ticket.symbol
tag = order_ticket.tag
entry_price = order_ticket.average_fill_price
qty = order_ticket.quantity_filled
if "Long Entry" in tag:
self.trailing_stop_order(symbol, -qty, 0.15, True)
class symbol_data():
def __init__(self, symbol, algo):
self.symbol = symbol
self.sma200 = algo.sma(symbol, 200, Resolution.DAILY)
self.ema50 = algo.ema(symbol, 50, Resolution.DAILY)
self.ema100 = algo.ema(symbol, 100, Resolution.DAILY)
self.max100 = algo.max(symbol, 100, selector=Field.CLOSE, resolution=Resolution.DAILY)
self.min50 = algo.min(symbol, 50, selector=Field.CLOSE, resolution=Resolution.DAILY)
self.atr20 = algo.atr(symbol, 20, MovingAverageType.SIMPLE, resolution=Resolution.DAILY)
#self.dch = algo.dch(symbol=symbol, upper_period=100, lower_period=50, resolution=Resolution.DAILY, selector=Field.CLOSE)
self.dch = algo.dch(symbol, 100, 50, resolution=Resolution.DAILY)
#self.bb_upper = algo.bb(symbol, )
self.close = algo.securities[symbol].close
self.entry_signal = False
self.exit_signal = False
self.pending_roll = None
algo.warm_up_indicator(symbol, self.sma200, Resolution.DAILY)
algo.warm_up_indicator(symbol, self.ema50, Resolution.DAILY)
algo.warm_up_indicator(symbol, self.ema100, Resolution.DAILY)
algo.warm_up_indicator(symbol, self.max100, Resolution.DAILY)
algo.warm_up_indicator(symbol, self.min50, Resolution.DAILY)
algo.warm_up_indicator(symbol, self.atr20, Resolution.DAILY)
algo.warm_up_indicator(symbol, self.dch, Resolution.DAILY)
def update(self, algo):
self.close = algo.securities[self.symbol].close
self.entry_signal = self.close >= self.max100.current.value and self.ema50.current.value > self.ema100.current.value
self.exit_signal = self.close <= self.min50.current.value