Overall Statistics |
Total Trades 1 Average Win 0% Average Loss 0% Compounding Annual Return 16.263% Drawdown 9.400% Expectancy 0 Net Profit 0% Sharpe Ratio 1.424 Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0.013 Beta 0.982 Annual Standard Deviation 0.109 Annual Variance 0.012 Information Ratio 0.739 Tracking Error 0.014 Treynor Ratio 0.158 Total Fees $1.00 |
namespace QuantConnect.Algorithm.CSharp { /* Illustrates how to access Amazon S3 from QuantConnect cloud. Because the whole point of this S3 configuration is to prevent unauthorized access, I can't share a bucket without additional effort. Configuring Amazon S3 buckets is unfortunately a bit of work (at least if you do it rarely like I do or for the first time). Examples of what you can do this code with some modification: 1) Warm up algorithm through backtests, with restored state from S3 when launching live (this example) - S3State 2) Update parameters or signals by polling S3 bucket (not efficient and pretty ugly, but it works) - S3ParamSet 3) Some kind of external logging - S3Client generic usage This comes without any warranty of any kind. Use at your own risk. */ public class S3ExampleAlgorithm : QCAlgorithm { private const string _algoID = "helloworld"; public override void Initialize() { SetStartDate(2016, 1, 1); SetEndDate(DateTime.Now); SetCash(25000); //at a minimum, you need to replace xxx, yyy, and zzz below var s3Client = new S3Client(); s3Client.BaseURL = new Uri("https://s3-eu-west-1.amazonaws.com"); s3Client.UserID = "xxx"; s3Client.Password = "yyy"; _s3state = new S3State(this, s3Client); _s3state.Bucket = "zzz"; _s3state.AlgoID = _algoID; _assets.Add(new Asset(this, "SPY")); _s3state.DownloadWarmupState(_assets); } private S3State _s3state; private readonly List<Asset> _assets = new List<Asset>(); public override void OnEndOfAlgorithm() { _s3state.UploadWarmupState(_assets); } private class AssetState // must be serializable by Json.net { public decimal MySavedValue; } private class Asset : S3State.Serializable { private readonly Security _security; public Asset(QCAlgorithm algo, string symbol) { _security = algo.AddEquity(symbol, Resolution.Minute); } public string GetKey() { return _security.Symbol.ToString(); } public object SerializeState() { var state = new AssetState(); state.MySavedValue = 3.14m; //should come from your class, obviously return state; } public bool DeserializeState(object o) { var state = (AssetState)o; //do something with state.MySavedValue here return true; } } /* * New data arrives here. * The "Slice" data represents a slice of time, it has all the data you need for a moment. */ public override void OnData(Slice data) { // slice has lots of useful information TradeBars bars = data.Bars; Splits splits = data.Splits; Dividends dividends = data.Dividends; //Get just this bar. TradeBar bar; if (bars.ContainsKey("SPY")) bar = bars["SPY"]; if (!Portfolio.HoldStock) { // place an order, positive is long, negative is short. // Order("SPY", quantity); // or request a fixed fraction of a specific asset. // +1 = 100% long. -2 = short all capital with 2x leverage. SetHoldings("SPY", 1); // debug message to your console. Time is the algorithm time. // send longer messages to a file - these are capped to 10kb Debug("Purchased SPY on " + Time.ToShortDateString()); //Log("This is a longer message send to log."); } } } }
using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Net; using System.Runtime.Serialization; using System.Security.Cryptography; using System.Text; using System.Xml.Linq; namespace QuantConnect.Algorithm.CSharp { sealed class S3Client { public string UserID { get; set; } public string Password { get; set; } public Uri BaseURL { get; set; } private void CheckParameters() { if (UserID == null || UserID == "") throw new ArgumentException("UserID not set"); if (Password == null || Password == "") throw new ArgumentException("Password not set"); if (BaseURL == null) throw new ArgumentException("BaseURL not set"); } private static void WriteToRequestWorkaround(HttpWebRequest request, byte[] data) { var stream = request.GetRequestStream(); stream.Write(data, 0, data.Length); } private static void ReadFromResponseWorkaround(HttpWebResponse response, byte[] data) { var stream = response.GetResponseStream(); for (int count = 0; count < data.Length; ) { int numRead = stream.Read(data, count, data.Length - count); if (numRead > 0) count += numRead; else break; } } private static void ReadFromResponseUntilLineWorkaround(HttpWebResponse response, out byte[] data) { var bytes = new List<byte>(); var stream = response.GetResponseStream(); for (;;) { int next = stream.ReadByte(); if (next == '\n') break; bytes.Add((byte)next); } data = bytes.ToArray(); } private HttpWebResponse Request(string method, string pathName, string objectName, byte[] content) { //ugly code follows, copied it from a buggy online Stack Overflow help request and fixed it string hash = ""; if (content != null) { using (var md5 = new MD5CryptoServiceProvider()) { hash = Convert.ToBase64String(md5.ComputeHash(content)); } } string contentType = "application/octet-stream"; string date = DateTime.UtcNow.ToString("yyyyMMddTHHmmssZ"); string file = "/" + pathName + "/" + objectName; //Creating signature var sBuilder = new StringBuilder(); sBuilder.Append(method).Append("\n"); if (content != null) { sBuilder.Append(hash).Append("\n"); sBuilder.Append(contentType); } else { sBuilder.Append("\n"); } sBuilder.Append("\n\n"); sBuilder.Append("x-amz-date:").Append(date).Append("\n"); sBuilder.Append(file); string stringToSign = sBuilder.ToString(); //Console.WriteLine(stringToSign); var signature = Convert.ToBase64String(new HMACSHA1(Encoding.UTF8.GetBytes(Password)).ComputeHash(Encoding.UTF8.GetBytes(stringToSign))); var uri = new Uri(BaseURL, file); var request = WebRequest.Create(uri) as HttpWebRequest; request.Method = method; if (content != null) { request.ContentType = contentType; request.ContentLength = content.Length; } var headers = request.Headers; headers.Add("x-amz-date", date); if (content != null) headers.Add("Content-MD5", hash); headers.Add("Authorization", string.Format("AWS {0}:{1}", UserID, signature)); if (content != null) { try { WriteToRequestWorkaround(request, content); } catch (WebException e) { Console.WriteLine(e.Message); return null; } } HttpWebResponse response; try { response = request.GetResponse() as HttpWebResponse; } catch (WebException e) { Console.WriteLine(e.Message); response = e.Response as HttpWebResponse; } return response; } private class SerializationBinderWithoutAssembly : SerializationBinder { public override void BindToName(Type serializedType, out string assemblyName, out string typeName) { assemblyName = null; typeName = serializedType.FullName; } public override Type BindToType(string assemblyName, string typeName) { return Type.GetType(typeName); } } private readonly JsonSerializerSettings _jsonSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Binder = new SerializationBinderWithoutAssembly() }; public bool GetObject<T>(string pathName, string objectName, out T data) { data = default(T); byte[] bytes; if (!GetObject(pathName, objectName, out bytes)) return false; data = JsonConvert.DeserializeObject<T>(Encoding.UTF8.GetString(bytes), _jsonSettings); return true; } public bool GetObject(string pathName, string objectName, out byte[] data) { data = null; CheckParameters(); var response = Request("GET", pathName, objectName, null); if (response == null) return false; if (response.StatusCode == HttpStatusCode.OK) { data = new byte[(int)response.ContentLength]; ReadFromResponseWorkaround(response, data); return true; } var content = new byte[0]; ReadFromResponseUntilLineWorkaround(response, out content); string responseContent = Encoding.UTF8.GetString(content); //var xmlResponse = XDocument.Parse(responseContent); Console.WriteLine(response.StatusCode); Console.WriteLine(responseContent); //TODO: extract error message from response return false; } public bool PutObject(string pathName, string objectName, object data) { var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data, _jsonSettings)); return PutObject(pathName, objectName, bytes); } public bool PutObject(string pathName, string objectName, byte[] data) { CheckParameters(); var response = Request("PUT", pathName, objectName, data); if (response == null) return false; if (response.StatusCode == HttpStatusCode.OK || response.StatusCode == HttpStatusCode.Accepted) return true; var content = new byte[0]; ReadFromResponseUntilLineWorkaround(response, out content); string responseContent = Encoding.UTF8.GetString(content); //var xmlResponse = XDocument.Parse(responseContent); Console.WriteLine(response.StatusCode); Console.WriteLine(responseContent); //TODO: extract error message from response return false; } } }
using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; namespace QuantConnect.Algorithm.CSharp { sealed class S3State { public interface Serializable { string GetKey(); object SerializeState(); bool DeserializeState(object o); } private readonly QCAlgorithm _algo; private readonly S3Client _s3Client; public string AlgoID { get; set; } public string Bucket { get; set; } public S3State(QCAlgorithm algo, S3Client client) { _algo = algo; _s3Client = client; AlgoID = "untitled"; Bucket = "examplebucket"; } public void UploadWarmupState(IEnumerable<Serializable> objects) { if (_algo.LiveMode) return; //shouldn't be true from where it's called but check anyway //reasoning is that we might have several live versions of algo running and it's better to warm up from controlled backtest Dictionary<string, object> states; if (_s3Client.GetObject(Bucket, AlgoID + ".js", out states)) { object lastDateObject; if (states.TryGetValue("LastDate", out lastDateObject) && lastDateObject is DateTime) { var lastDate = (DateTime)lastDateObject; if (_algo.Time.Date < lastDate.Date) { Print("Not uploading serialized state, there is already a newer state"); return; } } } states = new Dictionary<string, object>(); states["LastDate"] = _algo.Time.Date; foreach (var tradable in objects) { string ID = tradable.GetKey(); object tradableState = tradable.SerializeState(); if (tradableState == null) { Print("Not uploading serialized state, too little warm up time for " + ID); return; } states[ID] = tradableState; } if (_s3Client.PutObject(Bucket, AlgoID + ".js", states)) Print("Uploaded serialized state to " + Bucket); else Print("Unable to upload serialized state to " + Bucket); } public void DownloadWarmupState(IEnumerable<Serializable> objects) { Dictionary<string, object> states; if (!_s3Client.GetObject(Bucket, AlgoID + ".js", out states)) { string msg = "Unable to download warmup state from " + Bucket; if (_algo.LiveMode) throw new Exception(msg); else _algo.Debug(msg); return; } object lastDateObject; if (states.TryGetValue("LastDate", out lastDateObject) && lastDateObject is DateTime) { var lastDate = (DateTime)lastDateObject; double daysDifference = (_algo.Time.Date - lastDate.Date).TotalDays; if (daysDifference > 4) { string msg = "Algo state on " + Bucket + " seems very old (" + daysDifference + " days)"; if (_algo.LiveMode) throw new Exception(msg); else _algo.Debug(msg); } else if (daysDifference < -1 && !_algo.LiveMode) { string msg = "Algo state on " + Bucket + " is in the future (" + -daysDifference + " days), skipping"; Print(msg); return; } } states.Remove("LastDate"); var tradablesRemaining = new List<Serializable>(); tradablesRemaining.AddRange(objects); foreach (var kv in states) { var tradable = tradablesRemaining.Find(x => x.GetKey() == kv.Key); if (tradable != null) { if (tradable.DeserializeState(kv.Value)) tradablesRemaining.Remove(tradable); } } if (tradablesRemaining.Count > 0) { string msg = "There wasn't correct algo state to deserialize for all tradables, e.g. " + tradablesRemaining[0].GetKey(); if (_algo.LiveMode) throw new Exception(msg); else _algo.Debug(msg); } Print("Downloaded warmup state from " + Bucket); } private void Print(string msg) { if (_algo.LiveMode) _algo.Log(msg); else _algo.Debug(msg); } } }
using System; namespace QuantConnect.Algorithm.CSharp { //new(): because references are atomic, and we wish to support calling DownloadParameters() from another thread while using Parameters property sealed class S3ParamSet<ParamType> where ParamType : new() { private readonly QCAlgorithm _algo; private readonly S3Client _s3Client; public string ParamSetID { get; set; } public string Bucket { get; set; } public bool ThrowOnErrorBeforeOnceSuccessful { get; set; } public ParamType Parameters { get { return _params; } } private ParamType _params = new ParamType(); private bool _lastSuccess = true; private bool _anySuccess = false; public S3ParamSet(QCAlgorithm algo, S3Client client) { _algo = algo; _s3Client = client; ThrowOnErrorBeforeOnceSuccessful = true; ParamSetID = "config"; Bucket = "examplebucket"; } public bool DownloadParameters() { string filename = ParamSetID; if (_algo.LiveMode) filename += ".live"; else filename += ".test"; filename += ".js"; string path = Bucket + "/" + filename; ParamType newParams; if (_s3Client.GetObject(Bucket, filename, out newParams)) { _params = newParams; _lastSuccess = true; _anySuccess = true; } else { if (_lastSuccess) { string msg = "Unable to download parameters from " + path; if (ThrowOnErrorBeforeOnceSuccessful && !_anySuccess) throw new Exception(msg); else _algo.Error(msg); } _lastSuccess = false; } return _lastSuccess; } } }