| Overall Statistics |
|
Total Trades 77 Average Win 0.15% Average Loss -0.51% Compounding Annual Return -12.735% Drawdown 24.800% Expectancy -0.486 Net Profit -3.116% Sharpe Ratio 0.216 Probabilistic Sharpe Ratio 32.308% Loss Rate 60% Win Rate 40% Profit-Loss Ratio 0.29 Alpha 0 Beta 0 Annual Standard Deviation 0.683 Annual Variance 0.466 Information Ratio 0.216 Tracking Error 0.683 Treynor Ratio 0 Total Fees BUSD510.37 Estimated Strategy Capacity BUSD5200000.00 Lowest Capacity Asset ETHBUSD 18N |
#region imports
from AlgorithmImports import *
from QuantConnect.Indicators import *
#endregion
class MyAlphaModelDown(AlphaModel):
def __init__(self, mainAlgo):
self.algo = mainAlgo
def Update(self, algorithm, data):
insights = []
# always emit Down Insight
insights.append((Insight.Price(self.algo.eth, Expiry.EndOfDay, InsightDirection.Down)))
self.rebalanceTime = Expiry.EndOfDay(algorithm.Time)
return insights
def OnSecuritiesChanged(self, algorithm, changes):
'''Event fired each time the we add/remove securities from the data feed
Args:
algorithm: The algorithm instance that experienced the change in securities
changes: The security additions and removals from the algorithm'''
pass
#region imports
from AlgorithmImports import *
from QuantConnect.Indicators import *
#endregion
class MyAlphaModelUp(AlphaModel):
def __init__(self, mainAlgo):
self.algo = mainAlgo
def Update(self, algorithm, data):
insights = []
# always emit Up Insight
insights.append((Insight.Price(self.algo.eth, Expiry.EndOfDay, InsightDirection.Up)))
self.rebalanceTime = Expiry.EndOfDay(algorithm.Time)
return insights
def OnSecuritiesChanged(self, algorithm, changes):
'''Event fired each time the we add/remove securities from the data feed
Args:
algorithm: The algorithm instance that experienced the change in securities
changes: The security additions and removals from the algorithm'''
pass
#region imports
from AlgorithmImports import *
import datetime
#endregion
class MyExecutionModel(ExecutionModel):
def __init__(self, mainAlgo):
'''Initializes a new instance of the MyExecutionModel class'''
self.targetsCollection = PortfolioTargetCollection()
self.minOrderSizeInUSD = 10
self.algo = mainAlgo
# Fill the supplied portfolio targets efficiently
def Execute(self, algorithm, targets: List[PortfolioTarget]) -> None:
'''Immediately submits orders for the specified portfolio targets.
Args:
algorithm: The algorithm instance
targets: The portfolio targets to be ordered'''
# for performance we check count value, OrderByMarginImpact and ClearFulfilled are expensive to call
self.targetsCollection.AddRange(targets)
if not self.targetsCollection.IsEmpty:
for target in self.targetsCollection.OrderByMarginImpact(algorithm):
security = algorithm.Securities[target.Symbol]
# calculate remaining quantity to be ordered
quantity = OrderSizing.GetUnorderedQuantity(algorithm, target, security)
if quantity != 0:
# Helper method to determine if the requested quantity is above the algorithm minimum order margin portfolio percentage
aboveMinimumPortfolio = BuyingPowerModelExtensions.AboveMinimumOrderMarginPortfolioPercentage(security.BuyingPowerModel, security, quantity, algorithm.Portfolio, algorithm.Settings.MinimumOrderMarginPortfolioPercentage)
if aboveMinimumPortfolio:
# expiryDatetime = self.algo.Time + timedelta(hours=6)
# expiryStringTag = "expires: " + expiryDatetime.strftime("%m/%d/%Y, %H:%M:%S") + "CB: " + str(self.algo.Portfolio.CashBook["BUSD"].ValueInAccountCurrency)
# order_properties = OrderProperties()
# order_properties.TimeInForce = TimeInForce.GoodTilDate(expiryDatetime)
# algorithm.MarketOrder(security, quantity, tag=expiryStringTag, orderProperties=order_properties)
algorithm.MarketOrder(security, quantity)
# leading to lingering small orders - issue?
# self.targetsCollection.ClearFulfilled(algorithm)
self.targetsCollection.Clear()
# print all CashBook Positions after trading
# for cash in self.algo.Portfolio.CashBook.Values:
# self.algo.Log(f"Holding {cash.Amount} of {cash.Symbol} ({cash.ValueInAccountCurrency} BUSD value)")
# Optional: Securities changes event for handling new securities.
def OnSecuritiesChanged(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
# Security additions and removals are pushed here.
# This can be used for setting up algorithm state.
# changes.AddedSecurities
# changes.RemovedSecurities
pass
#region imports
from AlgorithmImports import *
import datetime
#endregion
# https://www.quantconnect.com/docs/v2/writing-algorithms/algorithm-framework/portfolio-construction/key-concepts
# Portfolio construction scaffolding class; basic method args.
class MyPortfolioConstructionModel(PortfolioConstructionModel):
def __init__(self, rebalance, mainAlgo):
'''Initialize a new instance of MyPortfolioConstructionModel
Args:
rebalance: Rebalancing parameter. If it is a timedelta, date rules or Resolution, it will be converted into a function.
If None will be ignored.
The function returns the next expected rebalance time for a given algorithm UTC DateTime.
The function returns null if unknown, in which case the function will be called again in the
next loop. Returning current time will trigger rebalance.
portfolioBias: Specifies the bias of the portfolio (Short, Long/Short, Long)'''
super().__init__()
self.algo = mainAlgo
self.portfolioBias = PortfolioBias.LongShort
# If the argument is an instance of Resolution or Timedelta
# Redefine rebalancingFunc
rebalancingFunc = rebalance
if isinstance(rebalance, int):
rebalance = Extensions.ToTimeSpan(rebalance)
if isinstance(rebalance, timedelta):
rebalancingFunc = lambda dt: dt + rebalance
if rebalancingFunc:
self.SetRebalancingFunc(rebalancingFunc)
# REQUIRED: Will determine the target percent for each insight
def DetermineTargetPercent(self, activeInsights):
'''Will determine the target percent for each insight
Args:
activeInsights: The active insights to generate a target for'''
result = {}
# give equal weighting to each security
count = sum(x.Direction != InsightDirection.Flat and self.RespectPortfolioBias(x) for x in activeInsights)
percent = 0 if count == 0 else 1.0 / count
for insight in activeInsights:
# return insight only when all active insights have the same direction
if all(insight.Direction == other_insights.Direction for other_insights in activeInsights):
result[insight] = (insight.Direction if self.RespectPortfolioBias(insight) else InsightDirection.Flat) * percent
else:
result[insight] = (InsightDirection.Flat) * percent
return result
# Determines if the portfolio should be rebalanced base on the provided rebalancing func
def IsRebalanceDue(self, insights: List[Insight], algorithmUtc: datetime) -> bool:
return True
def RespectPortfolioBias(self, insight):
'''Method that will determine if a given insight respects the portfolio bias
Args:
insight: The insight to create a target for
'''
return self.portfolioBias == PortfolioBias.LongShort or insight.Direction == self.portfolioBias
# OPTIONAL: Security change details
def OnSecuritiesChanged(self, algorithm: QCAlgorithm, changes: SecurityChanges) -> None:
pass# region imports
from AlgorithmImports import *
from AlphaModelUp import *
from AlphaModelDown import *
from ExecutionModel import *
from PortfolioConstructionModel import *
# endregion
class FocusedBrownJaguar(QCAlgorithm):
def Initialize(self):
# *** initial configurations and backtest ***
self.SetStartDate(2022, 9, 30) # Set Start Date
self.SetEndDate(2022, 12, 22) # Set End Date
self.SetSecurityInitializer(self.CustomSecurityInitializer)
self.SetAccountCurrency("BUSD") # Set Account Currency
self.SetCash("BUSD", 100000, 1) # Set Strategy Cash
self.UniverseSettings.Resolution = Resolution.Daily
self.AddUniverse(CryptoCoarseFundamentalUniverse(Market.Binance, self.UniverseSettings, self.universe_filter))
self.SetBenchmark(Symbol.Create("BTCUSDC", SecurityType.Crypto, Market.Binance))
self.ethSecurity = self.AddCrypto("ETHBUSD", Resolution.Daily, market=Market.Binance)
self.eth = self.ethSecurity.Symbol
# *** Framework initialization ***
self.AddAlpha(MyAlphaModelUp(self))
self.AddAlpha(MyAlphaModelDown(self))
self.SetPortfolioConstruction(MyPortfolioConstructionModel(self.DateRules.EveryDay(), self))
self.AddRiskManagement(NullRiskManagementModel())
self.SetExecution(ImmediateExecutionModel())
# *** Brokerage settings
self.SetBrokerageModel(BrokerageName.Binance, AccountType.Margin)
self.order_properties = BinanceOrderProperties()
def OnData(self, data):
pass
def OnSecuritiesChanged(self, changes: SecurityChanges) -> None:
pass
def CustomSecurityInitializer(self, security):
security.SetFeeModel(BinanceFeeModel())
security.SetFillModel(ImmediateFillModel())
security.SetBuyingPowerModel(SecurityMarginModel(1))
def universe_filter(self, crypto_coarse: List[CryptoCoarseFundamental]) -> List[Symbol]:
# for demonstration purposes, return only Ethereum
static_universe_list = [self.eth]
return static_universe_list