| Overall Statistics |
|
Total Trades 5 Average Win 4.94% Average Loss -2.74% Compounding Annual Return -6.888% Drawdown 2.100% Expectancy -0.066 Net Profit -0.740% Sharpe Ratio -0.791 Loss Rate 67% Win Rate 33% Profit-Loss Ratio 1.80 Alpha -0.156 Beta 6.711 Annual Standard Deviation 0.067 Annual Variance 0.004 Information Ratio -1.022 Tracking Error 0.067 Treynor Ratio -0.008 Total Fees $26.25 |
namespace net.phobot.quant {
public class ExecutionData
{
public decimal Price { get; set; }
public decimal Quantity { get; set; }
}
public class MyExecutionModel : ExecutionModel
{
private static decimal LEG_PRICE_LOWER_LIMIT = 0.03M;
private readonly Dictionary<Symbol, Security> _symbolData;
private Dictionary<DateTime, IPortfolioTarget[]> _targetsCollection;
public MyExecutionModel()
{
_symbolData = new Dictionary<Symbol, Security>();
_targetsCollection = new Dictionary<DateTime, IPortfolioTarget[]>();
}
public override void Execute(QCAlgorithmFramework algorithm, IPortfolioTarget[] targets)
{
var prices_by_symbol = new Dictionary<Symbol,Dictionary<Symbol,ExecutionData>>();
foreach (var target in targets)
{
var existing = algorithm.Securities[target.Symbol].Holdings.Quantity
+ algorithm.Transactions.GetOpenOrders(target.Symbol).Sum(o => o.Quantity);
var quantity = target.Quantity - existing;
if (quantity != 0)
{
var limit_price = 0.0m;
if (target.Symbol.EndsWith("181019C00034000"))
{
limit_price = 2.35m;
}
if (target.Symbol.EndsWith("181019C00036000"))
{
limit_price = 1.20m;
}
if (target.Symbol.HasUnderlying)
{
var underlying = target.Symbol.Underlying;
if(! prices_by_symbol.ContainsKey(underlying))
{
prices_by_symbol[underlying] = new Dictionary<Symbol,ExecutionData>();
}
prices_by_symbol[underlying][target.Symbol] =
new ExecutionData{ Price = limit_price, Quantity = quantity};
}
else
{
if (Math.Abs(limit_price) < LEG_PRICE_LOWER_LIMIT)
{
algorithm.LimitOrder(target.Symbol, quantity, limit_price);
}
}
}
}
foreach (var underlying in prices_by_symbol.Keys)
{
var legs = prices_by_symbol[underlying];
if(legs.Values.Select(data => data.Price).Where(price => Math.Abs(price) < LEG_PRICE_LOWER_LIMIT).Any())
{
continue;
}
foreach (var leg_symbol in legs.Keys)
{
var execution_data = legs[leg_symbol];
algorithm.LimitOrder(leg_symbol, execution_data.Quantity, execution_data.Price);
var log_msg = $"Entered limit order for {execution_data.Quantity} contracts of {leg_symbol} at price {execution_data.Price}";
Security security;
if (_symbolData.TryGetValue(leg_symbol, out security))
{
log_msg += $" Bid: {security.BidPrice} Ask: {security.AskPrice}";
}
algorithm.Log(log_msg);
}
}
}
public override void OnSecuritiesChanged(QCAlgorithmFramework algorithm, SecurityChanges changes)
{
foreach (var added in changes.AddedSecurities)
{
if (!_symbolData.ContainsKey(added.Symbol))
{
_symbolData[added.Symbol] = added;
}
}
foreach (var removed in changes.RemovedSecurities)
{
if (_symbolData.ContainsKey(removed.Symbol) && IsSafeToRemove(algorithm, removed.Symbol))
{
_symbolData.Remove(removed.Symbol);
}
}
}
protected virtual bool IsSafeToRemove(QCAlgorithmFramework algorithm, Symbol symbol)
{
// confirm the security isn't currently a member of any universe
return !algorithm.UniverseManager.Any(kvp => kvp.Value.ContainsMember(symbol));
}
}
}namespace net.phobot.quant {
public class MyPortfolioConstructionModel : PortfolioConstructionModel
{
public override IEnumerable<IPortfolioTarget> CreateTargets(QCAlgorithmFramework algorithm, Insight[] insights)
{
var number_of_contracts = 21;
return insights.Select(insight => new PortfolioTarget(insight.Symbol, ((int) insight.Direction * number_of_contracts)));
}
}
}using QuantConnect.Securities.Option;
namespace net.phobot.quant {
public class MyAlphaModel : AlphaModel
{
List<Security> _securities = new List<Security>();
public override void OnSecuritiesChanged(QCAlgorithmFramework algorithm, SecurityChanges changes)
{
foreach(var added in changes.AddedSecurities)
{
_securities.Add(added);
}
foreach(var removed in changes.RemovedSecurities)
{
_securities.RemoveAll(item => item.Equals(removed));
}
}
public override IEnumerable<Insight> Update(QCAlgorithmFramework algorithm, Slice data)
{
var insights = new List<Insight>();
var date_str = algorithm.Time.ToString("yyyy-MM-dd HH:mm");
if (date_str.Equals("2018-09-18 09:31"))
{
foreach (var security in _securities)
{
if (security.Symbol.EndsWith("181019C00034000"))
{
insights.Add(new Insight(security.Symbol, new TimeSpan(6, 30, 0), InsightType.Price, InsightDirection.Down));
}
if (security.Symbol.EndsWith("181019C00036000"))
{
insights.Add(new Insight(security.Symbol, new TimeSpan(6, 30, 0), InsightType.Price, InsightDirection.Up));
}
}
}
// If we've been assigned or have exercised and are holding stock, liquidate the entire position:
foreach (var underlying_symbol in UnderlyingsOfHoldings(algorithm))
{
var equity_holdings = EquityHoldings(underlying_symbol, algorithm);
if (equity_holdings.Any())
{
var option_holdings = OptionHoldingsMatching(underlying_symbol, algorithm);
LiquidatePosition(option_holdings, algorithm);
LiquidateEquity(underlying_symbol, algorithm);
}
}
return insights;
}
private IEnumerable<Symbol> UnderlyingsOfHoldings(QCAlgorithmFramework algorithm)
{
var option_underlyings =
algorithm.Securities.Keys
.Where(symbol => symbol.HasUnderlying)
.Where(symbol => algorithm.Securities[symbol].Holdings.Quantity != 0)
.Select(symbol => symbol.Underlying);
var underlying_symbols = new HashSet<Symbol>(option_underlyings);
var equity_holdings =
algorithm.Securities.Keys
.Where(symbol => ! symbol.HasUnderlying)
.Where(symbol => algorithm.Securities[symbol].Holdings.Quantity != 0);
var equity_symbols = new HashSet<Symbol>(equity_holdings);
underlying_symbols.UnionWith(equity_symbols);
return underlying_symbols;
}
private IEnumerable<OptionHolding> OptionHoldingsMatching(Symbol equity_symbol, QCAlgorithmFramework algorithm)
{
var holdings =
algorithm.Securities.Keys
.Where(symbol => symbol.HasUnderlying)
.Where(symbol => symbol.Underlying.Value.Equals(equity_symbol.Value))
.Where(symbol => algorithm.Securities[symbol].Holdings.Quantity != 0)
.Select(symbol => (OptionHolding) algorithm.Securities[symbol].Holdings);
return holdings;
}
private IEnumerable<SecurityHolding> EquityHoldings(Symbol equity_symbol, QCAlgorithmFramework algorithm)
{
var holdings =
algorithm.Securities.Keys
.Where(symbol => ! symbol.HasUnderlying)
.Where(symbol => symbol.Value.Equals(equity_symbol.Value))
.Where(symbol => algorithm.Securities[symbol].Holdings.Quantity != 0)
.Select(symbol => algorithm.Securities[symbol].Holdings);
return holdings;
}
private void LiquidatePosition(IEnumerable<SecurityHolding> security_holdings, QCAlgorithmFramework algorithm)
{
foreach (var holding in security_holdings)
{
algorithm.RemoveSecurity(holding.Symbol);
}
}
private void LiquidateEquity(Symbol symbol, QCAlgorithmFramework algorithm)
{
// algorithm.RemoveSecurity(symbol);
algorithm.SetHoldings(symbol, 0);
}
}
}namespace net.phobot.quant {
static class ExtensionMethods
{
public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dict, K key)
{
return dict.GetValueOrDefault(key, default(V));
}
public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dict, K key, V defVal)
{
V value;
return dict.TryGetValue(key, out value) ? value : defVal;
}
public static V GetValueOrDefault<K, V>(this IDictionary<K, V> dict, K key, Func<V> defValSelector)
{
V value;
return dict.TryGetValue(key, out value) ? value : defValSelector();
}
}
}namespace net.phobot.quant
{
public class SchaefferCs : QCAlgorithm
{
public override void Initialize()
{
SetStartDate(2018, 9, 11);
SetEndDate(2018, 10, 18);
SetCash(100000);
UniverseSettings.Resolution = Resolution.Hour;
SetUniverseSelection(new MyOptionUniverseSelectionModel());
SetAlpha(new MyAlphaModel());
SetPortfolioConstruction(new MyPortfolioConstructionModel());
SetExecution(new MyExecutionModel());
SetRiskManagement(new NullRiskManagementModel());
}
}
}namespace net.phobot.quant {
public class MyOptionUniverseSelectionModel : FundamentalUniverseSelectionModel
{
public MyOptionUniverseSelectionModel(
Boolean filterFineData = true,
UniverseSettings universeSettings = null,
ISecurityInitializer securityInitializer = null): base(filterFineData, universeSettings, securityInitializer)
{
}
public override IEnumerable<Symbol> SelectCoarse(QCAlgorithmFramework algorithm, IEnumerable<CoarseFundamental> coarse)
{
var filtered = coarse
.Where(asset => asset.Symbol.Value.Equals("TRN"))
.Select(asset => asset.Symbol);
return filtered;
}
public override IEnumerable<Symbol> SelectFine(QCAlgorithmFramework algorithm, IEnumerable<FineFundamental> fine)
{
var current_date = NextTradingDay(algorithm);
var equity_symbols = fine.Select(asset => asset.Symbol);
var symbols_to_return = new List<Symbol>(equity_symbols);
foreach (Symbol equity_symbol in equity_symbols)
{
var symbol_string = equity_symbol.Value;
var equity = algorithm.AddEquity(symbol_string);
equity.SetDataNormalizationMode(DataNormalizationMode.Raw); // Remove warning messages
var contracts =
algorithm.OptionChainProvider
.GetOptionContractList(equity_symbol, current_date)
.Where(symbol => symbol.EndsWith("181019C00034000") || symbol.EndsWith("181019C00036000"))
.OrderBy(symbol => symbol.ID.StrikePrice)
.ToList();
if(contracts.Count() == 0)
{
continue;
}
var short_option = contracts[0];
algorithm.AddOptionContract(short_option);
symbols_to_return.Add(short_option);
var long_option = contracts[1];
algorithm.AddOptionContract(long_option);
symbols_to_return.Add(long_option);
}
return symbols_to_return;
}
private DateTime NextTradingDay(QCAlgorithmFramework algorithm)
{
var tomorrow = algorithm.Time.AddDays(1);
var five_days_after = tomorrow.AddDays(5);
var next_trading_days = algorithm.TradingCalendar.GetDaysByType(TradingDayType.BusinessDay, tomorrow, five_days_after);
return next_trading_days.First().Date;
}
}
}