| Overall Statistics |
|
Total Trades 16 Average Win 0% Average Loss 0% Compounding Annual Return -3.742% Drawdown 3.800% Expectancy 0 Net Profit -1.627% Sharpe Ratio -0.483 Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha -0.177 Beta 7.192 Annual Standard Deviation 0.072 Annual Variance 0.005 Information Ratio -0.755 Tracking Error 0.072 Treynor Ratio -0.005 Total Fees $129.09 |
using System.Collections.Concurrent;
using System;
using System.Collections.Generic;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Indicators;
using QuantConnect.Securities;
namespace QuantConnect
{
/// <summary>
/// In this algorithm we demonstrate how to perform some technical analysis as
/// part of your coarse fundamental universe selection
/// </summary>
public class SpringDaySelectionAlgorithm : QCAlgorithm
{
//Trading vars
public string benchmark = "SPY"; //Benchmark to compare results to
public decimal max_trading_amount = 250000; //Total Portfolio Cash to spend
public int max_long_positions = 25; //Maximum long postions
public const int min_price_to_trade = 4; //Minimum price
public long min_volume_to_trade = 50000; //Minimum daily volume
public float max_amount_per_trade = 0; //The maximium dollar amount to use per trade
public float max_target_percent = 0;
public float holdings_cnt = 0;
private RollingWindow<TradeBar> history;
// tolerance to prevent bouncing
const decimal Tolerance = 0.01m;
private const int Count = 10;
// use Buffer+Count to leave a little in cash
private const decimal TargetPercent = 0.1m;
private SecurityChanges _changes = SecurityChanges.None;
// holds our coarse fundamental indicators by symbol
private readonly ConcurrentDictionary<Symbol, SelectionData> _averages = new ConcurrentDictionary<Symbol, SelectionData>();
// class used to improve readability of the coarse selection function
private class SelectionData
{
public readonly SimpleMovingAverage Fast;
public readonly SimpleMovingAverage Slow;
public readonly SimpleMovingAverage Price;
public readonly SimpleMovingAverage AverageVolume;
public SelectionData()
{
Fast = new SimpleMovingAverage(10);
Slow = new SimpleMovingAverage(45);
Price = new SimpleMovingAverage(1);
AverageVolume = new SimpleMovingAverage(45);
}
// computes an object score of how much large the fast is than the slow
public decimal ScaledDelta
{
get { return (Fast - Slow)/((Fast + Slow)/2m); }
}
// updates the EMA50 and EMA100 indicators, returning true when they're both ready
public bool Update(DateTime time, decimal value, decimal volume)
{
//return Fast.Update(time, value) && Slow.Update(time, value) && Price.Update(time, value) && AverageVolume.Update(time, volume);
return Fast.Update(time, value) && Slow.Update(time, value) && Price.Update(time, value);
}
}
/// <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()
{
UniverseSettings.Leverage = 1.0m;
UniverseSettings.Resolution = Resolution.Daily;
SetStartDate(2017, 11, 01);
SetEndDate(2018, 04, 08);
SetCash(max_trading_amount);
history = new RollingWindow<TradeBar>(3);
max_amount_per_trade = (float) max_trading_amount / (float) max_long_positions;
AddUniverse(coarse =>
{
return (from cf in coarse
where cf.Price > 5
where cf.Price < 7
where cf.Volume > 250000
let avg = _averages.GetOrAdd(cf.Symbol, sym => new SelectionData())
where avg.Update(cf.EndTime, cf.Price, cf.Volume) // Update returns true when the indicators are ready, so don't accept until they are
where cf.Price > avg.Slow
where cf.Volume > avg.AverageVolume
where cf.HasFundamentalData
select cf.Symbol);
//select cf.Symbol).Take(Count);
});
}
//===============Main Entry Point after the Coarse/Fine Selections==============
public void OnData(TradeBars data)
{
int i1= 0;
//Get the symbols that made it through the Selection and get their history
foreach (var security in _changes.AddedSecurities) {
i1++;
if (!data.ContainsKey(security.Symbol)) {
Debug( String.Format("Missing {0}", security.Symbol ));
RemoveSecurity(security.Symbol); //Not Working
}
else
{
//acceptableSymbols.Add(symbol); //Symbols with data
history.Add(data[security.Symbol]);
switch (history.IsReady)
{
case false:
return;
case true:
if(!security.Invested) {
if ( (int)Securities.Count <= max_long_positions) { //If we are at max holdings exit
int qty = (int) (max_amount_per_trade / (float) data[security.Symbol].Close);
var newTicket = MarketOrder(security.Symbol, qty, asynchronous: true);
Debug (String.Format("1Buy {0} {1} - OHLC[{2:0.00}, {3:0.00}, {4:0.00}, {5:0.00}, {6:0},{7:0}]",
data.Time.ToString("o"), security.Symbol, data[security.Symbol].Open,
data[security.Symbol].High, data[security.Symbol].Low, data[security.Symbol].Close,
data[security.Symbol].Volume,qty));
for (int i2 = 0; i2 < 3; i2++) {
Debug (String.Format("Hist {0} {1} - OHLC[{2:0.00}, {3:0.00}, {4:0.00}, {5:0.00}, {6:0}]",
data.Time.ToString("o"), security.Symbol, history[i2].Open,
history[i2].High, history[i2].Low, history[i2].Close,
history[i2].Volume));
}
}
}
return;
} // End of Case
}
Debug (String.Format("Total Symbols from Selection: {0} data count: {1}",i1, data.Count));
if (_changes == SecurityChanges.None) return;
}
}
/// Event fired each time the we add/remove securities from the data feed
public override void OnSecuritiesChanged(SecurityChanges changes)
{
_changes = changes;
}
}
}