| Overall Statistics |
|
Total Trades 481 Average Win 1.79% Average Loss -3.81% Compounding Annual Return 111.371% Drawdown 44.500% Expectancy 0.203 Net Profit 550.161% Sharpe Ratio 1.825 Probabilistic Sharpe Ratio 73.297% Loss Rate 18% Win Rate 82% Profit-Loss Ratio 0.47 Alpha 0.762 Beta 0.192 Annual Standard Deviation 0.481 Annual Variance 0.231 Information Ratio 0.405 Tracking Error 0.687 Treynor Ratio 4.58 Total Fees $213323.56 Estimated Strategy Capacity $240000.00 Lowest Capacity Asset ETHUSD 10B Portfolio Turnover 19.63% |
from AlgorithmImports import *
class PriceActionAlpha(AlphaModel):
def __init__(self, algorithm, symbol):
self.algorithm = algorithm
self.symbol = symbol
self.hour = -1
self.selltrig = None
self.buytrig = None
self.currentopen = None
self.consolidator = TradeBarConsolidator(timedelta(1))
self.consolidator.DataConsolidated += self.OnConsolidated
self.window = RollingWindow[TradeBar](4)
history = algorithm.History[TradeBar](self.symbol, 4*24*60, Resolution.Minute)
for bar in history:
self.consolidator.Update(bar)
algorithm.SubscriptionManager.AddConsolidator(self.symbol, self.consolidator)
def OnConsolidated(self, sender, bar):
self.window.Add(bar)
self.currentopen = bar.Open
if not self.window.IsReady: return
df = self.algorithm.PandasConverter.GetDataFrame[TradeBar](self.window)
k1 = 0.5
k2 = 0.5
HH, HC, LC, LL = max(df['high']), max(df['close']), min(df['close']), min(df['low'])
if (HH - LC) >= (HC - LL):
signalrange = HH - LC
else:
signalrange = HC - LL
self.selltrig = self.currentopen - k2 * signalrange
self.buytrig = self.currentopen + k1 * signalrange
def Update(self, algorithm, data):
# We only use hourly signals
if not data.ContainsKey(self.symbol) or not self.window.IsReady:
return []
if self.hour == algorithm.Time.hour:
return []
self.hour = algorithm.Time.hour
price = data[self.symbol].Price
if algorithm.LiveMode:
algorithm.Log(f'Buy Trigger {self.buytrig} > Price {price} > {self.selltrig}')
if price >= self.buytrig:
# self.algorithm.pcm.maxcap = (price - self.buytrig) / self.buytrig * self.algorithm.multiplier
return [Insight(self.symbol, timedelta(days=365), InsightType.Price, InsightDirection.Up)]
# magnitude = size,
# confidence = size,
# weight = size)]
elif price < self.selltrig:
algorithm.Insights.Cancel([self.symbol])
return []from AlgorithmImports import *
from alpha import PriceActionAlpha
from pcm import EqualWeightingMaxCapPortfolioConstructionModel
# BTCUSD Long Only Dual Thrust Algorithm
# Originated by Michael Vitucci
class DualThrustAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 1, 1)
self.SetEndDate(2022, 7, 1)
self.SetCash(100000)
self.SetBrokerageModel(BrokerageName.Kraken, AccountType.Margin)
# self.stopLoss = self.GetParameter('stopLoss', 0.04)
# self.takeProfit = self.GetParameter('takeProfit', 0.25)
# self.lookback = self.GetParameter('lookback', 4)
# self.maxcap = self.GetParameter('maxcap', 1.0)
# self.multiplier = self.GetParameter('multiplier', 1)
tickers = ['ETHUSD']
for ticker in tickers:
symbol = self.AddCrypto(ticker, Resolution.Minute).Symbol
self.AddAlpha(PriceActionAlpha(self, symbol))
self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel(lambda time: None))
# self.pcm = EqualWeightingMaxCapPortfolioConstructionModel()
# self.SetPortfolioConstruction(self.pcm)
# self.SetPortfolioConstruction(InsightWeightingPortfolioConstructionModel())
# self.SetPortfolioConstruction(AccumulativeInsightPortfolioConstructionModel(percent = 0.64))
self.SetExecution(ImmediateExecutionModel())
# execution_model = VolumeWeightedAveragePriceExecutionModel()
# execution_model.MaximumOrderQuantityPercentVolume = 0.05
# self.SetExecution(execution_model)
# self.AddRiskManagement(MaximumDrawdownPercentPortfolio(self.stopLoss, isTrailing=True))
# self.AddRiskManagement(MaximumUnrealizedProfitPercentPerSecurity(self.takeProfit))#region imports
from AlgorithmImports import *
#endregion
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from AlgorithmImports import *
class EqualWeightingMaxCapPortfolioConstructionModel(PortfolioConstructionModel):
'''Provides an implementation of IPortfolioConstructionModel that gives equal weighting to all securities.
The target percent holdings of each security is 1/N where N is the number of securities.
For insights of direction InsightDirection.Up, long targets are returned and
for insights of direction InsightDirection.Down, short targets are returned.'''
def __init__(self, rebalance = Resolution.Daily, portfolioBias = PortfolioBias.LongShort, maxcap = 1.0):
'''Initialize a new instance of EqualWeightingPortfolioConstructionModel
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.portfolioBias = portfolioBias
self.maxcap = maxcap
# 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)
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 self.maxcap / count
for insight in activeInsights:
result[insight] = (insight.Direction if self.RespectPortfolioBias(insight) else InsightDirection.Flat) * percent
return result
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