| Overall Statistics |
|
Total Orders 10001 Average Win 0.10% Average Loss -0.10% Compounding Annual Return 26.876% Drawdown 7.400% Expectancy 0.161 Start Equity 100000 End Equity 219857.44 Net Profit 119.857% Sharpe Ratio 2.23 Sortino Ratio 2.475 Probabilistic Sharpe Ratio 99.760% Loss Rate 43% Win Rate 57% Profit-Loss Ratio 1.04 Alpha 0.139 Beta 0.289 Annual Standard Deviation 0.073 Annual Variance 0.005 Information Ratio 0.836 Tracking Error 0.099 Treynor Ratio 0.562 Total Fees $15359.52 Estimated Strategy Capacity $3800000.00 Lowest Capacity Asset BEP VHBJM1A2DQAT Portfolio Turnover 32.72% |
#region imports
from AlgorithmImports import *
#endregion
class Cramer(PythonData):
'''Cramer Recommendation'''
def get_source(self, config: SubscriptionDataConfig, date: datetime, is_live_mode: bool) -> SubscriptionDataSource:
url = "https://raw.githubusercontent.com/aghalandar/Eventstudy/main/Transformed_Cramer2.csv"
return SubscriptionDataSource(url, SubscriptionTransportMedium.REMOTE_FILE)
def reader(self, config: SubscriptionDataConfig, line: str, date: datetime, is_live_mode: bool) -> BaseData:
if not (line.strip() and line[0].isdigit()): return None
cramer = Cramer()
cramer.Symbol = config.Symbol
# Example File Format:
# Date,Buy,Sell,Negative Mention,Positive Mention
# 2016-04-18,"DEPO,OXY,RVNC,VZ","ADMS,GPS,MYGN","AEGN,IBM,IONS","CAT,COP,UTX,WMT,XOM"
data = line.split(',')
cramer.Time = datetime.strptime(data[0], "%Y-%m-%d")
cramer.Buy = data[1].split(' ') if data[1] else []
cramer.Sell = data[2].split(' ') if data[2] else []
cramer.Positive_Mention = data[4].split(' ') if data[4] else []
cramer.Negative_Mention = data[3].split(' ') if data[3] else []
return cramer
# region imports
from AlgorithmImports import *
from CustomData import Cramer
# endregion
class FatLightBrownCaribou(QCAlgorithm):
def initialize(self):
self.set_start_date(2016, 4, 1)
self.set_end_date(2023, 1, 1)
self.set_cash(100000)
# add Cutome Data: Stock recommendation
self.recommendation_symbol = self.AddData(Cramer, "Cramer", Resolution.Daily).Symbol
# Universe Selection
self.UniverseSettings.Resolution = Resolution.Daily
self.fundamental_count = 5000
self.AddUniverse(self.FundamentalSelectionFunction)
# self.universe_settings.leverage = 2.0
self.process_recomm = -1
self.buy_recommendations = [] # list of daily Buy recommendation
self.sell_recommendations = [] # list of daily sell recommendation
self.entry_dates = {} # Track the entry date of each position
self.holding_period = 3 # Number of days to hold a position
def FundamentalSelectionFunction(self, fundamental):
selected = [x for x in fundamental if x.HasFundamentalData and x.MarketCap != 0 and
x.Market == 'usa' and x.Price > 1]
if len(selected) > self.fundamental_count:
selected = [x for x in sorted(selected, key = lambda x: x.DollarVolume, reverse = True)]\
[:self.fundamental_count]
return [x.Symbol for x in selected]
def on_data(self, data):
# if we already process the recommendation today
if self.process_recomm == self.Time.date():
return
self.process_recomm = self.Time.date()
#Check if holding period has been reached and close positions
to_liquidate = []
for symbol, entry_date in self.entry_dates.items():
if self.Time.date() > entry_date + timedelta(days=self.holding_period):
to_liquidate.append(symbol)
for symbol in to_liquidate:
self.Liquidate(symbol)
del self.entry_dates[symbol] # Remove from the entry_dates dictionary
# check if there is any recommendation
if not data.ContainsKey(self.recommendation_symbol):
#self.Debug(f"No data for {self.recommendation_symbol} at {self.Time}")
return
#self.Debug(f"Buy Recommendation are : {data[self.recommendation_symbol].Buy}")
#self.Debug(f"sell Recommendation are : {data[self.recommendation_symbol].Sell}")
# Process Buy recommendations
self.buy_recommendations = data[self.recommendation_symbol].Buy
self.sell_recommendations = data[self.recommendation_symbol].Sell
# setting the target based on recommendation
target = {}
for i, recomm in enumerate([self.buy_recommendations, self.sell_recommendations]):
# if there is any recommendation on the list
if not recomm: continue
for ticker in recomm:
# create the ticker. without adding the ticker to our universe.
_symbol = Symbol.create(ticker, SecurityType.EQUITY, Market.USA)
if data.ContainsKey(_symbol) and data[_symbol] is not None and not self.Portfolio[_symbol].Invested:
target[_symbol] = ((-1)**i / len(recomm) / self.holding_period)
# the weight for buy is maximum 10% and for sell is minimum -10%.
target[_symbol] = min(target[_symbol], 0.1) if i == 0 else max(target[_symbol], -0.1)
# Record the entry date
self.entry_dates[_symbol] = self.Time.date()
self.SetHoldings([PortfolioTarget(symbol, weight) for symbol, weight in target.items()])
self.buy_recommendations = []
self.sell_recommendations = []
# # Just to check the orders.
# def OnOrderEvent(self, orderEvent):
# if orderEvent.Status == OrderStatus.Filled:
# self.Debug(f"Order filled: {orderEvent.Symbol} at {orderEvent.FillPrice} on {self.Time}")