Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02ee69826b | ||
|
|
2a5c939266 | ||
|
|
7220895900 | ||
|
|
f76de4b738 | ||
|
|
b659c7a2f1 | ||
|
|
cdeb2e1fcb | ||
|
|
442fbd3012 | ||
|
|
1ee6db8b60 |
158
Algorithm.CSharp/AlphaStreamsBasicTemplateAlgorithm.cs
Normal file
158
Algorithm.CSharp/AlphaStreamsBasicTemplateAlgorithm.cs
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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.Linq;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Data.Custom.AlphaStreams;
|
||||
using QuantConnect.Algorithm.Framework.Execution;
|
||||
using QuantConnect.Algorithm.Framework.Portfolio;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Example algorithm consuming an alpha streams portfolio state and trading based on it
|
||||
/// </summary>
|
||||
public class AlphaStreamsBasicTemplateAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Dictionary<Symbol, HashSet<Symbol>> _symbolsPerAlpha;
|
||||
|
||||
/// <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(2018, 04, 04);
|
||||
SetEndDate(2018, 04, 06);
|
||||
|
||||
SetExecution(new ImmediateExecutionModel());
|
||||
Settings.MinimumOrderMarginPortfolioPercentage = 0.01m;
|
||||
_symbolsPerAlpha = new Dictionary<Symbol, HashSet<Symbol>>();
|
||||
SetPortfolioConstruction(new EqualWeightingAlphaStreamsPortfolioConstructionModel());
|
||||
|
||||
foreach (var alphaId in new [] { "623b06b231eb1cc1aa3643a46", "9fc8ef73792331b11dbd5429a" })
|
||||
{
|
||||
var alpha = AddData<AlphaStreamsPortfolioState>(alphaId);
|
||||
_symbolsPerAlpha[alpha.Symbol] = new HashSet<Symbol>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
foreach (var portfolioState in data.Get<AlphaStreamsPortfolioState>().Values)
|
||||
{
|
||||
var alphaId = portfolioState.Symbol;
|
||||
var currentSymbols = _symbolsPerAlpha[alphaId];
|
||||
|
||||
var newSymbols = new HashSet<Symbol>(currentSymbols.Count);
|
||||
foreach (var symbol in portfolioState.PositionGroups?.SelectMany(positionGroup => positionGroup.Positions).Select(state => state.Symbol) ?? Enumerable.Empty<Symbol>())
|
||||
{
|
||||
// only add it if it's not used by any alpha (already added check)
|
||||
if (newSymbols.Add(symbol) && !UsedBySomeAlpha(symbol))
|
||||
{
|
||||
AddSecurity(symbol);
|
||||
}
|
||||
}
|
||||
_symbolsPerAlpha[alphaId] = newSymbols;
|
||||
|
||||
foreach (var symbol in currentSymbols.Where(symbol => !UsedBySomeAlpha(symbol)))
|
||||
{
|
||||
RemoveSecurity(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnOrderEvent(OrderEvent orderEvent)
|
||||
{
|
||||
Debug($"OnOrderEvent: {orderEvent}");
|
||||
}
|
||||
|
||||
public override void OnSecuritiesChanged(SecurityChanges changes)
|
||||
{
|
||||
Debug($"OnSecuritiesChanged: {changes}");
|
||||
}
|
||||
|
||||
private bool UsedBySomeAlpha(Symbol asset)
|
||||
{
|
||||
return _symbolsPerAlpha.Any(pair => pair.Value.Contains(asset));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
|
||||
/// </summary>
|
||||
public bool CanRunLocally { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public Language[] Languages { get; } = { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "2"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "-0.12%"},
|
||||
{"Compounding Annual Return", "-14.756%"},
|
||||
{"Drawdown", "0.200%"},
|
||||
{"Expectancy", "-1"},
|
||||
{"Net Profit", "-0.117%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "100%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "2.474"},
|
||||
{"Tracking Error", "0.339"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Estimated Strategy Capacity", "$83000.00"},
|
||||
{"Lowest Capacity Asset", "BTCUSD XJ"},
|
||||
{"Fitness Score", "0.017"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "-138.559"},
|
||||
{"Portfolio Turnover", "0.034"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "d3b6e0db0929e96d23c1cffd394858f1"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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.Linq;
|
||||
using System.Collections.Generic;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Data.Custom.AlphaStreams;
|
||||
using QuantConnect.Algorithm.Framework.Alphas;
|
||||
|
||||
namespace QuantConnect.Algorithm.Framework.Portfolio
|
||||
{
|
||||
/// <summary>
|
||||
/// Equal weighting alpha streams portfolio construction model that will generate aggregated security targets taking into account all the alphas positions
|
||||
/// and an equal weighting factor for each alpha, which is also factored by the relation of the alphas portfolio value and the current algorithms portfolio value,
|
||||
/// overriding <see cref="GetAlphaWeight"/> allows custom weighting implementations
|
||||
/// </summary>
|
||||
public class EqualWeightingAlphaStreamsPortfolioConstructionModel : IPortfolioConstructionModel
|
||||
{
|
||||
private bool _rebalance;
|
||||
private readonly Queue<Symbol> _removedSymbols = new Queue<Symbol>();
|
||||
private Dictionary<Symbol, decimal> _unitQuantity = new Dictionary<Symbol, decimal>();
|
||||
private Dictionary<Symbol, PortfolioTarget> _targetsPerSymbol = new Dictionary<Symbol, PortfolioTarget>();
|
||||
private Dictionary<Symbol, Dictionary<Symbol, PortfolioTarget>> _targetsPerSymbolPerAlpha = new Dictionary<Symbol, Dictionary<Symbol, PortfolioTarget>>();
|
||||
|
||||
/// <summary>
|
||||
/// Access the last portfolio state per alpha
|
||||
/// </summary>
|
||||
protected Dictionary<Symbol, AlphaStreamsPortfolioState> LastPortfolioPerAlpha = new Dictionary<Symbol, AlphaStreamsPortfolioState>();
|
||||
|
||||
/// <summary>
|
||||
/// Create portfolio targets from the specified insights
|
||||
/// </summary>
|
||||
/// <param name="algorithm">The algorithm instance</param>
|
||||
/// <param name="insights">The insights to create portfolio targets from</param>
|
||||
/// <returns>An enumerable of portfolio targets to be sent to the execution model</returns>
|
||||
public IEnumerable<IPortfolioTarget> CreateTargets(QCAlgorithm algorithm, Insight[] insights)
|
||||
{
|
||||
while (_removedSymbols.TryDequeue(out var removedSymbol))
|
||||
{
|
||||
yield return new PortfolioTarget(removedSymbol, 0);
|
||||
}
|
||||
|
||||
var updatedTargets = new Dictionary<Symbol, IPortfolioTarget>();
|
||||
foreach (var portfolioState in algorithm.CurrentSlice?.Get<AlphaStreamsPortfolioState>().Values ?? Enumerable.Empty<AlphaStreamsPortfolioState>())
|
||||
{
|
||||
if (!_rebalance)
|
||||
{
|
||||
foreach (var portfolioTarget in ProcessPortfolioState(portfolioState, algorithm))
|
||||
{
|
||||
updatedTargets[portfolioTarget.Symbol] = portfolioTarget;
|
||||
}
|
||||
}
|
||||
// keep the last state per alpha
|
||||
LastPortfolioPerAlpha[portfolioState.Symbol] = portfolioState;
|
||||
}
|
||||
|
||||
// if an alpha is removed or added we just rebalance all the targets because the weight changes of each alpha
|
||||
if (_rebalance)
|
||||
{
|
||||
foreach (var portfolioTarget in LastPortfolioPerAlpha.Values.SelectMany(portfolioState => ProcessPortfolioState(portfolioState, algorithm)))
|
||||
{
|
||||
updatedTargets[portfolioTarget.Symbol] = portfolioTarget;
|
||||
}
|
||||
_rebalance = false;
|
||||
}
|
||||
|
||||
foreach (var portfolioTarget in updatedTargets.Values)
|
||||
{
|
||||
yield return portfolioTarget;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event fired each time the we add/remove securities from the data feed
|
||||
/// </summary>
|
||||
/// <param name="algorithm">The algorithm instance that experienced the change in securities</param>
|
||||
/// <param name="changes">The security additions and removals from the algorithm</param>
|
||||
public void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes)
|
||||
{
|
||||
changes.FilterCustomSecurities = false;
|
||||
|
||||
foreach (var security in changes.RemovedSecurities)
|
||||
{
|
||||
if (security.Type != SecurityType.Base)
|
||||
{
|
||||
_removedSymbols.Enqueue(security.Symbol);
|
||||
}
|
||||
else if (IsAlphaStreamsPortfolioState(security.Symbol))
|
||||
{
|
||||
_rebalance = true;
|
||||
_targetsPerSymbolPerAlpha.Remove(security.Symbol);
|
||||
LastPortfolioPerAlpha.Remove(security.Symbol);
|
||||
}
|
||||
}
|
||||
foreach (var security in changes.AddedSecurities)
|
||||
{
|
||||
if (security.Type == SecurityType.Base && IsAlphaStreamsPortfolioState(security.Symbol))
|
||||
{
|
||||
_rebalance = true;
|
||||
_targetsPerSymbolPerAlpha[security.Symbol] = new Dictionary<Symbol, PortfolioTarget>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the portfolio weight to give a specific alpha. Default implementation just returns equal weighting
|
||||
/// </summary>
|
||||
protected virtual decimal GetAlphaWeight(AlphaStreamsPortfolioState portfolioState, QCAlgorithm algorithm)
|
||||
{
|
||||
if (portfolioState.TotalPortfolioValue == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
var equalWeightFactor = 1m / _targetsPerSymbolPerAlpha.Count;
|
||||
return (algorithm.Portfolio.TotalPortfolioValue * equalWeightFactor) / portfolioState.TotalPortfolioValue;
|
||||
}
|
||||
|
||||
private IEnumerable<IPortfolioTarget> ProcessPortfolioState(AlphaStreamsPortfolioState portfolioState, QCAlgorithm algorithm)
|
||||
{
|
||||
var alphaId = portfolioState.Symbol;
|
||||
|
||||
if(!_targetsPerSymbolPerAlpha.TryGetValue(alphaId, out var ourExistingTargets))
|
||||
{
|
||||
_targetsPerSymbolPerAlpha[alphaId] = ourExistingTargets = new Dictionary<Symbol, PortfolioTarget>();
|
||||
}
|
||||
|
||||
var alphaWeightFactor = GetAlphaWeight(portfolioState, algorithm);
|
||||
// first we create all the new aggregated targets for the provided portfolio state
|
||||
var newTargets = new Dictionary<Symbol, PortfolioTarget>();
|
||||
foreach (var positionGroup in portfolioState.PositionGroups ?? Enumerable.Empty<PositionGroupState>())
|
||||
{
|
||||
foreach (var position in positionGroup.Positions)
|
||||
{
|
||||
// let's keep the unit quantity so we can round by it
|
||||
_unitQuantity[position.Symbol] = position.UnitQuantity;
|
||||
|
||||
newTargets.TryGetValue(position.Symbol, out var existingAggregatedTarget);
|
||||
var quantity = position.Quantity * alphaWeightFactor + (existingAggregatedTarget?.Quantity ?? 0);
|
||||
newTargets[position.Symbol] = new PortfolioTarget(position.Symbol, quantity.DiscretelyRoundBy(position.UnitQuantity, MidpointRounding.ToZero));
|
||||
}
|
||||
}
|
||||
|
||||
// We adjust the new targets based on what we already have:
|
||||
// - We add any existing targets if any -> other alphas
|
||||
// - But we deduct our own existing target from it if any (previous state)
|
||||
foreach (var ourNewTarget in newTargets.Values)
|
||||
{
|
||||
var symbol = ourNewTarget.Symbol;
|
||||
var newAggregatedTarget = ourNewTarget;
|
||||
if (_targetsPerSymbol.TryGetValue(symbol, out var existingAggregatedTarget))
|
||||
{
|
||||
ourExistingTargets.TryGetValue(symbol, out var ourExistingTarget);
|
||||
|
||||
var quantity = existingAggregatedTarget.Quantity + ourNewTarget.Quantity - (ourExistingTarget?.Quantity ?? 0);
|
||||
newAggregatedTarget = new PortfolioTarget(symbol, quantity.DiscretelyRoundBy(_unitQuantity[symbol], MidpointRounding.ToZero));
|
||||
}
|
||||
|
||||
ourExistingTargets[symbol] = ourNewTarget;
|
||||
_targetsPerSymbol[symbol] = newAggregatedTarget;
|
||||
yield return newAggregatedTarget;
|
||||
}
|
||||
|
||||
// We adjust existing targets for symbols that got removed from this alpha
|
||||
foreach (var removedTarget in ourExistingTargets.Values.Where(target => !newTargets.ContainsKey(target.Symbol)))
|
||||
{
|
||||
var symbol = removedTarget.Symbol;
|
||||
var newAggregatedTarget = removedTarget;
|
||||
if (_targetsPerSymbol.TryGetValue(symbol, out var existingAggregatedTarget))
|
||||
{
|
||||
var quantity = existingAggregatedTarget.Quantity - removedTarget.Quantity;
|
||||
newAggregatedTarget = new PortfolioTarget(symbol, quantity.DiscretelyRoundBy(_unitQuantity[symbol], MidpointRounding.ToZero));
|
||||
}
|
||||
|
||||
ourExistingTargets.Remove(symbol);
|
||||
if (newAggregatedTarget.Quantity != 0)
|
||||
{
|
||||
_targetsPerSymbol[symbol] = newAggregatedTarget;
|
||||
}
|
||||
else
|
||||
{
|
||||
_targetsPerSymbol.Remove(symbol);
|
||||
}
|
||||
|
||||
yield return newAggregatedTarget;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsAlphaStreamsPortfolioState(Symbol symbol)
|
||||
{
|
||||
return symbol.ID.Symbol.TryGetCustomDataType(out var type) && type.Equals(nameof(AlphaStreamsPortfolioState), StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -692,7 +692,7 @@ namespace QuantConnect.Algorithm
|
||||
}
|
||||
else
|
||||
{
|
||||
var entry = MarketHoursDatabase.GetEntry(symbol.ID.Market, symbol, symbol.ID.SecurityType);
|
||||
var entry = MarketHoursDatabase.GetEntry(symbol, new []{ type });
|
||||
resolution = GetResolution(symbol, resolution);
|
||||
|
||||
return SubscriptionManager
|
||||
|
||||
@@ -80,6 +80,49 @@ namespace QuantConnect.Api
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "credit")]
|
||||
public Credit Credit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Alphas
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "alpha")]
|
||||
public Alpha Alpha { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Alphas
|
||||
/// </summary>
|
||||
public class Alpha
|
||||
{
|
||||
/// <summary>
|
||||
/// Current licensed alphas
|
||||
/// </summary>
|
||||
public List<AlphaLicense> AlphaLicenses { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An alpha license
|
||||
/// </summary>
|
||||
public class AlphaLicense
|
||||
{
|
||||
/// <summary>
|
||||
/// Allocated shares
|
||||
/// </summary>
|
||||
public decimal Shares { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alpha id
|
||||
/// </summary>
|
||||
public string AlphaId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The start time of this license
|
||||
/// </summary>
|
||||
public DateTime Start { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The end time of this license
|
||||
/// </summary>
|
||||
public DateTime End { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -20,6 +20,7 @@ using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using NodaTime;
|
||||
using ProtoBuf;
|
||||
using QuantConnect.Data.Custom.AlphaStreams;
|
||||
using QuantConnect.Data.Custom.Benzinga;
|
||||
using QuantConnect.Data.Custom.Estimize;
|
||||
using QuantConnect.Data.Custom.Tiingo;
|
||||
@@ -43,6 +44,7 @@ namespace QuantConnect.Data
|
||||
[ProtoInclude(700, typeof(EstimizeEstimate))]
|
||||
[ProtoInclude(800, typeof(EstimizeRelease))]
|
||||
[ProtoInclude(900, typeof(EstimizeConsensus))]
|
||||
[ProtoInclude(555, typeof(AlphaStreamsPortfolioState))]
|
||||
public abstract class BaseData : IBaseData
|
||||
{
|
||||
private decimal _value;
|
||||
|
||||
240
Common/Data/Custom/AlphaStreams/AlphaStreamsPortfolioState.cs
Normal file
240
Common/Data/Custom/AlphaStreams/AlphaStreamsPortfolioState.cs
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* 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 NodaTime;
|
||||
using ProtoBuf;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
using QuantConnect.Securities;
|
||||
using System.Collections.Generic;
|
||||
using QuantConnect.Securities.Positions;
|
||||
|
||||
namespace QuantConnect.Data.Custom.AlphaStreams
|
||||
{
|
||||
/// <summary>
|
||||
/// Snapshot of an algorithms portfolio state
|
||||
/// </summary>
|
||||
[ProtoContract(SkipConstructor = true)]
|
||||
public class AlphaStreamsPortfolioState : BaseData
|
||||
{
|
||||
/// <summary>
|
||||
/// The deployed alpha id. This is the id generated upon submission to the alpha marketplace
|
||||
/// </summary>
|
||||
[JsonProperty("alphaId", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
[ProtoMember(10)]
|
||||
public string AlphaId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The algorithm's unique deploy identifier
|
||||
/// </summary>
|
||||
[JsonProperty("algorithmId", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
[ProtoMember(11)]
|
||||
public string AlgorithmId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The source of this data point, 'live trading' or in sample
|
||||
/// </summary>
|
||||
[ProtoMember(12)]
|
||||
public string Source { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Portfolio state id
|
||||
/// </summary>
|
||||
[ProtoMember(13)]
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Algorithms account currency
|
||||
/// </summary>
|
||||
[ProtoMember(14)]
|
||||
public string AccountCurrency { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current total portfolio value
|
||||
/// </summary>
|
||||
[ProtoMember(15)]
|
||||
public decimal TotalPortfolioValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The margin used
|
||||
/// </summary>
|
||||
[ProtoMember(16)]
|
||||
public decimal TotalMarginUsed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The different positions groups
|
||||
/// </summary>
|
||||
[JsonProperty("positionGroups", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
[ProtoMember(17)]
|
||||
public List<PositionGroupState> PositionGroups { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cash book that keeps track of all currency holdings (only settled cash)
|
||||
/// </summary>
|
||||
[JsonProperty("cashBook", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
[ProtoMember(18)]
|
||||
public Dictionary<string, Cash> CashBook { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cash book that keeps track of all currency holdings (only unsettled cash)
|
||||
/// </summary>
|
||||
[JsonProperty("unsettledCashBook", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
[ProtoMember(19)]
|
||||
public Dictionary<string, Cash> UnsettledCashBook { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Return the Subscription Data Source
|
||||
/// </summary>
|
||||
/// <param name="config">Configuration object</param>
|
||||
/// <param name="date">Date of this source file</param>
|
||||
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
|
||||
/// <returns>Subscription Data Source.</returns>
|
||||
public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
|
||||
{
|
||||
var source = Path.Combine(
|
||||
Globals.DataFolder,
|
||||
"alternative",
|
||||
"alphastreams",
|
||||
"portfoliostate",
|
||||
config.Symbol.Value.ToLowerInvariant(),
|
||||
$"{date:yyyyMMdd}.json"
|
||||
);
|
||||
return new SubscriptionDataSource(source, SubscriptionTransportMedium.LocalFile, FileFormat.Csv);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reader converts each line of the data source into BaseData objects.
|
||||
/// </summary>
|
||||
/// <param name="config">Subscription data config setup object</param>
|
||||
/// <param name="line">Content of the source document</param>
|
||||
/// <param name="date">Date of the requested data</param>
|
||||
/// <param name="isLiveMode">true if we're in live mode, false for backtesting mode</param>
|
||||
/// <returns>New data point object</returns>
|
||||
public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
|
||||
{
|
||||
var dataPoint = JsonConvert.DeserializeObject<AlphaStreamsPortfolioState>(line);
|
||||
dataPoint.Symbol = config.Symbol;
|
||||
return dataPoint;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the data time zone for this data type
|
||||
/// </summary>
|
||||
/// <remarks>Will throw <see cref="InvalidOperationException"/> for security types
|
||||
/// other than <see cref="SecurityType.Base"/></remarks>
|
||||
/// <returns>The <see cref="DateTimeZone"/> of this data type</returns>
|
||||
public override DateTimeZone DataTimeZone()
|
||||
{
|
||||
return DateTimeZone.Utc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a new instance clone of this object, used in fill forward
|
||||
/// </summary>
|
||||
public override BaseData Clone()
|
||||
{
|
||||
return new AlphaStreamsPortfolioState
|
||||
{
|
||||
Id = Id,
|
||||
Time = Time,
|
||||
Source = Source,
|
||||
Symbol = Symbol,
|
||||
AlphaId = AlphaId,
|
||||
DataType = DataType,
|
||||
CashBook = CashBook,
|
||||
AlgorithmId = AlgorithmId,
|
||||
PositionGroups = PositionGroups,
|
||||
TotalMarginUsed = TotalMarginUsed,
|
||||
AccountCurrency = AccountCurrency,
|
||||
UnsettledCashBook = UnsettledCashBook,
|
||||
TotalPortfolioValue = TotalPortfolioValue,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the data set is expected to be sparse
|
||||
/// </summary>
|
||||
public override bool IsSparseData()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Snapshot of a position group state
|
||||
/// </summary>
|
||||
[ProtoContract(SkipConstructor = true)]
|
||||
public class PositionGroupState
|
||||
{
|
||||
/// <summary>
|
||||
/// Currently margin used
|
||||
/// </summary>
|
||||
[ProtoMember(1)]
|
||||
public decimal MarginUsed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The margin used by this position in relation to the total portfolio value
|
||||
/// </summary>
|
||||
[ProtoMember(2)]
|
||||
public decimal PortfolioValuePercentage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// THe positions which compose this group
|
||||
/// </summary>
|
||||
[ProtoMember(3)]
|
||||
public List<PositionState> Positions { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Snapshot of a position state
|
||||
/// </summary>
|
||||
[ProtoContract(SkipConstructor = true)]
|
||||
public class PositionState : IPosition
|
||||
{
|
||||
/// <summary>
|
||||
/// The symbol
|
||||
/// </summary>
|
||||
[ProtoMember(1)]
|
||||
public Symbol Symbol { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The quantity
|
||||
/// </summary>
|
||||
[ProtoMember(2)]
|
||||
public decimal Quantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unit quantity. The unit quantities of a group define the group. For example, a covered
|
||||
/// call has 100 units of stock and -1 units of call contracts.
|
||||
/// </summary>
|
||||
[ProtoMember(3)]
|
||||
public decimal UnitQuantity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance
|
||||
/// </summary>
|
||||
public static PositionState Create(IPosition position)
|
||||
{
|
||||
return new PositionState
|
||||
{
|
||||
Symbol = position.Symbol,
|
||||
Quantity = position.Quantity,
|
||||
UnitQuantity = position.UnitQuantity
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,6 +105,50 @@ namespace QuantConnect
|
||||
return !DateCheck.IsMatch(fileName) && DateTime.Now - TimeSpan.FromDays(DataUpdatePeriod) > File.GetLastWriteTime(filepath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to fetch the custom data type associated with a symbol
|
||||
/// </summary>
|
||||
/// <remarks>Custom data type <see cref="SecurityIdentifier"/> symbol value holds their data type</remarks>
|
||||
public static bool TryGetCustomDataType(this string symbol, out string type)
|
||||
{
|
||||
type = null;
|
||||
if (symbol != null)
|
||||
{
|
||||
var index = symbol.LastIndexOf('.');
|
||||
if (index != -1 && symbol.Length > index + 1)
|
||||
{
|
||||
type = symbol.Substring(index + 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to get a market hours entry
|
||||
/// </summary>
|
||||
/// <param name="marketHoursDatabase">The market hours data base instance</param>
|
||||
/// <param name="symbol">The symbol to get the entry for</param>
|
||||
/// <param name="dataTypes">For custom data types can optionally provide data type so that a new entry is added</param>
|
||||
public static MarketHoursDatabase.Entry GetEntry(this MarketHoursDatabase marketHoursDatabase, Symbol symbol, IEnumerable<Type> dataTypes)
|
||||
{
|
||||
if (symbol.SecurityType == SecurityType.Base)
|
||||
{
|
||||
if (!marketHoursDatabase.TryGetEntry(symbol.ID.Market, symbol, symbol.ID.SecurityType, out var entry))
|
||||
{
|
||||
var type = dataTypes.Single();
|
||||
var baseInstance = type.GetBaseDataInstance();
|
||||
baseInstance.Symbol = symbol;
|
||||
symbol.ID.Symbol.TryGetCustomDataType(out var customType);
|
||||
// for custom types we will add an entry for that type
|
||||
entry = marketHoursDatabase.SetEntryAlwaysOpen(symbol.ID.Market, customType != null ? $"TYPE.{customType}" : null, SecurityType.Base, baseInstance.DataTimeZone());
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
return marketHoursDatabase.GetEntry(symbol.ID.Market, symbol, symbol.ID.SecurityType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to download a provided url as a string
|
||||
/// </summary>
|
||||
@@ -342,6 +386,12 @@ namespace QuantConnect
|
||||
resultPacket.Orders = resultPacket.Orders.GroupBy(order => order.Id)
|
||||
.Select(ordersGroup => ordersGroup.Last()).ToList();
|
||||
}
|
||||
|
||||
if (newerPacket.Portfolio != null)
|
||||
{
|
||||
// we just keep the newest state if not null
|
||||
resultPacket.Portfolio = newerPacket.Portfolio;
|
||||
}
|
||||
}
|
||||
}
|
||||
return resultPacket;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
@@ -14,15 +14,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.2 Copyright 2015 QuantConnect Corporation.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using QuantConnect.Algorithm.Framework.Alphas;
|
||||
using QuantConnect.Orders;
|
||||
using System.Collections.Generic;
|
||||
using QuantConnect.Data.Custom.AlphaStreams;
|
||||
using QuantConnect.Algorithm.Framework.Alphas;
|
||||
|
||||
namespace QuantConnect.Packets
|
||||
{
|
||||
@@ -68,6 +64,12 @@ namespace QuantConnect.Packets
|
||||
[JsonProperty("orders", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
public List<Order> Orders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The algorithms current portfolio state
|
||||
/// </summary>
|
||||
[JsonProperty("portfolio", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
public AlphaStreamsPortfolioState Portfolio { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AlphaResultPacket"/> class
|
||||
/// </summary>
|
||||
@@ -84,7 +86,8 @@ namespace QuantConnect.Packets
|
||||
/// <param name="insights">Alphas generated by the algorithm</param>
|
||||
/// <param name="orderEvents">OrderEvents generated by the algorithm</param>
|
||||
/// <param name="orders">Orders generated or updated by the algorithm</param>
|
||||
public AlphaResultPacket(string algorithmId, int userId, List<Insight> insights = null, List<OrderEvent> orderEvents = null, List<Order> orders = null)
|
||||
/// <param name="portfolio">The algorithms current portfolio state</param>
|
||||
public AlphaResultPacket(string algorithmId, int userId, List<Insight> insights = null, List<OrderEvent> orderEvents = null, List<Order> orders = null, AlphaStreamsPortfolioState portfolio = null)
|
||||
: base(PacketType.AlphaResult)
|
||||
{
|
||||
UserId = userId;
|
||||
@@ -92,6 +95,7 @@ namespace QuantConnect.Packets
|
||||
Insights = insights;
|
||||
OrderEvents = orderEvents;
|
||||
Orders = orders;
|
||||
Portfolio = portfolio;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using ProtoBuf;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Interfaces;
|
||||
@@ -29,6 +30,7 @@ namespace QuantConnect.Securities
|
||||
/// <summary>
|
||||
/// Represents a holding of a currency in cash.
|
||||
/// </summary>
|
||||
[ProtoContract(SkipConstructor = true)]
|
||||
public class Cash
|
||||
{
|
||||
private decimal _conversionRate;
|
||||
@@ -59,16 +61,19 @@ namespace QuantConnect.Securities
|
||||
/// <summary>
|
||||
/// Gets the symbol used to represent this cash
|
||||
/// </summary>
|
||||
[ProtoMember(1)]
|
||||
public string Symbol { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the amount of cash held
|
||||
/// </summary>
|
||||
[ProtoMember(2)]
|
||||
public decimal Amount { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the conversion rate into account currency
|
||||
/// </summary>
|
||||
[ProtoMember(3)]
|
||||
public decimal ConversionRate
|
||||
{
|
||||
get
|
||||
@@ -96,6 +101,7 @@ namespace QuantConnect.Securities
|
||||
/// <summary>
|
||||
/// The symbol of the currency, such as $
|
||||
/// </summary>
|
||||
[ProtoMember(4)]
|
||||
public string CurrencySymbol { get; }
|
||||
|
||||
/// <summary>
|
||||
@@ -153,7 +159,8 @@ namespace QuantConnect.Securities
|
||||
/// <param name="amount">The amount to set the quantity to</param>
|
||||
public void SetAmount(decimal amount)
|
||||
{
|
||||
lock (_locker)
|
||||
// lock can be null when proto deserializing this instance
|
||||
lock (_locker ?? new object())
|
||||
{
|
||||
Amount = amount;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
@@ -206,8 +206,7 @@ namespace QuantConnect.Securities
|
||||
// Some FOPs have the same symbol properties as their futures counterparts.
|
||||
// So, to save ourselves some space, we can fall back on the existing entries
|
||||
// so that we don't duplicate the information.
|
||||
if (!TryGetEntry(market, symbol, securityType, out entry) &&
|
||||
!(securityType == SecurityType.FutureOption && TryGetEntry(market, FuturesOptionsSymbolMappings.MapFromOption(symbol), SecurityType.Future, out entry)))
|
||||
if (!TryGetEntry(market, symbol, securityType, out entry))
|
||||
{
|
||||
var key = new SecurityDatabaseKey(market, symbol, securityType);
|
||||
var keys = string.Join(", ", _entries.Keys);
|
||||
@@ -257,8 +256,14 @@ namespace QuantConnect.Securities
|
||||
public bool TryGetEntry(string market, string symbol, SecurityType securityType, out Entry entry)
|
||||
{
|
||||
return _entries.TryGetValue(new SecurityDatabaseKey(market, symbol, securityType), out entry)
|
||||
// now check with null symbol key
|
||||
|| _entries.TryGetValue(new SecurityDatabaseKey(market, null, securityType), out entry);
|
||||
// now check with null symbol key
|
||||
|| _entries.TryGetValue(new SecurityDatabaseKey(market, null, securityType), out entry)
|
||||
// if FOP check for future
|
||||
|| securityType == SecurityType.FutureOption && TryGetEntry(market,
|
||||
FuturesOptionsSymbolMappings.MapFromOption(symbol), SecurityType.Future, out entry)
|
||||
// if custom data type check for type specific entry
|
||||
|| (securityType == SecurityType.Base && symbol.TryGetCustomDataType(out var customType)
|
||||
&& _entries.TryGetValue(new SecurityDatabaseKey(market, $"TYPE.{customType}", securityType), out entry));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -79,14 +79,6 @@ namespace QuantConnect.Securities.Positions
|
||||
return consolidated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of lots contained within the specified <paramref name="position"/>
|
||||
/// </summary>
|
||||
public static decimal GetNumberOfLots(this IPosition position)
|
||||
{
|
||||
return position.Quantity / position.UnitQuantity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="IPosition"/> with quantity equal to <paramref name="numberOfLots"/> times its unit quantity
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
{"AlphaId":"623b06b231eb1cc1aa3643a46","AlgorithmId":"37b0922b-54d0-44bc-8dfb-b90ee4554884","Source":"live trading","AccountCurrency":"USD","TotalPortfolioValue":100000.0,"TotalMarginUsed":1.0,"Time":"2018-04-04T08:03:58.3653852Z","CashBook":{"USD":{"SecuritySymbols":[],"Symbol":"USD","Amount":10.0,"ConversionRate":1.0,"CurrencySymbol":"$","ValueInAccountCurrency":10.0},"EUR":{"SecuritySymbols":[],"Symbol":"EUR","Amount":1.0,"ConversionRate":1.2,"CurrencySymbol":"€","ValueInAccountCurrency":1.2}},"UnsettledCashBook":{"USD":{"SecuritySymbols":[],"Symbol":"USD","Amount":1.0,"ConversionRate":1.0,"CurrencySymbol":"$","ValueInAccountCurrency":1.0}},"PositionGroups":[{"MarginUsed":11.0,"PortfolioValuePercentage":0.1,"Positions":[{"Symbol":{"Value":"BTCUSD","ID":"BTCUSD XJ","Permtick":"BTCUSD"},"Quantity":0.999,"UnitQuantity":0.00000001}]}]}
|
||||
{"AlphaId":"623b06b231eb1cc1aa3643a46","AlgorithmId":"ba29373c-a1b4-4e45-a587-e31fb02a3557","Source":"live trading","AccountCurrency":"USD","TotalPortfolioValue":100000.0,"TotalMarginUsed":0.0,"Time":"2018-04-04T21:03:58.3782404Z","CashBook":{"USD":{"SecuritySymbols":[],"Symbol":"USD","Amount":10.0,"ConversionRate":1.0,"CurrencySymbol":"$","ValueInAccountCurrency":10.0},"EUR":{"SecuritySymbols":[],"Symbol":"EUR","Amount":1.0,"ConversionRate":1.2,"CurrencySymbol":"€","ValueInAccountCurrency":1.2}},"UnsettledCashBook":{},"PositionGroups":[]}
|
||||
@@ -0,0 +1,4 @@
|
||||
{"AlphaId":"9fc8ef73792331b11dbd5429a","AlgorithmId":"ba29373c-a1b4-4e45-429a-e31fb02a3557","Source":"live trading","Id":1,"AccountCurrency":"USD","TotalPortfolioValue":100000,"TotalMarginUsed":112281.0000000,"Time":"2018-04-04T15:03:58.3653852Z","positionGroups":[{"MarginUsed":93567.5000000,"PortfolioValuePercentage":0.4703,"Positions":[{"Symbol":{"Value":"GOOG 160617C00750000","ID":"GOOCV WBGM92QHIYO6|GOOCV VP83T1ZUHROL","Permtick":"GOOG 160617C00750000","Underlying":{"Value":"GOOG","ID":"GOOCV VP83T1ZUHROL","Permtick":"GOOG"}},"Quantity":-5.0,"UnitQuantity":1.0},{"Symbol":{"Value":"GOOG","ID":"GOOCV VP83T1ZUHROL","Permtick":"GOOG"},"Quantity":500.0,"UnitQuantity":1.0}]},{"MarginUsed":18713.5000000,"PortfolioValuePercentage":0.09407,"Positions":[{"Symbol":{"Value":"GOOG","ID":"GOOCV VP83T1ZUHROL","Permtick":"GOOG"},"Quantity":100.0,"UnitQuantity":1.0}]}],"cashBook":{"USD":{"SecuritySymbols":[],"Symbol":"USD","Amount":-222812.25,"ConversionRate":1.0,"CurrencySymbol":"$","ValueInAccountCurrency":-222812.250}}}
|
||||
{"AlphaId":"9fc8ef73792331b11dbd5429a","AlgorithmId":"ba29373c-a1b4-4e45-429a-e31fb02a3557","Source":"live trading","Id":2,"AccountCurrency":"USD","TotalPortfolioValue":100000,"TotalMarginUsed":18713.5000000,"Time":"2018-04-04T16:03:58.3653852Z","positionGroups":[{"MarginUsed":18713.5000000,"PortfolioValuePercentage":0.09407,"Positions":[{"Symbol":{"Value":"GOOG","ID":"GOOCV VP83T1ZUHROL","Permtick":"GOOG"},"Quantity":100.0,"UnitQuantity":1.0}]}],"cashBook":{"USD":{"SecuritySymbols":[],"Symbol":"USD","Amount":-222812.25,"ConversionRate":1.0,"CurrencySymbol":"$","ValueInAccountCurrency":-222812.250}}}
|
||||
{"AlphaId":"9fc8ef73792331b11dbd5429a","AlgorithmId":"ba29373c-a1b4-4e45-429a-e31fb02a3557","Source":"live trading","Id":3,"AccountCurrency":"USD","TotalPortfolioValue":100000,"TotalMarginUsed":0,"Time":"2018-04-04T16:33:58.3653852Z","cashBook":{"USD":{"SecuritySymbols":[],"Symbol":"USD","Amount":222812.25,"ConversionRate":1.0,"CurrencySymbol":"$","ValueInAccountCurrency":222812.250}}}
|
||||
{"AlphaId":"9fc8ef73792331b11dbd5429a","AlgorithmId":"ba29373c-a1b4-4e45-429a-e31fb02a3557","Source":"live trading","Id":4,"AccountCurrency":"USD","TotalPortfolioValue":100000,"TotalMarginUsed":3886.0,"Time":"2018-04-04T16:50:58.3653852Z","positionGroups":[{"MarginUsed":3886.0,"PortfolioValuePercentage":0.01953,"Positions":[{"Symbol":{"Value":"ES19M20 200619C03200000","ID":"ES XFH59UPBIJ7O|ES XFH59UK0MYO1","Permtick":"ES19M20 200619C03200000","Underlying":{"Value":"ES19M20","ID":"ES XFH59UK0MYO1","Permtick":"ES19M20"}},"Quantity":1.0,"UnitQuantity":1.0}]}],"cashBook":{"USD":{"SecuritySymbols":[],"Symbol":"USD","Amount":92585.650,"ConversionRate":1.0,"CurrencySymbol":"$","ValueInAccountCurrency":92585.6500}}}
|
||||
@@ -458,19 +458,7 @@ namespace QuantConnect.Lean.Engine.DataFeeds
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MarketHoursDatabase.Entry marketHoursDbEntry;
|
||||
if (!_marketHoursDatabase.TryGetEntry(symbol.ID.Market, symbol, symbol.ID.SecurityType, out marketHoursDbEntry))
|
||||
{
|
||||
if (symbol.SecurityType == SecurityType.Base)
|
||||
{
|
||||
var baseInstance = dataTypes.Single().Item1.GetBaseDataInstance();
|
||||
baseInstance.Symbol = symbol;
|
||||
_marketHoursDatabase.SetEntryAlwaysOpen(symbol.ID.Market, null, SecurityType.Base, baseInstance.DataTimeZone());
|
||||
}
|
||||
|
||||
marketHoursDbEntry = _marketHoursDatabase.GetEntry(symbol.ID.Market, symbol, symbol.ID.SecurityType);
|
||||
}
|
||||
var marketHoursDbEntry = _marketHoursDatabase.GetEntry(symbol, dataTypes.Select(tuple => tuple.Item1));
|
||||
|
||||
var exchangeHours = marketHoursDbEntry.ExchangeHours;
|
||||
if (symbol.ID.SecurityType.IsOption() ||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
@@ -14,6 +14,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Algorithm;
|
||||
using QuantConnect.Securities.Cfd;
|
||||
@@ -23,7 +24,7 @@ using QuantConnect.Securities.Forex;
|
||||
using QuantConnect.Securities.Future;
|
||||
using QuantConnect.Securities.Option;
|
||||
using QuantConnect.Tests.Engine.DataFeeds;
|
||||
using System;
|
||||
using QuantConnect.Data.Custom.AlphaStreams;
|
||||
using Index = QuantConnect.Securities.Index.Index;
|
||||
|
||||
namespace QuantConnect.Tests.Algorithm
|
||||
@@ -108,10 +109,11 @@ namespace QuantConnect.Tests.Algorithm
|
||||
new TestCaseData(Symbols.SPY_Option_Chain),
|
||||
new TestCaseData(Symbols.SPY_C_192_Feb19_2016),
|
||||
new TestCaseData(Symbols.SPY_P_192_Feb19_2016),
|
||||
new TestCaseData(Symbol.CreateBase(typeof(AlphaStreamsPortfolioState), Symbols.SPY, Market.USA)),
|
||||
new TestCaseData(Symbol.Create("CustomData", SecurityType.Base, Market.Binance)),
|
||||
new TestCaseData(Symbol.Create("CustomData2", SecurityType.Base, Market.COMEX))
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* 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.Linq;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Util;
|
||||
using QuantConnect.Algorithm;
|
||||
using System.Collections.Generic;
|
||||
using QuantConnect.Data.Auxiliary;
|
||||
using QuantConnect.Lean.Engine.DataFeeds;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Tests.Engine.DataFeeds;
|
||||
using QuantConnect.Data.Custom.AlphaStreams;
|
||||
using QuantConnect.Lean.Engine.HistoricalData;
|
||||
using QuantConnect.Algorithm.Framework.Alphas;
|
||||
using QuantConnect.Algorithm.Framework.Portfolio;
|
||||
|
||||
namespace QuantConnect.Tests.Algorithm.Framework.Portfolio
|
||||
{
|
||||
[TestFixture]
|
||||
public class EqualWeightingAlphaStreamsPortfolioConstructionModelTests
|
||||
{
|
||||
private ZipDataCacheProvider _cacheProvider;
|
||||
private DefaultDataProvider _dataProvider;
|
||||
private QCAlgorithm _algorithm;
|
||||
|
||||
[SetUp]
|
||||
public virtual void SetUp()
|
||||
{
|
||||
_algorithm = new QCAlgorithm();
|
||||
_dataProvider = new DefaultDataProvider();
|
||||
var mapFileProvider = new LocalDiskMapFileProvider();
|
||||
mapFileProvider.Initialize(_dataProvider);
|
||||
var factorFileProvider = new LocalZipFactorFileProvider();
|
||||
factorFileProvider.Initialize(mapFileProvider, _dataProvider);
|
||||
var historyProvider = new SubscriptionDataReaderHistoryProvider();
|
||||
_cacheProvider = new ZipDataCacheProvider(_dataProvider);
|
||||
historyProvider.Initialize(new HistoryProviderInitializeParameters(null, null,
|
||||
_dataProvider, _cacheProvider, mapFileProvider, factorFileProvider,
|
||||
null, true, new DataPermissionManager()));
|
||||
_algorithm.SetHistoryProvider(historyProvider);
|
||||
_algorithm.SubscriptionManager.SetDataManager(new DataManagerStub(_algorithm));
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public virtual void TearDown()
|
||||
{
|
||||
_cacheProvider.DisposeSafely();
|
||||
_dataProvider.DisposeSafely();
|
||||
}
|
||||
|
||||
[TestCase(Language.CSharp)]
|
||||
public void NoTargets(Language language)
|
||||
{
|
||||
SetPortfolioConstruction(language);
|
||||
var targets = _algorithm.PortfolioConstruction.CreateTargets(_algorithm, Array.Empty<Insight>()).ToList();
|
||||
Assert.AreEqual(0, targets.Count);
|
||||
}
|
||||
|
||||
[TestCase(Language.CSharp)]
|
||||
public void IgnoresInsights(Language language)
|
||||
{
|
||||
SetPortfolioConstruction(language);
|
||||
var insight = Insight.Price(Symbols.AAPL, Resolution.Minute, 1, InsightDirection.Down, 1, 1, "AlphaId", 0.5);
|
||||
insight.GeneratedTimeUtc = _algorithm.UtcTime;
|
||||
insight.CloseTimeUtc = _algorithm.UtcTime.Add(insight.Period);
|
||||
var targets = _algorithm.PortfolioConstruction.CreateTargets(_algorithm, new[] { insight }).ToList();
|
||||
Assert.AreEqual(0, targets.Count);
|
||||
}
|
||||
|
||||
[TestCase(Language.CSharp)]
|
||||
public void SingleAlphaSinglePosition(Language language)
|
||||
{
|
||||
SetPortfolioConstruction(language);
|
||||
|
||||
var alpha = _algorithm.AddData<AlphaStreamsPortfolioState>("9fc8ef73792331b11dbd5429a").Symbol;
|
||||
var data = _algorithm.History<AlphaStreamsPortfolioState>(alpha, TimeSpan.FromDays(2)).Last();
|
||||
_algorithm.SetCurrentSlice(new Slice(_algorithm.UtcTime, new List<BaseData> { data }));
|
||||
|
||||
var targets = _algorithm.PortfolioConstruction.CreateTargets(_algorithm, Array.Empty<Insight>()).ToList();
|
||||
Assert.AreEqual(1, targets.Count);
|
||||
var position = data.PositionGroups.Single().Positions.Single();
|
||||
Assert.AreEqual(position.Symbol, targets.Single().Symbol);
|
||||
Assert.AreEqual(position.Quantity, targets.Single().Quantity);
|
||||
}
|
||||
|
||||
[TestCase(Language.CSharp)]
|
||||
public void SingleAlphaMultiplePositions(Language language)
|
||||
{
|
||||
SetPortfolioConstruction(language);
|
||||
|
||||
var alpha = _algorithm.AddData<AlphaStreamsPortfolioState>("9fc8ef73792331b11dbd5429a").Symbol;
|
||||
var data = _algorithm.History<AlphaStreamsPortfolioState>(alpha, TimeSpan.FromDays(2)).ToList()[0];
|
||||
_algorithm.SetCurrentSlice(new Slice(_algorithm.UtcTime, new List<BaseData> { data }));
|
||||
|
||||
var targets = _algorithm.PortfolioConstruction.CreateTargets(_algorithm, Array.Empty<Insight>()).ToList();
|
||||
|
||||
Assert.AreEqual(2, targets.Count);
|
||||
Assert.AreEqual(600, targets.Single(target => target.Symbol == Symbols.GOOG).Quantity);
|
||||
|
||||
var option = Symbol.CreateOption(Symbols.GOOG, Market.USA, OptionStyle.American, OptionRight.Call, 750, new DateTime(2016, 6, 17));
|
||||
Assert.AreEqual(-5, targets.Single(target => target.Symbol == option).Quantity);
|
||||
}
|
||||
|
||||
[TestCase(Language.CSharp)]
|
||||
public void SingleAlphaPositionRemoval(Language language)
|
||||
{
|
||||
SetPortfolioConstruction(language);
|
||||
|
||||
var alpha = _algorithm.AddData<AlphaStreamsPortfolioState>("9fc8ef73792331b11dbd5429a").Symbol;
|
||||
var data = _algorithm.History<AlphaStreamsPortfolioState>(alpha, TimeSpan.FromDays(2)).Last();
|
||||
var position = data.PositionGroups.Single().Positions.Single();
|
||||
_algorithm.SetCurrentSlice(new Slice(_algorithm.UtcTime, new List<BaseData> { data }));
|
||||
|
||||
var targets = _algorithm.PortfolioConstruction.CreateTargets(_algorithm, Array.Empty<Insight>()).ToList();
|
||||
Assert.AreEqual(1, targets.Count);
|
||||
Assert.AreEqual(position.Symbol, targets.Single().Symbol);
|
||||
Assert.AreEqual(position.Quantity, targets.Single().Quantity);
|
||||
|
||||
|
||||
_algorithm.SetCurrentSlice(new Slice(_algorithm.UtcTime, new List<BaseData> { new AlphaStreamsPortfolioState { Symbol = alpha } }));
|
||||
targets = _algorithm.PortfolioConstruction.CreateTargets(_algorithm, Array.Empty<Insight>()).ToList();
|
||||
Assert.AreEqual(1, targets.Count);
|
||||
Assert.AreEqual(position.Symbol, targets.Single().Symbol);
|
||||
Assert.AreEqual(0, targets.Single().Quantity);
|
||||
|
||||
// no new targets
|
||||
targets = _algorithm.PortfolioConstruction.CreateTargets(_algorithm, Array.Empty<Insight>()).ToList();
|
||||
Assert.AreEqual(0, targets.Count);
|
||||
}
|
||||
|
||||
[TestCase(Language.CSharp, 1000000, 10000)]
|
||||
[TestCase(Language.CSharp, 10000, 1000000)]
|
||||
[TestCase(Language.CSharp, 10000, 10000)]
|
||||
[TestCase(Language.CSharp, 100000, 100000)]
|
||||
[TestCase(Language.CSharp, 1000000, 1000000)]
|
||||
public void MultipleAlphaPositionAggregation(Language language, decimal totalPortfolioValueAlpha1, decimal totalPortfolioValueAlpha2)
|
||||
{
|
||||
SetPortfolioConstruction(language);
|
||||
|
||||
var alpha1 = _algorithm.AddData<AlphaStreamsPortfolioState>("9fc8ef73792331b11dbd5429a");
|
||||
var alpha2 = _algorithm.AddData<AlphaStreamsPortfolioState>("623b06b231eb1cc1aa3643a46");
|
||||
_algorithm.OnFrameworkSecuritiesChanged(SecurityChanges.Added(alpha1, alpha2));
|
||||
var symbol = alpha1.Symbol;
|
||||
var symbol2 = alpha2.Symbol;
|
||||
var data = _algorithm.History<AlphaStreamsPortfolioState>(symbol, TimeSpan.FromDays(1)).Last();
|
||||
data.TotalPortfolioValue = totalPortfolioValueAlpha1;
|
||||
var position = data.PositionGroups.Single().Positions.Single();
|
||||
|
||||
var data2 = (AlphaStreamsPortfolioState)data.Clone();
|
||||
data2.Symbol = symbol2;
|
||||
data2.TotalPortfolioValue = totalPortfolioValueAlpha2;
|
||||
data2.PositionGroups =
|
||||
new List<PositionGroupState>
|
||||
{
|
||||
new PositionGroupState { Positions =
|
||||
new List<PositionState>
|
||||
{
|
||||
new PositionState
|
||||
{
|
||||
Quantity = position.Quantity * -10,
|
||||
Symbol = position.Symbol,
|
||||
UnitQuantity = 1
|
||||
}
|
||||
}}
|
||||
};
|
||||
|
||||
_algorithm.SetCurrentSlice(new Slice(_algorithm.UtcTime, new List<BaseData> { data, data2 }));
|
||||
|
||||
var targets = _algorithm.PortfolioConstruction.CreateTargets(_algorithm, Array.Empty<Insight>()).ToList();
|
||||
Assert.AreEqual(1, targets.Count);
|
||||
Assert.AreEqual(position.Symbol, targets.Single().Symbol);
|
||||
|
||||
var tvpPerAlpha = _algorithm.Portfolio.TotalPortfolioValue * 0.5m;
|
||||
var alpha1Weight = tvpPerAlpha / data.TotalPortfolioValue;
|
||||
var alpha2Weight = tvpPerAlpha / data2.TotalPortfolioValue;
|
||||
|
||||
Assert.AreEqual((position.Quantity * alpha1Weight).DiscretelyRoundBy(1, MidpointRounding.ToZero)
|
||||
+ (position.Quantity * -10m * alpha2Weight).DiscretelyRoundBy(1, MidpointRounding.ToZero),
|
||||
targets.Single().Quantity);
|
||||
}
|
||||
|
||||
private void SetUtcTime(DateTime dateTime) => _algorithm.SetDateTime(dateTime.ConvertToUtc(_algorithm.TimeZone));
|
||||
|
||||
private void SetPortfolioConstruction(Language language)
|
||||
{
|
||||
_algorithm.SetCurrentSlice(null);
|
||||
IPortfolioConstructionModel model;
|
||||
if (language == Language.CSharp)
|
||||
{
|
||||
model = new EqualWeightingAlphaStreamsPortfolioConstructionModel();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException($"{language} not implemented");
|
||||
}
|
||||
_algorithm.SetPortfolioConstruction(model);
|
||||
|
||||
foreach (var kvp in _algorithm.Portfolio)
|
||||
{
|
||||
kvp.Value.SetHoldings(kvp.Value.Price, 0);
|
||||
}
|
||||
_algorithm.Portfolio.SetCash(100000);
|
||||
SetUtcTime(new DateTime(2018, 4, 5));
|
||||
|
||||
var changes = SecurityChanges.Added(_algorithm.Securities.Values.ToArray());
|
||||
_algorithm.PortfolioConstruction.OnSecuritiesChanged(_algorithm, changes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
@@ -22,6 +23,7 @@ using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using ProtoBuf;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Custom.AlphaStreams;
|
||||
using QuantConnect.Data.Custom.Benzinga;
|
||||
using QuantConnect.Data.Custom.Estimize;
|
||||
using QuantConnect.Data.Custom.Tiingo;
|
||||
@@ -626,6 +628,60 @@ namespace QuantConnect.Tests.Common
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AlphaStreamsPortfolioStateRoundTrip()
|
||||
{
|
||||
var symbol = Symbol.CreateBase(typeof(AlphaStreamsPortfolioState),
|
||||
Symbol.Create("9fc8ef73792331b11dbd5429a", SecurityType.Base, Market.USA),
|
||||
Market.USA);
|
||||
|
||||
var state = new AlphaStreamsPortfolioState
|
||||
{
|
||||
Id = 1000,
|
||||
Time = DateTime.UtcNow,
|
||||
Symbol = symbol,
|
||||
Source = "Live trading",
|
||||
AccountCurrency = Currencies.EUR,
|
||||
AlgorithmId = "BasicTemplateAlgorithm",
|
||||
AlphaId = "9fc8ef73792331b11dbd5429a",
|
||||
CashBook = new Dictionary<string, Cash>
|
||||
{
|
||||
{ Currencies.EUR, new Cash(Currencies.EUR, 1, 1)}
|
||||
},
|
||||
UnsettledCashBook = new Dictionary<string, Cash>
|
||||
{
|
||||
{ Currencies.USD, new Cash(Currencies.USD, 1, 1.2m)}
|
||||
},
|
||||
PositionGroups = new List<PositionGroupState>
|
||||
{
|
||||
new PositionGroupState
|
||||
{
|
||||
MarginUsed = 10,
|
||||
PortfolioValuePercentage = 0.001m,
|
||||
Positions = new List<PositionState>
|
||||
{
|
||||
new PositionState
|
||||
{
|
||||
Quantity = 1,
|
||||
UnitQuantity = 1,
|
||||
Symbol = Symbols.SPY
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
TotalMarginUsed = 1000,
|
||||
TotalPortfolioValue = 100000,
|
||||
};
|
||||
|
||||
var serializedState = state.ProtobufSerialize();
|
||||
using (var stream = new MemoryStream(serializedState))
|
||||
{
|
||||
var result = (AlphaStreamsPortfolioState)Serializer.Deserialize<IEnumerable<BaseData>>(stream).First();
|
||||
|
||||
AssertAreEqual(state, result);
|
||||
}
|
||||
}
|
||||
|
||||
[Test, Ignore("Performance test")]
|
||||
public void SpeedTest()
|
||||
{
|
||||
@@ -698,5 +754,47 @@ namespace QuantConnect.Tests.Common
|
||||
Log.Trace($"JSON TOOK {end - start}");
|
||||
}
|
||||
}
|
||||
|
||||
private void AssertAreEqual(object expected, object result)
|
||||
{
|
||||
foreach (var propertyInfo in expected.GetType().GetProperties())
|
||||
{
|
||||
if (propertyInfo.CustomAttributes.Any(data => data.AttributeType == typeof(ProtoMemberAttribute)))
|
||||
{
|
||||
var expectedValue = propertyInfo.GetValue(expected);
|
||||
var resultValue = propertyInfo.GetValue(result);
|
||||
if (expectedValue is IList)
|
||||
{
|
||||
var expectedValueList = (IList) expectedValue;
|
||||
var resultValueList = (IList) resultValue;
|
||||
for (var i = 0; i < expectedValueList.Count; i++)
|
||||
{
|
||||
AssertAreEqual(expectedValueList[i], resultValueList[i]);
|
||||
}
|
||||
}
|
||||
else if (expectedValue is IDictionary)
|
||||
{
|
||||
var expectedValueDictionary = (IDictionary) expectedValue;
|
||||
var resultValueDictionary = (IDictionary) resultValue;
|
||||
foreach (dynamic kvp in expectedValueDictionary)
|
||||
{
|
||||
AssertAreEqual(kvp.Key, resultValueDictionary.Contains(kvp.Key));
|
||||
AssertAreEqual(kvp.Value, resultValueDictionary[kvp.Key]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(expectedValue, resultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (var fieldInfo in expected.GetType().GetFields())
|
||||
{
|
||||
if (fieldInfo.CustomAttributes.Any(data => data.AttributeType == typeof(ProtoMemberAttribute)))
|
||||
{
|
||||
Assert.AreEqual(fieldInfo.GetValue(expected), fieldInfo.GetValue(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ using QuantConnect.Algorithm;
|
||||
using QuantConnect.Algorithm.Framework.Alphas;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Auxiliary;
|
||||
using QuantConnect.Data.Custom.AlphaStreams;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Indicators;
|
||||
@@ -67,15 +68,16 @@ namespace QuantConnect.Tests.Common.Util
|
||||
};
|
||||
var orders = new List<Order> { new MarketOrder(btcusd, 1000, DateTime.UtcNow, "ExpensiveOrder") { Id = 1 } };
|
||||
|
||||
var packet1 = new AlphaResultPacket("1", 1, insights: insights);
|
||||
var packet1 = new AlphaResultPacket("1", 1, insights: insights, portfolio: new AlphaStreamsPortfolioState { TotalPortfolioValue = 11 });
|
||||
var packet2 = new AlphaResultPacket("1", 1, orders: orders);
|
||||
var packet3 = new AlphaResultPacket("1", 1, orderEvents: orderEvents);
|
||||
var packet3 = new AlphaResultPacket("1", 1, orderEvents: orderEvents, portfolio: new AlphaStreamsPortfolioState { TotalPortfolioValue = 12 });
|
||||
|
||||
var result = new List<AlphaResultPacket> { packet1, packet2, packet3 }.Batch();
|
||||
|
||||
Assert.AreEqual(2, result.Insights.Count);
|
||||
Assert.AreEqual(2, result.OrderEvents.Count);
|
||||
Assert.AreEqual(1, result.Orders.Count);
|
||||
Assert.AreEqual(12, result.Portfolio.TotalPortfolioValue);
|
||||
|
||||
Assert.IsTrue(result.Insights.SequenceEqual(insights));
|
||||
Assert.IsTrue(result.OrderEvents.SequenceEqual(orderEvents));
|
||||
|
||||
Reference in New Issue
Block a user