Overall Statistics |
Total Trades 5343 Average Win 0.11% Average Loss -0.08% Compounding Annual Return 43.097% Drawdown 29.900% Expectancy 0.534 Net Profit 207.457% Sharpe Ratio 1.151 Probabilistic Sharpe Ratio 49.459% Loss Rate 34% Win Rate 66% Profit-Loss Ratio 1.33 Alpha 0 Beta 0 Annual Standard Deviation 0.288 Annual Variance 0.083 Information Ratio 1.151 Tracking Error 0.288 Treynor Ratio 0 Total Fees $566224.62 Estimated Strategy Capacity $120000.00 Lowest Capacity Asset DLNG VLK5WIZ88O6D Portfolio Turnover 4.22% |
#region imports from AlgorithmImports import * #endregion def GetROAScore(fine): '''Get the Profitability - Return of Asset sub-score of Piotroski F-Score Arg: fine: Fine fundamental object of a stock Return: Profitability - Return of Asset sub-score''' # Nearest ROA as current year data roa = fine.OperationRatios.ROA.ThreeMonths # 1 score if ROA datum exists and positive, else 0 score = 1 if roa and roa > 0 else 0 return score def GetOperatingCashFlowScore(fine): '''Get the Profitability - Operating Cash Flow sub-score of Piotroski F-Score Arg: fine: Fine fundamental object of a stock Return: Profitability - Operating Cash Flow sub-score''' # Nearest Operating Cash Flow as current year data operating_cashflow = fine.FinancialStatements.CashFlowStatement.CashFlowFromContinuingOperatingActivities.ThreeMonths # 1 score if operating cash flow datum exists and positive, else 0 score = 1 if operating_cashflow and operating_cashflow > 0 else 0 return score def GetROAChangeScore(fine): '''Get the Profitability - Change in Return of Assets sub-score of Piotroski F-Score Arg: fine: Fine fundamental object of a stock Return: Profitability - Change in Return of Assets sub-score''' # if current or previous year's ROA data does not exist, return 0 score roa = fine.OperationRatios.ROA if not roa.ThreeMonths or not roa.OneYear: return 0 # 1 score if change in ROA positive, else 0 score score = 1 if roa.ThreeMonths > roa.OneYear else 0 return score def GetAccrualsScore(fine): '''Get the Profitability - Accruals sub-score of Piotroski F-Score Arg: fine: Fine fundamental object of a stock Return: Profitability - Accruals sub-score''' # Nearest Operating Cash Flow, Total Assets, ROA as current year data operating_cashflow = fine.FinancialStatements.CashFlowStatement.CashFlowFromContinuingOperatingActivities.ThreeMonths total_assets = fine.FinancialStatements.BalanceSheet.TotalAssets.ThreeMonths roa = fine.OperationRatios.ROA.ThreeMonths # 1 score if operating cash flow, total assets and ROA exists, and operating cash flow / total assets > ROA, else 0 score = 1 if operating_cashflow and total_assets and roa and operating_cashflow / total_assets > roa else 0 return score def GetLeverageScore(fine): '''Get the Leverage, Liquidity and Source of Funds - Change in Leverage sub-score of Piotroski F-Score Arg: fine: Fine fundamental object of a stock Return: Leverage, Liquidity and Source of Funds - Change in Leverage sub-score''' # if current or previous year's long term debt to equity ratio data does not exist, return 0 score long_term_debt_ratio = fine.OperationRatios.LongTermDebtEquityRatio if not long_term_debt_ratio.ThreeMonths or not long_term_debt_ratio.OneYear: return 0 # 1 score if long term debt ratio is lower in the current year, else 0 score score = 1 if long_term_debt_ratio.ThreeMonths < long_term_debt_ratio.OneYear else 0 return score def GetLiquidityScore(fine): '''Get the Leverage, Liquidity and Source of Funds - Change in Liquidity sub-score of Piotroski F-Score Arg: fine: Fine fundamental object of a stock Return: Leverage, Liquidity and Source of Funds - Change in Liquidity sub-score''' # if current or previous year's current ratio data does not exist, return 0 score current_ratio = fine.OperationRatios.CurrentRatio if not current_ratio.ThreeMonths or not current_ratio.OneYear: return 0 # 1 score if current ratio is higher in the current year, else 0 score score = 1 if current_ratio.ThreeMonths > current_ratio.OneYear else 0 return score def GetShareIssuedScore(fine): '''Get the Leverage, Liquidity and Source of Funds - Change in Number of Shares sub-score of Piotroski F-Score Arg: fine: Fine fundamental object of a stock Return: Leverage, Liquidity and Source of Funds - Change in Number of Shares sub-score''' # if current or previous year's issued shares data does not exist, return 0 score shares_issued = fine.FinancialStatements.BalanceSheet.ShareIssued if not shares_issued.ThreeMonths or not shares_issued.TwelveMonths: return 0 # 1 score if shares issued did not increase in the current year, else 0 score score = 1 if shares_issued.ThreeMonths <= shares_issued.TwelveMonths else 0 return score def GetGrossMarginScore(fine): '''Get the Leverage, Liquidity and Source of Funds - Change in Gross Margin sub-score of Piotroski F-Score Arg: fine: Fine fundamental object of a stock Return: Leverage, Liquidity and Source of Funds - Change in Gross Margin sub-score''' # if current or previous year's gross margin data does not exist, return 0 score gross_margin = fine.OperationRatios.GrossMargin if not gross_margin.ThreeMonths or not gross_margin.OneYear: return 0 # 1 score if gross margin is higher in the current year, else 0 score score = 1 if gross_margin.ThreeMonths > gross_margin.OneYear else 0 return score def GetAssetTurnoverScore(fine): '''Get the Leverage, Liquidity and Source of Funds - Change in Asset Turnover Ratio sub-score of Piotroski F-Score Arg: fine: Fine fundamental object of a stock Return: Leverage, Liquidity and Source of Funds - Change in Asset Turnover Ratio sub-score''' # if current or previous year's asset turnover data does not exist, return 0 score asset_turnover = fine.OperationRatios.AssetsTurnover if not asset_turnover.ThreeMonths or not asset_turnover.OneYear: return 0 # 1 score if asset turnover is higher in the current year, else 0 score score = 1 if asset_turnover.ThreeMonths > asset_turnover.OneYear else 0 return score
# region imports from AlgorithmImports import * from security_initializer import CustomSecurityInitializer from universe import FScoreUniverseSelectionModel # endregion class PensiveFluorescentYellowParrot(QCAlgorithm): def Initialize(self): self.SetStartDate(2020, 7, 1) # Set Start Date self.SetEndDate(2023, 8, 19) # Set End Date self.SetCash(10000000) # Set Strategy Cash ### Parameters ### # The Piotroski F-Score threshold we would like to invest into stocks with F-Score >= of that fscore_threshold = self.GetParameter("fscore_threshold", 7) ### Reality Modeling ### # Interactive Broker Brokerage fees and margin self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin) # Custom security initializer self.SetSecurityInitializer(CustomSecurityInitializer(self.BrokerageModel, FuncSecuritySeeder(self.GetLastKnownPrices))) ### Universe Settings ### self.UniverseSettings.Resolution = Resolution.Minute # Our universe is selected by Piotroski's F-Score self.AddUniverseSelection(FScoreUniverseSelectionModel(self, fscore_threshold)) # Assume we want to just buy and hold the selected stocks, rebalance daily self.AddAlpha(ConstantAlphaModel(InsightType.Price, InsightDirection.Up, timedelta(1))) # Avoid overconcentration of risk in related stocks in the same sector, we invest the same size in every sector self.SetPortfolioConstruction(SectorWeightingPortfolioConstructionModel()) # Avoid placing orders with big bid-ask spread to reduce friction cost self.SetExecution(SpreadExecutionModel(0.01)) # maximum 1% spread allowed # Assume we do not have any risk management measures self.AddRiskManagement(NullRiskManagementModel()) def OnSecuritiesChanged(self, changes): # Log the universe changes to test the universe selection model # In this case, the added security should be the same as the logged stocks with F-score >= 7 self.Log(changes)
# region imports from AlgorithmImports import * # endregion class CustomSecurityInitializer(BrokerageModelSecurityInitializer): def __init__(self, brokerage_model: IBrokerageModel, security_seeder: ISecuritySeeder) -> None: super().__init__(brokerage_model, security_seeder) def Initialize(self, security: Security) -> None: # First, call the superclass definition # This method sets the reality models of each security using the default reality models of the brokerage model super().Initialize(security) # We want a slippage model with price impact by order size for reality modeling security.SetSlippageModel(VolumeShareSlippageModel())
# region imports from AlgorithmImports import * from f_score import * # endregion class FScoreUniverseSelectionModel(FineFundamentalUniverseSelectionModel): def __init__(self, algorithm, fscore_threshold): super().__init__(self.SelectCoarse, self.SelectFine) self.algorithm = algorithm self.fscore_threshold = fscore_threshold def SelectCoarse(self, coarse): '''Defines the coarse fundamental selection function. Args: algorithm: The algorithm instance coarse: The coarse fundamental data used to perform filtering Returns: An enumerable of symbols passing the filter''' # We only want stocks with fundamental data and price > $1 filtered = [x.Symbol for x in coarse if x.HasFundamentalData and x.Price > 1] return filtered def SelectFine(self, fine): '''Defines the fine fundamental selection function. Args: algorithm: The algorithm instance fine: The fine fundamental data used to perform filtering Returns: An enumerable of symbols passing the filter''' # We use a dictionary to hold the F-Score of each stock f_scores = {} for f in fine: # Calculate the Piotroski F-Score of the given stock f_scores[f.Symbol] = self.GetPiotroskiFScore(f) if f_scores[f.Symbol] >= self.fscore_threshold: self.algorithm.Log(f"Stock: {f.Symbol.ID} :: F-Score: {f_scores[f.Symbol]}") # Select the stocks with F-Score higher than the threshold selected = [symbol for symbol, fscore in f_scores.items() if fscore >= self.fscore_threshold] return selected def GetPiotroskiFScore(self, fine): '''A helper function to calculate the Piotroski F-Score of a stock Arg: fine: MorningStar fine fundamental data of the stock return: the Piotroski F-Score of the stock ''' # initial F-Score as 0 fscore = 0 # Add up the sub-scores in different aspects fscore += GetROAScore(fine) fscore += GetOperatingCashFlowScore(fine) fscore += GetROAChangeScore(fine) fscore += GetAccrualsScore(fine) fscore += GetLeverageScore(fine) fscore += GetLiquidityScore(fine) fscore += GetShareIssuedScore(fine) fscore += GetGrossMarginScore(fine) fscore += GetAssetTurnoverScore(fine) return fscore