Overall Statistics |
Total Trades 15 Average Win 1.45% Average Loss -3.97% Compounding Annual Return -20.564% Drawdown 42.900% Expectancy -0.025 Net Profit -21.640% Sharpe Ratio -1.01 Loss Rate 29% Win Rate 71% Profit-Loss Ratio 0.36 Alpha -0.293 Beta 4.245 Annual Standard Deviation 0.206 Annual Variance 0.042 Information Ratio -1.106 Tracking Error 0.206 Treynor Ratio -0.049 Total Fees $0.00 |
/* */
using QuantConnect.Algorithm; namespace QuantConnect_AlixClaireErie { public partial class Main : QCAlgorithm, IAlgorithm { //Static variables public static string currProcess = "MainProcess"; public static bool isInitialized = false; //Internal processes not initialized //Non-static variables private int holdingTimeLimit; private int stopLimitCounter; private decimal buyPrice; private decimal quantity; private Equity equity; private OrderTicket orderTicket; private Parameters parameters; private Security security; private SecurityHolding holdings; private Signal signal; private Symbol symbolID; private System.DateTime purchaseTime; //Parameters bool useUniverse; bool useSignal; bool useMomp; protected string symbol; // int holdingTimeLimit; // decimal percentPriceIncrease; public override void Initialize() { ParametersInit(); useUniverse = parameters.useUniverse; useSignal = parameters.useSignal; useMomp = parameters.useMomp; var handPicked = new AssetSelection.HandPicked() { symbol = parameters.symbol }; PortfolioInit(handPicked); if(useSignal) { signal = new Signal(this); if(useMomp) { signal.MOMPInit(); } else { AddEquity(symbol, Resolution.Minute); } } } ///<summary> /// ///</summary> public override void OnData(Slice data) { var execute = new Execution(this); execute.NoPerTradeFeeModel(this); System.DateTime date = Time; int dayOfMonth = date.Day; int daysInMonth = DateTime.DaysInMonth(date.Year, date.Month); if (!Portfolio.Invested) { stopLimitCounter = 0; if(useSignal) { var filter = new Filter(); int triggerCounter; int triggerThreshold = 1; if(useMomp) { signal.MOMPOnData(data, filter, symbolID); } triggerCounter = filter.TriggerCounter(0, symbolID); if (triggerCounter >= triggerThreshold) { orderTicket = execute.BasicLongOrder(data, symbolID); purchaseTime = orderTicket.Time; buyPrice = orderTicket.AverageFillPrice; quantity = orderTicket.QuantityFilled; } } else Error("No defined method of execution."); } if (holdings.Quantity > 0) { var AcqPricePerShare = holdings.HoldingsCost / holdings.Quantity; decimal currPrice = Securities[symbolID].Price; decimal percentPriceIncrease = parameters.percentPriceIncrease; decimal percentPriceDecrease = parameters.percentPriceDecrease; decimal sellPrice = (1.0m + percentPriceIncrease)*AcqPricePerShare; decimal stopLossLimitPrice = (1.0m - percentPriceDecrease)*AcqPricePerShare; holdingTimeLimit = parameters.holdingTimeLimit; System.DateTime currTime = Time; System.TimeSpan holdingTime = currTime.Subtract(purchaseTime); int holdingTimeDays = holdingTime.Days; //returning 0 if(currPrice >= sellPrice) { execute.PercentPriceIncreaseMethod( symbolID, sellPrice); } else if (holdingTimeDays >= holdingTimeLimit && currPrice > buyPrice) { Liquidate(symbolID); Debug("Liquidated " + symbol + " due to " + holdingTimeDays + " holdingTimeDays on " + Time.ToShortDateString()); } /* else if (currPrice <= (stopLossLimitPrice*0.96m) && stopLimitCounter == 0) { orderTicket = execute.StopLossOrder(symbolID, buyPrice, quantity); var orderStatus = orderTicket.Status; Debug("orderStatus = " + orderStatus); if(orderStatus != OrderStatus.Filled) { var cancelOrder = orderTicket.Cancel(); var orderClosed = orderTicket.OrderClosed; Liquidate(symbolID); Debug("Liquidated " + symbol + " because order status was not filled."); } stopLimitCounter++; // var orderClosed = orderTicket.OrderClosed; } else if (currPrice <= (buyPrice*0.94m)) { Liquidate(symbolID); Debug("Liquidated " + symbol + " because order to stop severe losses on " + Time.ToShortDateString()); } */ else if (holdingTimeDays > 1 && currPrice <= stopLossLimitPrice && stopLimitCounter == 0) { orderTicket = execute.StopLossOrder(symbolID, buyPrice, quantity); var orderStatus = orderTicket.Status; Debug("orderStatus = " + orderStatus); if(orderStatus != OrderStatus.Filled) { var cancelOrder = orderTicket.Cancel(); var orderClosed = orderTicket.OrderClosed; Order(symbolID, -quantity); Debug("Sold " + symbol + " because order status was not filled."); //Liquidate(symbolID); // Debug("Liquidated " + symbol + " because order status was not filled."); } stopLimitCounter++; // var orderClosed = orderTicket.OrderClosed; } // else if (stopLimitCounter > 0 && holdingTimeDays >= holdingTimeLimit) /* else if (stopLimitCounter > 0) { var orderClosed = orderTicket.OrderClosed; Debug("StopLoss: Sold " + symbol + " to prevent unnecessary losses of " + holdingTimeDays + " holdingTimeDays on " + Time.ToShortDateString() + " for " + sellPrice); } else if (stopLimitCounter > 0 && holdingTimeDays >= holdingTimeLimit) { Liquidate(symbolID); Debug("StopLoss: Sold " + symbol + " to prevent unnecessary losses of " + holdingTimeDays + " holdingTimeDays on " + Time.ToShortDateString() + " for " + sellPrice); } else if (holdingTimeDays > 4 && currPrice <= stopLossLimitPrice && stopLimitCounter > 0) { Debug("holdingTimeDays = " + holdingTimeDays); Liquidate(symbolID); Debug("StopLoss: Liquidated " + symbol + " to prevent unnecessary losses."); stopLimitCounter = 0; } */ } } } }
using System; using System.Diagnostics; using System.Reflection; namespace QuantConnect_AlixClaireErie { public partial class Main : QCAlgorithm, IAlgorithm { public static class Utility<T> { //Call with ex: Utility<Signal.MOMP>.VerifyInstantiation(this, momp, nameof(momp), MethodBase.GetCurrentMethod().Name); public static void VerifyInstantiation(Main main, T obj, string str, string methodName) { if(obj == null) { main.Error(str + " is not instantiated in " + main.GetType().Name + "–>" + methodName); return; } else return; } } public static class Utility { public static void LaunchToConsole(Main main, string currProcess) { main.Debug("Launching " + currProcess); return; } } } }
using System; using System.Diagnostics; using QuantConnect.Algorithm; using QuantConnect.Data; using QuantConnect.Data.Custom; using QuantConnect.Data.Market; using QuantConnect.Orders; using QuantConnect.Indicators; using QuantConnect.Securities; namespace QuantConnect_AlixClaireErie { public partial class Main : QCAlgorithm, IAlgorithm { public partial class Execution { private static Main main; public Execution(Main _main) { main = _main; } public int TradesInADay(int _counter) { int counter = _counter + 1; return counter; } public void DefaultLongTrade(Symbol symbolID, Security security) { main.SetHoldings(symbolID, 1); SecurityHolding holdings = security.Holdings; decimal securityHoldingsQuantity = holdings.Quantity; main.Debug("Purchased Stock"); main.Debug("Purchased " + securityHoldingsQuantity + " shares of " + main.Securities[symbolID] + " on " + main.Time.ToShortDateString()); main.Debug("holdings = " + security.Holdings); } // public void BasicLongOrder(Slice data, Symbol symbolID) public OrderTicket BasicLongOrder(Slice data, Symbol symbolID) { TradeBars bars = data.Bars; // if (!bars.ContainsKey(symbolID)) return; int quantity = (int)Math.Floor(main.Portfolio.Cash / bars[symbolID].Close); OrderTicket orderTicket = main.Order(symbolID, quantity); var orderClosed = orderTicket.OrderClosed; System.DateTime timeClosed = orderTicket.Time; decimal buyPrice = orderTicket.AverageFillPrice; decimal quantityFilled = orderTicket.QuantityFilled; main.Debug("Purchased " + quantityFilled + " shares of " + symbolID + " on " + timeClosed + " for " + buyPrice); // main.Debug("holdings.Quantity = " + main.holdings.Quantity); return orderTicket; } public OrderTicket LongShortOrder(Slice data, Symbol symbolID) { TradeBars bars = data.Bars; // if (!bars.ContainsKey(symbolID)) return; int quantity = (int)Math.Floor(main.Portfolio.Cash / bars[symbolID].Close); OrderTicket orderTicket = main.Order(symbolID, quantity); var orderClosed = orderTicket.OrderClosed; //instructs the program to wait until the order is filled System.DateTime timeClosed = orderTicket.Time; decimal buyPrice = orderTicket.AverageFillPrice; decimal quantityFilled = orderTicket.QuantityFilled; main.Debug("Purchased " + quantityFilled + " shares of " + symbolID + " on " + timeClosed + " for " + buyPrice); // main.Debug("holdings.Quantity = " + main.holdings.Quantity); //Create a StopLoss order StopLossOrder(symbolID, buyPrice, quantity); return orderTicket; } public OrderTicket StopLossOrder(Symbol symbolID, decimal buyPrice, decimal quantity) { //Implement parameters.percentPriceDecrease var parameters = new Parameters(this); decimal stopPrice = (1.0m - parameters.percentPriceDecrease)*buyPrice; decimal limitPrice = stopPrice*0.97m; OrderTicket stopLossTicket = main.StopLimitOrder(symbolID, -quantity, stopPrice, limitPrice); main.Debug("Put in a stop-loss order for " + symbolID + " against stopPrice " + stopPrice + ", limitPrice " + limitPrice + " on " + main.Time.ToShortDateString()); var orderClosed = stopLossTicket.OrderClosed; //instructs the program to wait until the order is filled System.DateTime timeClosed = stopLossTicket.Time; decimal sellPrice = stopLossTicket.AverageFillPrice; decimal quantityFilled = stopLossTicket.QuantityFilled; main.Debug("Sold " + quantityFilled + " shares of " + symbolID + " on " + timeClosed + " for " + sellPrice); return stopLossTicket; } public void PercentPriceIncreaseMethod(Symbol symbolID, decimal sellPrice) { main.Liquidate(symbolID); main.Debug("Sold " + symbolID + " on " + main.Time.ToShortDateString() + " for " + sellPrice); } } } }
using QuantConnect.Algorithm; namespace QuantConnect_AlixClaireErie { public partial class Main : QCAlgorithm, IAlgorithm { public void Init(bool isInitialized, string currProcess) { _isInitialized = isInitialized; _currProcess = currProcess; if(!_isInitialized) { Debug("Error: Trace " + _currProcess); return; } else { Debug("Launching " + _currProcess); } } } }
/* See Strategy.cs EARNINGS FORMULA: E_N(x) = E_0*(1+x)^N ----------------- PARAMETERS E = Current Equity ($) w = Price Range Identifier (#, w ∈ [0,1]) x = Weekly Growth Rate (%) ----------------- FUNCTION P = Price Range for stocks P = (P_L, P_H), P_H = E/100, P_L = w*P_H Ex.1: E = $2000 , w = 0.8, P_H = $20, P_L = $16 Ex.2: E = $2000 , w = 0.75, P_H = $20, P_L = $15 Ex.3: E = $10,000, w = 0.75, P_H = $100, P_L = $75 ----------------- FUNCTIONS //Define the following methods in separate files and write the algorithm here //that calls, combines, and weighs in thier respective degrees each method, //returning THE _symbol //Pre-Method Selection: //Price Range //Share Quantity Range //Method 1: symbolCrossoverTrigger(); //Select based on crossover to/from bearish/bullish for a given symbol //Test which is more effective, or if both are effective in different situations //Method 2: mompTrigger(); //Method 3: movingAverageTrigger(); in movingAverageTrigger //private bool use_MACD; //private MovingAverageConvergenceDivergence _macd; //private ExponentialMovingAverage _ema; //MovingAverageAlgorithm oc = new MovingAverageAlgorithm(); //Method 4: betaRsquaredTrigger(); */ //Inspired by CoarseFineFundamentalComboAlgorithm.cs /* using System.Collections.Generic; using System.Linq; using QuantConnect.Data.Fundamental; using QuantConnect.Data.Market; using QuantConnect.Data.UniverseSelection; using QuantConnect.Algorithm; namespace QuantConnect_AlixClaireErie { public partial class CoarseFineUniverse { } } */
/* MainProjectBeta_v3 V3.1: -Adding the condition to a holding time limit that the current price of the security is above the price at which it was bought -In the opposing case, we would implement a stop-loss order -Added a working stop-loss order, although it is not performing very well -As well the holding time limit is working but also not giving good performance. -I need to start working with more than one security to see how these functions perform together. V3.2: -Realized a big problem for my mistakes: I was using || instead of && in my condition statements!!! -Realized another big problem: I had the stopLimitCounter initialized in the wrong place -Got most of the executions working!!! V3.3: -Test Stop Limit Order (for short) on RDS.B's recent decline -RDS.B w/o stop-limit, before price drop: 28% return -RDS.B w/o stop-limit, after price drop: 12% return -A problem occurs at the drop-off after the stop-limit sell: -The algorithm continues to purchase stock even as the price plummets -Next task is to implement the moving averages in the buy Signals -SMA, EMA, MACD V3.4 --------------------------------------------------------------------------------------------------- MainProjectBeta_v2 V2.1: -Cloned and Created V2.1 -Building and backtesting -Cloning and building V2.2 V2.2: -Cloned and backtesting -First task: Clear Archive Folder, restructure files and naming system -Reference: https://www.quantconnect.com/forum/discussion/1816/qc-algorithm-plugin-framework---open-call-for-feedback -Started defining a convention in Notes/Convention.cs -After restructuring, had error codes, so adding piece by piece back in while implementing new convention -I have done a lot of restructuring, so now cloning a working version before continuing V2.3: -Backtests fine -Continuing with adding the commented code into the new restructure -Big project Cloning a working version V2.4: -Starting with a non-working version and comparing against last working backtest, which is now cloned as V2.3 -Not Initializing error :Failed to initialize algorithm: Initialize(): Value cannot be null. Parameter name: key" -Main.cs is same for both files -Execution.cs same -Found the problem... in AssetSelection -> HandPicked, I had removed the definition of the security in main and moved it to HandPicked, but there still needs to be a definition of -Have a working shell that launches AssetSelection: HandPicked, Signal, Signal: MOMP, and Execution -Cloning V2.5: -Starting by adding AssetSelectionInit() to Main in AssetSelection.cs -Calling it in Main.cs –> Initialize -MOMP.cs transfer is complete, but the parts that were moved to Execution are still commented out. -Working on finishing the transfer in Execution -The contents of Execution –> BasicSellMethod is completely commented out still -Creating ParametersInit() methods for all of the existing classes -Created a default constructor for Parameters class -Created a basic TriggerCounter in Signal/Filter.cs V2.6: -Get Sell orders working again! -Observing QC conventions and trying to make my code reflect that. -Removing all ParametersInit() methods... -Including meta tags <summary>, <param>, and <returns> -Completed update of Parameters.cs V2.7: -Work with the TriggerCounter -Get the percentPriceIncrease Sell Model going again -As a basic Signal method, that fires a TriggerCounter V2.8: -Finally got the structure working. -Error about key was that I had not passed symbol as an argument -Cloning this working version. V2.9: -Adding Filter and TriggerCounter -Made big changes to MOMPAlgorithm and Main files... moved generic window from the momp file to the main file V2.10: -Removing ParametersInit -It seems perhaps the changes made in V2.9 removed the sell trades -Cloning V2.8 V2.11: -so attempting to move the sell part of mompondata back to main now (was it just the moving of the windows that messed everything up?) -The move worked. Variables were declared in the wrong place. V2.12: -Work with getting AssetSelection working -Works with PortfolioInit in Main rather than in HandPicked V2.13: -Moved !Portfolio.Invested from mompOnData to main -Switch out symbol with symbolID after its attribution in Initialize to retain security ID -Get the trigger working -WORKS!!! --------------------------------------------------------------------------------------------------- MainProjectBeta_v1 V14: -Cloned the "/Archive/PartialClass_MultiFileTest_v1" file as the new MainProject and started working from there as V14 -Got a working (empty) framework -Cloning and making V15 -In V15 will work with transferring the MOMP algorithm into this working framework V15: -Working off of v13 to integrate MOMP algorithm into the current working basic framework -Established a working mompInit function that initializes the MOMPAlgorithm in the main.cs -FAIL: CLONING LAST WORKING BACKTEST INTO V16 -ARCHIVING V15 V16: -Backtesting -Got the debug working in mompInit -Working on importing core momp algorithm -Wrote separate functions for a general Init(string) and a mompInit() -Added OnDataInit(); -Added Init.cs and moved Init() and OnDataInit() to it -Working on the Universe.cs file -Putting MOMPAlgorithm on hold for a few -Ok, so maybe I should at least get MOMPAlgorithm building, set up the declarations that are giving me an error. -GREAT! Got it working -Cloning and archiving V17: -Backtesting -Beautiful. I got the Parameters class initialized, and added first parameter _useMomp -mompAlgorithm effin works! -Cloning and archiving V18: -Backtesting -Testing Momp on RDS.B shows where a very imortant addition should be made: -Implement a StopLossLimit -Adding to Parameters -Doing a Case Study on RDS.B; Conclusions: -Sell (StopLimit) trigger if MOMP drops -Sell if the security has been held for 2 full days -Having a problem getting the StopLimit to work -The StopLimit is cancelled after the first liquidation, and a new one is not created -I need to make sure that with each security buy there is a StopLimit set in place -Try by creating a separate method for StopLimit and inserting it into -Trying to get classes working. Always getting assemblyPath errors when trying to call derived or separate classes V19: -Cloned after 9 days of inactivity -Decided not to make Parameters a class of its own, but to implement it as a function of MainProcess -Deleting Parameters class and reinstating parametersInit() -Reverting to the working v17 model -I can't get an instance of the MainProcess to work inside of another class -At least I can't get Debug to work inside of another class when I have created an instance of MainProcess -I wanted to assign a class of it's own to each indicator, nested in the IndicatorSuiteAlgorithm class, but it just isn't working -I will try to just split up the indicators into separate files as functions, rather than as separate classes V20: -Cloning "WINNER momp, 0.5% looser, 0.5% increase" as V20 -StopLimit was not yet implemented in this cloned version v17 -I had added an "Orders.csARX" file. Adding that now. -I want to be able to form nested classes in the basic (MainProcess) QCAlgorithm base class. -I had trouble with this before (see v18) -Making SignalClass.cs to see if I can get a basic debug output -FINALLY! Got the right syntax for getting Debug to work from within a nested class. -Reference: https://stackoverflow.com/questions/1702763/nested-c-sharp-classes-calling-outer-method-from-inner -Cloning and archiving V21: -Backtesting -ReAdding Classes for MOMPAlgorithm -WORKS!! -Now testing with _useMomp = true; -Got a basic framework working, so cloning and archiving. V22: -Building and backtesting -Moving more of the mompOnData method piece by piece into active code -FAILED. Cloning V21 again, and making v23 -Having problems with getting the right "_holdings". V23: -Cloning -Backtesting -Works at present -Attempted to make a Parameters class so that I could vary parameters according to different models -The class gave a runtime error of null value at Initialize() -"Failed to initialize algorithm: Initialize(): Object reference not set to an instance of an object" -I have restored the simple version without using a class, but I have kept the code I used -Cloning and archiving V24: -Cloning and backtesting -Attempting again to implement the Parameters class -This will be very interesting in the future, and will help the model to be scalable -The alpha should be able to determine which parameters are best based on market activity -WORKS! Cloning and archiving V25: -Cloning and Backtesting -Cleaning up the scraps from building Parameters class -Continuing now to work on getting mompOnData to work completely -WOOOOHOOOOO!!! WORKS!!! -Cloning and archiving V26: -Cloning and Backtesting -Next goal to make StopLimit work, referring to RDS.B --------------------------------- Creating: MainProject_Beta_v2 (Above version all MainProject_Beta_v1, e.g. v1.1 - v1) */
/* Formula: E_N(x) = E_0*(1+x)^N Where E_N is equity at week N, E_0 is the initial investment, and x is the weekly percent growth rate. ------------------------------------- Putting in Perspective: For E_0 = $2000, trading 100 shares each time => picking stocks valued at $15-20. $1.00 is 5% of $20 $0.40 is 2% of $20 $0.20 is 1% of $20 Optimistic: x = 0.05 E_52(0.05) = 25,285 More Realistic: x = 0.02 E_52(0.02) = $5,600 E_10Y(0.02) = $59 Million Most Realistic: x = 0.01 E_52(0.01) = $3355 E_10Y(0.01) = $350K E_30Y(0.01) = $11 Billion ------------------------------------- For E_0 = $10,000, I can buy 100 shares of stocks in the $70-100 range. **$1 is 1% of $100** NOT A BIG DIFFERENCE! E_52(0.01, E_0 = $10k) = $16,776 - This is basically the line for poverty level. - So I can keep this money as personal income without paying taxes. - Reinvest it in the next year. - Then all income that exceeds the minimum income, put it in the Limitless Trust. - It will be tax deductible. - Trade as the Limitless Trust. E_10Y(0.01, E_0 = $10k) = $1.7 Million E_30Y(0.01, E_0 = $10k) = $55 Billion ------------------------------------- Fixed Income: $100k invested in a fund (e.g. SDIV) that yeilds $0.10/share/month SDIV is $20/share => 5,000 shares => $500/month $1M invested => 50,000 shares => $5,000/month E_9Y(0.01, E_0 = $10k) = $1 Million ----------------- PARAMETERS E = Current Equity ($) w = Price Range Low End weight (#, w ∈ [0,1]) x = Weekly Growth Rate (%) ----------------- FUNCTION P = Price Range for stocks P = (P_L, P_H), P_H = E/100, P_L = w*P_H Ex.1: E = $2000 , w = 0.8, P_H = $20, P_L = $16 Ex.2: E = $2000 , w = 0.75, P_H = $20, P_L = $15 Ex.3: E = $10,000, w = 0.75, P_H = $100, P_L = $75 ----------------- FUNCTIONS //Define the following methods in separate files and write the algorithm here //that calls, combines, and weighs in thier respective degrees each method, //returning THE _symbol //Pre-Method Selection: //Price Range //Share Quantity Range //Method 1: symbolCrossoverTrigger(); //Select based on crossover to/from bearish/bullish for a given symbol //Test which is more effective, or if both are effective in different situations //Method 2: mompTrigger(); //Method 3: movingAverageTrigger(); in movingAverageTrigger //private bool use_MACD; //private MovingAverageConvergenceDivergence _macd; //private ExponentialMovingAverage _ema; //The 12 and 26-day EMAs are the most popular short-term averages, 50- and 200- for LT //MovingAverageAlgorithm oc = new MovingAverageAlgorithm(); //Method 4: betaRsquaredTrigger(); */
/* * 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. */ using System.Collections.Generic; using System.Linq; using QuantConnect.Data.Fundamental; using QuantConnect.Data.Market; using QuantConnect.Data.UniverseSelection; namespace QuantConnect.Algorithm.CSharp { /// <summary> /// Demonstration of how to define a universe filtered by the combination of coarse /// fundamental data and fine fundamental data. This lets you do a first pass based on the asset volume; then later /// select based on the company fundamentals. /// </summary> /// <meta name="tag" content="using data" /> /// <meta name="tag" content="universes" /> /// <meta name="tag" content="coarse universes" /> /// <meta name="tag" content="fine universes" /> public class CoarseFineFundamentalComboAlgorithm : QCAlgorithm { private const int NumberOfSymbolsCoarse = 5; private const int NumberOfSymbolsFine = 2; // initialize our changes to nothing private SecurityChanges _changes = SecurityChanges.None; public override void Initialize() { UniverseSettings.Resolution = Resolution.Daily; SetStartDate(2014, 04, 01); SetEndDate(2014, 04, 30); SetCash(50000); // this add universe method accepts two parameters: // - coarse selection function: accepts an IEnumerable<CoarseFundamental> and returns an IEnumerable<Symbol> // - fine selection function: accepts an IEnumerable<FineFundamental> and returns an IEnumerable<Symbol> AddUniverse(CoarseSelectionFunction, FineSelectionFunction); } // sort the data by daily dollar volume and take the top 'NumberOfSymbolsCoarse' public IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse) { // select only symbols with fundamental data and sort descending by daily dollar volume // var sortedByDollarVolume = coarse // .Where(x => x.HasFundamentalData) // .OrderByDescending(x => x.DollarVolume); var sortedByDollarVolume = coarse.Where(x => x.HasFundamentalData).OrderByDescending(x => x.DollarVolume); // take the top entries from our sorted collection var top5 = sortedByDollarVolume.Take(NumberOfSymbolsCoarse); // we need to return only the symbol objects return top5.Select(x => x.Symbol); } // sort the data by P/E ratio and take the top 'NumberOfSymbolsFine' public IEnumerable<Symbol> FineSelectionFunction(IEnumerable<FineFundamental> fine) { // sort descending by P/E ratio var sortedByPeRatio = fine.OrderByDescending(x => x.ValuationRatios.PERatio); // take the top entries from our sorted collection var topFine = sortedByPeRatio.Take(NumberOfSymbolsFine); // we need to return only the symbol objects return topFine.Select(x => x.Symbol); } //Data Event Handler: New data arrives here. "TradeBars" type is a dictionary of strings so you can access it by symbol. public void OnData(TradeBars data) { // if we have no changes, do nothing if (_changes == SecurityChanges.None) return; // liquidate removed securities foreach (var security in _changes.RemovedSecurities) { if (security.Invested) { Liquidate(security.Symbol); Debug("Liquidated Stock: " + security.Symbol.Value); } } // we want 50% allocation in each security in our universe foreach (var security in _changes.AddedSecurities) { SetHoldings(security.Symbol, 0.5m); Debug("Purchased Stock: " + security.Symbol.Value); } _changes = SecurityChanges.None; } // this event fires whenever we have changes to our universe public override void OnSecuritiesChanged(SecurityChanges changes) { _changes = changes; if (changes.AddedSecurities.Count > 0) { Debug("Securities added: " + string.Join(",", changes.AddedSecurities.Select(x => x.Symbol.Value))); } if (changes.RemovedSecurities.Count > 0) { Debug("Securities removed: " + string.Join(",", changes.RemovedSecurities.Select(x => x.Symbol.Value))); } } } }
using System.Collections.Generic; using System.Linq; using QuantConnect.Data.Fundamental; using QuantConnect.Data.Market; using QuantConnect.Data.UniverseSelection; using QuantConnect.Algorithm; namespace QuantConnect_AlixClaireErie { public partial class MainProcess : QCAlgorithm, IAlgorithm { /// <summary> /// Demonstration of how to define a universe filtered by the combination of coarse /// fundamental data and fine fundamental data. This lets you do a first pass based on the asset volume; then later /// select based on the company fundamentals. /// </summary> /// <meta name="tag" content="using data" /> /// <meta name="tag" content="universes" /> /// <meta name="tag" content="coarse universes" /> /// <meta name="tag" content="fine universes" /> public partial class CoarseFineFundamentalComboAlgorithm : QCAlgorithm { private const int NumberOfSymbolsCoarse = 5; private const int NumberOfSymbolsFine = 2; // initialize our changes to nothing private SecurityChanges _changes = SecurityChanges.None; public override void Initialize() { UniverseSettings.Resolution = Resolution.Daily; SetStartDate(2014, 04, 01); SetEndDate(2014, 04, 30); SetCash(50000); // this add universe method accepts two parameters: // - coarse selection function: accepts an IEnumerable<CoarseFundamental> and returns an IEnumerable<Symbol> // - fine selection function: accepts an IEnumerable<FineFundamental> and returns an IEnumerable<Symbol> AddUniverse(CoarseSelectionFunction, FineSelectionFunction); } // sort the data by daily dollar volume and take the top 'NumberOfSymbolsCoarse' public IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse) { // select only symbols with fundamental data and sort descending by daily dollar volume // var sortedByDollarVolume = coarse // .Where(x => x.HasFundamentalData) // .OrderByDescending(x => x.DollarVolume); var sortedByDollarVolume = coarse.Where(x => x.HasFundamentalData).OrderByDescending(x => x.DollarVolume); // take the top entries from our sorted collection var top5 = sortedByDollarVolume.Take(NumberOfSymbolsCoarse); // we need to return only the symbol objects return top5.Select(x => x.Symbol); } // sort the data by P/E ratio and take the top 'NumberOfSymbolsFine' public IEnumerable<Symbol> FineSelectionFunction(IEnumerable<FineFundamental> fine) { // sort descending by P/E ratio var sortedByPeRatio = fine.OrderByDescending(x => x.ValuationRatios.PERatio); // take the top entries from our sorted collection var topFine = sortedByPeRatio.Take(NumberOfSymbolsFine); // we need to return only the symbol objects return topFine.Select(x => x.Symbol); } //Data Event Handler: New data arrives here. "TradeBars" type is a dictionary of strings so you can access it by symbol. public void OnData(TradeBars data) { // if we have no changes, do nothing if (_changes == SecurityChanges.None) return; // liquidate removed securities foreach (var security in _changes.RemovedSecurities) { if (security.Invested) { Liquidate(security.Symbol); Debug("Liquidated Stock: " + security.Symbol.Value); } } // we want 50% allocation in each security in our universe foreach (var security in _changes.AddedSecurities) { SetHoldings(security.Symbol, 0.5m); Debug("Purchased Stock: " + security.Symbol.Value); } _changes = SecurityChanges.None; } // this event fires whenever we have changes to our universe public override void OnSecuritiesChanged(SecurityChanges changes) { _changes = changes; if (changes.AddedSecurities.Count > 0) { Debug("Securities added: " + string.Join(",", changes.AddedSecurities.Select(x => x.Symbol.Value))); } if (changes.RemovedSecurities.Count > 0) { Debug("Securities removed: " + string.Join(",", changes.RemovedSecurities.Select(x => x.Symbol.Value))); } } } } }
/* * 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. */ using System; using System.Collections.Generic; using QuantConnect.Data; using QuantConnect.Orders; namespace QuantConnect.Algorithm.CSharp { /// <summary> /// In this algorithm we submit/update/cancel each order type /// </summary> /// <meta name="tag" content="trading and orders" /> /// <meta name="tag" content="placing orders" /> /// <meta name="tag" content="managing orders" /> /// <meta name="tag" content="order tickets" /> /// <meta name="tag" content="updating orders" /> public class OrderTicketDemoAlgorithm : QCAlgorithm { private const string symbol = "SPY"; private readonly List<OrderTicket> _openMarketOnOpenOrders = new List<OrderTicket>(); private readonly List<OrderTicket> _openMarketOnCloseOrders = new List<OrderTicket>(); private readonly List<OrderTicket> _openLimitOrders = new List<OrderTicket>(); private readonly List<OrderTicket> _openStopMarketOrders = new List<OrderTicket>(); private readonly List<OrderTicket> _openStopLimitOrders = new List<OrderTicket>(); /// <summary> /// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized. /// </summary> public override void Initialize() { SetStartDate(2013, 10, 7); //Set Start Date SetEndDate(2013, 10, 11); //Set End Date SetCash(100000); //Set Strategy Cash // Find more symbols here: http://quantconnect.com/data AddSecurity(SecurityType.Equity, symbol, Resolution.Minute); } /// <summary> /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. /// </summary> /// <param name="data">Slice object keyed by symbol containing the stock data</param> public override void OnData(Slice data) { // MARKET ORDERS MarketOrders(); // LIMIT ORDERS LimitOrders(); // STOP MARKET ORDERS StopMarketOrders(); // STOP LIMIT ORDERS StopLimitOrders(); // MARKET ON OPEN ORDERS MarketOnOpenOrders(); // MARKET ON CLOSE ORDERS MarketOnCloseOrders(); } /// <summary> /// MarketOrders are the only orders that are processed synchronously by default, so /// they'll fill by the next line of code. This behavior equally applies to live mode. /// You can opt out of this behavior by specifying the 'asynchronous' parameter as true. /// </summary> private void MarketOrders() { if (TimeIs(7, 9, 31)) { Log("Submitting MarketOrder"); // submit a market order to buy 10 shares, this function returns an OrderTicket object // we submit the order with asynchronous:false, so it block until it is filled var newTicket = MarketOrder(symbol, 10, asynchronous: false); if (newTicket.Status != OrderStatus.Filled) { Log("Synchronous market order was not filled synchronously!"); Quit(); } // we can also submit the ticket asynchronously. In a backtest, we'll still perform // the fill before the next time events for your algorithm. here we'll submit the order // asynchronously and try to cancel it, sometimes it will, sometimes it will be filled // first. newTicket = MarketOrder(symbol, 10, asynchronous: true); var response = newTicket.Cancel("Attempt to cancel async order"); if (response.IsSuccess) { Log("Successfully canceled async market order: " + newTicket.OrderId); } else { Log("Unable to cancel async market order: " + response.ErrorCode); } } } /// <summary> /// LimitOrders are always processed asynchronously. Limit orders are used to /// set 'good' entry points for an order. For example, you may wish to go /// long a stock, but want a good price, so can place a LimitOrder to buy with /// a limit price below the current market price. Likewise the opposite is true /// when selling, you can place a LimitOrder to sell with a limit price above the /// current market price to get a better sale price. /// You can submit requests to update or cancel the LimitOrder at any time. /// The 'LimitPrice' for an order can be retrieved from the ticket using the /// OrderTicket.Get(OrderField) method, for example: /// <code> /// var currentLimitPrice = orderTicket.Get(OrderField.LimitPrice); /// </code> /// </summary> private void LimitOrders() { if (TimeIs(7, 12, 0)) { Log("Submitting LimitOrder"); // submit a limit order to buy 10 shares at .1% below the bar's close var close = Securities[symbol].Close; var newTicket = LimitOrder(symbol, 10, close * .999m); _openLimitOrders.Add(newTicket); // submit another limit order to sell 10 shares at .1% above the bar's close newTicket = LimitOrder(symbol, -10, close * 1.001m); _openLimitOrders.Add(newTicket); } // when we submitted new limit orders we placed them into this list, // so while there's two entries they're still open and need processing if (_openLimitOrders.Count == 2) { var openOrders = _openLimitOrders; // check if either is filled and cancel the other var longOrder = openOrders[0]; var shortOrder = openOrders[1]; if (CheckPairOrdersForFills(longOrder, shortOrder)) { _openLimitOrders.Clear(); return; } // if niether order has filled, bring in the limits by a penny var newLongLimit = longOrder.Get(OrderField.LimitPrice) + 0.01m; var newShortLimit = shortOrder.Get(OrderField.LimitPrice) - 0.01m; Log("Updating limits - Long: " + newLongLimit.ToString("0.00") + " Short: " + newShortLimit.ToString("0.00")); longOrder.Update(new UpdateOrderFields { // we could change the quantity, but need to specify it //Quantity = LimitPrice = newLongLimit, Tag = "Update #" + (longOrder.UpdateRequests.Count + 1) }); shortOrder.Update(new UpdateOrderFields { LimitPrice = newShortLimit, Tag = "Update #" + (shortOrder.UpdateRequests.Count + 1) }); } } /// <summary> /// StopMarketOrders work in the opposite way that limit orders do. /// When placing a long trade, the stop price must be above current /// market price. In this way it's a 'stop loss' for a short trade. /// When placing a short trade, the stop price must be below current /// market price. In this way it's a 'stop loss' for a long trade. /// You can submit requests to update or cancel the StopMarketOrder at any time. /// The 'StopPrice' for an order can be retrieved from the ticket using the /// OrderTicket.Get(OrderField) method, for example: /// <code> /// var currentStopPrice = orderTicket.Get(OrderField.StopPrice); /// </code> /// </summary> private void StopMarketOrders() { if (TimeIs(7, 12 + 4, 0)) { Log("Submitting StopMarketOrder"); // a long stop is triggered when the price rises above the value // so we'll set a long stop .25% above the current bar's close var close = Securities[symbol].Close; var stopPrice = close * 1.0025m; var newTicket = StopMarketOrder(symbol, 10, stopPrice); _openStopMarketOrders.Add(newTicket); // a short stop is triggered when the price falls below the value // so we'll set a short stop .25% below the current bar's close stopPrice = close * .9975m; newTicket = StopMarketOrder(symbol, -10, stopPrice); _openStopMarketOrders.Add(newTicket); } // when we submitted new stop market orders we placed them into this list, // so while there's two entries they're still open and need processing if (_openStopMarketOrders.Count == 2) { // check if either is filled and cancel the other var longOrder = _openStopMarketOrders[0]; var shortOrder = _openStopMarketOrders[1]; if (CheckPairOrdersForFills(longOrder, shortOrder)) { _openStopMarketOrders.Clear(); return; } // if niether order has filled, bring in the stops by a penny var newLongStop = longOrder.Get(OrderField.StopPrice) - 0.01m; var newShortStop = shortOrder.Get(OrderField.StopPrice) + 0.01m; Log("Updating stops - Long: " + newLongStop.ToString("0.00") + " Short: " + newShortStop.ToString("0.00")); longOrder.Update(new UpdateOrderFields { // we could change the quantity, but need to specify it //Quantity = StopPrice = newLongStop, Tag = "Update #" + (longOrder.UpdateRequests.Count + 1) }); shortOrder.Update(new UpdateOrderFields { StopPrice = newShortStop, Tag = "Update #" + (shortOrder.UpdateRequests.Count + 1) }); } } /// <summary> /// StopLimitOrders work as a combined stop and limit order. First, the /// price must pass the stop price in the same way a StopMarketOrder works, /// but then we're also gauranteed a fill price at least as good as the /// limit price. This order type can be beneficial in gap down scenarios /// where a StopMarketOrder would have triggered and given the not as beneficial /// gapped down price, whereas the StopLimitOrder could protect you from /// getting the gapped down price through prudent placement of the limit price. /// You can submit requests to update or cancel the StopLimitOrder at any time. /// The 'StopPrice' or 'LimitPrice' for an order can be retrieved from the ticket /// using the OrderTicket.Get(OrderField) method, for example: /// <code> /// var currentStopPrice = orderTicket.Get(OrderField.StopPrice); /// var currentLimitPrice = orderTicket.Get(OrderField.LimitPrice); /// </code> /// </summary> private void StopLimitOrders() { if (TimeIs(8, 12, 1)) { Log("Submitting StopLimitOrder"); // a long stop is triggered when the price rises above the value // so we'll set a long stop .25% above the current bar's close // now we'll also be setting a limit, this means we are gauranteed // to get at least the limit price for our fills, so make the limit // price a little softer than the stop price var close = Securities[symbol].Close; var stopPrice = close * 1.001m; var limitPrice = close - 0.03m; var newTicket = StopLimitOrder(symbol, 10, stopPrice, limitPrice); _openStopLimitOrders.Add(newTicket); // a short stop is triggered when the price falls below the value // so we'll set a short stop .25% below the current bar's close // now we'll also be setting a limit, this means we are gauranteed // to get at least the limit price for our fills, so make the limit // price a little softer than the stop price stopPrice = close * .999m; limitPrice = close + 0.03m; newTicket = StopLimitOrder(symbol, -10, stopPrice, limitPrice); _openStopLimitOrders.Add(newTicket); } // when we submitted new stop limit orders we placed them into this list, // so while there's two entries they're still open and need processing if (_openStopLimitOrders.Count == 2) { // check if either is filled and cancel the other var longOrder = _openStopLimitOrders[0]; var shortOrder = _openStopLimitOrders[1]; if (CheckPairOrdersForFills(longOrder, shortOrder)) { _openStopLimitOrders.Clear(); return; } // if niether order has filled, bring in the stops/limits in by a penny var newLongStop = longOrder.Get(OrderField.StopPrice) - 0.01m; var newLongLimit = longOrder.Get(OrderField.LimitPrice) + 0.01m; var newShortStop = shortOrder.Get(OrderField.StopPrice) + 0.01m; var newShortLimit = shortOrder.Get(OrderField.LimitPrice) - 0.01m; Log("Updating stops - Long: " + newLongStop.ToString("0.00") + " Short: " + newShortStop.ToString("0.00")); Log("Updating limits - Long: " + newLongLimit.ToString("0.00") + " Short: " + newShortLimit.ToString("0.00")); longOrder.Update(new UpdateOrderFields { // we could change the quantity, but need to specify it //Quantity = StopPrice = newLongStop, LimitPrice = newLongLimit, Tag = "Update #" + (longOrder.UpdateRequests.Count + 1) }); shortOrder.Update(new UpdateOrderFields { StopPrice = newShortStop, LimitPrice = newShortLimit, Tag = "Update #" + (shortOrder.UpdateRequests.Count + 1) }); } } /// <summary> /// MarketOnCloseOrders are always executed at the next market's closing /// price. The only properties that can be updated are the quantity and /// order tag properties. /// </summary> private void MarketOnCloseOrders() { if (TimeIs(9, 12, 0)) { Log("Submitting MarketOnCloseOrder"); // open a new position or triple our existing position var qty = Portfolio[symbol].Quantity; qty = qty == 0 ? 100 : 2*qty; var newTicket = MarketOnCloseOrder(symbol, qty); _openMarketOnCloseOrders.Add(newTicket); } if (_openMarketOnCloseOrders.Count == 1 && Time.Minute == 59) { var ticket = _openMarketOnCloseOrders[0]; // check for fills if (ticket.Status == OrderStatus.Filled) { _openMarketOnCloseOrders.Clear(); return; } var quantity = ticket.Quantity + 1; Log("Updating quantity - New Quantity: " + quantity); // we can update the quantity and tag ticket.Update(new UpdateOrderFields { Quantity = quantity, Tag = "Update #" + (ticket.UpdateRequests.Count + 1) }); } if (TimeIs(EndDate.Day, 12 + 3, 45)) { Log("Submitting MarketOnCloseOrder to liquidate end of algorithm"); MarketOnCloseOrder(symbol, -Portfolio[symbol].Quantity, "Liquidate end of algorithm"); } } /// <summary> /// MarketOnOpenOrders are always executed at the next market's opening /// price. The only properties that can be updated are the quantity and /// order tag properties. /// </summary> private void MarketOnOpenOrders() { if (TimeIs(8, 12 + 2, 0)) { Log("Submitting MarketOnOpenOrder"); // its EOD, let's submit a market on open order to short even more! var newTicket = MarketOnOpenOrder(symbol, 50); _openMarketOnOpenOrders.Add(newTicket); } if (_openMarketOnOpenOrders.Count == 1 && Time.Minute == 59) { var ticket = _openMarketOnOpenOrders[0]; // check for fills if (ticket.Status == OrderStatus.Filled) { _openMarketOnOpenOrders.Clear(); return; } var quantity = ticket.Quantity + 1; Log("Updating quantity - New Quantity: " + quantity); // we can update the quantity and tag ticket.Update(new UpdateOrderFields { Quantity = quantity, Tag = "Update #" + (ticket.UpdateRequests.Count + 1) }); } } public override void OnOrderEvent(OrderEvent orderEvent) { var order = Transactions.GetOrderById(orderEvent.OrderId); Console.WriteLine("{0}: {1}: {2}", Time, order.Type, orderEvent); } private bool CheckPairOrdersForFills(OrderTicket longOrder, OrderTicket shortOrder) { if (longOrder.Status == OrderStatus.Filled) { Log(shortOrder.OrderType + ": Cancelling short order, long order is filled."); shortOrder.Cancel("Long filled."); return true; } if (shortOrder.Status == OrderStatus.Filled) { Log(longOrder.OrderType + ": Cancelling long order, short order is filled."); longOrder.Cancel("Short filled"); return true; } return false; } private bool TimeIs(int day, int hour, int minute) { return Time.Day == day && Time.Hour == hour && Time.Minute == minute; } } }
namespace QuantConnect_AlixClaireErie { public partial class MainProcess : QCAlgorithm, IAlgorithm { public void Orders(string symbol, SecurityHolding holdings) { private string _symbol = _symbol; private SecurityHolding _holdings = holdings; private decimal _price; int _stopLimitOrderId = 0; OrderTicket _stopLimitOrder; int _rebalancePeriod = 1; DateTime _updatedDate = new DateTime(); public void priceIncreaseLiquidateOrder() { } public void stopLimitOrder(string symbol, SecurityHolding holdings) { /* _symbol = symbol; _holdings = holdings; _stopLimitOrder = StopLimitOrder(_symbol, _holdings.Quantity, (_price * .97m), (_price * .96m)); Debug("Created limit order for " + _symbol + " Price: " + _price.ToString("C") + " id: " + _stopLimitOrderId); if (_stopLimitOrder == null) { //Set our first order to less than MS's price: //public OrderTicket StopLimitOrder(Symbol symbol, int quantity, decimal stopPrice, decimal limitPrice, string tag) _stopLimitOrder = StopLimitOrder(_symbol, _holdings.Quantity, (_price * .97m), (_price * .96m)); Debug("Created first limit order with " + _symbol + " Price: " + _price.ToString("C") + " id: " + _stopLimitOrderId); } else { //the first order is cancelled when } //Update the limit price once per week: if (_updatedDate.Date < Time.Date) { _updatedDate = Time.Date.AddDays(_rebalancePeriod); // Debug("_updatedDate " + _updatedDate); _stopLimitOrder.Update(new UpdateOrderFields { StopPrice = _price * .97m, LimitPrice = _price * .96m }); } */ /* if holdings > 0 else if(_currPrice <= (1.0m - _stopLossLimit)*_acqPricePerShare) { Liquidate(_symbol); Debug("Sold " + _symbol + " on " + Time.ToShortDateString() + " for " + _sellPrice); } */ } } } }
using System; using System.Diagnostics; using QuantConnect.Algorithm; using QuantConnect.Data; using QuantConnect.Data.Custom; using QuantConnect.Data.Market; using QuantConnect.Indicators; using QuantConnect.Securities; namespace QuantConnect_AlixClaireErie { public partial class Main : QCAlgorithm, IAlgorithm { public partial class Signal { //Static variables protected static Main main; // protected static string symbol; protected static string symbolID; protected static Indicators indicators; public Signal(Main _main) { main = _main; // symbol = main.symbol; symbolID = main.symbolID; } public partial class Indicators { public BollingerBands BB; public RelativeStrengthIndex RSI; public MomentumPercent MOMP1; public MomentumPercent MOMP2; public MomentumPercent MOMP5; public MomentumPercent MOMP14; public MomentumPercent MOMP20; public Minimum MIN; public Maximum MAX; } } } }
/* **Parameters.cs studies gives us a bar for data scraping: -1st level: Run different mompTrig and percentPriceIncreases without any time limit -If the first level scrape yeilds a parameter with >25% return over the last year then proceed with 2nd level scraping -2nd level: If the security passes the 3-day limit test then it is a buy */
namespace QuantConnect_AlixClaireErie { public partial class Main : QCAlgorithm, IAlgorithm { public void ParametersInit() { parameters = new Parameters(this) { //HandPicked Symbols // symbol = "CS", symbol = "RDS.B", //Variables startYear = 2017, startMonth = 2, percentPriceIncrease = 0.02m, //Percent expressed as a division by 1 percentPriceDecrease = 0.04m, holdingTimeLimit = 15, // stopLossLimit =-0.05m; //Percent expressed divided by 1 // Symbol = QuantConnect.Symbol.Create(symbol, SecurityType.Equity, Market.USA); //Processes useUniverse = true, useSignal = true, useMomp = true }; // SetStartDate(DateTime.Now.Date.AddDays(-360)); //Set Start Date SetStartDate(parameters.startYear, parameters.startMonth, 22); //Set Start Date // SetEndDate(2018, 1, 18); //Set End Date SetEndDate(DateTime.Now); //Set End Date SetCash(2000); } public partial class Parameters { protected static Main main; //Variables public string symbol; public int startYear; public int startMonth; public int holdingTimeLimit; public decimal mompTrigger; public decimal percentPriceIncrease; public decimal percentPriceDecrease; // public static decimal _stopLossLimit; // public static Symbol _Symbol; //Processes public bool useUniverse; public bool useSignal; public bool useMomp; public Parameters(Main _main) { main = _main; } public Parameters(Signal signal) { mompTrigger = -0.5m; //Actual percent value } public Parameters(Execution execute) { percentPriceDecrease = 0.02m; //Actual percent value } } } } /* HOLDING TIME LIMIT Testing with CS: No time limit: 32.84 Return 3 days: 26.54 5 days: 27.77 Actually, this study is inconclusive, because a holding time limit is only valuable when wanting to free up your buying power for a more opportune position. Meaning, it should be used in conjunction with more than one symbol. Furthermore, CS happens to have had very positive growth over this period and the best rates of return are seen when not selling this stock at all. For the case of CS it is probably most useful to implement Stop Loss order Honestly, I don't want to hold a stock for more than 2-3 days, so I will have to work around that with the model -------------------------------------------------- TEST PARAMETERS (MOMP20, "CS", $10k) Looser Increase Return 2 1 10.6 2 2 17.32 3 2.5 3 1.5 1 20.86 1.5 1.5 20.03 1 0.5 12.44 1 1 25.80 !!! 0.5 0.5 31.38 !!! 0.1 0.1 19.98 -------------------------------------------------- TEST PARAMETERS (MOMP20, "RDS-B", $10k) End Date - Present mompTrig Increase Return 1 1 !!! 0.5 0.5 2.63 !!! (RDS.B plummeted after last buy!) //Signal to define/implement _stopLossLimit End Date - Jan 18, 2018 (Before plummet) mompTrig Increase Return 2 1 2 2 3 2.5 1.5 1 1.5 1.5 1 0.5 1 1 5.2 !!! 0.5 0.5 8.44 !!! Still not very good 0.5 1 11.84 0.5 2 21.45 We see 2.5 months of holding the stock from May 18 to Aug 1: Need to implement selling if it is held for more than 2 days. 0.1 0.1 -The majority of RDS.B under the 0.5->2 model are held for at least 7 days. This indicates a bad model. We want to be able to hold a stock for a max of 2 days. -This can be one of our criteria for determining the symbol and model is if we can make profits by trading with a max of 2 day limit. **RDS.B Fails under 2-day max holding time. (See corresponding results.): Using 0.5 mompTrig and 2 increase, we get a 2.88% return as opposed to an 8.44% without the 2-day max. Try this with CS now with its winner parameters: Looser Increase Return 0.5 0.5 15.47 Unsuccessful! Try CS now with 10-day max: Looser Increase Return 0.5 0.5 21.09 **What this confirms is that, we need a model that only holds securities for a short period of time, and we build our portfolio around a number of different securities. Try one last limit with CS: 3 days max Looser Increase Return 0.5 0.5 20.30 Jackpot! **This gives us a bar for data scraping: -1st level: Run different mompTrig and percentPriceIncreases without any time limit -If the first level scrape yeilds a parameter with >25% return over the last year then proceed with 2nd level scraping -2nd level: If the security passes the 3-day limit test then it is a buy */
using System; using System.Diagnostics; using QuantConnect.Algorithm; using QuantConnect.Data; using QuantConnect.Data.Custom; using QuantConnect.Data.Market; using QuantConnect.Indicators; using QuantConnect.Securities; namespace QuantConnect_AlixClaireErie { public partial class Main : QCAlgorithm, IAlgorithm { public partial class Signal { // private Parameters parameters; private RollingWindow<TradeBar> window; private RollingWindow<IndicatorDataPoint> mompWin; private decimal price; public void MOMPInit() { // parameters = new Parameters(this); indicators = new Indicators //Declared in Signal before Signal Constructor { BB = main.BB(symbolID, 20, 1, MovingAverageType.Simple, Resolution.Minute), RSI = main.RSI(symbolID, 14, MovingAverageType.Simple, Resolution.Minute), MOMP1 = main.MOMP(symbolID, 1, Resolution.Daily), MOMP2 = main.MOMP(symbolID, 2, Resolution.Daily), MOMP5 = main.MOMP(symbolID, 5, Resolution.Daily), MOMP14 = main.MOMP(symbolID, 14, Resolution.Daily), MOMP20 = main.MOMP(symbolID, 20, Resolution.Daily), MIN = main.MIN(symbolID, 14, Resolution.Daily), // by default if the symbol is a tradebar type then it will be the min of the low property MAX = main.MAX(symbolID, 14, Resolution.Daily) // by default if the symbol is a tradebar type then it will be the max of the high property }; window = new RollingWindow<TradeBar>(2); main.MOMP(symbolID, 20).Updated += (sender, updated) => mompWin.Add(updated); //Fills _mompWin with data of MOMP20 mompWin = new RollingWindow<IndicatorDataPoint>(20); } // public void MOMPOnData(Slice data, decimal _percentPriceIncrease, int _holdingTimeLimit) public void MOMPOnData(Slice data, Filter filter, Symbol symbolID) { var parameters = new Parameters(this); decimal mompTrigger = parameters.mompTrigger; // decimal percentPriceIncrease = _percentPriceIncrease; // int holdingTimeLimit = _holdingTimeLimit; TradeBars bars = data.Bars; if (!indicators.BB.IsReady || !indicators.RSI.IsReady) return; if (bars.ContainsKey(symbolID)) { price = bars[symbolID].Close; window.Add(data[symbolID]); if (!window.IsReady || !mompWin.IsReady) return; var currBar = window[0]; // Current bar had index zero. var pastBar = window[1]; // Past bar has index one. main.Log($"Price: {pastBar.Time} -> {pastBar.Close} ... {currBar.Time} -> {currBar.Close}"); var currMomp = mompWin[0]; // Current MOMP had index zero. var pastMomp = mompWin[mompWin.Count - 1]; // Oldest MOMP has index of window count minus 1. main.Log($"MOMP: {pastMomp.Time} -> {pastMomp.Value} ... {currMomp.Time} -> {currMomp.Value}"); // if (!main.Portfolio.Invested && currMomp <= mompTrigger) if (currMomp <= mompTrigger) { //Send to trigger filter.TriggerCounter(1, symbolID); // var execute = new Execution(main); // execute.OnMOMPTrig(data, symbolID); } } } } } }
using System; using System.Diagnostics; using QuantConnect.Algorithm; using QuantConnect.Data; using QuantConnect.Data.Custom; using QuantConnect.Data.Market; using QuantConnect.Indicators; using QuantConnect.Securities; namespace QuantConnect_AlixClaireErie { public partial class Main : QCAlgorithm, IAlgorithm { public void PortfolioInit(AssetSelection.HandPicked handPicked) { //Future Implementation //Tuple<> for several handpicked symbols... plus pass an argument for the symbol in PortfolioInit symbol = handPicked.symbol; symbolID = QuantConnect.Symbol.Create(symbol, SecurityType.Equity, Market.USA); security = AddSecurity(SecurityType.Equity, symbol, Resolution.Minute, true); equity = AddEquity(symbol, Resolution.Minute); holdings = security.Holdings; Debug("Initial holdings.Quantity of " + security + " = "+ holdings.Quantity); } public partial class AssetSelection { public partial class HandPicked { public string symbol; } } } }
namespace QuantConnect_AlixClaireErie { public partial class Main : QCAlgorithm, IAlgorithm { public partial class Filter { private static Main main; private int t; ///<summary> ///Filter is initiated in the Main –> Initialize method /// ///</summary> public Filter() { t = 0; } public Filter(Main _main) { main = _main; t = 0; Utility.LaunchToConsole(main, this.GetType().Name); } //One Counter: For a given security, once a threshold (buy or sell) has been met, then the trigger resets to 0 //So there should be both a trigger for AssetSelection, and then another buy, another for sell... //There only needs to be one trigger method, but it can have several instantiations //When the a threshold is reached, there is also a disposal of the instantiation using filter.Dispose(); //So the original instantiation of filter should be moved to the AssetSelection class, with the security/equity //And the trigger should take the asset symbol or Symbol as an argument ///<summary> ///A counter that determines if a a threshold is reached for a trade ///</summary> public int TriggerCounter(int trigger, Symbol symbolID) { t += trigger; return t; } ///<summary> ///Sell Model ///A method to determine if a held security has increased by a threshold amount "percentPriceIncrease", defined in Parameters ///</summary> /* public void PercentPriceIncrease(Security security, string symbol) { var parameters = new Parameters(); decimal percentPriceIncrease = parameters.percentPriceIncrease; SecurityHolding holdings = security.Holdings; decimal currPrice = security.Price; decimal avgPricePerShare = holdings.AveragePrice; decimal targetSellPrice = (1.0m + percentPriceIncrease)*avgPricePerShare; if(currPrice >= targetSellPrice) { // main.Liquidate(symbol); // mainProcess.Debug("Sold " + symbol + " on " + MP.Time.ToShortDateString() + " for " + sellPrice); } } */ } } }
/* ***Task List - To Do*** -Stop Limit Order, Test using RDS.B -Create first Universe: -Loosers, MOMP, MACD -Implement MOMP Period Testing using discrete integration -The MOMP Period test that is most valuable will be the one that has the highest absolute value in area under/above the curve -StopLoss orders and holding time limits need to be coordinated with moving averages etc. -Ex: in the case of CS, the holding time limit should not be implemented, because it has a positve growth but in the case of RDS.B it is different ***Task List - Completed*** -Clear Archive Folder and implement new naming system -Implement nested classes for each indicator. Do this BEFORE moving to MovingAverage -Insert meta tags -Make sure to implement Disposal of instantiated classes */
/* public IFeeModel FeeModel {get; set;} */ using System; using QuantConnect.Algorithm; using QuantConnect.Securities; namespace QuantConnect_AlixClaireErie { public partial class Main : QCAlgorithm, IAlgorithm { public partial class Execution { ///<summary> ///Override Fee Model of Interactive Brokers with a RobinHood fee Model ///Except, still haven't found a way to incorporate monthly fees ///</summary> public void NoPerTradeFeeModel(Main main) { main.Securities[main.symbol].FeeModel = new ConstantFeeTransactionModel(0); } ///<summary> ///Override Fee Model of Interactive Brokers with a Monthly fee Model ///This particular model incorporates a $20 per month monthly fee for QuantConnect ///Plus a $6 per month fee for Robinhood ///</summary> /* public void MonthlyFeeModel(Main main, int _feeCounter, System.DateTime date) { //This is wrong... Now is the real now... not the now in the loop // System.DateTime date = DateTime.Now; int dayOfMonth = date.Day; int daysInMonth = DateTime.DaysInMonth(date.Year, date.Month); //Not working because if there is a buy and sell in the same day, they are both charged $6 //Move the fee model inside of the buy, sell loops for (int dayCounter = 1; dayCounter <= daysInMonth; dayCounter++) { if(dayOfMonth == dayCounter && _feeCounter == 0) { main.Securities[main.symbol].FeeModel = new ConstantFeeTransactionModel(6); main.feeCounter += 1; } else if(dayOfMonth == dayCounter && _feeCounter > 0) { main.Securities[main.symbol].FeeModel = new ConstantFeeTransactionModel(0); } } -------------------------------------- // execute.MonthlyFeeModel(this); // for (int dayCounter = 1; dayCounter <= daysInMonth; dayCounter++) for(int yyyy = parameters.startYear; yyyy <= System.DateTime.Now.Year; yyyy++) { for(int m = parameters.startMonth; m <= System.DateTime.Now.Month; m++) { Schedule.On(DateRules.On(yyyy, m, 1), TimeRules.At(10, 0), () => { holdings.AddNewFee(6.0m); }); } } -------------------------------------- Schedule.On(DateRules.MonthStart(), TimeRules.AfterMarketOpen(symbolID), () => { holdings.AddNewFee(6.0m); }); } */ /* public void LastDayOfMonth(Main main) { System.DateTime date = main.Time; int dayOfMonth = date.Day; int daysInMonth = DateTime.DaysInMonth(date.Year, date.Month); if (dayOfMonth == daysInMonth) main.feeCounter = 0; } */ } } }
using System; using System.Diagnostics; using QuantConnect.Algorithm; using QuantConnect.Data; using QuantConnect.Data.Custom; using QuantConnect.Data.Market; using QuantConnect.Indicators; using QuantConnect.Securities; namespace QuantConnect_AlixClaireErie { public partial class Main : QCAlgorithm, IAlgorithm { public void PortfolioInit(AssetSelection.HandPicked handPicked) { //Future Implementation //Tuple<> for several handpicked symbols... plus pass an argument for the symbol in PortfolioInit symbol = handPicked.symbol; symbolID = QuantConnect.Symbol.Create(symbol, SecurityType.Equity, Market.USA); security = AddSecurity(SecurityType.Equity, symbol, Resolution.Minute, true); equity = AddEquity(symbol, Resolution.Minute); holdings = security.Holdings; Debug("Initial holdings.Quantity of " + security + " = "+ holdings.Quantity); } public partial class AssetSelection { public partial class HandPicked { public string symbol; } } } }