Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c75b4a98b4 | ||
|
|
288501bd3b | ||
|
|
9e7ead1a19 | ||
|
|
b80e274d4f | ||
|
|
9a355c9be5 | ||
|
|
303b95ab50 | ||
|
|
d826d267f4 | ||
|
|
eb55311052 | ||
|
|
27d18fa2e8 | ||
|
|
bb0c671e7c | ||
|
|
c8dc343c13 | ||
|
|
b6815d22de | ||
|
|
459f60603b | ||
|
|
1aaaa20c61 | ||
|
|
07b6572bf9 | ||
|
|
a675aca7e5 | ||
|
|
87db3fe379 | ||
|
|
74321d1727 | ||
|
|
9fd50a302e |
2
.github/workflows/gh-actions.yml
vendored
2
.github/workflows/gh-actions.yml
vendored
@@ -28,3 +28,5 @@ jobs:
|
||||
./ci_build_stubs.sh -t -g -p
|
||||
env:
|
||||
PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
|
||||
ADDITIONAL_STUBS_REPOS: ${{ secrets.ADDITIONAL_STUBS_REPOS }}
|
||||
QC_GIT_TOKEN: ${{ secrets.QC_GIT_TOKEN }}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Interfaces;
|
||||
@@ -62,7 +63,11 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
foreach (var changedEvent in data.SymbolChangedEvents.Values)
|
||||
{
|
||||
Log($"{Time} - SymbolChanged event: {changedEvent}");
|
||||
Debug($"{Time} - SymbolChanged event: {changedEvent}");
|
||||
if (Time.TimeOfDay != TimeSpan.Zero)
|
||||
{
|
||||
throw new Exception($"{Time} unexpected symbol changed event {changedEvent}!");
|
||||
}
|
||||
}
|
||||
|
||||
if (!Portfolio.Invested)
|
||||
|
||||
@@ -76,6 +76,15 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <param name="slice">The current slice of data keyed by symbol string</param>
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
foreach (var changedEvent in slice.SymbolChangedEvents.Values)
|
||||
{
|
||||
Debug($"{Time} - SymbolChanged event: {changedEvent}");
|
||||
if (Time.TimeOfDay != TimeSpan.Zero)
|
||||
{
|
||||
throw new Exception($"{Time} unexpected symbol changed event {changedEvent}!");
|
||||
}
|
||||
}
|
||||
|
||||
if (!Portfolio.Invested)
|
||||
{
|
||||
foreach(var chain in slice.FutureChains)
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2013, 10, 08);
|
||||
SetEndDate(2013, 10, 10);
|
||||
SetEndDate(2014, 10, 10);
|
||||
SetCash(1000000);
|
||||
|
||||
var futureSP500 = AddFuture(RootSP500, Resolution);
|
||||
@@ -77,7 +77,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
).FirstOrDefault();
|
||||
|
||||
// if found, trade it
|
||||
if (contract != null)
|
||||
if (contract != null && IsMarketOpen(contract.Symbol))
|
||||
{
|
||||
_contractSymbol = contract.Symbol;
|
||||
MarketOrder(_contractSymbol, 1);
|
||||
@@ -88,6 +88,14 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
Liquidate();
|
||||
}
|
||||
|
||||
foreach (var changedEvent in slice.SymbolChangedEvents.Values)
|
||||
{
|
||||
if (Time.TimeOfDay != TimeSpan.Zero)
|
||||
{
|
||||
throw new Exception($"{Time} unexpected symbol changed event {changedEvent}!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -105,34 +113,34 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// </summary>
|
||||
public virtual Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "6"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "-0.10%"},
|
||||
{"Compounding Annual Return", "-23.119%"},
|
||||
{"Drawdown", "0.300%"},
|
||||
{"Expectancy", "-1"},
|
||||
{"Net Profit", "-0.276%"},
|
||||
{"Sharpe Ratio", "-13.736"},
|
||||
{"Total Trades", "92"},
|
||||
{"Average Win", "0.08%"},
|
||||
{"Average Loss", "-0.01%"},
|
||||
{"Compounding Annual Return", "-0.450%"},
|
||||
{"Drawdown", "0.500%"},
|
||||
{"Expectancy", "-0.824"},
|
||||
{"Net Profit", "-0.453%"},
|
||||
{"Sharpe Ratio", "-1.803"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "100%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "-0.526"},
|
||||
{"Beta", "0.057"},
|
||||
{"Annual Standard Deviation", "0.015"},
|
||||
{"Loss Rate", "98%"},
|
||||
{"Win Rate", "2%"},
|
||||
{"Profit-Loss Ratio", "7.09"},
|
||||
{"Alpha", "-0.003"},
|
||||
{"Beta", "-0.001"},
|
||||
{"Annual Standard Deviation", "0.002"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-31.088"},
|
||||
{"Tracking Error", "0.189"},
|
||||
{"Treynor Ratio", "-3.51"},
|
||||
{"Total Fees", "$11.10"},
|
||||
{"Estimated Strategy Capacity", "$200000000.00"},
|
||||
{"Lowest Capacity Asset", "GC VOFJUCDY9XNH"},
|
||||
{"Fitness Score", "0"},
|
||||
{"Information Ratio", "-1.394"},
|
||||
{"Tracking Error", "0.089"},
|
||||
{"Treynor Ratio", "4.298"},
|
||||
{"Total Fees", "$170.20"},
|
||||
{"Estimated Strategy Capacity", "$36000.00"},
|
||||
{"Lowest Capacity Asset", "ES VP274HSU1AF5"},
|
||||
{"Fitness Score", "0.009"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "-17.118"},
|
||||
{"Return Over Maximum Drawdown", "-83.844"},
|
||||
{"Portfolio Turnover", "0.16"},
|
||||
{"Sortino Ratio", "-0.8"},
|
||||
{"Return Over Maximum Drawdown", "-0.992"},
|
||||
{"Portfolio Turnover", "0.025"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
@@ -146,7 +154,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "512f55519e5221c7e82e1d9f5ddd1b9f"}
|
||||
{"OrderListHash", "09b2f274fa2385597a803e58b784f675"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,34 +49,34 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// </summary>
|
||||
public override Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "140"},
|
||||
{"Total Trades", "1988"},
|
||||
{"Average Win", "0.01%"},
|
||||
{"Average Loss", "-0.02%"},
|
||||
{"Compounding Annual Return", "-38.171%"},
|
||||
{"Drawdown", "0.400%"},
|
||||
{"Expectancy", "-0.369"},
|
||||
{"Net Profit", "-0.394%"},
|
||||
{"Sharpe Ratio", "-24.82"},
|
||||
{"Average Loss", "0.00%"},
|
||||
{"Compounding Annual Return", "-4.120%"},
|
||||
{"Drawdown", "4.200%"},
|
||||
{"Expectancy", "-0.870"},
|
||||
{"Net Profit", "-4.150%"},
|
||||
{"Sharpe Ratio", "-6.061"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "66%"},
|
||||
{"Win Rate", "34%"},
|
||||
{"Profit-Loss Ratio", "0.84"},
|
||||
{"Alpha", "0.42"},
|
||||
{"Beta", "-0.041"},
|
||||
{"Annual Standard Deviation", "0.01"},
|
||||
{"Loss Rate", "97%"},
|
||||
{"Win Rate", "3%"},
|
||||
{"Profit-Loss Ratio", "2.92"},
|
||||
{"Alpha", "-0.027"},
|
||||
{"Beta", "-0.006"},
|
||||
{"Annual Standard Deviation", "0.005"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-65.112"},
|
||||
{"Tracking Error", "0.253"},
|
||||
{"Treynor Ratio", "6.024"},
|
||||
{"Total Fees", "$259.00"},
|
||||
{"Estimated Strategy Capacity", "$130000.00"},
|
||||
{"Lowest Capacity Asset", "GC VOFJUCDY9XNH"},
|
||||
{"Fitness Score", "0"},
|
||||
{"Information Ratio", "-1.66"},
|
||||
{"Tracking Error", "0.089"},
|
||||
{"Treynor Ratio", "4.919"},
|
||||
{"Total Fees", "$3677.80"},
|
||||
{"Estimated Strategy Capacity", "$2000.00"},
|
||||
{"Lowest Capacity Asset", "ES VP274HSU1AF5"},
|
||||
{"Fitness Score", "0.128"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "-43.422"},
|
||||
{"Return Over Maximum Drawdown", "-100.459"},
|
||||
{"Portfolio Turnover", "4.716"},
|
||||
{"Sortino Ratio", "-6.856"},
|
||||
{"Return Over Maximum Drawdown", "-0.995"},
|
||||
{"Portfolio Turnover", "0.648"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
@@ -90,7 +90,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "320067074c8dd771f69602ab07001f1e"}
|
||||
{"OrderListHash", "87d2b127c9859cad9d2c65ac9d76deb5"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
var contractsByExpiration = chain.Where(x => x.Expiry != Time.Date).OrderBy(x => x.Expiry);
|
||||
var contract = contractsByExpiration.FirstOrDefault();
|
||||
|
||||
if (contract != null)
|
||||
if (contract != null && IsMarketOpen(contract.Symbol))
|
||||
{
|
||||
// if found, trade it
|
||||
MarketOrder(contract.Symbol, 1);
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
.ThenByDescending(x => x.Right)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (atmContract != null)
|
||||
if (atmContract != null && IsMarketOpen(atmContract.Symbol))
|
||||
{
|
||||
// if found, trade it
|
||||
MarketOrder(atmContract.Symbol, 1);
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "26.189"},
|
||||
{"Portfolio Turnover", "0.208"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
|
||||
@@ -13,7 +13,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using QuantConnect.Util;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Brokerages;
|
||||
using System.Collections.Generic;
|
||||
@@ -54,7 +58,28 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
if (!Portfolio.Invested)
|
||||
{
|
||||
Buy(_symbol, 0.1m);
|
||||
CurrencyPairUtil.DecomposeCurrencyPair(_symbol, out var baseCurrency, out var quoteCurrency);
|
||||
|
||||
var initialQuoteCurrency = Portfolio.CashBook[quoteCurrency].Amount;
|
||||
var ticket = Buy(_symbol, 0.1m);
|
||||
var filledEvent = ticket.OrderEvents.Single(orderEvent => orderEvent.Status == OrderStatus.Filled);
|
||||
|
||||
if (Portfolio.CashBook[baseCurrency].Amount != ticket.QuantityFilled
|
||||
|| filledEvent.FillQuantity != ticket.QuantityFilled
|
||||
|| (0.1m - filledEvent.OrderFee.Value.Amount) != ticket.QuantityFilled)
|
||||
{
|
||||
throw new Exception($"Unexpected BaseCurrency porfoltio status. Event {filledEvent}. CashBook: {Portfolio.CashBook}. ");
|
||||
}
|
||||
|
||||
if (Portfolio.CashBook[quoteCurrency].Amount != (initialQuoteCurrency - 0.1m * filledEvent.FillPrice))
|
||||
{
|
||||
throw new Exception($"Unexpected QuoteCurrency porfoltio status. Event {filledEvent}. CashBook: {Portfolio.CashBook}. ");
|
||||
}
|
||||
|
||||
if (Securities[_symbol].Holdings.Quantity != (0.1m - filledEvent.OrderFee.Value.Amount))
|
||||
{
|
||||
throw new Exception($"Unexpected Holdings: {Securities[_symbol].Holdings}. Event {filledEvent}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -18,7 +18,9 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Newtonsoft.Json;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
@@ -31,12 +33,14 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <meta name="tag" content="regression test" />
|
||||
public class CustomDataRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private bool _warmedUpChecked = false;
|
||||
|
||||
/// <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(2011, 9, 13);
|
||||
SetStartDate(2011, 9, 14);
|
||||
SetEndDate(2015, 12, 01);
|
||||
|
||||
//Set the cash for the strategy:
|
||||
@@ -45,6 +49,9 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
//Define the symbol and "type" of our generic data:
|
||||
var resolution = LiveMode ? Resolution.Second : Resolution.Daily;
|
||||
AddData<Bitcoin>("BTC", resolution);
|
||||
|
||||
var seeder = new FuncSecuritySeeder(GetLastKnownPrices);
|
||||
SetSecurityInitializer(security => seeder.SeedSecurity(security));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -66,6 +73,30 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnSecuritiesChanged(SecurityChanges changes)
|
||||
{
|
||||
changes.FilterCustomSecurities = false;
|
||||
foreach (var addedSecurity in changes.AddedSecurities)
|
||||
{
|
||||
if (addedSecurity.Symbol.Value == "BTC")
|
||||
{
|
||||
_warmedUpChecked = true;
|
||||
}
|
||||
if (!addedSecurity.HasData)
|
||||
{
|
||||
throw new Exception($"Security {addedSecurity.Symbol} was not warmed up!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (!_warmedUpChecked)
|
||||
{
|
||||
throw new Exception($"Security was not warmed up!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
@@ -84,30 +115,30 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Total Trades", "1"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "157.497%"},
|
||||
{"Compounding Annual Return", "157.655%"},
|
||||
{"Drawdown", "84.800%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "5319.007%"},
|
||||
{"Sharpe Ratio", "2.086"},
|
||||
{"Probabilistic Sharpe Ratio", "69.456%"},
|
||||
{"Sharpe Ratio", "2.123"},
|
||||
{"Probabilistic Sharpe Ratio", "70.581%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "1.747"},
|
||||
{"Beta", "0.047"},
|
||||
{"Alpha", "1.776"},
|
||||
{"Beta", "0.059"},
|
||||
{"Annual Standard Deviation", "0.84"},
|
||||
{"Annual Variance", "0.706"},
|
||||
{"Information Ratio", "1.922"},
|
||||
{"Tracking Error", "0.848"},
|
||||
{"Treynor Ratio", "37.473"},
|
||||
{"Information Ratio", "1.962"},
|
||||
{"Tracking Error", "0.847"},
|
||||
{"Treynor Ratio", "30.455"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Estimated Strategy Capacity", "$0"},
|
||||
{"Lowest Capacity Asset", "BTC.Bitcoin 2S"},
|
||||
{"Fitness Score", "0"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "2.269"},
|
||||
{"Return Over Maximum Drawdown", "1.858"},
|
||||
{"Sortino Ratio", "2.271"},
|
||||
{"Return Over Maximum Drawdown", "1.86"},
|
||||
{"Portfolio Turnover", "0"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
@@ -18,6 +18,7 @@ using QuantConnect.Indicators;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Storage;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
@@ -29,7 +30,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// history call. This pattern can be equally applied to a machine learning model being
|
||||
/// trained and then saving the model weights in the object store.
|
||||
/// </summary>
|
||||
public class ObjectStoreExampleAlgorithm : QCAlgorithm
|
||||
public class ObjectStoreExampleAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private const string SPY_Close_ObjectStore_Key = "spy_close";
|
||||
private Symbol SPY;
|
||||
@@ -127,5 +128,64 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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, Language.Python };
|
||||
|
||||
/// <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", "1"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "271.453%"},
|
||||
{"Drawdown", "2.200%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "1.692%"},
|
||||
{"Sharpe Ratio", "8.888"},
|
||||
{"Probabilistic Sharpe Ratio", "67.609%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "-0.005"},
|
||||
{"Beta", "0.996"},
|
||||
{"Annual Standard Deviation", "0.222"},
|
||||
{"Annual Variance", "0.049"},
|
||||
{"Information Ratio", "-14.565"},
|
||||
{"Tracking Error", "0.001"},
|
||||
{"Treynor Ratio", "1.978"},
|
||||
{"Total Fees", "$3.44"},
|
||||
{"Estimated Strategy Capacity", "$56000000.00"},
|
||||
{"Lowest Capacity Asset", "SPY R735QTJ8XC9X"},
|
||||
{"Fitness Score", "0.248"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "93.728"},
|
||||
{"Portfolio Turnover", "0.248"},
|
||||
{"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", "9e4bfd2eb0b81ee5bc1b197a87ccedbe"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class BasicTemplateFuturesDailyAlgorithm(QCAlgorithm):
|
||||
|
||||
def Initialize(self):
|
||||
self.SetStartDate(2013, 10, 8)
|
||||
self.SetEndDate(2013, 10, 10)
|
||||
self.SetEndDate(2014, 10, 10)
|
||||
self.SetCash(1000000)
|
||||
|
||||
self.contractSymbol = None
|
||||
@@ -50,6 +50,7 @@ class BasicTemplateFuturesDailyAlgorithm(QCAlgorithm):
|
||||
front = sorted(contracts, key = lambda x: x.Expiry, reverse=True)[0]
|
||||
|
||||
self.contractSymbol = front.Symbol
|
||||
self.MarketOrder(front.Symbol , 1)
|
||||
if self.IsMarketOpen(self.contractSymbol):
|
||||
self.MarketOrder(front.Symbol , 1)
|
||||
else:
|
||||
self.Liquidate()
|
||||
|
||||
@@ -23,7 +23,7 @@ class BasicTemplateFuturesHourlyAlgorithm(QCAlgorithm):
|
||||
|
||||
def Initialize(self):
|
||||
self.SetStartDate(2013, 10, 8)
|
||||
self.SetEndDate(2013, 10, 10)
|
||||
self.SetEndDate(2014, 10, 10)
|
||||
self.SetCash(1000000)
|
||||
|
||||
self.contractSymbol = None
|
||||
@@ -50,6 +50,7 @@ class BasicTemplateFuturesHourlyAlgorithm(QCAlgorithm):
|
||||
front = sorted(contracts, key = lambda x: x.Expiry, reverse=True)[0]
|
||||
|
||||
self.contractSymbol = front.Symbol
|
||||
self.MarketOrder(front.Symbol , 1)
|
||||
if self.IsMarketOpen(self.contractSymbol):
|
||||
self.MarketOrder(front.Symbol , 1)
|
||||
else:
|
||||
self.Liquidate()
|
||||
|
||||
@@ -51,7 +51,7 @@ class BasicTemplateOptionsDailyAlgorithm(QCAlgorithm):
|
||||
contracts = sorted(chain, key = lambda x: x.Expiry)
|
||||
|
||||
# if found, trade it
|
||||
if len(contracts) == 0: return
|
||||
if len(contracts) == 0 or not self.IsMarketOpen(contracts[0].Symbol): return
|
||||
symbol = contracts[0].Symbol
|
||||
self.MarketOrder(symbol, 1)
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ class BasicTemplateOptionsHourlyAlgorithm(QCAlgorithm):
|
||||
key = lambda x: x.Right, reverse=True)
|
||||
|
||||
# if found, trade it
|
||||
if len(contracts) == 0: return
|
||||
if len(contracts) == 0 or not self.IsMarketOpen(contracts[0].Symbol): return
|
||||
symbol = contracts[0].Symbol
|
||||
self.MarketOrder(symbol, 1)
|
||||
self.MarketOnCloseOrder(symbol, -1)
|
||||
|
||||
@@ -25,18 +25,33 @@ class CustomDataRegressionAlgorithm(QCAlgorithm):
|
||||
|
||||
def Initialize(self):
|
||||
|
||||
self.SetStartDate(2011,9,13) # Set Start Date
|
||||
self.SetStartDate(2011,9,14) # Set Start Date
|
||||
self.SetEndDate(2015,12,1) # Set End Date
|
||||
self.SetCash(100000) # Set Strategy Cash
|
||||
|
||||
resolution = Resolution.Second if self.LiveMode else Resolution.Daily
|
||||
self.AddData(Bitcoin, "BTC", resolution)
|
||||
|
||||
seeder = FuncSecuritySeeder(self.GetLastKnownPrices)
|
||||
self.SetSecurityInitializer(lambda x: seeder.SeedSecurity(x))
|
||||
self._warmedUpChecked = False
|
||||
|
||||
def OnData(self, data):
|
||||
if not self.Portfolio.Invested:
|
||||
if data['BTC'].Close != 0 :
|
||||
self.Order('BTC', self.Portfolio.MarginRemaining/abs(data['BTC'].Close + 1))
|
||||
|
||||
def OnSecuritiesChanged(self, changes):
|
||||
changes.FilterCustomSecurities = False
|
||||
for addedSecurity in changes.AddedSecurities:
|
||||
if addedSecurity.Symbol.Value == "BTC":
|
||||
self._warmedUpChecked = True
|
||||
if not addedSecurity.HasData:
|
||||
raise ValueError(f"Security {addedSecurity.Symbol} was not warmed up!")
|
||||
|
||||
def OnEndOfAlgorithm(self):
|
||||
if not self._warmedUpChecked:
|
||||
raise ValueError("Security was not warmed up!")
|
||||
|
||||
class Bitcoin(PythonData):
|
||||
'''Custom Data Type: Bitcoin data from Quandl - http://www.quandl.com/help/api-for-bitcoin-data'''
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
from AlgorithmImports import *
|
||||
from QuantConnect.Algorithm.CSharp import *
|
||||
|
||||
### <summary>
|
||||
### Basic template algorithm simply initializes the date range and cash. This is a skeleton
|
||||
|
||||
@@ -13,11 +13,10 @@
|
||||
|
||||
from AlgorithmImports import *
|
||||
|
||||
from io import StringIO
|
||||
from keras.models import Sequential
|
||||
from keras.models import *
|
||||
from tensorflow import keras
|
||||
from keras.layers import Dense, Activation
|
||||
from keras.optimizers import SGD
|
||||
from keras.utils.generic_utils import serialize_keras_object
|
||||
|
||||
class KerasNeuralNetworkAlgorithm(QCAlgorithm):
|
||||
|
||||
@@ -32,11 +31,13 @@ class KerasNeuralNetworkAlgorithm(QCAlgorithm):
|
||||
symbol = self.AddEquity(ticker).Symbol
|
||||
|
||||
# Read the model saved in the ObjectStore
|
||||
if self.ObjectStore.ContainsKey(f'{symbol}_model'):
|
||||
modelStr = self.ObjectStore.Read(f'{symbol}_model')
|
||||
config = json.loads(modelStr)['config']
|
||||
self.modelBySymbol[symbol] = Sequential.from_config(config)
|
||||
self.Debug(f'Model for {symbol} sucessfully retrieved from the ObjectStore')
|
||||
for kvp in self.ObjectStore:
|
||||
key = f'{symbol}_model'
|
||||
if not key == kvp.Key or kvp.Value is None:
|
||||
continue
|
||||
filePath = self.ObjectStore.GetFilePath(kvp.Key)
|
||||
self.modelBySymbol[symbol] = keras.models.load_model(filePath)
|
||||
self.Debug(f'Model for {symbol} sucessfully retrieved. File {filePath}. Size {kvp.Value.Length}. Weights {self.modelBySymbol[symbol].get_weights()}')
|
||||
|
||||
# Look-back period for training set
|
||||
self.lookback = 30
|
||||
@@ -51,19 +52,21 @@ class KerasNeuralNetworkAlgorithm(QCAlgorithm):
|
||||
self.Schedule.On(
|
||||
self.DateRules.EveryDay("SPY"),
|
||||
self.TimeRules.AfterMarketOpen("SPY", 30),
|
||||
self.Trade)
|
||||
self.Trade)
|
||||
|
||||
|
||||
def OnEndOfAlgorithm(self):
|
||||
''' Save the data and the mode using the ObjectStore '''
|
||||
for symbol, model in self.modelBySymbol.items():
|
||||
modelStr = json.dumps(serialize_keras_object(model))
|
||||
self.ObjectStore.Save(f'{symbol}_model', modelStr)
|
||||
key = f'{symbol}_model'
|
||||
file = self.ObjectStore.GetFilePath(key)
|
||||
model.save(file)
|
||||
self.ObjectStore.Save(key)
|
||||
self.Debug(f'Model for {symbol} sucessfully saved in the ObjectStore')
|
||||
|
||||
|
||||
def NeuralNetworkTraining(self):
|
||||
'''Train the Neural Network and save the model in the ObjectStore'''
|
||||
'''Train the Neural Network and save the model in the ObjectStore'''
|
||||
symbols = self.Securities.keys()
|
||||
|
||||
# Daily historical data is used to train the machine learning model
|
||||
@@ -89,19 +92,18 @@ class KerasNeuralNetworkAlgorithm(QCAlgorithm):
|
||||
# choose loss function and optimizing method
|
||||
model.compile(loss='mse', optimizer=sgd)
|
||||
|
||||
# pick an iteration number large enough for convergence
|
||||
# pick an iteration number large enough for convergence
|
||||
for step in range(200):
|
||||
# training the model
|
||||
cost = model.train_on_batch(predictor, predictand)
|
||||
|
||||
self.modelBySymbol[symbol] = model
|
||||
|
||||
|
||||
def Trade(self):
|
||||
'''
|
||||
Predict the price using the trained model and out-of-sample data
|
||||
Enter or exit positions based on relationship of the open price of the current bar and the prices defined by the machine learning model.
|
||||
Liquidate if the open price is below the sell price and buy if the open price is above the buy price
|
||||
Liquidate if the open price is below the sell price and buy if the open price is above the buy price
|
||||
'''
|
||||
target = 1 / len(self.Securities)
|
||||
|
||||
|
||||
@@ -1269,13 +1269,11 @@ namespace QuantConnect.Algorithm
|
||||
[DocumentationAttribute(SecuritiesAndPortfolio)]
|
||||
public bool IsMarketOpen(Symbol symbol)
|
||||
{
|
||||
var exchangeHours = MarketHoursDatabase
|
||||
.FromDataFolder()
|
||||
.GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType);
|
||||
|
||||
var time = UtcTime.ConvertFromUtc(exchangeHours.TimeZone);
|
||||
|
||||
return exchangeHours.IsOpen(time, false);
|
||||
if (Securities.TryGetValue(symbol, out var security))
|
||||
{
|
||||
return security.IsMarketOpen(false);
|
||||
}
|
||||
return symbol.IsMarketOpen(UtcTime, false);
|
||||
}
|
||||
|
||||
private SubmitOrderRequest CreateSubmitOrderRequest(OrderType orderType, Security security, decimal quantity, string tag, IOrderProperties properties, decimal stopPrice = 0m, decimal limitPrice = 0m, decimal triggerPrice = 0m)
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="NodaTime" Version="3.0.5" />
|
||||
<PackageReference Include="RestSharp" Version="106.12.0" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.2.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
|
||||
|
||||
@@ -279,12 +279,12 @@ namespace QuantConnect.Brokerages.Backtesting
|
||||
continue;
|
||||
}
|
||||
|
||||
var fills = new OrderEvent[0];
|
||||
var fills = Array.Empty<OrderEvent>();
|
||||
|
||||
Security security;
|
||||
if (!Algorithm.Securities.TryGetValue(order.Symbol, out security))
|
||||
{
|
||||
Log.Error("BacktestingBrokerage.Scan(): Unable to process order: " + order.Id + ". The security no longer exists.");
|
||||
Log.Error($"BacktestingBrokerage.Scan(): Unable to process order: {order.Id}. The security no longer exists. UtcTime: {Algorithm.UtcTime}");
|
||||
// invalidate the order in the algorithm before removing
|
||||
OnOrderEvent(new OrderEvent(order,
|
||||
Algorithm.UtcTime,
|
||||
|
||||
@@ -149,7 +149,10 @@ namespace QuantConnect.Brokerages
|
||||
return SubscriptionManager?.GetSubscribedSymbols() ?? Enumerable.Empty<Symbol>();
|
||||
}
|
||||
|
||||
private void ConnectSync()
|
||||
/// <summary>
|
||||
/// Start websocket connect
|
||||
/// </summary>
|
||||
protected void ConnectSync()
|
||||
{
|
||||
var resetEvent = new ManualResetEvent(false);
|
||||
EventHandler triggerEvent = (o, args) => resetEvent.Set();
|
||||
|
||||
@@ -28,21 +28,27 @@ using System.Linq;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using QuantConnect.Brokerages.Binance.Messages;
|
||||
using Order = QuantConnect.Orders.Order;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance
|
||||
{
|
||||
/// <summary>
|
||||
/// Binance REST API implementation
|
||||
/// Binance REST API base implementation
|
||||
/// </summary>
|
||||
public class BinanceRestApiClient : IDisposable
|
||||
public abstract class BinanceBaseRestApiClient : IDisposable
|
||||
{
|
||||
private const string UserDataStreamEndpoint = "/api/v3/userDataStream";
|
||||
// depends on SPOT or MARGIN trading
|
||||
private readonly string _apiPrefix;
|
||||
private readonly string _wsPrefix;
|
||||
|
||||
private string UserDataStreamEndpoint => $"{_wsPrefix}/userDataStream";
|
||||
|
||||
private readonly SymbolPropertiesDatabaseSymbolMapper _symbolMapper;
|
||||
private readonly ISecurityProvider _securityProvider;
|
||||
private readonly IRestClient _restClient;
|
||||
private readonly RateGate _restRateLimiter = new RateGate(10, TimeSpan.FromSeconds(1));
|
||||
private readonly object _listenKeyLocker = new object();
|
||||
private readonly RateGate _restRateLimiter = new(10, TimeSpan.FromSeconds(1));
|
||||
private readonly object _listenKeyLocker = new();
|
||||
|
||||
/// <summary>
|
||||
/// Event that fires each time an order is filled
|
||||
@@ -80,21 +86,25 @@ namespace QuantConnect.Brokerages.Binance
|
||||
public string SessionId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BinanceRestApiClient"/> class.
|
||||
/// Initializes a new instance of the <see cref="BinanceBaseRestApiClient"/> class.
|
||||
/// </summary>
|
||||
/// <param name="symbolMapper">The symbol mapper.</param>
|
||||
/// <param name="securityProvider">The holdings provider.</param>
|
||||
/// <param name="apiKey">The Binance API key</param>
|
||||
/// <param name="apiSecret">The The Binance API secret</param>
|
||||
/// <param name="restApiUrl">The Binance API rest url</param>
|
||||
public BinanceRestApiClient(SymbolPropertiesDatabaseSymbolMapper symbolMapper, ISecurityProvider securityProvider,
|
||||
string apiKey, string apiSecret, string restApiUrl)
|
||||
/// <param name="restApiPrefix">REST API path prefix depending on SPOT or CROSS MARGIN trading</param>
|
||||
/// <param name="wsApiPrefix">REST API path prefix for user data streaming auth process depending on SPOT or CROSS MARGIN trading</param>
|
||||
public BinanceBaseRestApiClient(SymbolPropertiesDatabaseSymbolMapper symbolMapper, ISecurityProvider securityProvider,
|
||||
string apiKey, string apiSecret, string restApiUrl, string restApiPrefix, string wsApiPrefix)
|
||||
{
|
||||
_symbolMapper = symbolMapper;
|
||||
_securityProvider = securityProvider;
|
||||
_restClient = new RestClient(restApiUrl);
|
||||
ApiKey = apiKey;
|
||||
ApiSecret = apiSecret;
|
||||
_apiPrefix = restApiPrefix;
|
||||
_wsPrefix = wsApiPrefix;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -110,10 +120,10 @@ namespace QuantConnect.Brokerages.Binance
|
||||
/// Gets the total account cash balance for specified account type
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Messages.AccountInformation GetCashBalance()
|
||||
public BalanceEntry[] GetCashBalance()
|
||||
{
|
||||
var queryString = $"timestamp={GetNonce()}";
|
||||
var endpoint = $"/api/v3/account?{queryString}&signature={AuthenticationToken(queryString)}";
|
||||
var endpoint = $"{_apiPrefix}/account?{queryString}&signature={AuthenticationToken(queryString)}";
|
||||
var request = new RestRequest(endpoint, Method.GET);
|
||||
request.AddHeader(KeyHeader, ApiKey);
|
||||
|
||||
@@ -123,9 +133,20 @@ namespace QuantConnect.Brokerages.Binance
|
||||
throw new Exception($"BinanceBrokerage.GetCashBalance: request failed: [{(int)response.StatusCode}] {response.StatusDescription}, Content: {response.Content}, ErrorMessage: {response.ErrorMessage}");
|
||||
}
|
||||
|
||||
return JsonConvert.DeserializeObject<Messages.AccountInformation>(response.Content);
|
||||
return JsonConvert
|
||||
.DeserializeObject<AccountInformation>(response.Content, CreateAccountConverter())
|
||||
.Balances
|
||||
.Where(s => s.Amount != 0)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserialize Binance account information
|
||||
/// </summary>
|
||||
/// <param name="content">API response content</param>
|
||||
/// <returns>Cash or Margin Account</returns>
|
||||
protected abstract JsonConverter CreateAccountConverter();
|
||||
|
||||
/// <summary>
|
||||
/// Gets all orders not yet closed
|
||||
/// </summary>
|
||||
@@ -133,7 +154,7 @@ namespace QuantConnect.Brokerages.Binance
|
||||
public IEnumerable<Messages.OpenOrder> GetOpenOrders()
|
||||
{
|
||||
var queryString = $"timestamp={GetNonce()}";
|
||||
var endpoint = $"/api/v3/openOrders?{queryString}&signature={AuthenticationToken(queryString)}";
|
||||
var endpoint = $"{_apiPrefix}/openOrders?{queryString}&signature={AuthenticationToken(queryString)}";
|
||||
var request = new RestRequest(endpoint, Method.GET);
|
||||
request.AddHeader(KeyHeader, ApiKey);
|
||||
|
||||
@@ -153,53 +174,11 @@ namespace QuantConnect.Brokerages.Binance
|
||||
/// <returns>True if the request for a new order has been placed, false otherwise</returns>
|
||||
public bool PlaceOrder(Order order)
|
||||
{
|
||||
// supported time in force values {GTC, IOC, FOK}
|
||||
// use GTC as LEAN doesn't support others yet
|
||||
IDictionary<string, object> body = new Dictionary<string, object>()
|
||||
{
|
||||
{ "symbol", _symbolMapper.GetBrokerageSymbol(order.Symbol) },
|
||||
{ "quantity", Math.Abs(order.Quantity).ToString(CultureInfo.InvariantCulture) },
|
||||
{ "side", ConvertOrderDirection(order.Direction) }
|
||||
};
|
||||
var body = CreateOrderBody(order);
|
||||
|
||||
switch (order)
|
||||
{
|
||||
case LimitOrder limitOrder:
|
||||
body["type"] = (order.Properties as BinanceOrderProperties)?.PostOnly == true
|
||||
? "LIMIT_MAKER"
|
||||
: "LIMIT";
|
||||
body["price"] = limitOrder.LimitPrice.ToString(CultureInfo.InvariantCulture);
|
||||
// timeInForce is not required for LIMIT_MAKER
|
||||
if (Equals(body["type"], "LIMIT"))
|
||||
body["timeInForce"] = "GTC";
|
||||
break;
|
||||
case MarketOrder:
|
||||
body["type"] = "MARKET";
|
||||
break;
|
||||
case StopLimitOrder stopLimitOrder:
|
||||
var ticker = GetTickerPrice(order);
|
||||
var stopPrice = stopLimitOrder.StopPrice;
|
||||
if (order.Direction == OrderDirection.Sell)
|
||||
{
|
||||
body["type"] = stopPrice <= ticker ? "STOP_LOSS_LIMIT" : "TAKE_PROFIT_LIMIT";
|
||||
}
|
||||
else
|
||||
{
|
||||
body["type"] = stopPrice <= ticker ? "TAKE_PROFIT_LIMIT" : "STOP_LOSS_LIMIT";
|
||||
}
|
||||
|
||||
body["timeInForce"] = "GTC";
|
||||
body["stopPrice"] = stopPrice.ToStringInvariant();
|
||||
body["price"] = stopLimitOrder.LimitPrice.ToStringInvariant();
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException($"BinanceBrokerage.ConvertOrderType: Unsupported order type: {order.Type}");
|
||||
}
|
||||
|
||||
const string endpoint = "/api/v3/order";
|
||||
body["timestamp"] = GetNonce();
|
||||
body["signature"] = AuthenticationToken(body.ToQueryString());
|
||||
var request = new RestRequest(endpoint, Method.POST);
|
||||
var request = new RestRequest($"{_apiPrefix}/order", Method.POST);
|
||||
request.AddHeader(KeyHeader, ApiKey);
|
||||
request.AddParameter(
|
||||
"application/x-www-form-urlencoded",
|
||||
@@ -242,6 +221,58 @@ namespace QuantConnect.Brokerages.Binance
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create account new order body payload
|
||||
/// </summary>
|
||||
/// <param name="order">Lean order</param>
|
||||
protected virtual IDictionary<string, object> CreateOrderBody(Order order)
|
||||
{
|
||||
// supported time in force values {GTC, IOC, FOK}
|
||||
// use GTC as LEAN doesn't support others yet
|
||||
var body = new Dictionary<string, object>
|
||||
{
|
||||
{ "symbol", _symbolMapper.GetBrokerageSymbol(order.Symbol) },
|
||||
{ "quantity", Math.Abs(order.Quantity).ToString(CultureInfo.InvariantCulture) },
|
||||
{ "side", ConvertOrderDirection(order.Direction) }
|
||||
};
|
||||
|
||||
switch (order)
|
||||
{
|
||||
case LimitOrder limitOrder:
|
||||
body["type"] = (order.Properties as BinanceOrderProperties)?.PostOnly == true
|
||||
? "LIMIT_MAKER"
|
||||
: "LIMIT";
|
||||
body["price"] = limitOrder.LimitPrice.ToString(CultureInfo.InvariantCulture);
|
||||
// timeInForce is not required for LIMIT_MAKER
|
||||
if (Equals(body["type"], "LIMIT"))
|
||||
body["timeInForce"] = "GTC";
|
||||
break;
|
||||
case MarketOrder:
|
||||
body["type"] = "MARKET";
|
||||
break;
|
||||
case StopLimitOrder stopLimitOrder:
|
||||
var ticker = GetTickerPrice(order);
|
||||
var stopPrice = stopLimitOrder.StopPrice;
|
||||
if (order.Direction == OrderDirection.Sell)
|
||||
{
|
||||
body["type"] = stopPrice <= ticker ? "STOP_LOSS_LIMIT" : "TAKE_PROFIT_LIMIT";
|
||||
}
|
||||
else
|
||||
{
|
||||
body["type"] = stopPrice <= ticker ? "TAKE_PROFIT_LIMIT" : "STOP_LOSS_LIMIT";
|
||||
}
|
||||
|
||||
body["timeInForce"] = "GTC";
|
||||
body["stopPrice"] = stopPrice.ToStringInvariant();
|
||||
body["price"] = stopLimitOrder.LimitPrice.ToStringInvariant();
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException($"BinanceBrokerage.ConvertOrderType: Unsupported order type: {order.Type}");
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the order with the specified ID
|
||||
/// </summary>
|
||||
@@ -264,7 +295,7 @@ namespace QuantConnect.Brokerages.Binance
|
||||
body["timestamp"] = GetNonce();
|
||||
body["signature"] = AuthenticationToken(body.ToQueryString());
|
||||
|
||||
var request = new RestRequest("/api/v3/order", Method.DELETE);
|
||||
var request = new RestRequest($"{_apiPrefix}/order", Method.DELETE);
|
||||
request.AddHeader(KeyHeader, ApiKey);
|
||||
request.AddParameter(
|
||||
"application/x-www-form-urlencoded",
|
||||
@@ -382,7 +413,10 @@ namespace QuantConnect.Brokerages.Binance
|
||||
Encoding.UTF8.GetBytes($"listenKey={SessionId}"),
|
||||
ParameterType.RequestBody
|
||||
);
|
||||
ExecuteRestRequest(request);
|
||||
if (ExecuteRestRequest(request).StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
SessionId = null;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
@@ -27,7 +27,7 @@ namespace QuantConnect.Brokerages.Binance
|
||||
switch (raw.LazyToUpper())
|
||||
{
|
||||
case "NEW":
|
||||
return OrderStatus.New;
|
||||
return OrderStatus.Submitted;
|
||||
|
||||
case "PARTIALLY_FILLED":
|
||||
return OrderStatus.PartiallyFilled;
|
||||
|
||||
@@ -51,7 +51,9 @@ namespace QuantConnect.Brokerages.Binance
|
||||
private string _webSocketBaseUrl;
|
||||
private Timer _keepAliveTimer;
|
||||
private Timer _reconnectTimer;
|
||||
private BinanceRestApiClient _apiClient;
|
||||
private Lazy<BinanceBaseRestApiClient> _apiClientLazy;
|
||||
private BinanceBaseRestApiClient ApiClient => _apiClientLazy?.Value;
|
||||
|
||||
private BrokerageConcurrentMessageHandler<WebSocketMessage> _messageHandler;
|
||||
|
||||
private const int MaximumSymbolsPerConnection = 512;
|
||||
@@ -91,8 +93,9 @@ namespace QuantConnect.Brokerages.Binance
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the websocket connection is connected or in the process of connecting
|
||||
/// WebSocket is responsible for Binance UserData stream only.
|
||||
/// </summary>
|
||||
public override bool IsConnected => WebSocket.IsOpen;
|
||||
public override bool IsConnected => _apiClientLazy?.IsValueCreated != true || WebSocket?.IsOpen == true;
|
||||
|
||||
/// <summary>
|
||||
/// Creates wss connection
|
||||
@@ -102,12 +105,12 @@ namespace QuantConnect.Brokerages.Binance
|
||||
if (IsConnected)
|
||||
return;
|
||||
|
||||
_apiClient.CreateListenKey();
|
||||
_reconnectTimer.Start();
|
||||
|
||||
WebSocket.Initialize($"{_webSocketBaseUrl}/{_apiClient.SessionId}");
|
||||
|
||||
base.Connect();
|
||||
// cannot reach this code if rest api client is not created
|
||||
// WebSocket is responsible for Binance UserData stream only
|
||||
// as a result we don't need to connect user data stream if BinanceBrokerage is used as DQH only
|
||||
// or until Algorithm is actually initialized
|
||||
ApiClient.CreateListenKey();
|
||||
Connect(ApiClient.SessionId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -115,10 +118,11 @@ namespace QuantConnect.Brokerages.Binance
|
||||
/// </summary>
|
||||
public override void Disconnect()
|
||||
{
|
||||
_reconnectTimer.Stop();
|
||||
if (WebSocket?.IsOpen != true)
|
||||
return;
|
||||
|
||||
WebSocket?.Close();
|
||||
_apiClient.StopSession();
|
||||
_reconnectTimer.Stop();
|
||||
WebSocket.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -127,11 +131,7 @@ namespace QuantConnect.Brokerages.Binance
|
||||
/// <returns></returns>
|
||||
public override List<Holding> GetAccountHoldings()
|
||||
{
|
||||
if (_algorithm.BrokerageModel.AccountType == AccountType.Cash)
|
||||
{
|
||||
return base.GetAccountHoldings(_job?.BrokerageData, _algorithm.Securities.Values);
|
||||
}
|
||||
return _apiClient.GetAccountHoldings();
|
||||
return base.GetAccountHoldings(_job?.BrokerageData, _algorithm.Securities.Values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -140,8 +140,7 @@ namespace QuantConnect.Brokerages.Binance
|
||||
/// <returns></returns>
|
||||
public override List<CashAmount> GetCashBalance()
|
||||
{
|
||||
var account = _apiClient.GetCashBalance();
|
||||
var balances = account.Balances?.Where(balance => balance.Amount > 0).ToList();
|
||||
var balances = ApiClient.GetCashBalance();
|
||||
if (balances == null || !balances.Any())
|
||||
return new List<CashAmount>();
|
||||
|
||||
@@ -156,7 +155,7 @@ namespace QuantConnect.Brokerages.Binance
|
||||
/// <returns></returns>
|
||||
public override List<Order> GetOpenOrders()
|
||||
{
|
||||
var orders = _apiClient.GetOpenOrders();
|
||||
var orders = ApiClient.GetOpenOrders();
|
||||
List<Order> list = new List<Order>();
|
||||
foreach (var item in orders)
|
||||
{
|
||||
@@ -221,7 +220,7 @@ namespace QuantConnect.Brokerages.Binance
|
||||
|
||||
_messageHandler.WithLockedStream(() =>
|
||||
{
|
||||
submitted = _apiClient.PlaceOrder(order);
|
||||
submitted = ApiClient.PlaceOrder(order);
|
||||
});
|
||||
|
||||
return submitted;
|
||||
@@ -248,7 +247,7 @@ namespace QuantConnect.Brokerages.Binance
|
||||
|
||||
_messageHandler.WithLockedStream(() =>
|
||||
{
|
||||
submitted = _apiClient.CancelOrder(order);
|
||||
submitted = ApiClient.CancelOrder(order);
|
||||
});
|
||||
|
||||
return submitted;
|
||||
@@ -277,7 +276,7 @@ namespace QuantConnect.Brokerages.Binance
|
||||
|
||||
var period = request.Resolution.ToTimeSpan();
|
||||
|
||||
foreach (var kline in _apiClient.GetHistory(request))
|
||||
foreach (var kline in ApiClient.GetHistory(request))
|
||||
{
|
||||
yield return new TradeBar()
|
||||
{
|
||||
@@ -388,7 +387,10 @@ namespace QuantConnect.Brokerages.Binance
|
||||
{
|
||||
_keepAliveTimer.DisposeSafely();
|
||||
_reconnectTimer.DisposeSafely();
|
||||
_apiClient.DisposeSafely();
|
||||
if (_apiClientLazy?.IsValueCreated == true)
|
||||
{
|
||||
ApiClient.DisposeSafely();
|
||||
}
|
||||
_webSocketRateLimiter.DisposeSafely();
|
||||
}
|
||||
|
||||
@@ -411,14 +413,14 @@ namespace QuantConnect.Brokerages.Binance
|
||||
/// <param name="algorithm">the algorithm instance is required to retrieve account type</param>
|
||||
/// <param name="aggregator">the aggregator for consolidating ticks</param>
|
||||
/// <param name="job">The live job packet</param>
|
||||
private void Initialize(string wssUrl, string restApiUrl,string apiKey, string apiSecret,
|
||||
private void Initialize(string wssUrl, string restApiUrl, string apiKey, string apiSecret,
|
||||
IAlgorithm algorithm, IDataAggregator aggregator, LiveNodePacket job)
|
||||
{
|
||||
if (IsInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
base.Initialize(wssUrl, new WebSocketClientWrapper(), null, apiKey, apiSecret);
|
||||
base.Initialize(wssUrl, new WebSocketClientWrapper(), null, apiKey, apiSecret);
|
||||
_job = job;
|
||||
_algorithm = algorithm;
|
||||
_aggregator = aggregator;
|
||||
@@ -441,15 +443,32 @@ namespace QuantConnect.Brokerages.Binance
|
||||
|
||||
SubscriptionManager = subscriptionManager;
|
||||
|
||||
_apiClient = new BinanceRestApiClient(_symbolMapper,
|
||||
algorithm?.Portfolio,
|
||||
apiKey,
|
||||
apiSecret,
|
||||
restApiUrl);
|
||||
// can be null, if BinanceBrokerage is used as DataQueueHandler only
|
||||
if (_algorithm != null)
|
||||
{
|
||||
// Binance rest api endpoint is different for sport and margin trading
|
||||
// we need to delay initialization of rest api client until Algorithm is initialized
|
||||
// and user brokerage choise is actually applied
|
||||
_apiClientLazy = new Lazy<BinanceBaseRestApiClient>(() =>
|
||||
{
|
||||
BinanceBaseRestApiClient apiClient = _algorithm.BrokerageModel.AccountType == AccountType.Cash
|
||||
? new BinanceSpotRestApiClient(_symbolMapper, algorithm?.Portfolio, apiKey, apiSecret, restApiUrl)
|
||||
: new BinanceCrossMarginRestApiClient(_symbolMapper, algorithm?.Portfolio, apiKey, apiSecret,
|
||||
restApiUrl);
|
||||
|
||||
_apiClient.OrderSubmit += (s, e) => OnOrderSubmit(e);
|
||||
_apiClient.OrderStatusChanged += (s, e) => OnOrderEvent(e);
|
||||
_apiClient.Message += (s, e) => OnMessage(e);
|
||||
apiClient.OrderSubmit += (s, e) => OnOrderSubmit(e);
|
||||
apiClient.OrderStatusChanged += (s, e) => OnOrderEvent(e);
|
||||
apiClient.Message += (s, e) => OnMessage(e);
|
||||
|
||||
// once we know the api endpoint we can subscribe to user data stream
|
||||
apiClient.CreateListenKey();
|
||||
_keepAliveTimer.Elapsed += (s, e) => apiClient.SessionKeepAlive();
|
||||
|
||||
Connect(apiClient.SessionId);
|
||||
|
||||
return apiClient;
|
||||
});
|
||||
}
|
||||
|
||||
// User data streams will close after 60 minutes. It's recommended to send a ping about every 30 minutes.
|
||||
// Source: https://github.com/binance-exchange/binance-official-api-docs/blob/master/user-data-stream.md#pingkeep-alive-a-listenkey
|
||||
@@ -458,10 +477,16 @@ namespace QuantConnect.Brokerages.Binance
|
||||
// 30 minutes
|
||||
Interval = 30 * 60 * 1000
|
||||
};
|
||||
_keepAliveTimer.Elapsed += (s, e) => _apiClient.SessionKeepAlive();
|
||||
|
||||
WebSocket.Open += (s, e) => { _keepAliveTimer.Start(); };
|
||||
WebSocket.Closed += (s, e) => { _keepAliveTimer.Stop(); };
|
||||
WebSocket.Open += (s, e) =>
|
||||
{
|
||||
_keepAliveTimer.Start();
|
||||
};
|
||||
WebSocket.Closed += (s, e) =>
|
||||
{
|
||||
ApiClient.StopSession();
|
||||
_keepAliveTimer.Stop();
|
||||
};
|
||||
|
||||
// A single connection to stream.binance.com is only valid for 24 hours; expect to be disconnected at the 24 hour mark
|
||||
// Source: https://github.com/binance-exchange/binance-official-api-docs/blob/master/web-socket-streams.md#general-wss-information
|
||||
@@ -592,5 +617,17 @@ namespace QuantConnect.Brokerages.Binance
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force reconnect websocket
|
||||
/// </summary>
|
||||
private void Connect(string sessionId)
|
||||
{
|
||||
Log.Trace("BaseWebSocketsBrokerage.Connect(): Connecting...");
|
||||
|
||||
_reconnectTimer.Start();
|
||||
WebSocket.Initialize($"{_webSocketBaseUrl}/{sessionId}");
|
||||
ConnectSync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
55
Brokerages/Binance/BinanceCrossMarginRestApiClient.cs
Normal file
55
Brokerages/Binance/BinanceCrossMarginRestApiClient.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 Newtonsoft.Json;
|
||||
using QuantConnect.Brokerages.Binance.Messages;
|
||||
using QuantConnect.Securities;
|
||||
using Order = QuantConnect.Orders.Order;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance
|
||||
{
|
||||
/// <summary>
|
||||
/// Binance REST API implementation
|
||||
/// </summary>
|
||||
public class BinanceCrossMarginRestApiClient : BinanceBaseRestApiClient
|
||||
{
|
||||
private const string _apiPrefix = "/sapi/v1/margin";
|
||||
private const string _wsPrefix = "/sapi/v1";
|
||||
|
||||
public BinanceCrossMarginRestApiClient(
|
||||
SymbolPropertiesDatabaseSymbolMapper symbolMapper,
|
||||
ISecurityProvider securityProvider,
|
||||
string apiKey,
|
||||
string apiSecret,
|
||||
string restApiUrl
|
||||
)
|
||||
: base(symbolMapper, securityProvider, apiKey, apiSecret, restApiUrl, _apiPrefix, _wsPrefix)
|
||||
{
|
||||
}
|
||||
|
||||
protected override JsonConverter CreateAccountConverter()
|
||||
=> new MarginAccountConverter();
|
||||
|
||||
protected override IDictionary<string, object> CreateOrderBody(Order order)
|
||||
{
|
||||
var body = base.CreateOrderBody(order);
|
||||
body["isisolated"] = "FALSE";
|
||||
body["sideEffectType"] = "MARGIN_BUY";
|
||||
|
||||
return body;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
@@ -11,28 +11,33 @@
|
||||
* 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 Newtonsoft.Json;
|
||||
using QuantConnect.Brokerages.Binance.Messages;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Packets
|
||||
namespace QuantConnect.Brokerages.Binance
|
||||
{
|
||||
/// <summary>
|
||||
/// A debugging breakpoint
|
||||
/// Binance Spot REST API implementation
|
||||
/// </summary>
|
||||
public class Breakpoint
|
||||
public class BinanceSpotRestApiClient : BinanceBaseRestApiClient
|
||||
{
|
||||
/// <summary>
|
||||
/// The file name
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "fileName")]
|
||||
public string FileName { get; set; }
|
||||
private const string _apiPrefix = "/api/v3";
|
||||
|
||||
/// <summary>
|
||||
/// The line number
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "lineNumber")]
|
||||
public int LineNumber { get; set; }
|
||||
public BinanceSpotRestApiClient(
|
||||
SymbolPropertiesDatabaseSymbolMapper symbolMapper,
|
||||
ISecurityProvider securityProvider,
|
||||
string apiKey,
|
||||
string apiSecret,
|
||||
string restApiUrl
|
||||
)
|
||||
: base(symbolMapper, securityProvider, apiKey, apiSecret, restApiUrl, _apiPrefix, _apiPrefix)
|
||||
{
|
||||
}
|
||||
|
||||
protected override JsonConverter CreateAccountConverter()
|
||||
=> new SpotAccountConverter();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
@@ -24,15 +24,27 @@ namespace QuantConnect.Brokerages.Binance.Messages
|
||||
|
||||
public class AccountInformation
|
||||
{
|
||||
public Balance[] Balances { get; set; }
|
||||
public BalanceEntry[] Balances { get; set; }
|
||||
}
|
||||
|
||||
public class Balance
|
||||
{
|
||||
public string Asset { get; set; }
|
||||
public decimal Free { get; set; }
|
||||
public decimal Locked { get; set; }
|
||||
public decimal Amount => Free + Locked;
|
||||
}
|
||||
public class BalanceEntry
|
||||
{
|
||||
public string Asset { get; set; }
|
||||
public decimal Free { get; set; }
|
||||
public decimal Locked { get; set; }
|
||||
public virtual decimal Amount { get; }
|
||||
}
|
||||
|
||||
public class SpotBalance : BalanceEntry
|
||||
{
|
||||
public override decimal Amount => Free + Locked;
|
||||
}
|
||||
|
||||
public class MarginBalance : BalanceEntry
|
||||
{
|
||||
public decimal Borrowed { get; set; }
|
||||
public decimal NetAsset { get; set; }
|
||||
public override decimal Amount => NetAsset;
|
||||
}
|
||||
|
||||
public class PriceTicker
|
||||
|
||||
57
Brokerages/Binance/Messages/MarginAccountConverter.cs
Normal file
57
Brokerages/Binance/Messages/MarginAccountConverter.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 Newtonsoft.Json;
|
||||
using System;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Deserializes cross margin Account data
|
||||
/// https://binance-docs.github.io/apidocs/spot/en/#query-cross-margin-account-details-user_data
|
||||
/// </summary>
|
||||
public class MarginAccountConverter : JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType)
|
||||
=> typeof(AccountInformation).IsAssignableFrom(objectType);
|
||||
|
||||
/// <summary>Reads the JSON representation of the margin account data and asset balances.</summary>
|
||||
/// <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader" /> to read from.</param>
|
||||
/// <param name="objectType">Type of the object.</param>
|
||||
/// <param name="existingValue">The existing value of object being read.</param>
|
||||
/// <param name="serializer">The calling serializer.</param>
|
||||
/// <returns>The object value.</returns>
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
JObject token = JObject.Load(reader);
|
||||
var balances = token.GetValue("userAssets", StringComparison.OrdinalIgnoreCase).ToObject<MarginBalance[]>();
|
||||
if (balances == null)
|
||||
{
|
||||
throw new ArgumentException("userAssets parameter name is not specified.");
|
||||
}
|
||||
|
||||
return new AccountInformation()
|
||||
{
|
||||
Balances = balances
|
||||
};
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
58
Brokerages/Binance/Messages/SpotAccountConverter.cs
Normal file
58
Brokerages/Binance/Messages/SpotAccountConverter.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 Newtonsoft.Json;
|
||||
using System;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Deserializes Spot Account data
|
||||
/// https://binance-docs.github.io/apidocs/spot/en/#account-information-user_data
|
||||
/// </summary>
|
||||
public class SpotAccountConverter : JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType)
|
||||
=> typeof(AccountInformation).IsAssignableFrom(objectType);
|
||||
|
||||
/// <summary>Reads the JSON representation of the spot account data and asset balances.</summary>
|
||||
/// <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader" /> to read from.</param>
|
||||
/// <param name="objectType">Type of the object.</param>
|
||||
/// <param name="existingValue">The existing value of object being read.</param>
|
||||
/// <param name="serializer">The calling serializer.</param>
|
||||
/// <returns>The object value.</returns>
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
JObject token = JObject.Load(reader);
|
||||
var balances = token.GetValue("balances", StringComparison.OrdinalIgnoreCase).ToObject<SpotBalance[]>();
|
||||
if (balances == null)
|
||||
{
|
||||
throw new ArgumentException("userAssets parameter name is not specified.");
|
||||
}
|
||||
|
||||
return new AccountInformation()
|
||||
{
|
||||
Balances = balances
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,7 +148,7 @@ namespace QuantConnect.Brokerages
|
||||
/// <summary>
|
||||
/// Wraps IsAlive
|
||||
/// </summary>
|
||||
public bool IsOpen => _client != null && _client.State == WebSocketState.Open;
|
||||
public bool IsOpen => _client?.State == WebSocketState.Open;
|
||||
|
||||
/// <summary>
|
||||
/// Wraps message event
|
||||
|
||||
@@ -61,7 +61,6 @@ from QuantConnect.Data.Shortable import *
|
||||
from QuantConnect.Orders.Slippage import *
|
||||
from QuantConnect.Securities.Forex import *
|
||||
from QuantConnect.Data.Fundamental import *
|
||||
from QuantConnect.Algorithm.CSharp import *
|
||||
from QuantConnect.Securities.Option import *
|
||||
from QuantConnect.Securities.Equity import *
|
||||
from QuantConnect.Securities.Future import *
|
||||
|
||||
@@ -18,7 +18,6 @@ using QuantConnect.Orders;
|
||||
using QuantConnect.Orders.Fees;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Util;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static QuantConnect.StringExtensions;
|
||||
@@ -30,6 +29,8 @@ namespace QuantConnect.Brokerages
|
||||
/// </summary>
|
||||
public class BinanceBrokerageModel : DefaultBrokerageModel
|
||||
{
|
||||
private const decimal _defaultLeverage = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a map of the default markets to be used for each security type
|
||||
/// </summary>
|
||||
@@ -41,22 +42,20 @@ namespace QuantConnect.Brokerages
|
||||
/// <param name="accountType">The type of account to be modeled, defaults to <see cref="AccountType.Cash"/></param>
|
||||
public BinanceBrokerageModel(AccountType accountType = AccountType.Cash) : base(accountType)
|
||||
{
|
||||
if (accountType == AccountType.Margin)
|
||||
{
|
||||
throw new ArgumentException("The Binance brokerage does not currently support Margin trading.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a new buying power model for the security, returning the default model with the security's configured leverage.
|
||||
/// For cash accounts, leverage = 1 is used.
|
||||
/// Margin trading is not currently supported
|
||||
/// For standard account margin trading the leverage is 3x, leverage 5x only supported in the master account
|
||||
/// </summary>
|
||||
/// <param name="security">The security to get a buying power model for</param>
|
||||
/// <returns>The buying power model for this brokerage/security</returns>
|
||||
public override IBuyingPowerModel GetBuyingPowerModel(Security security)
|
||||
{
|
||||
return new CashBuyingPowerModel();
|
||||
return AccountType == AccountType.Cash
|
||||
? new CashBuyingPowerModel()
|
||||
: new SecurityMarginModel(GetLeverage(security));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -66,8 +65,12 @@ namespace QuantConnect.Brokerages
|
||||
/// <returns></returns>
|
||||
public override decimal GetLeverage(Security security)
|
||||
{
|
||||
// margin trading is not currently supported by Binance
|
||||
return 1m;
|
||||
if (AccountType == AccountType.Cash || security.IsInternalFeed() || security.Type == SecurityType.Base)
|
||||
{
|
||||
return 1m;
|
||||
}
|
||||
|
||||
return _defaultLeverage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace QuantConnect.Brokerages
|
||||
/// Transaction and submit/execution rules will use TradingTechnologies models
|
||||
/// </summary>
|
||||
TradingTechnologies,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Transaction and submit/execution rules will use Kraken models
|
||||
/// </summary>
|
||||
@@ -109,6 +109,11 @@ namespace QuantConnect.Brokerages
|
||||
/// <summary>
|
||||
/// Transaction and submit/execution rules will use ftx us models
|
||||
/// </summary>
|
||||
FTXUS
|
||||
FTXUS,
|
||||
|
||||
/// <summary>
|
||||
/// Transaction and submit/execution rules will use Exante models
|
||||
/// </summary>
|
||||
Exante,
|
||||
}
|
||||
}
|
||||
|
||||
132
Common/Brokerages/ExanteBrokerageModel.cs
Normal file
132
Common/Brokerages/ExanteBrokerageModel.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using QuantConnect.Benchmarks;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Orders.Fees;
|
||||
using QuantConnect.Securities;
|
||||
using static QuantConnect.StringExtensions;
|
||||
using static QuantConnect.Util.SecurityExtensions;
|
||||
|
||||
namespace QuantConnect.Brokerages
|
||||
{
|
||||
/// <summary>
|
||||
/// Exante Brokerage Model Implementation for Back Testing.
|
||||
/// </summary>
|
||||
public class ExanteBrokerageModel : DefaultBrokerageModel
|
||||
{
|
||||
private const decimal EquityLeverage = 1.2m;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for Exante brokerage model
|
||||
/// </summary>
|
||||
/// <param name="accountType">Cash or Margin</param>
|
||||
public ExanteBrokerageModel(AccountType accountType = AccountType.Cash)
|
||||
: base(accountType)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the benchmark for this model
|
||||
/// </summary>
|
||||
/// <param name="securities">SecurityService to create the security with if needed</param>
|
||||
/// <returns>The benchmark for this brokerage</returns>
|
||||
public override IBenchmark GetBenchmark(SecurityManager securities)
|
||||
{
|
||||
var symbol = Symbol.Create("SPY", SecurityType.Equity, Market.USA);
|
||||
return SecurityBenchmark.CreateInstance(securities, symbol);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the brokerage could accept this order. This takes into account
|
||||
/// order type, security type, and order size limits.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For example, a brokerage may have no connectivity at certain times, or an order rate/size limit
|
||||
/// </remarks>
|
||||
/// <param name="security">The security being ordered</param>
|
||||
/// <param name="order">The order to be processed</param>
|
||||
/// <param name="message">If this function returns false, a brokerage message detailing why the order may not be submitted</param>
|
||||
/// <returns>True if the brokerage could process the order, false otherwise</returns>
|
||||
public override bool CanSubmitOrder(Security security, Order order, out BrokerageMessageEvent message)
|
||||
{
|
||||
message = null;
|
||||
|
||||
if (order == null)
|
||||
{
|
||||
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
|
||||
Invariant($"Order is null.")
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (order.Price == 0m)
|
||||
{
|
||||
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
|
||||
Invariant($"Price is not set.")
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (security.Type != SecurityType.Forex &&
|
||||
security.Type != SecurityType.Equity &&
|
||||
security.Type != SecurityType.Index &&
|
||||
security.Type != SecurityType.Option &&
|
||||
security.Type != SecurityType.Future &&
|
||||
security.Type != SecurityType.Cfd &&
|
||||
security.Type != SecurityType.Crypto &&
|
||||
security.Type != SecurityType.Index)
|
||||
{
|
||||
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
|
||||
Invariant(
|
||||
$"The {nameof(ExanteBrokerageModel)} does not support {security.Type} security type.")
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a new fee model that represents this brokerage's fee structure
|
||||
/// </summary>
|
||||
/// <param name="security">The security to get a fee model for</param>
|
||||
/// <returns>The new fee model for this brokerage</returns>
|
||||
public override IFeeModel GetFeeModel(Security security) => new ExanteFeeModel();
|
||||
|
||||
/// <summary>
|
||||
/// Exante global leverage rule
|
||||
/// </summary>
|
||||
/// <param name="security">The security's whose leverage we seek</param>
|
||||
/// <returns>The leverage for the specified security</returns>
|
||||
public override decimal GetLeverage(Security security)
|
||||
{
|
||||
if (AccountType == AccountType.Cash || security.IsInternalFeed() || security.Type == SecurityType.Base)
|
||||
{
|
||||
return 1m;
|
||||
}
|
||||
|
||||
return security.Type switch
|
||||
{
|
||||
SecurityType.Forex => 1.05m,
|
||||
SecurityType.Equity => EquityLeverage,
|
||||
_ => 1.0m,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -227,6 +227,9 @@ namespace QuantConnect.Brokerages
|
||||
|
||||
case BrokerageName.Kraken:
|
||||
return new KrakenBrokerageModel(accountType);
|
||||
|
||||
case BrokerageName.Exante:
|
||||
return new ExanteBrokerageModel(accountType);
|
||||
|
||||
case BrokerageName.FTX:
|
||||
return new FTXBrokerageModel(accountType);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
using System;
|
||||
using QuantConnect.Securities;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Data
|
||||
{
|
||||
@@ -51,6 +52,11 @@ namespace QuantConnect.Data
|
||||
/// </summary>
|
||||
public SecurityExchangeHours ExchangeHours { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tradable days specified by this request, in the security's data time zone
|
||||
/// </summary>
|
||||
public abstract IEnumerable<DateTime> TradableDays { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the base data request
|
||||
/// </summary>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
using System;
|
||||
using NodaTime;
|
||||
using QuantConnect.Securities;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Data
|
||||
{
|
||||
@@ -94,6 +95,15 @@ namespace QuantConnect.Data
|
||||
/// </summary>
|
||||
public uint ContractDepthOffset { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tradable days specified by this request, in the security's data time zone
|
||||
/// </summary>
|
||||
public override IEnumerable<DateTime> TradableDays => Time.EachTradeableDayInTimeZone(ExchangeHours,
|
||||
StartTimeLocal,
|
||||
EndTimeLocal,
|
||||
DataTimeZone,
|
||||
IncludeExtendedMarketHours);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HistoryRequest"/> class from the specified parameters
|
||||
/// </summary>
|
||||
|
||||
@@ -340,7 +340,7 @@ namespace QuantConnect.Data
|
||||
var requestedOpenInterest = type == typeof(OpenInterest);
|
||||
if (type == typeof(Tick) || requestedOpenInterest)
|
||||
{
|
||||
var dataDictionaryCache = GenericDataDictionary.Get(type);
|
||||
var dataDictionaryCache = GenericDataDictionary.Get(type, isPythonData: false);
|
||||
dictionary = Activator.CreateInstance(dataDictionaryCache.GenericType);
|
||||
|
||||
foreach (var data in instance.Ticks)
|
||||
@@ -390,10 +390,23 @@ namespace QuantConnect.Data
|
||||
}
|
||||
else
|
||||
{
|
||||
var dataDictionaryCache = GenericDataDictionary.Get(type);
|
||||
var isPythonData = type.IsAssignableTo(typeof(PythonData));
|
||||
|
||||
var dataDictionaryCache = GenericDataDictionary.Get(type, isPythonData);
|
||||
dictionary = Activator.CreateInstance(dataDictionaryCache.GenericType);
|
||||
|
||||
foreach (var data in instance._data.Value.Values.Select(x => x.Custom).Where(o => o != null && o.GetType() == type))
|
||||
foreach (var data in instance._data.Value.Values.Select(x => x.Custom).Where(o =>
|
||||
{
|
||||
if (o == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (isPythonData && o is PythonData data)
|
||||
{
|
||||
return data.IsOfType(type);
|
||||
}
|
||||
return o.GetType() == type;
|
||||
}))
|
||||
{
|
||||
dataDictionaryCache.MethodInfo.Invoke(dictionary, new object[] { data.Symbol, data });
|
||||
}
|
||||
@@ -653,15 +666,22 @@ namespace QuantConnect.Data
|
||||
/// <summary>
|
||||
/// Provides a <see cref="GenericDataDictionary"/> instance for a given <see cref="Type"/>
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="type">The requested data type</param>
|
||||
/// <param name="isPythonData">True if data is of <see cref="PythonData"/> type</param>
|
||||
/// <returns>A new instance or retrieved from the cache</returns>
|
||||
public static GenericDataDictionary Get(Type type)
|
||||
public static GenericDataDictionary Get(Type type, bool isPythonData)
|
||||
{
|
||||
GenericDataDictionary dataDictionaryCache;
|
||||
if (!_genericCache.TryGetValue(type, out dataDictionaryCache))
|
||||
{
|
||||
var generic = typeof(DataDictionary<>).MakeGenericType(type);
|
||||
var method = generic.GetMethod("Add", new[] { typeof(Symbol), type });
|
||||
var dictionaryType = type;
|
||||
if (isPythonData)
|
||||
{
|
||||
// let's create a python data dictionary because the data itself will be a PythonData type in C#
|
||||
dictionaryType = typeof(PythonData);
|
||||
}
|
||||
var generic = typeof(DataDictionary<>).MakeGenericType(dictionaryType);
|
||||
var method = generic.GetMethod("Add", new[] { typeof(Symbol), dictionaryType });
|
||||
_genericCache[type] = dataDictionaryCache = new GenericDataDictionary(generic, method);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,18 +47,11 @@ namespace QuantConnect.Data.UniverseSelection
|
||||
/// <summary>
|
||||
/// Gets the tradable days specified by this request, in the security's data time zone
|
||||
/// </summary>
|
||||
public IEnumerable<DateTime> TradableDays
|
||||
{
|
||||
get
|
||||
{
|
||||
return Time.EachTradeableDayInTimeZone(Security.Exchange.Hours,
|
||||
StartTimeLocal,
|
||||
EndTimeLocal,
|
||||
Configuration.DataTimeZone,
|
||||
Configuration.ExtendedMarketHours
|
||||
);
|
||||
}
|
||||
}
|
||||
public override IEnumerable<DateTime> TradableDays => Time.EachTradeableDayInTimeZone(Security.Exchange.Hours,
|
||||
StartTimeLocal,
|
||||
EndTimeLocal,
|
||||
Configuration.DataTimeZone,
|
||||
Configuration.ExtendedMarketHours);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SubscriptionRequest"/> class
|
||||
@@ -105,4 +98,4 @@ namespace QuantConnect.Data.UniverseSelection
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,4 +348,4 @@ namespace QuantConnect
|
||||
return GetValues.ToPyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1634,6 +1634,45 @@ namespace QuantConnect
|
||||
return rounded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to determine if a specific market is open
|
||||
/// </summary>
|
||||
/// <param name="security">The target security</param>
|
||||
/// <param name="extendedMarketHours">True if should consider extended market hours</param>
|
||||
/// <returns>True if the market is open</returns>
|
||||
public static bool IsMarketOpen(this Security security, bool extendedMarketHours)
|
||||
{
|
||||
if (!security.Exchange.Hours.IsOpen(security.LocalTime, extendedMarketHours))
|
||||
{
|
||||
// if we're not open at the current time exactly, check the bar size, this handle large sized bars (hours/days)
|
||||
var currentBar = security.GetLastData();
|
||||
if (currentBar == null
|
||||
|| security.LocalTime.Date != currentBar.EndTime.Date
|
||||
|| !security.Exchange.IsOpenDuringBar(currentBar.Time, currentBar.EndTime, extendedMarketHours))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to determine if a specific market is open
|
||||
/// </summary>
|
||||
/// <param name="symbol">The target symbol</param>
|
||||
/// <param name="utcTime">The current UTC time</param>
|
||||
/// <param name="extendedMarketHours">True if should consider extended market hours</param>
|
||||
/// <returns>True if the market is open</returns>
|
||||
public static bool IsMarketOpen(this Symbol symbol, DateTime utcTime, bool extendedMarketHours)
|
||||
{
|
||||
var exchangeHours = MarketHoursDatabase.FromDataFolder()
|
||||
.GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType);
|
||||
|
||||
var time = utcTime.ConvertFromUtc(exchangeHours.TimeZone);
|
||||
|
||||
return exchangeHours.IsOpen(time, extendedMarketHours);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension method to round a datetime to the nearest unit timespan.
|
||||
/// </summary>
|
||||
@@ -2857,15 +2896,12 @@ namespace QuantConnect
|
||||
PythonActivator pythonType;
|
||||
if (!PythonActivators.TryGetValue(pyObject.Handle, out pythonType))
|
||||
{
|
||||
AssemblyName an;
|
||||
using (Py.GIL())
|
||||
{
|
||||
an = new AssemblyName(pyObject.Repr().Split('\'')[1]);
|
||||
}
|
||||
var assemblyName = pyObject.GetAssemblyName();
|
||||
var typeBuilder = AssemblyBuilder
|
||||
.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run)
|
||||
.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run)
|
||||
.DefineDynamicModule("MainModule")
|
||||
.DefineType(an.Name, TypeAttributes.Class, type);
|
||||
// creating the type as public is required to allow 'dynamic' to be able to bind at runtime
|
||||
.DefineType(assemblyName.Name, TypeAttributes.Class | TypeAttributes.Public, type);
|
||||
|
||||
pythonType = new PythonActivator(typeBuilder.CreateType(), pyObject);
|
||||
|
||||
@@ -2877,6 +2913,19 @@ namespace QuantConnect
|
||||
return pythonType.Type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to get the assembly name from a python type
|
||||
/// </summary>
|
||||
/// <param name="pyObject">Python object pointing to the python type. <see cref="PyObject.GetPythonType"/></param>
|
||||
/// <returns>The python type assembly name</returns>
|
||||
public static AssemblyName GetAssemblyName(this PyObject pyObject)
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
return new AssemblyName(pyObject.Repr().Split('\'')[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs on-line batching of the specified enumerator, emitting chunks of the requested batch size
|
||||
/// </summary>
|
||||
|
||||
124
Common/Orders/Fees/ExanteFeeModel.cs
Normal file
124
Common/Orders/Fees/ExanteFeeModel.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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 QuantConnect.Securities;
|
||||
using static QuantConnect.StringExtensions;
|
||||
|
||||
namespace QuantConnect.Orders.Fees
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides an implementation of <see cref="FeeModel"/> that models Exante order fees.
|
||||
/// According to:
|
||||
/// <list type="bullet">
|
||||
/// <item>https://support.exante.eu/hc/en-us/articles/115005873143-Fees-overview-exchange-imposed-fees?source=search</item>
|
||||
/// <item>https://exante.eu/markets/</item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public class ExanteFeeModel : FeeModel
|
||||
{
|
||||
public const decimal MarketUsaRate = 0.02m;
|
||||
public const decimal DefaultRate = 0.02m;
|
||||
|
||||
private readonly decimal _forexCommissionRate;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance
|
||||
/// </summary>
|
||||
/// <param name="forexCommissionRate">Commission rate for FX operations</param>
|
||||
public ExanteFeeModel(decimal forexCommissionRate = 0.25m)
|
||||
{
|
||||
_forexCommissionRate = forexCommissionRate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the order fee associated with the specified order.
|
||||
/// </summary>
|
||||
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object
|
||||
/// containing the security and order</param>
|
||||
/// <returns>The cost of the order in a <see cref="CashAmount"/> instance</returns>
|
||||
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
|
||||
{
|
||||
var order = parameters.Order;
|
||||
var security = parameters.Security;
|
||||
|
||||
decimal feeResult;
|
||||
string feeCurrency;
|
||||
switch (security.Type)
|
||||
{
|
||||
case SecurityType.Forex:
|
||||
var totalOrderValue = order.GetValue(security);
|
||||
feeResult = Math.Abs(_forexCommissionRate * totalOrderValue);
|
||||
feeCurrency = Currencies.USD;
|
||||
break;
|
||||
|
||||
case SecurityType.Equity:
|
||||
var equityFee = ComputeEquityFee(order);
|
||||
feeResult = equityFee.Amount;
|
||||
feeCurrency = equityFee.Currency;
|
||||
break;
|
||||
|
||||
case SecurityType.Option:
|
||||
case SecurityType.IndexOption:
|
||||
var optionsFee = ComputeOptionFee(order);
|
||||
feeResult = optionsFee.Amount;
|
||||
feeCurrency = optionsFee.Currency;
|
||||
break;
|
||||
|
||||
case SecurityType.Future:
|
||||
case SecurityType.FutureOption:
|
||||
feeResult = 1.5m;
|
||||
feeCurrency = Currencies.USD;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException(Invariant($"Unsupported security type: {security.Type}"));
|
||||
}
|
||||
|
||||
return new OrderFee(new CashAmount(feeResult, feeCurrency));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes fee for equity order
|
||||
/// </summary>
|
||||
/// <param name="order">LEAN order</param>
|
||||
private static CashAmount ComputeEquityFee(Order order)
|
||||
{
|
||||
switch (order.Symbol.ID.Market)
|
||||
{
|
||||
case Market.USA:
|
||||
return new CashAmount(order.AbsoluteQuantity * MarketUsaRate, Currencies.USD);
|
||||
|
||||
default:
|
||||
return new CashAmount(order.AbsoluteQuantity * order.Price * DefaultRate, Currencies.USD);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes fee for option order
|
||||
/// </summary>
|
||||
/// <param name="order">LEAN order</param>
|
||||
private static CashAmount ComputeOptionFee(Order order)
|
||||
{
|
||||
return order.Symbol.ID.Market switch
|
||||
{
|
||||
Market.USA => new CashAmount(order.AbsoluteQuantity * 1.5m, Currencies.USD),
|
||||
_ =>
|
||||
// ToDo: clarify the value for different exchanges
|
||||
throw new ArgumentException(Invariant($"Unsupported exchange: ${order.Symbol.ID.Market}"))
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,8 @@ using QuantConnect.Securities;
|
||||
namespace QuantConnect.Orders.Fees
|
||||
{
|
||||
/// <summary>
|
||||
/// An order fee where the fee quantity has already been subtracted from the filled quantity
|
||||
/// An order fee where the fee quantity has already been subtracted from the filled quantity so instead we subtracted
|
||||
/// from the quote currency when applied to the portfolio
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This type of order fee is returned by some crypto brokerages (e.g. Bitfinex and Binance)
|
||||
@@ -26,13 +27,20 @@ namespace QuantConnect.Orders.Fees
|
||||
/// </remarks>
|
||||
public class ModifiedFillQuantityOrderFee : OrderFee
|
||||
{
|
||||
private readonly string _quoteCurrency;
|
||||
private readonly decimal _contractMultiplier;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ModifiedFillQuantityOrderFee"/> class
|
||||
/// </summary>
|
||||
/// <param name="orderFee">The order fee</param>
|
||||
public ModifiedFillQuantityOrderFee(CashAmount orderFee)
|
||||
/// <param name="quoteCurrency">The associated security quote currency</param>
|
||||
/// <param name="contractMultiplier">The associated security contract multiplier</param>
|
||||
public ModifiedFillQuantityOrderFee(CashAmount orderFee, string quoteCurrency, decimal contractMultiplier)
|
||||
: base(orderFee)
|
||||
{
|
||||
_quoteCurrency = quoteCurrency;
|
||||
_contractMultiplier = contractMultiplier;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -42,7 +50,7 @@ namespace QuantConnect.Orders.Fees
|
||||
/// <param name="fill">The order fill event</param>
|
||||
public override void ApplyToPortfolio(SecurityPortfolioManager portfolio, OrderEvent fill)
|
||||
{
|
||||
// do not apply the fee twice
|
||||
portfolio.CashBook[_quoteCurrency].AddAmount(-Value.Amount * fill.FillPrice * _contractMultiplier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -790,17 +790,7 @@ namespace QuantConnect.Orders.Fills
|
||||
/// </summary>
|
||||
protected static bool IsExchangeOpen(Security asset, bool isExtendedMarketHours)
|
||||
{
|
||||
if (!asset.Exchange.DateTimeIsOpen(asset.LocalTime))
|
||||
{
|
||||
// if we're not open at the current time exactly, check the bar size, this handle large sized bars (hours/days)
|
||||
var currentBar = asset.GetLastData();
|
||||
if (asset.LocalTime.Date != currentBar.EndTime.Date
|
||||
|| !asset.Exchange.IsOpenDuringBar(currentBar.Time, currentBar.EndTime, isExtendedMarketHours))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return asset.IsMarketOpen(isExtendedMarketHours);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
@@ -68,22 +67,11 @@ namespace QuantConnect.Packets
|
||||
[JsonProperty(PropertyName = "iTradeableDates")]
|
||||
public int TradeableDates = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The initial breakpoints for debugging, if any
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "aBreakpoints")]
|
||||
public List<Breakpoint> Breakpoints = new List<Breakpoint>();
|
||||
|
||||
/// <summary>
|
||||
/// The initial Watchlist for debugging, if any
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "aWatchlist")]
|
||||
public List<string> Watchlist = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// True, if this is a debugging backtest
|
||||
/// </summary>
|
||||
public bool IsDebugging => Breakpoints.Any();
|
||||
[JsonProperty(PropertyName = "bDebugging")]
|
||||
public bool IsDebugging;
|
||||
|
||||
/// <summary>
|
||||
/// Optional initial cash amount if set
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace QuantConnect.Python
|
||||
/// </summary>
|
||||
public class PythonData : DynamicData
|
||||
{
|
||||
private readonly string _pythonTypeName;
|
||||
private readonly dynamic _pythonData;
|
||||
private readonly dynamic _defaultResolution;
|
||||
private readonly dynamic _supportedResolutions;
|
||||
@@ -54,6 +55,7 @@ namespace QuantConnect.Python
|
||||
_isSparseData = pythonData.GetPythonMethod("IsSparseData");
|
||||
_defaultResolution = pythonData.GetPythonMethod("DefaultResolution");
|
||||
_supportedResolutions = pythonData.GetPythonMethod("SupportedResolutions");
|
||||
_pythonTypeName = pythonData.GetPythonType().GetAssemblyName().Name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +88,11 @@ namespace QuantConnect.Python
|
||||
using (Py.GIL())
|
||||
{
|
||||
var data = _pythonData.Reader(config, line, date, isLiveMode);
|
||||
return (data as PyObject).GetAndDispose<BaseData>();
|
||||
var result = (data as PyObject).GetAndDispose<BaseData>();
|
||||
|
||||
(result as PythonData)?.SetProperty("__typename", _pythonTypeName);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,5 +180,19 @@ namespace QuantConnect.Python
|
||||
SetProperty(index, value is double ? value.ConvertInvariant<decimal>() : value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to determine if the current instance is of the provided type
|
||||
/// </summary>
|
||||
/// <param name="type">Target type to check against</param>
|
||||
/// <returns>True if this instance is of the provided type</returns>
|
||||
public bool IsOfType(Type type)
|
||||
{
|
||||
if (HasProperty("__typename"))
|
||||
{
|
||||
return (string)GetProperty("__typename") == type.FullName;
|
||||
}
|
||||
return GetType() == type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
@@ -229,6 +229,96 @@ namespace QuantConnect.Securities
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string JapaneseYenEmini = "J7";
|
||||
|
||||
/// <summary>
|
||||
/// Micro EUR/USD Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroEUR = "M6E";
|
||||
|
||||
/// <summary>
|
||||
/// Micro AUD/USD Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroAUD = "M6A";
|
||||
|
||||
/// <summary>
|
||||
/// Micro GBP/USD Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroGBP = "M6B";
|
||||
|
||||
/// <summary>
|
||||
/// Micro CAD/USD Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroCADUSD = "MCD";
|
||||
|
||||
/// <summary>
|
||||
/// Micro JPY/USD Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroJPY = "MJY";
|
||||
|
||||
/// <summary>
|
||||
/// Micro CHF/USD Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroCHF = "MSF";
|
||||
|
||||
/// <summary>
|
||||
/// Micro USD/JPY Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroUSDJPY = "M6J";
|
||||
|
||||
/// <summary>
|
||||
/// Micro INR/USD Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroINRUSD = "MIR";
|
||||
|
||||
/// <summary>
|
||||
/// Micro USD/CAD Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroCAD = "M6C";
|
||||
|
||||
/// <summary>
|
||||
/// Micro USD/CHF Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroUSDCHF = "M6S";
|
||||
|
||||
/// <summary>
|
||||
/// Micro USD/CNH Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroUSDCNH = "MNH";
|
||||
|
||||
/// <summary>
|
||||
/// Micro Ether Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroEther = "MET";
|
||||
|
||||
/// <summary>
|
||||
/// Micro Bitcoin Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroBTC = "MBT";
|
||||
|
||||
/// <summary>
|
||||
/// BTIC on Micro Ether Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string BTICMicroEther = "MRB";
|
||||
|
||||
/// <summary>
|
||||
/// BTIC on Micro Bitcoin Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string BTICMicroBTC = "MIB";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -660,6 +750,54 @@ namespace QuantConnect.Securities
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string LowSulfurGasoil = "G";
|
||||
|
||||
/// <summary>
|
||||
/// Micro WTI Crude Oil Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroCrudeOilWTI = "MCL";
|
||||
|
||||
/// <summary>
|
||||
/// Micro Singapore FOB Marine Fuel 0.5% (Platts) Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroSingaporeFOBMarineFuelZeroPointFivePercetPlatts = "S5O";
|
||||
|
||||
/// <summary>
|
||||
/// Micro Gasoil 0.1% Barges FOB ARA (Platts) Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroGasoilZeroPointOnePercentBargesFOBARAPlatts = "M1B";
|
||||
|
||||
/// <summary>
|
||||
/// Micro European FOB Rdam Marine Fuel 0.5% Barges (Platts) Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroEuropeanFOBRdamMarineFuelZeroPointFivePercentBargesPlatts = "R5O";
|
||||
|
||||
/// <summary>
|
||||
/// Micro European 3.5% Fuel Oil Barges FOB Rdam (Platts) Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroEuropeanThreePointFivePercentOilBargesFOBRdamPlatts = "MEF";
|
||||
|
||||
/// <summary>
|
||||
/// Micro Singapore Fuel Oil 380CST (Platts) Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroSingaporeFuelOil380CSTPlatts = "MAF";
|
||||
|
||||
/// <summary>
|
||||
/// Micro Coal (API 5) fob Newcastle (Argus/McCloskey) Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroCoalAPIFivefobNewcastleArgusMcCloskey = "M5F";
|
||||
|
||||
/// <summary>
|
||||
/// Micro European 3.5% Fuel Oil Cargoes FOB Med (Platts) Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroEuropeanThreePointFivePercentFuelOilCargoesFOBMedPlatts = "M35";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -712,6 +850,30 @@ namespace QuantConnect.Securities
|
||||
/// Ultra 10-Year U.S. Treasury Note Futures
|
||||
/// </summary>
|
||||
public const string UltraTenYearUSTreasuryNote = "TN";
|
||||
|
||||
/// <summary>
|
||||
/// Micro 10-Year Yield Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroY10TreasuryNote = "10Y";
|
||||
|
||||
/// <summary>
|
||||
/// Micro 30-Year Yield Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroY30TreasuryBond = "30Y";
|
||||
|
||||
/// <summary>
|
||||
/// Micro 2-Year Yield Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroY2TreasuryBond = "2YY";
|
||||
|
||||
/// <summary>
|
||||
/// Micro 5-Year Yield Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroY5TreasuryBond = "5YY";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -824,7 +986,32 @@ namespace QuantConnect.Securities
|
||||
/// <summary>
|
||||
/// Hang Seng Index
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string HangSeng = "HSI";
|
||||
|
||||
/// <summary>
|
||||
/// Micro E-mini S&P 500 Index Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroSP500EMini = "MES";
|
||||
|
||||
/// <summary>
|
||||
/// Micro E-mini Nasdaq-100 Index Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroNASDAQ100EMini = "MNQ";
|
||||
|
||||
/// <summary>
|
||||
/// Micro E-mini Russell 2000 Index Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroRussell2000EMini = "M2K";
|
||||
|
||||
/// <summary>
|
||||
/// Micro E-mini Dow Jones Industrial Average Index Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroDow30EMini = "MYM";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -915,6 +1102,30 @@ namespace QuantConnect.Securities
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string USMidwestDomesticHotRolledCoilSteelCRUIndex = "HRC";
|
||||
|
||||
/// <summary>
|
||||
/// Micro Gold Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroGold = "MGC";
|
||||
|
||||
/// <summary>
|
||||
/// Micro Silver Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroSilver = "SIL";
|
||||
|
||||
/// <summary>
|
||||
/// Micro Gold TAS Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroGoldTAS = "MGT";
|
||||
|
||||
/// <summary>
|
||||
/// Micro Palladium Futures
|
||||
/// </summary>
|
||||
/// <returns>The symbol</returns>
|
||||
public const string MicroPalladium = "PAM";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1001,4 +1212,4 @@ namespace QuantConnect.Securities
|
||||
public const string NonfatDryMilk = "GNF";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1073,12 +1073,10 @@ namespace QuantConnect.Securities.Future
|
||||
{
|
||||
// Monthly contracts listed for 6 consecutive months and 2 additional Dec contract months. If the 6 consecutive months includes Dec, list only 1 additional Dec contract month.
|
||||
// Trading terminates at 4:00 p.m. London time on the last Friday of the contract month. If that day is not a business day in both the U.K. and the US, trading terminates on the preceding day that is a business day for both the U.K. and the U.S..
|
||||
var lastFriday = (from day in Enumerable.Range(1, DateTime.DaysInMonth(time.Year, time.Month))
|
||||
where new DateTime(time.Year, time.Month, day).DayOfWeek == DayOfWeek.Friday
|
||||
select new DateTime(time.Year, time.Month, day)).Last();
|
||||
var lastFriday =FuturesExpiryUtilityFunctions.LastFriday(time);
|
||||
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.CME, Futures.Currencies.AUDNZD, SecurityType.Future)
|
||||
.GetEntry(Market.CME, Futures.Currencies.BTC, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
@@ -1940,8 +1938,8 @@ namespace QuantConnect.Securities.Future
|
||||
}
|
||||
else
|
||||
{
|
||||
var lastBuisnessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(twentyFifth,-1);
|
||||
return FuturesExpiryUtilityFunctions.AddBusinessDays(lastBuisnessDay,-3);
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(twentyFifth,-1);
|
||||
return FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay,-3);
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -2606,6 +2604,762 @@ namespace QuantConnect.Securities.Future
|
||||
return FuturesExpiryUtilityFunctions.DairyLastTradeDate(time);
|
||||
})
|
||||
},
|
||||
// Micro Gold Futures (MGC): https://www.cmegroup.com/markets/metals/precious/e-micro-gold.contractSpecs.html
|
||||
{Symbol.Create(Futures.Metals.MicroGold, SecurityType.Future, Market.COMEX), (time =>
|
||||
{
|
||||
// Monthly contracts
|
||||
// Trading terminates on the third last business day of the contract month.
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 3);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.COMEX, Futures.Metals.MicroGold, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(lastBusinessDay))
|
||||
{
|
||||
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
|
||||
}
|
||||
|
||||
return lastBusinessDay;
|
||||
})
|
||||
},
|
||||
// Micro Silver Futures (SIL): https://www.cmegroup.com/markets/metals/precious/1000-oz-silver.contractSpecs.html
|
||||
{Symbol.Create(Futures.Metals.MicroSilver, SecurityType.Future, Market.COMEX), (time =>
|
||||
{
|
||||
// Monthly contracts
|
||||
// Trading terminates on the third last business day of the contract month.
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 3);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.COMEX, Futures.Metals.MicroSilver, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(lastBusinessDay))
|
||||
{
|
||||
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
|
||||
}
|
||||
|
||||
return lastBusinessDay;
|
||||
})
|
||||
},
|
||||
// Micro Gold TAS Futures (MGT): https://www.cmegroup.com/markets/metals/precious/e-micro-gold.contractSpecs.html
|
||||
{Symbol.Create(Futures.Metals.MicroGoldTAS, SecurityType.Future, Market.COMEX), (time =>
|
||||
{
|
||||
// Monthly contracts
|
||||
// Trading terminates on the third last business day of the contract month.
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 3);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.COMEX, Futures.Metals.MicroGoldTAS, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(lastBusinessDay))
|
||||
{
|
||||
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
|
||||
}
|
||||
|
||||
return lastBusinessDay;
|
||||
})
|
||||
},
|
||||
// Micro Palladium Futures (PAM): https://www.cmegroup.com/markets/metals/precious/e-micro-palladium.contractSpecs.html
|
||||
{Symbol.Create(Futures.Metals.MicroPalladium, SecurityType.Future, Market.NYMEX), (time =>
|
||||
{
|
||||
// Monthly contracts
|
||||
// Trading terminates on the third last business day of the contract month.
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 3);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.NYMEX, Futures.Metals.MicroPalladium, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(lastBusinessDay))
|
||||
{
|
||||
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
|
||||
}
|
||||
|
||||
return lastBusinessDay;
|
||||
})
|
||||
},
|
||||
// Micro 10-Year Yield Futures (10Y): https://www.cmegroup.com/markets/interest-rates/us-treasury/micro-10-year-yield.contractSpecs.html
|
||||
{Symbol.Create(Futures.Financials.MicroY10TreasuryNote, SecurityType.Future, Market.CBOT), (time =>
|
||||
{
|
||||
// Monthly contracts
|
||||
// Trading terminates on the last business day of the contract month.
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.CBOT, Futures.Financials.MicroY10TreasuryNote, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(lastBusinessDay))
|
||||
{
|
||||
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
|
||||
}
|
||||
|
||||
return lastBusinessDay;
|
||||
})
|
||||
},
|
||||
// Micro 30-Year Yield Futures (30Y): https://www.cmegroup.com/markets/interest-rates/us-treasury/micro-30-year-yield_contract_specifications.html
|
||||
{Symbol.Create(Futures.Financials.MicroY30TreasuryBond, SecurityType.Future, Market.CBOT), (time =>
|
||||
{
|
||||
// Monthly contracts
|
||||
// Trading terminates on the last business day of the contract month.
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.CBOT, Futures.Financials.MicroY30TreasuryBond, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(lastBusinessDay))
|
||||
{
|
||||
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
|
||||
}
|
||||
|
||||
return lastBusinessDay;
|
||||
})
|
||||
},
|
||||
// Micro 2-Year Yield Futures (2YY): https://www.cmegroup.com/markets/interest-rates/us-treasury/micro-2-year-yield.contractSpecs.html
|
||||
{Symbol.Create(Futures.Financials.MicroY2TreasuryBond, SecurityType.Future, Market.CBOT), (time =>
|
||||
{
|
||||
// Monthly contracts
|
||||
// Trading terminates on the last business day of the contract month.
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.CBOT, Futures.Financials.MicroY2TreasuryBond, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(lastBusinessDay))
|
||||
{
|
||||
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
|
||||
}
|
||||
|
||||
return lastBusinessDay;
|
||||
})
|
||||
},
|
||||
// Micro 5-Year Yield Futures (5YY): https://www.cmegroup.com/markets/interest-rates/us-treasury/micro-5-year-yield.contractSpecs.html
|
||||
{Symbol.Create(Futures.Financials.MicroY5TreasuryBond, SecurityType.Future, Market.CBOT), (time =>
|
||||
{
|
||||
// Monthly contracts
|
||||
// Trading terminates on the last business day of the contract month.
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.CBOT, Futures.Financials.MicroY5TreasuryBond, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(lastBusinessDay))
|
||||
{
|
||||
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
|
||||
}
|
||||
|
||||
return lastBusinessDay;
|
||||
})
|
||||
},
|
||||
// Micro EUR/USD Futures (M6E): https://www.cmegroup.com/markets/fx/g10/e-micro-euro.contractSpecs.html
|
||||
{Symbol.Create(Futures.Currencies.MicroEUR, SecurityType.Future, Market.CME), (time =>
|
||||
{
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 2 consecutive quarters
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
time = time.AddMonths(1);
|
||||
}
|
||||
|
||||
// Trading terminates at 9:16 a.m. CT 2 business day prior to the 3rd Wednesday of the contract quqrter.
|
||||
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
|
||||
var secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday, -2);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.CME, Futures.Currencies.MicroEUR, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(secondBusinessDayPrecedingThirdWednesday))
|
||||
{
|
||||
secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(secondBusinessDayPrecedingThirdWednesday, -1);
|
||||
}
|
||||
|
||||
return secondBusinessDayPrecedingThirdWednesday.Add(new TimeSpan(14, 16, 0));
|
||||
})
|
||||
},
|
||||
// Micro AUD/USD Futures (M6A): https://www.cmegroup.com/markets/fx/g10/e-micro-australian-dollar.contractSpecs.html
|
||||
{Symbol.Create(Futures.Currencies.MicroAUD, SecurityType.Future, Market.CME), (time =>
|
||||
{
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 2 consecutive quarters
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
time = time.AddMonths(1);
|
||||
}
|
||||
|
||||
// On the second business day immediately preceding the third Wednesday of the contract month (usually Monday).
|
||||
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
|
||||
var secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday, -2);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.CME, Futures.Currencies.MicroAUD, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(secondBusinessDayPrecedingThirdWednesday))
|
||||
{
|
||||
secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(secondBusinessDayPrecedingThirdWednesday, -1);
|
||||
}
|
||||
|
||||
return secondBusinessDayPrecedingThirdWednesday;
|
||||
})
|
||||
},
|
||||
// Micro GBP/USD Futures (M6B): https://www.cmegroup.com/markets/fx/g10/e-micro-british-pound.contractSpecs.html
|
||||
{Symbol.Create(Futures.Currencies.MicroGBP, SecurityType.Future, Market.CME), (time =>
|
||||
{
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 2 consecutive quarters
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
time = time.AddMonths(1);
|
||||
}
|
||||
|
||||
// 9:16 a.m. Central Time (CT) on the second business day immediately preceding the third Wednesday of the contract month (usually Monday).
|
||||
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
|
||||
var secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday, -2);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.CME, Futures.Currencies.MicroGBP, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(secondBusinessDayPrecedingThirdWednesday))
|
||||
{
|
||||
secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(secondBusinessDayPrecedingThirdWednesday, -1);
|
||||
}
|
||||
|
||||
return secondBusinessDayPrecedingThirdWednesday.Add(new TimeSpan(14, 16, 0));
|
||||
})
|
||||
},
|
||||
// Micro CAD/USD Futures (MCD): https://www.cmegroup.com/markets/fx/g10/e-micro-canadian-dollar-us-dollar.contractSpecs.html
|
||||
{Symbol.Create(Futures.Currencies.MicroCADUSD, SecurityType.Future, Market.CME), (time =>
|
||||
{
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 2 consecutive quarters
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
time = time.AddMonths(1);
|
||||
}
|
||||
|
||||
// Trading terminates 1 business day prior to the 3rd Wednesday of the contract quarter.
|
||||
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
|
||||
var firstBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday, -1);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.CME, Futures.Currencies.MicroCADUSD, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(firstBusinessDayPrecedingThirdWednesday))
|
||||
{
|
||||
firstBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(firstBusinessDayPrecedingThirdWednesday, -1);
|
||||
}
|
||||
|
||||
return firstBusinessDayPrecedingThirdWednesday;
|
||||
})
|
||||
},
|
||||
// Micro JPY/USD Futures (MJY): https://www.cmegroup.com/markets/fx/g10/e-micro-japanese-yen-us-dollar.contractSpecs.html
|
||||
{Symbol.Create(Futures.Currencies.MicroJPY, SecurityType.Future, Market.CME), (time =>
|
||||
{
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 2 consecutive quarters
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
time = time.AddMonths(1);
|
||||
}
|
||||
|
||||
// 9:16 a.m. Central Time (CT) on the second business day immediately preceding the third Wednesday of the contract month (usually Monday).
|
||||
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
|
||||
var secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday, -2);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.CME, Futures.Currencies.MicroJPY, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(secondBusinessDayPrecedingThirdWednesday))
|
||||
{
|
||||
secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(secondBusinessDayPrecedingThirdWednesday, -1);
|
||||
}
|
||||
|
||||
return secondBusinessDayPrecedingThirdWednesday.Add(new TimeSpan(14, 16, 0));
|
||||
})
|
||||
},
|
||||
// Micro CHF/USD Futures (MSF): https://www.cmegroup.com/markets/fx/g10/e-micro-swiss-franc-us-dollar.contractSpecs.html
|
||||
{Symbol.Create(Futures.Currencies.MicroCHF, SecurityType.Future, Market.CME), (time =>
|
||||
{
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 2 consecutive quarters
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
time = time.AddMonths(1);
|
||||
}
|
||||
|
||||
// 9:16 a.m. Central Time (CT) on the second business day immediately preceding the third Wednesday of the contract month (usually Monday).
|
||||
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
|
||||
var secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday, -2);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.CME, Futures.Currencies.MicroCHF, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(secondBusinessDayPrecedingThirdWednesday))
|
||||
{
|
||||
secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(secondBusinessDayPrecedingThirdWednesday, -1);
|
||||
}
|
||||
|
||||
return secondBusinessDayPrecedingThirdWednesday.Add(new TimeSpan(14, 16, 0));
|
||||
})
|
||||
},
|
||||
// Micro USD/JPY Futures (M6J): https://www.cmegroup.com/markets/fx/g10/micro-usd-jpy.contractSpecs.html
|
||||
{Symbol.Create(Futures.Currencies.MicroUSDJPY, SecurityType.Future, Market.CME), (time =>
|
||||
{
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 2 consecutive quarters
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
time = time.AddMonths(1);
|
||||
}
|
||||
|
||||
// 9:16 a.m. Central Time (CT) on the second business day immediately preceding the third Wednesday of the contract month (usually Monday).
|
||||
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
|
||||
var secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday, -2);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.CME, Futures.Currencies.MicroUSDJPY, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(secondBusinessDayPrecedingThirdWednesday))
|
||||
{
|
||||
secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(secondBusinessDayPrecedingThirdWednesday, -1);
|
||||
}
|
||||
|
||||
return secondBusinessDayPrecedingThirdWednesday.Add(new TimeSpan(14, 16, 0));
|
||||
})
|
||||
},
|
||||
// Micro INR/USD Futures (MIR): https://www.cmegroup.com/markets/fx/g10/e-micro-indian-rupee.contractSpecs.html
|
||||
{Symbol.Create(Futures.Currencies.MicroINRUSD, SecurityType.Future, Market.CME), (time =>
|
||||
{
|
||||
// Monthly contracts listed for 12 consecutive months.
|
||||
|
||||
// Trading terminates at 12:00 noon Mumbai time two Indian business days immediately preceding the last Indian
|
||||
// business day of the contract month.
|
||||
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.CME, Futures.Currencies.MicroINRUSD, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(lastBusinessDay))
|
||||
{
|
||||
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
|
||||
}
|
||||
|
||||
var secondBusinessDayPrecedingLastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay,-2);
|
||||
return secondBusinessDayPrecedingLastBusinessDay.Add(new TimeSpan(6,30,0));
|
||||
})
|
||||
},
|
||||
// Micro USD/CAD Futures (M6C): https://www.cmegroup.com/markets/fx/g10/micro-usd-cad.contractSpecs.html
|
||||
{Symbol.Create(Futures.Currencies.MicroCAD, SecurityType.Future, Market.CME), (time =>
|
||||
{
|
||||
// Two months in the March quarterly cycle (Mar, Jun, Sep, Dec)
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
time = time.AddMonths(1);
|
||||
}
|
||||
|
||||
// Trading terminates at 9:16 a.m. CT, 1 business day prior to the third Wednesday of the contract month.
|
||||
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
|
||||
var firstBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday, -1);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.CME, Futures.Currencies.MicroCAD, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(firstBusinessDayPrecedingThirdWednesday))
|
||||
{
|
||||
firstBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(firstBusinessDayPrecedingThirdWednesday, -1);
|
||||
}
|
||||
|
||||
return firstBusinessDayPrecedingThirdWednesday.Add(new TimeSpan(14, 16, 0));
|
||||
})
|
||||
},
|
||||
// Micro USD/CHF Futures (M6S): https://www.cmegroup.com/markets/fx/g10/micro-usd-chf.contractSpecs.html
|
||||
{Symbol.Create(Futures.Currencies.MicroUSDCHF, SecurityType.Future, Market.CME), (time =>
|
||||
{
|
||||
// Two months in the March quarterly cycle (Mar, Jun, Sep, Dec)
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
time = time.AddMonths(1);
|
||||
}
|
||||
|
||||
// Trading terminates at 9:16 a.m. CT, 2 business days prior to the third Wednesday of the contract month.
|
||||
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
|
||||
var secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday, -2);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.CME, Futures.Currencies.MicroUSDCHF, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(secondBusinessDayPrecedingThirdWednesday))
|
||||
{
|
||||
secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(secondBusinessDayPrecedingThirdWednesday, -1);
|
||||
}
|
||||
|
||||
return secondBusinessDayPrecedingThirdWednesday.Add(new TimeSpan(14, 16, 0));
|
||||
})
|
||||
},
|
||||
// Micro USD/CNH Futures (MNH): https://www.cmegroup.com/markets/fx/g10/e-micro-cnh.contractSpecs.html
|
||||
{Symbol.Create(Futures.Currencies.MicroUSDCNH, SecurityType.Future, Market.CME), (time =>
|
||||
{
|
||||
// Monthly contracts listed for 12 consecutive months.
|
||||
|
||||
// Trading terminates at 11:00 a.m. Hong Kong time on the second Hong Kong business day prior
|
||||
// to the third Wednesday of the contract month.
|
||||
|
||||
var thirdWednesday = FuturesExpiryUtilityFunctions.ThirdWednesday(time);
|
||||
var secondBusinessDayPrecedingThirdWednesday = FuturesExpiryUtilityFunctions.AddBusinessDays(thirdWednesday,-2);
|
||||
return secondBusinessDayPrecedingThirdWednesday.Add(new TimeSpan(3,0,0));
|
||||
})
|
||||
},
|
||||
// Micro E-mini S&P 500 Index Futures (MES): https://www.cmegroup.com/markets/equities/sp/micro-e-mini-sandp-500.contractSpecs.html
|
||||
{Symbol.Create(Futures.Indices.MicroSP500EMini, SecurityType.Future, Market.CME), (time =>
|
||||
{
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 5 consecutive quarters
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
time = time.AddMonths(1);
|
||||
}
|
||||
|
||||
// Trading terminates at 9:30 a.m. ET on the 3rd Friday of the contract month.
|
||||
var thirdFriday = FuturesExpiryUtilityFunctions.ThirdFriday(time);
|
||||
return thirdFriday.Add(new TimeSpan(13,30,0));
|
||||
})
|
||||
},
|
||||
// Micro E-mini Nasdaq-100 Index Futures (MNQ): https://www.cmegroup.com/markets/equities/nasdaq/micro-e-mini-nasdaq-100.contractSpecs.html
|
||||
{Symbol.Create(Futures.Indices.MicroNASDAQ100EMini, SecurityType.Future, Market.CME), (time =>
|
||||
{
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 5 consecutive quarters
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
time = time.AddMonths(1);
|
||||
}
|
||||
|
||||
// Trading terminates at 9:30 a.m. ET on the 3rd Friday of the contract month.
|
||||
var thirdFriday = FuturesExpiryUtilityFunctions.ThirdFriday(time);
|
||||
return thirdFriday.Add(new TimeSpan(13,30,0));
|
||||
})
|
||||
},
|
||||
// Micro E-mini Russell 2000 Index Futures (M2K): https://www.cmegroup.com/markets/equities/russell/micro-e-mini-russell-2000.contractSpecs.html
|
||||
{Symbol.Create(Futures.Indices.MicroRussell2000EMini, SecurityType.Future, Market.CME), (time =>
|
||||
{
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 5 consecutive quarters
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
time = time.AddMonths(1);
|
||||
}
|
||||
|
||||
// Trading terminates at 9:30 a.m. ET on the 3rd Friday of the contract month.
|
||||
var thirdFriday = FuturesExpiryUtilityFunctions.ThirdFriday(time);
|
||||
return thirdFriday.Add(new TimeSpan(13,30,0));
|
||||
})
|
||||
},
|
||||
// Micro E-mini Dow Jones Industrial Average Index Futures (MYM): https://www.cmegroup.com/markets/equities/dow-jones/micro-e-mini-dow.contractSpecs.html
|
||||
{Symbol.Create(Futures.Indices.MicroDow30EMini, SecurityType.Future, Market.CBOT), (time =>
|
||||
{
|
||||
// Quarterly contracts (Mar, Jun, Sep, Dec) listed for 4 consecutive quarters
|
||||
while (!FutureExpirationCycles.HMUZ.Contains(time.Month))
|
||||
{
|
||||
time = time.AddMonths(1);
|
||||
}
|
||||
|
||||
// Trading can occur up to 9:30 a.m. Eastern Time (ET) on the 3rd Friday of the contract month
|
||||
var thirdFriday = FuturesExpiryUtilityFunctions.ThirdFriday(time);
|
||||
return thirdFriday.Add(new TimeSpan(13,30,0));
|
||||
})
|
||||
},
|
||||
// Micro WTI Crude Oil Futures (MCL): https://www.cmegroup.com/markets/energy/crude-oil/micro-wti-crude-oil.contractSpecs.html
|
||||
{Symbol.Create(Futures.Energies.MicroCrudeOilWTI, SecurityType.Future, Market.NYMEX), (time =>
|
||||
{
|
||||
// Monthly contracts listed for 12 consecutive months and additional Jun and Dec contract months
|
||||
|
||||
// Trading terminates 4 business days prior to the 25th calendar day of the month prior to the
|
||||
// contract month (1 business day prior to CL LTD)
|
||||
|
||||
var previousMonth = time.AddMonths(-1);
|
||||
var twentyFifthDay = new DateTime(previousMonth.Year, previousMonth.Month, 25);
|
||||
var twentyFifthDayLessFour = FuturesExpiryUtilityFunctions.AddBusinessDays(twentyFifthDay, -4);
|
||||
return twentyFifthDayLessFour;
|
||||
})
|
||||
},
|
||||
// Micro Singapore FOB Marine Fuel 0.5% (Platts) Futures (S50): https://www.cmegroup.com/markets/energy/refined-products/micro-singapore-fob-marine-fuel-05-platts.contractSpecs.html
|
||||
{Symbol.Create(Futures.Energies.MicroSingaporeFOBMarineFuelZeroPointFivePercetPlatts, SecurityType.Future, Market.NYMEX), (time =>
|
||||
{
|
||||
// Monthly contracts listed for the current year and next 3 calendar years
|
||||
// Add monthly contracts for a new calendar year following the termination of trading in the
|
||||
// December contract of the current year.
|
||||
|
||||
// Trading terminates on the last business day of the contract month.
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.NYMEX, Futures.Energies.MicroSingaporeFOBMarineFuelZeroPointFivePercetPlatts, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(lastBusinessDay))
|
||||
{
|
||||
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
|
||||
}
|
||||
|
||||
return lastBusinessDay;
|
||||
})
|
||||
},
|
||||
// Micro Gasoil 0.1% Barges FOB ARA (Platts) Futures (M1B): https://www.cmegroup.com/markets/energy/refined-products/micro-gasoil-01-barges-fob-rdam-platts.contractSpecs.html
|
||||
{Symbol.Create(Futures.Energies.MicroGasoilZeroPointOnePercentBargesFOBARAPlatts, SecurityType.Future, Market.NYMEX), (time =>
|
||||
{
|
||||
// Monthly contracts listed for 36 consecutive months
|
||||
|
||||
// Trading terminates on the last London business day of the contract month.
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.NYMEX, Futures.Energies.MicroGasoilZeroPointOnePercentBargesFOBARAPlatts, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(lastBusinessDay))
|
||||
{
|
||||
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
|
||||
}
|
||||
|
||||
return lastBusinessDay;
|
||||
})
|
||||
},
|
||||
// Micro European FOB Rdam Marine Fuel 0.5% Barges (Platts) Futures (R50): https://www.cmegroup.com/markets/energy/refined-products/micro-european-fob-rdam-marine-fuel-05-barges-platts.contractSpecs.html
|
||||
{Symbol.Create(Futures.Energies.MicroEuropeanFOBRdamMarineFuelZeroPointFivePercentBargesPlatts, SecurityType.Future, Market.NYMEX), (time =>
|
||||
{
|
||||
// Monthly contracts listed for the current year and next 3 calendar years.
|
||||
// Add monthly contracts for a new calendar year following the termination of trading
|
||||
// in the December contract of the current year.
|
||||
|
||||
// Trading terminates on the last London business day of the contract month.
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.NYMEX, Futures.Energies.MicroEuropeanFOBRdamMarineFuelZeroPointFivePercentBargesPlatts, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(lastBusinessDay))
|
||||
{
|
||||
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
|
||||
}
|
||||
|
||||
return lastBusinessDay;
|
||||
})
|
||||
},
|
||||
// Micro European 3.5% Fuel Oil Barges FOB Rdam (Platts) Futures (MEF): https://www.cmegroup.com/markets/energy/refined-products/micro-european-35-fuel-oil-barges-fob-rdam-platts.contractSpecs.html
|
||||
{Symbol.Create(Futures.Energies.MicroEuropeanThreePointFivePercentOilBargesFOBRdamPlatts, SecurityType.Future, Market.NYMEX), (time =>
|
||||
{
|
||||
// Monthly contracts listed for the current year and 5 calendar years.Monthly contracts for a new calendar
|
||||
// year will be added following the termination of trading in the December contract of the current year.
|
||||
|
||||
// Trading terminates on the last business day of the contract month.
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.NYMEX, Futures.Energies.MicroEuropeanThreePointFivePercentOilBargesFOBRdamPlatts, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(lastBusinessDay))
|
||||
{
|
||||
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
|
||||
}
|
||||
|
||||
return lastBusinessDay;
|
||||
})
|
||||
},
|
||||
// Micro Singapore Fuel Oil 380CST (Platts) Futures (MAF): https://www.cmegroup.com/markets/energy/refined-products/micro-singapore-fuel-oil-380cst-platts.contractSpecs.html
|
||||
{Symbol.Create(Futures.Energies.MicroSingaporeFuelOil380CSTPlatts, SecurityType.Future, Market.NYMEX), (time =>
|
||||
{
|
||||
// Monthly contracts listed for the current year and 5 calendar years.Monthly contracts for a new calendar
|
||||
// year will be added following the termination of trading in the December contract of the current year.
|
||||
|
||||
// Trading terminates on the last business day of the contract month.
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.NYMEX, Futures.Energies.MicroSingaporeFuelOil380CSTPlatts, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(lastBusinessDay))
|
||||
{
|
||||
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
|
||||
}
|
||||
|
||||
return lastBusinessDay;
|
||||
})
|
||||
},
|
||||
// Micro Coal (API 5) fob Newcastle (Argus/McCloskey) Futures (M5F): https://www.cmegroup.com/markets/energy/coal/micro-coal-api-5-fob-newcastle-argus-mccloskey.contractSpecs.html
|
||||
{Symbol.Create(Futures.Energies.MicroCoalAPIFivefobNewcastleArgusMcCloskey, SecurityType.Future, Market.NYMEX), (time =>
|
||||
{
|
||||
// Monthly contracts listed for the current year and the next calendar year. Monthly contracts
|
||||
// for a new calendar year will be added following the termination of trading in the December
|
||||
// contract of the current year.
|
||||
|
||||
// Trading terminates on the last Friday of the contract month. If such Friday is a UK holiday,
|
||||
// trading terminates on the UK business day immediately prior to the last Friday of the contract
|
||||
// month unless such day is not an Exchange business day, in which case trading terminates on the
|
||||
// Exchange business day immediately prior.
|
||||
|
||||
var lastFriday = FuturesExpiryUtilityFunctions.LastFriday(time);
|
||||
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.NYMEX, Futures.Energies.MicroCoalAPIFivefobNewcastleArgusMcCloskey, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(lastFriday))
|
||||
{
|
||||
lastFriday = FuturesExpiryUtilityFunctions.AddBusinessDays(lastFriday, -1);
|
||||
while (holidays.Contains(lastFriday))
|
||||
{
|
||||
lastFriday = FuturesExpiryUtilityFunctions.AddBusinessDays(lastFriday, -1);
|
||||
}
|
||||
}
|
||||
|
||||
return lastFriday;
|
||||
})
|
||||
},
|
||||
// Micro European 3.5% Fuel Oil Cargoes FOB Med (Platts) Futures (M35): https://www.cmegroup.com/markets/energy/refined-products/micro-european-35-fuel-oil-cargoes-fob-med-platts.contractSpecs.html
|
||||
{Symbol.Create(Futures.Energies.MicroEuropeanThreePointFivePercentFuelOilCargoesFOBMedPlatts, SecurityType.Future, Market.NYMEX), (time =>
|
||||
{
|
||||
// Monthly contracts listed for 36 consecutive months
|
||||
|
||||
// Trading terminates on the last business day of the contract month.
|
||||
var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1);
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.NYMEX, Futures.Energies.MicroEuropeanThreePointFivePercentFuelOilCargoesFOBMedPlatts, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(lastBusinessDay))
|
||||
{
|
||||
lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1);
|
||||
}
|
||||
|
||||
return lastBusinessDay;
|
||||
})
|
||||
},
|
||||
// Micro Ether Futures (MET): https://www.cmegroup.com/markets/cryptocurrencies/ether/micro-ether.contractSpecs.html
|
||||
{Symbol.Create(Futures.Currencies.MicroEther, SecurityType.Future, Market.CME), (time =>
|
||||
{
|
||||
// Monthly contracts listed for 6 consecutive months and 2 additional Dec contract months.
|
||||
|
||||
// Trading terminates at 4:00 p.m. London time on the last Friday of the contract month that
|
||||
// is either a London or U.S. business day. If the last Friday of the contract month day is
|
||||
// not a business day in both London and the U.S., trading terminates on the prior London or
|
||||
// U.S. business day.
|
||||
|
||||
// BTIC: Trading terminates at 4:00 p.m. London time on the last Thursday of the contract month
|
||||
// that is either a London or U.S. business day. If the last Thursday of the contract month day
|
||||
// is not a business day in both London and the U.S., trading terminates on the prior London or U.S.
|
||||
// business day.
|
||||
|
||||
var lastFriday = FuturesExpiryUtilityFunctions.LastFriday(time);
|
||||
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.CME, Futures.Currencies.MicroEther, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(lastFriday))
|
||||
{
|
||||
lastFriday = FuturesExpiryUtilityFunctions.AddBusinessDays(lastFriday, -1);
|
||||
}
|
||||
|
||||
return lastFriday.Add(new TimeSpan(15, 0, 0));
|
||||
})
|
||||
},
|
||||
// Micro Bitcoin Futures (MBT): https://www.cmegroup.com/markets/cryptocurrencies/bitcoin/micro-bitcoin.contractSpecs.html
|
||||
{Symbol.Create(Futures.Currencies.MicroBTC, SecurityType.Future, Market.CME), (time =>
|
||||
{
|
||||
// Monthly contracts listed for 6 consecutive months and 2 additional Dec contract months.
|
||||
// If the 6 consecutive months includes Dec, list only 1 additional Dec contract month.
|
||||
|
||||
// Trading terminates at 4:00 p.m. London time on the last Friday of the contract month.
|
||||
// If this is not both a London and U.S. business day, trading terminates on the prior
|
||||
// London and the U.S. business day.
|
||||
|
||||
// BTIC: Trading terminates at 4:00 p.m. London time on the last Thursday of the contract
|
||||
// month.If this is not both a London and U.S. business day, trading terminates on the prior
|
||||
// London and the U.S. business day.
|
||||
|
||||
var lastFriday = FuturesExpiryUtilityFunctions.LastFriday(time);
|
||||
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.CME, Futures.Currencies.MicroBTC, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(lastFriday))
|
||||
{
|
||||
lastFriday = FuturesExpiryUtilityFunctions.AddBusinessDays(lastFriday, -1);
|
||||
}
|
||||
|
||||
return lastFriday.Add(new TimeSpan(15, 0, 0));
|
||||
})
|
||||
},
|
||||
// BTIC on Micro Ether Futures (MRB): https://www.cmegroup.com/markets/cryptocurrencies/ether/micro-ether.contractSpecs.html
|
||||
{Symbol.Create(Futures.Currencies.BTICMicroEther, SecurityType.Future, Market.CME), (time =>
|
||||
{
|
||||
// Monthly contracts listed for 6 consecutive months and 2 additional Dec contract months.
|
||||
|
||||
// Trading terminates at 4:00 p.m. London time on the last Friday of the contract month.
|
||||
// If this is not both a London and U.S. business day, trading terminates on the prior
|
||||
// London and the U.S. business day.
|
||||
|
||||
// BTIC: Trading terminates at 4:00 p.m. London time on the last Thursday of the contract
|
||||
// month.If this is not both a London and U.S. business day, trading terminates on the prior
|
||||
// London and the U.S. business day.
|
||||
|
||||
var lastThursday = FuturesExpiryUtilityFunctions.LastThursday(time);
|
||||
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.CME, Futures.Currencies.BTICMicroEther, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(lastThursday))
|
||||
{
|
||||
lastThursday = FuturesExpiryUtilityFunctions.AddBusinessDays(lastThursday, -1);
|
||||
}
|
||||
|
||||
return lastThursday.Add(new TimeSpan(15, 0, 0));
|
||||
})
|
||||
},
|
||||
// BTIC on Micro Bitcoin Futures (MIB): https://www.cmegroup.com/markets/cryptocurrencies/bitcoin/micro-bitcoin.contractSpecs.html
|
||||
{Symbol.Create(Futures.Currencies.BTICMicroBTC, SecurityType.Future, Market.CME), (time =>
|
||||
{
|
||||
// Monthly contracts listed for 6 consecutive months and 2 additional Dec contract months.
|
||||
// If the 6 consecutive months includes Dec, list only 1 additional Dec contract month.
|
||||
|
||||
// Trading terminates at 4:00 p.m. London time on the last Friday of the contract month.
|
||||
// If this is not both a London and U.S. business day, trading terminates on the prior
|
||||
// London and the U.S. business day.
|
||||
|
||||
// BTIC: Trading terminates at 4:00 p.m. London time on the last Thursday of the contract
|
||||
// month.If this is not both a London and U.S. business day, trading terminates on the prior
|
||||
// London and the U.S. business day.
|
||||
|
||||
var lastThursday = FuturesExpiryUtilityFunctions.LastThursday(time);
|
||||
|
||||
var holidays = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(Market.CME, Futures.Currencies.BTICMicroBTC, SecurityType.Future)
|
||||
.ExchangeHours
|
||||
.Holidays;
|
||||
|
||||
while (holidays.Contains(lastThursday))
|
||||
{
|
||||
lastThursday = FuturesExpiryUtilityFunctions.AddBusinessDays(lastThursday, -1);
|
||||
}
|
||||
|
||||
return lastThursday.Add(new TimeSpan(15, 0, 0));
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
@@ -257,12 +257,19 @@ namespace QuantConnect.Securities.Future
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to retrieve the last weekday of any month
|
||||
/// Method to retrieve the last Thursday of any month
|
||||
/// </summary>
|
||||
/// <param name="time">Date from the given month</param>
|
||||
/// <returns>Last day of the we</returns>
|
||||
/// <returns>Last Thursday of the given month</returns>
|
||||
public static DateTime LastThursday(DateTime time) => LastWeekday(time, DayOfWeek.Thursday);
|
||||
|
||||
/// <summary>
|
||||
/// Method to retrieve the last Friday of any month
|
||||
/// </summary>
|
||||
/// <param name="time">Date from the given month</param>
|
||||
/// <returns>Last Friday of the given month</returns>
|
||||
public static DateTime LastFriday(DateTime time) => LastWeekday(time, DayOfWeek.Friday);
|
||||
|
||||
/// <summary>
|
||||
/// Method to check whether a given time is holiday or not
|
||||
/// </summary>
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace QuantConnect.Securities.Option.StrategyMatcher
|
||||
/// <summary>
|
||||
/// Gets a new <see cref="OptionPosition"/> with zero <see cref="Quantity"/>
|
||||
/// </summary>
|
||||
public static OptionPosition None(Symbol symbol)
|
||||
public static OptionPosition Empty(Symbol symbol)
|
||||
=> new OptionPosition(symbol, 0);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -247,14 +247,6 @@ namespace QuantConnect.Securities
|
||||
/// <param name="dataType">The data type</param>
|
||||
public void StoreData(IReadOnlyList<BaseData> data, Type dataType)
|
||||
{
|
||||
#if DEBUG // don't run this in release as we should never fail here, but it's also nice to have here as documentation of intent
|
||||
if (data.DistinctBy(d => d.GetType()).Skip(1).Any())
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"SecurityCache.StoreData data list must contain elements of the same type."
|
||||
);
|
||||
}
|
||||
#endif
|
||||
if (dataType == typeof(Tick))
|
||||
{
|
||||
var tick = data[data.Count - 1] as Tick;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
@@ -124,7 +124,8 @@ namespace QuantConnect.Storage
|
||||
{
|
||||
encoding = encoding ?? Encoding.UTF8;
|
||||
|
||||
return encoding.GetString(_store.ReadBytes(key));
|
||||
var data = _store.ReadBytes(key);
|
||||
return data != null ? encoding.GetString(data) : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -172,17 +173,35 @@ namespace QuantConnect.Storage
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the data from a local file path associated with the specified key
|
||||
/// </summary>
|
||||
/// <remarks>If the file does not exist it will throw an exception</remarks>
|
||||
/// <param name="key">The object key</param>
|
||||
/// <returns>True if the object was saved successfully</returns>
|
||||
public bool Save(string key)
|
||||
{
|
||||
// Check the file exists
|
||||
var filePath = GetFilePath(key);
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
throw new ArgumentException($"There is no file associated with key {key} in '{filePath}'");
|
||||
}
|
||||
var bytes = File.ReadAllBytes(filePath);
|
||||
|
||||
return _store.SaveBytes(key, bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the object data in text format for the specified key
|
||||
/// </summary>
|
||||
/// <param name="key">The object key</param>
|
||||
/// <param name="text">The string object to be saved</param>
|
||||
/// <param name="encoding">The string encoding used</param>
|
||||
/// <param name="encoding">The string encoding used, <see cref="Encoding.UTF8"/> by default</param>
|
||||
/// <returns>True if the object was saved successfully</returns>
|
||||
public bool Save(string key, string text, Encoding encoding = null)
|
||||
{
|
||||
encoding = encoding ?? Encoding.UTF8;
|
||||
|
||||
encoding ??= Encoding.UTF8;
|
||||
return _store.SaveBytes(key, encoding.GetBytes(text));
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using NodaTime;
|
||||
using QuantConnect.Logging;
|
||||
@@ -487,7 +486,7 @@ namespace QuantConnect
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is mainly used to bridge the gap between exchange time zone and data time zone for file written to disk. The returned
|
||||
/// enumerable of dates is gauranteed to be the same size or longer than those generated via <see cref="EachTradeableDay(ICollection{Security},DateTime,DateTime)"/>
|
||||
/// enumerable of dates is guaranteed to be the same size or longer than those generated via <see cref="EachTradeableDay(ICollection{Security},DateTime,DateTime)"/>
|
||||
/// </remarks>
|
||||
/// <param name="exchange">The exchange hours</param>
|
||||
/// <param name="from">The start time in the exchange time zone</param>
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SharpZipLib" Version="1.2.0" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Common\Properties\SharedAssemblyInfo.cs" Link="Properties\SharedAssemblyInfo.cs" />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -89,12 +89,17 @@ usa,[*],indexoption,,USD,100,0.05,1
|
||||
|
||||
# for backwards compatibility, order here is important for futures, since we could have the same symbol for more than 1 market in which case Lean will use the first
|
||||
cfe,VX,future,VIX futures ,USD,1000.0,0.05,1.0
|
||||
cbot,2YY,future,Micro 2-Year Yield Futures,USD,1000,0.001,1
|
||||
cbot,5YY,future,Micro 5-Year Yield Futures,USD,1000,0.001,1
|
||||
cbot,10Y,future,Micro 10-Year Yield Futures,USD,1000,0.001,1
|
||||
cbot,30Y,future,Micro 30-Year Yield Futures,USD,1000,0.001,1
|
||||
cbot,AW,future,Bloomberg Commodity Index Futures,USD,100.0,0.1,1.0
|
||||
cbot,BCF,future,Black Sea Corn Financially Settled (Platts) Futures,USD,50.0,0.25,1.0
|
||||
cbot,BWF,future,Black Sea Wheat Financially Settled (Platts) Futures,USD,50.0,0.25,1.0
|
||||
cbot,EH,future,Ethanol Futures,USD,29000.0,0.001,1.0
|
||||
cbot,F1U,future,5-Year USD MAC Swap Futures,USD,1000.0,0.0078125,1.0
|
||||
cbot,KE,future,KC HRW Wheat Futures,USD,5000.0,0.0025,1.0,,1,100
|
||||
cbot,MYM,future,Micro E-mini Dow Jones Industrial Average Index Futures,USD,0.5,1,1
|
||||
cbot,TN,future,Ultra 10-Year U.S. Treasury Note Futures,USD,1000.0,0.015625,1.0
|
||||
cbot,UB,future,Ultra U.S. Treasury Bond Futures,USD,1000.0,0.03125,1.0
|
||||
cbot,YM,future,E-mini Dow ($5) Futures,USD,5.0,1.0,1.0
|
||||
@@ -106,7 +111,7 @@ cbot,ZM,future,Soybean Meal Futures,USD,100.0,0.1,1.0
|
||||
cbot,ZN,future,10-Year T-Note Futures,USD,1000.0,0.015625,1.0
|
||||
cbot,ZO,future,Oats Futures,USD,5000.0,0.25,1.0,,1,100
|
||||
cbot,ZS,future,Soybean Futures,USD,5000.0,0.0025,1.0,,1,100
|
||||
cbot,ZT,future,2-Year T-Note Futures,USD,2000.0,0.00390625,1.0
|
||||
cbot,ZT,future,2-Year T-Note Futures,USD,200000,0.00390625,1.0
|
||||
cbot,ZW,future,Chicago SRW Wheat Futures,USD,5000.0,0.0025,1.0,,1,100
|
||||
cme,6A,future,Australian Dollar Futures,USD,100000.0,0.0001,1.0
|
||||
cme,6B,future,British Pound Futures,USD,62500.0,0.0001,1.0
|
||||
@@ -147,6 +152,24 @@ cme,IBV,future,USD-Denominated Ibovespa Index Futures,USD,1.0,5.0,1.0
|
||||
cme,J7,future,E-mini Japanese Yen Futures,USD,6250000.0,1e-06,1.0
|
||||
cme,LBS,future,Random Length Lumber Futures,USD,110.0,0.1,1.0
|
||||
cme,LE,future,Live Cattle Futures,USD,40000.0,0.025,1.0,,1,100
|
||||
cme,M2K,future,Micro E-mini Russell 2000 Index Futures,USD,5.0,0.1,1,,1
|
||||
cme,M6A,future,Micro Australian Dollar/U.S. Dollar (AUD/USD) Futures,USD,10000,0.0001,1,,1
|
||||
cme,M6B,future,Micro British Pound Sterling/U.S. Dollar (GBP/USD) Futures,USD,6250,0.0001,1,,1
|
||||
cme,M6C,future,Micro USD/CAD Futures,CAD,10000,0.0001,1
|
||||
cme,M6E,future,Micro Euro/U.S. Dollar (EUR/USD) Futures,USD,12500,0.0001,1,,1
|
||||
cme,M6J,future,Micro USD/JPY Futures,JPY,10000,0.01,1
|
||||
cme,M6S,future,Micro USD/CHF Futures,CHF,10000,0.0001,1
|
||||
cme,MBT,future,Micro Bitcoin Futures,USD,0.1,5,1,,1
|
||||
cme,MCD,future,Micro Canadian Dollar/U.S.Dollar(CAD/USD) Futures,USD,10000,0.0001,1,,1
|
||||
cme,MES,future,Micro E-mini Standard and Poor's 500 Stock Price Index Futures,USD,5,0.25,1,,1
|
||||
cme,MET,future,Micro Ether Futures,USD,0.1,0.5,1,,1
|
||||
cme,MIB,future,BTIC on Micro Bitcoin Futures,USD,0.1,5,1
|
||||
cme,MIR,future,Micro INR/USD Futures,USD,1000000,0.01,100,,1,100
|
||||
cme,MJY,future,Micro Japanese Yen/U.S. Dollar (JPY/USD) Futures,USD,1250000,1e-06,1,,1
|
||||
cme,MNH,future,Micro USD/CNH Futures,USD,10000,0.0001,1
|
||||
cme,MNQ,future,Micro E-mini Nasdaq-100 Index Futures,USD,2,0.25,1,,1
|
||||
cme,MRB,future,BTIC on Micro Ether Futures,USD,0.1,0.5,1
|
||||
cme,MSF,future,Micro Swiss Franc/U.S. Dollar (CHF/USD) Futures,USD,12500,0.0001,1,,1
|
||||
cme,NKD,future,Nikkei/USD Futures,USD,5.0,5.0,1.0
|
||||
cme,NQ,future,E-mini Nasdaq-100 Futures,USD,20.0,0.25,1.0
|
||||
cme,RTY,future,E-mini Russell 2000 Index Futures,USD,50.0,0.1,1.0
|
||||
@@ -154,7 +177,10 @@ comex,AUP,future,Aluminum MW U.S. Transaction Premium Platts (25MT) Futures,USD,
|
||||
comex,EDP,future,Aluminium European Premium Duty-Paid (Metal Bulletin) Futures,USD,25.0,0.01,1.0
|
||||
comex,GC,future,Gold Futures,USD,100.0,0.1,1.0
|
||||
comex,HG,future,Copper Futures,USD,25000.0,0.0005,1.0
|
||||
comex,MGC,future,Micro Gold Futures,USD,10,0.1,1,,1
|
||||
comex,MGT,future,Micro Gold TAS Futures,USD,10,0.1,1
|
||||
comex,SI,future,Silver Futures,USD,5000.0,0.005,1.0
|
||||
comex,SIL,future,Micro Silver Futures,USD,1000,0.005,1,,1
|
||||
ice,B,future,Brent Crude Futures,USD,1000.0,0.01,1.0
|
||||
ice,CC,future,Cocoa Futures,USD,10.0,1.0,1.0
|
||||
ice,CT,future,Cotton No. 2 Futures,USD,50000.0,0.0001,1.0
|
||||
@@ -230,10 +256,19 @@ nymex,HO,future,NY Harbor ULSD Futures,USD,42000.0,0.0001,1.0
|
||||
nymex,HP,future,Natural Gas (Henry Hub) Penultimate Financial Futures,USD,10000.0,0.001,1.0
|
||||
nymex,HRC,future,U.S. Midwest Domestic Hot-Rolled Coil Steel (CRU) Index Futures,USD,20.0,1.0,1.0
|
||||
nymex,HTT,future,WTI Houston (Argus) vs. WTI Trade Month Futures,USD,1000.0,0.01,1.0
|
||||
nymex,M1B,future,Micro Gasoil 0.1% Barges FOB ARA (Platts) Futures,USD,10,0.001,1,,1
|
||||
nymex,M35,future,Micro European 3.5% Fuel Oil Cargoes FOB Med (Platts) Futures,USD,10,0.001,1,,1
|
||||
nymex,M5F,future,Micro Coal (API 5) fob Newcastle (Argus/McCloskey) Futures,USD,10,0.01,1,,1
|
||||
nymex,MAF,future,Micro Singapore Fuel Oil 380CST (Platts) Futures ,USD,10,0.001,1,,1
|
||||
nymex,MCL,future,Micro WTI Crude Oil Futures,USD,100,0.01,1,,1
|
||||
nymex,MEF,future,Micro European 3.5% Fuel Oil Barges FOB Rdam (Platts) Futures,USD,10,0.001,1,,1
|
||||
nymex,NG,future,Henry Hub Natural Gas Futures,USD,10000.0,0.001,1.0
|
||||
nymex,PA,future,Palladium Futures,USD,100.0,0.05,1.0
|
||||
nymex,PA,future,Palladium Futures,USD,100.0,0.5,1.0
|
||||
nymex,PAM,future,Micro Palladium Futures,USD,10,0.5,1,,1
|
||||
nymex,PL,future,Platinum Futures,USD,50.0,0.1,1.0
|
||||
nymex,R5O,future,Micro European FOB Rdam Marine Fuel 0.5% Barges (Platts) Futures,USD,10,0.001,1,,1
|
||||
nymex,RB,future,RBOB Gasoline Futures,USD,42000.0,0.0001,1.0
|
||||
nymex,S5O,future,Micro Singapore FOB Marine Fuel 0.5% (Platts) Futures,USD,10,0.001,1,,1
|
||||
nymex,YO,future,No. 11 Sugar Futures,USD,112000.0,0.0001,1.0
|
||||
sgx,NK,future,SGX Nikkei 225 Index Futures,JPY,500,5,5.0
|
||||
sgx,TW,future,MSCI Taiwan Index Futures,USD,100,0.1,50.0
|
||||
|
||||
|
@@ -14,6 +14,10 @@ RUN pip install --no-cache-dir debugpy~=1.5.1 pydevd-pycharm~=201.8538.36
|
||||
# Install vsdbg for remote C# debugging in Visual Studio and Visual Studio Code
|
||||
RUN wget https://aka.ms/getvsdbgsh -O - 2>/dev/null | /bin/sh /dev/stdin -v 16.9.20122.2 -l /root/vsdbg
|
||||
|
||||
# Install NetCoreDbg
|
||||
RUN wget https://github.com/Samsung/netcoredbg/releases/download/2.0.0-880/netcoredbg-linux-amd64.tar.gz \
|
||||
&& tar xvzf netcoredbg-linux-amd64.tar.gz && rm netcoredbg-linux-amd64.tar.gz
|
||||
|
||||
COPY ./DataLibraries /Lean/Launcher/bin/Debug/
|
||||
COPY ./AlphaStreams/QuantConnect.AlphaStream/bin/Debug/ /Lean/Launcher/bin/Debug/
|
||||
COPY ./Lean/Launcher/bin/Debug/ /Lean/Launcher/bin/Debug/
|
||||
|
||||
@@ -21,8 +21,6 @@ RUN if [ "$(uname -m)" = "aarch64" ]; then \
|
||||
mv tini /usr/local/bin/tini && \
|
||||
chmod +x /usr/local/bin/tini
|
||||
|
||||
RUN conda install -y -c conda-forge notebook=6.0.3 && conda clean -y --all
|
||||
|
||||
# Install clr-loader for PythonNet
|
||||
RUN pip install --no-cache-dir clr-loader==0.1.6
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ RUN pip install --no-cache-dir \
|
||||
setuptools-git==1.2 \
|
||||
xarray==0.15.1 \
|
||||
plotly==4.7.1 \
|
||||
jupyterlab==2.1.2 \
|
||||
jupyterlab==3.2.6 \
|
||||
tensorflow==1.15.2 \
|
||||
docutils==0.14 \
|
||||
cvxopt==1.2.0 \
|
||||
|
||||
@@ -69,7 +69,7 @@ RUN conda install -y \
|
||||
pandas=0.25.3 \
|
||||
numpy=1.18.1 \
|
||||
wrapt=1.12.1 \
|
||||
jupyterlab=2.1.2 \
|
||||
jupyterlab=3.2.6 \
|
||||
matplotlib=3.2.1 \
|
||||
scipy=1.4.1 \
|
||||
&& conda clean -y --all
|
||||
|
||||
@@ -32,7 +32,6 @@ namespace QuantConnect.Lean.Engine.DataFeeds.Enumerators.Factories
|
||||
/// <remarks>Only used on backtesting by the <see cref="FileSystemDataFeed"/></remarks>
|
||||
public class SubscriptionDataReaderSubscriptionEnumeratorFactory : ISubscriptionEnumeratorFactory, IDisposable
|
||||
{
|
||||
private readonly bool _isLiveMode;
|
||||
private readonly IResultHandler _resultHandler;
|
||||
private readonly IFactorFileProvider _factorFileProvider;
|
||||
private readonly IDataCacheProvider _dataCacheProvider;
|
||||
@@ -40,7 +39,6 @@ namespace QuantConnect.Lean.Engine.DataFeeds.Enumerators.Factories
|
||||
private readonly int _numericalPrecisionLimitedWarningsMaxCount = 10;
|
||||
private readonly ConcurrentDictionary<Symbol, string> _startDateLimitedWarnings;
|
||||
private readonly int _startDateLimitedWarningsMaxCount = 10;
|
||||
private readonly Func<SubscriptionRequest, IEnumerable<DateTime>> _tradableDaysProvider;
|
||||
private readonly IMapFileProvider _mapFileProvider;
|
||||
private readonly bool _enablePriceScaling;
|
||||
|
||||
@@ -58,7 +56,6 @@ namespace QuantConnect.Lean.Engine.DataFeeds.Enumerators.Factories
|
||||
IMapFileProvider mapFileProvider,
|
||||
IFactorFileProvider factorFileProvider,
|
||||
IDataCacheProvider cacheProvider,
|
||||
Func<SubscriptionRequest, IEnumerable<DateTime>> tradableDaysProvider = null,
|
||||
bool enablePriceScaling = true
|
||||
)
|
||||
{
|
||||
@@ -68,8 +65,6 @@ namespace QuantConnect.Lean.Engine.DataFeeds.Enumerators.Factories
|
||||
_dataCacheProvider = cacheProvider;
|
||||
_numericalPrecisionLimitedWarnings = new ConcurrentDictionary<Symbol, string>();
|
||||
_startDateLimitedWarnings = new ConcurrentDictionary<Symbol, string>();
|
||||
_isLiveMode = false;
|
||||
_tradableDaysProvider = tradableDaysProvider ?? (request => request.TradableDays);
|
||||
_enablePriceScaling = enablePriceScaling;
|
||||
}
|
||||
|
||||
@@ -82,12 +77,9 @@ namespace QuantConnect.Lean.Engine.DataFeeds.Enumerators.Factories
|
||||
public IEnumerator<BaseData> CreateEnumerator(SubscriptionRequest request, IDataProvider dataProvider)
|
||||
{
|
||||
var dataReader = new SubscriptionDataReader(request.Configuration,
|
||||
request.StartTimeLocal,
|
||||
request.EndTimeLocal,
|
||||
request,
|
||||
_mapFileProvider,
|
||||
_factorFileProvider,
|
||||
_tradableDaysProvider(request),
|
||||
_isLiveMode,
|
||||
_dataCacheProvider,
|
||||
dataProvider
|
||||
);
|
||||
|
||||
@@ -306,11 +306,22 @@ namespace QuantConnect.Lean.Engine.DataFeeds.Enumerators
|
||||
// potentialBarEndTime should be calculated in the same way as bar.EndTime, i.e. Time + resolution
|
||||
var potentialBarEndTime = RoundDown(item.ReferenceDateTime, item.Interval).ConvertToUtc(Exchange.TimeZone) + item.Interval;
|
||||
|
||||
var period = _dataResolution;
|
||||
if (next.Time == next.EndTime)
|
||||
{
|
||||
// we merge corporate event data points (mapping, delisting, splits, dividend) which do not have
|
||||
// a period or resolution
|
||||
period = TimeSpan.Zero;
|
||||
}
|
||||
|
||||
// to avoid duality it's necessary to compare potentialBarEndTime with
|
||||
// next.EndTime calculated as Time + resolution,
|
||||
// and both should be based on the same TZ (for example UTC)
|
||||
var nextEndTimeUTC = next.Time.ConvertToUtc(Exchange.TimeZone) + _dataResolution;
|
||||
if (potentialBarEndTime < nextEndTimeUTC)
|
||||
var nextEndTimeUTC = next.Time.ConvertToUtc(Exchange.TimeZone) + period;
|
||||
if (potentialBarEndTime < nextEndTimeUTC
|
||||
// let's fill forward based on previous (which isn't auxiliary) if next is auxiliary and they share the end time
|
||||
// we do allow emitting both an auxiliary data point and a Filled Forwared data for the same end time
|
||||
|| next.DataType == MarketDataType.Auxiliary && potentialBarEndTime == nextEndTimeUTC)
|
||||
{
|
||||
// to check open hours we need to convert potential
|
||||
// bar EndTime into exchange time zone
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections;
|
||||
using QuantConnect.Data;
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
using QuantConnect.Logging;
|
||||
using QuantConnect.Interfaces;
|
||||
@@ -67,9 +67,6 @@ namespace QuantConnect.Lean.Engine.DataFeeds
|
||||
|
||||
private bool _pastDelistedDate;
|
||||
|
||||
// true if we're in live mode, false otherwise
|
||||
private readonly bool _isLiveMode;
|
||||
|
||||
private BaseData _previous;
|
||||
private decimal? _lastRawPrice;
|
||||
private readonly IEnumerator<DateTime> _tradeableDates;
|
||||
@@ -129,21 +126,15 @@ namespace QuantConnect.Lean.Engine.DataFeeds
|
||||
/// Subscription data reader takes a subscription request, loads the type, accepts the data source and enumerate on the results.
|
||||
/// </summary>
|
||||
/// <param name="config">Subscription configuration object</param>
|
||||
/// <param name="periodStart">Start date for the data request/backtest</param>
|
||||
/// <param name="periodFinish">Finish date for the data request/backtest</param>
|
||||
/// <param name="mapFileResolver">Used for resolving the correct map files</param>
|
||||
/// <param name="dataRequest">The data request</param>
|
||||
/// <param name="mapFileProvider">Used for resolving the correct map files</param>
|
||||
/// <param name="factorFileProvider">Used for getting factor files</param>
|
||||
/// <param name="dataCacheProvider">Used for caching files</param>
|
||||
/// <param name="tradeableDates">Defines the dates for which we'll request data, in order, in the security's data time zone</param>
|
||||
/// <param name="isLiveMode">True if we're in live mode, false otherwise</param>
|
||||
/// <param name="dataProvider">The data provider to use</param>
|
||||
public SubscriptionDataReader(SubscriptionDataConfig config,
|
||||
DateTime periodStart,
|
||||
DateTime periodFinish,
|
||||
BaseDataRequest dataRequest,
|
||||
IMapFileProvider mapFileProvider,
|
||||
IFactorFileProvider factorFileProvider,
|
||||
IEnumerable<DateTime> tradeableDates,
|
||||
bool isLiveMode,
|
||||
IDataCacheProvider dataCacheProvider,
|
||||
IDataProvider dataProvider)
|
||||
{
|
||||
@@ -151,15 +142,14 @@ namespace QuantConnect.Lean.Engine.DataFeeds
|
||||
_config = config;
|
||||
|
||||
//Save Start and End Dates:
|
||||
_periodStart = periodStart;
|
||||
_periodFinish = periodFinish;
|
||||
_periodStart = dataRequest.StartTimeLocal;
|
||||
_periodFinish = dataRequest.EndTimeLocal;
|
||||
_mapFileProvider = mapFileProvider;
|
||||
_factorFileProvider = factorFileProvider;
|
||||
_dataCacheProvider = dataCacheProvider;
|
||||
|
||||
//Save access to securities
|
||||
_isLiveMode = isLiveMode;
|
||||
_tradeableDates = tradeableDates.GetEnumerator();
|
||||
_tradeableDates = dataRequest.TradableDays.GetEnumerator();
|
||||
_dataProvider = dataProvider;
|
||||
}
|
||||
|
||||
@@ -217,7 +207,7 @@ namespace QuantConnect.Lean.Engine.DataFeeds
|
||||
_factorFile = factorFile;
|
||||
|
||||
// if factor file has minimum date, update start period if before minimum date
|
||||
if (!_isLiveMode && _factorFile != null && _factorFile.FactorFileMinimumDate.HasValue)
|
||||
if (_factorFile != null && _factorFile.FactorFileMinimumDate.HasValue)
|
||||
{
|
||||
if (_periodStart < _factorFile.FactorFileMinimumDate.Value)
|
||||
{
|
||||
@@ -254,7 +244,6 @@ namespace QuantConnect.Lean.Engine.DataFeeds
|
||||
|
||||
// adding a day so we stop at EOD
|
||||
_delistingDate = _delistingDate.AddDays(1);
|
||||
|
||||
UpdateDataEnumerator(true);
|
||||
|
||||
_initialized = true;
|
||||
@@ -289,14 +278,6 @@ namespace QuantConnect.Lean.Engine.DataFeeds
|
||||
|
||||
if (_subscriptionFactoryEnumerator == null)
|
||||
{
|
||||
// in live mode the trade able dates will eventually advance to the next
|
||||
if (_isLiveMode)
|
||||
{
|
||||
// HACK attack -- we don't want to block in live mode
|
||||
Current = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
_endOfStream = true;
|
||||
return false;
|
||||
}
|
||||
@@ -343,6 +324,7 @@ namespace QuantConnect.Lean.Engine.DataFeeds
|
||||
// if we move past our current 'date' then we need to do daily things, such
|
||||
// as updating factors and symbol mapping
|
||||
var shouldSkip = false;
|
||||
|
||||
while (instance.Time.ConvertTo(_config.ExchangeTimeZone, _config.DataTimeZone).Date > _tradeableDates.Current)
|
||||
{
|
||||
var currentTradeableDate = _tradeableDates.Current;
|
||||
@@ -416,7 +398,7 @@ namespace QuantConnect.Lean.Engine.DataFeeds
|
||||
// called on date changes, never return null for live mode, we'll always
|
||||
// just keep trying to refresh the subscription
|
||||
DateTime date;
|
||||
if (!TryGetNextDate(out date) && !_isLiveMode)
|
||||
if (!TryGetNextDate(out date))
|
||||
{
|
||||
_subscriptionFactoryEnumerator = null;
|
||||
// if we run out of dates then we're finished with this subscription
|
||||
@@ -424,12 +406,11 @@ namespace QuantConnect.Lean.Engine.DataFeeds
|
||||
}
|
||||
|
||||
// fetch the new source, using the data time zone for the date
|
||||
var newSource = _dataFactory.GetSource(_config, date, _isLiveMode);
|
||||
var newSource = _dataFactory.GetSource(_config, date, false);
|
||||
|
||||
// check if we should create a new subscription factory
|
||||
var sourceChanged = _source != newSource && newSource.Source != "";
|
||||
var liveRemoteFile = _isLiveMode && (_source == null || _source.TransportMedium == SubscriptionTransportMedium.RemoteFile);
|
||||
if (sourceChanged || liveRemoteFile)
|
||||
if (sourceChanged)
|
||||
{
|
||||
// dispose of the current enumerator before creating a new one
|
||||
Dispose();
|
||||
@@ -457,7 +438,7 @@ namespace QuantConnect.Lean.Engine.DataFeeds
|
||||
|
||||
private ISubscriptionDataSourceReader CreateSubscriptionFactory(SubscriptionDataSource source, BaseData baseDataInstance, IDataProvider dataProvider)
|
||||
{
|
||||
var factory = SubscriptionDataSourceReader.ForSource(source, _dataCacheProvider, _config, _tradeableDates.Current, _isLiveMode, baseDataInstance, dataProvider);
|
||||
var factory = SubscriptionDataSourceReader.ForSource(source, _dataCacheProvider, _config, _tradeableDates.Current, false, baseDataInstance, dataProvider);
|
||||
AttachEventHandlers(factory, source);
|
||||
return factory;
|
||||
}
|
||||
@@ -521,13 +502,6 @@ namespace QuantConnect.Lean.Engine.DataFeeds
|
||||
/// <returns>True if we got a new date from the enumerator, false if it's exhausted, or in live mode if we're already at today</returns>
|
||||
private bool TryGetNextDate(out DateTime date)
|
||||
{
|
||||
if (_isLiveMode && _tradeableDates.Current >= DateTime.Today)
|
||||
{
|
||||
// special behavior for live mode, don't advance past today
|
||||
date = _tradeableDates.Current;
|
||||
return false;
|
||||
}
|
||||
|
||||
while (_tradeableDates.MoveNext())
|
||||
{
|
||||
date = _tradeableDates.Current;
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace QuantConnect.Lean.Engine.HistoricalData
|
||||
var subscriptions = new List<Subscription>();
|
||||
foreach (var request in requests)
|
||||
{
|
||||
var subscription = CreateSubscription(request, request.StartTimeUtc, request.EndTimeUtc);
|
||||
var subscription = CreateSubscription(request);
|
||||
subscriptions.Add(subscription);
|
||||
}
|
||||
|
||||
@@ -90,14 +90,10 @@ namespace QuantConnect.Lean.Engine.HistoricalData
|
||||
/// <summary>
|
||||
/// Creates a subscription to process the request
|
||||
/// </summary>
|
||||
private Subscription CreateSubscription(HistoryRequest request, DateTime startUtc, DateTime endUtc)
|
||||
private Subscription CreateSubscription(HistoryRequest request)
|
||||
{
|
||||
// data reader expects these values in local times
|
||||
var startTimeLocal = startUtc.ConvertFromUtc(request.ExchangeHours.TimeZone);
|
||||
var endTimeLocal = endUtc.ConvertFromUtc(request.ExchangeHours.TimeZone);
|
||||
|
||||
var config = request.ToSubscriptionDataConfig();
|
||||
DataPermissionManager.AssertConfiguration(config, startTimeLocal, endTimeLocal);
|
||||
DataPermissionManager.AssertConfiguration(config, request.StartTimeLocal, request.EndTimeLocal);
|
||||
|
||||
var security = new Security(
|
||||
request.ExchangeHours,
|
||||
@@ -109,16 +105,10 @@ namespace QuantConnect.Lean.Engine.HistoricalData
|
||||
new SecurityCache()
|
||||
);
|
||||
|
||||
// Tradable dates are defined with the data time zone to access the right source
|
||||
var tradableDates = Time.EachTradeableDayInTimeZone(request.ExchangeHours, startTimeLocal, endTimeLocal, request.DataTimeZone, request.IncludeExtendedMarketHours);
|
||||
|
||||
var dataReader = new SubscriptionDataReader(config,
|
||||
startTimeLocal,
|
||||
endTimeLocal,
|
||||
request,
|
||||
_mapFileProvider,
|
||||
_factorFileProvider,
|
||||
tradableDates,
|
||||
false,
|
||||
_dataCacheProvider,
|
||||
_dataProvider
|
||||
);
|
||||
@@ -143,7 +133,7 @@ namespace QuantConnect.Lean.Engine.HistoricalData
|
||||
_factorFileProvider,
|
||||
dataReader,
|
||||
_mapFileProvider,
|
||||
startTimeLocal);
|
||||
request.StartTimeLocal);
|
||||
|
||||
// optionally apply fill forward behavior
|
||||
if (request.FillForwardResolution.HasValue)
|
||||
@@ -155,7 +145,7 @@ namespace QuantConnect.Lean.Engine.HistoricalData
|
||||
}
|
||||
|
||||
var readOnlyRef = Ref.CreateReadOnly(() => request.FillForwardResolution.Value.ToTimeSpan());
|
||||
reader = new FillForwardEnumerator(reader, security.Exchange, readOnlyRef, request.IncludeExtendedMarketHours, endTimeLocal, config.Increment, config.DataTimeZone);
|
||||
reader = new FillForwardEnumerator(reader, security.Exchange, readOnlyRef, request.IncludeExtendedMarketHours, request.EndTimeLocal, config.Increment, config.DataTimeZone);
|
||||
}
|
||||
|
||||
// since the SubscriptionDataReader performs an any overlap condition on the trade bar's entire
|
||||
@@ -163,7 +153,7 @@ namespace QuantConnect.Lean.Engine.HistoricalData
|
||||
// so to combat this we deliberately filter the results from the data reader to fix these cases
|
||||
// which only apply to non-tick data
|
||||
|
||||
reader = new SubscriptionFilterEnumerator(reader, security, endTimeLocal, config.ExtendedMarketHours, false, request.ExchangeHours);
|
||||
reader = new SubscriptionFilterEnumerator(reader, security, request.EndTimeLocal, config.ExtendedMarketHours, false, request.ExchangeHours);
|
||||
reader = new FilterEnumerator<BaseData>(reader, data =>
|
||||
{
|
||||
// allow all ticks
|
||||
@@ -171,9 +161,9 @@ namespace QuantConnect.Lean.Engine.HistoricalData
|
||||
// filter out all aux data
|
||||
if (data.DataType == MarketDataType.Auxiliary) return false;
|
||||
// filter out future data
|
||||
if (data.EndTime > endTimeLocal) return false;
|
||||
if (data.EndTime > request.EndTimeLocal) return false;
|
||||
// filter out data before the start
|
||||
return data.EndTime > startTimeLocal;
|
||||
return data.EndTime > request.StartTimeLocal;
|
||||
});
|
||||
var subscriptionRequest = new SubscriptionRequest(false, null, security, config, request.StartTimeUtc, request.EndTimeUtc);
|
||||
if (_parallelHistoryRequestsEnabled)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
@@ -203,7 +203,7 @@ namespace QuantConnect.Lean.Engine.Storage
|
||||
// Before saving confirm we are abiding by the control rules
|
||||
// Start by counting our file and its length
|
||||
var fileCount = 1;
|
||||
var expectedStorageSizeBytes = contents.Length;
|
||||
var expectedStorageSizeBytes = contents?.Length ?? 0;
|
||||
foreach (var kvp in _storage)
|
||||
{
|
||||
if (key.Equals(kvp.Key))
|
||||
@@ -214,7 +214,10 @@ namespace QuantConnect.Lean.Engine.Storage
|
||||
else
|
||||
{
|
||||
fileCount++;
|
||||
expectedStorageSizeBytes += kvp.Value.Length;
|
||||
if(kvp.Value != null)
|
||||
{
|
||||
expectedStorageSizeBytes += kvp.Value.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,6 +280,8 @@ namespace QuantConnect.Lean.Engine.Storage
|
||||
/// <summary>
|
||||
/// Returns the file path for the specified key
|
||||
/// </summary>
|
||||
/// <remarks>If the key is not already inserted it will just return a path associated with it
|
||||
/// and add the key with null value</remarks>
|
||||
/// <param name="key">The object key</param>
|
||||
/// <returns>The path for the file</returns>
|
||||
public virtual string GetFilePath(string key)
|
||||
@@ -284,13 +289,15 @@ namespace QuantConnect.Lean.Engine.Storage
|
||||
// Ensure we have an object for that key
|
||||
if (!ContainsKey(key))
|
||||
{
|
||||
throw new KeyNotFoundException($"Object with key '{key}' was not found in the current project. " +
|
||||
"Please use ObjectStore.ContainsKey(key) to check if an object exists before attempting to read."
|
||||
);
|
||||
// Add a key with null value to tell Persist() not to delete the file created in the path associated
|
||||
// with this key and not update it with the value associated with the key(null)
|
||||
SaveBytes(key, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Persist to ensure pur files are up to date
|
||||
Persist();
|
||||
}
|
||||
|
||||
// Persist to ensure pur files are up to date
|
||||
Persist();
|
||||
|
||||
// Fetch the path to file and return it
|
||||
return PathForKey(key);
|
||||
@@ -414,9 +421,13 @@ namespace QuantConnect.Lean.Engine.Storage
|
||||
// Write all our store data to disk
|
||||
foreach (var kvp in data)
|
||||
{
|
||||
// Get a path for this key and write to it
|
||||
var path = PathForKey(kvp.Key);
|
||||
File.WriteAllBytes(path, kvp.Value);
|
||||
// Skip the key associated with null values. They are not linked to a file yet
|
||||
if (kvp.Value != null)
|
||||
{
|
||||
// Get a path for this key and write to it
|
||||
var path = PathForKey(kvp.Key);
|
||||
File.WriteAllBytes(path, kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -1012,26 +1012,6 @@ namespace QuantConnect.Lean.Engine.TransactionHandlers
|
||||
order.Tag += " - " + orderEvent.Message;
|
||||
}
|
||||
}
|
||||
|
||||
if (_algorithm.BrokerageModel.AccountType == AccountType.Cash
|
||||
&& order.Direction == OrderDirection.Buy
|
||||
&& CurrencyPairUtil.TryDecomposeCurrencyPair(orderEvent.Symbol, out var baseCurrency, out _)
|
||||
&& orderEvent.OrderFee.Value.Currency == baseCurrency)
|
||||
{
|
||||
// fees are in the base currency, so we have to subtract them from the filled quantity
|
||||
// else the virtual position will bigger than the real size and we might no be able to liquidate
|
||||
orderEvent.FillQuantity -= orderEvent.OrderFee.Value.Amount;
|
||||
orderEvent.OrderFee = new ModifiedFillQuantityOrderFee(orderEvent.OrderFee.Value);
|
||||
|
||||
if (!_loggedFeeAdjustmentWarning)
|
||||
{
|
||||
_loggedFeeAdjustmentWarning = true;
|
||||
const string message = "When buying currency pairs, using Cash account types, fees in base currency" +
|
||||
" will be deducted from the filled quantity so virtual positions reflect actual holdings.";
|
||||
Log.Trace($"BrokerageTransactionHandler.HandleOrderEvent(): {message}");
|
||||
_algorithm.Debug(message);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OrderStatus.UpdateSubmitted:
|
||||
@@ -1076,15 +1056,26 @@ namespace QuantConnect.Lean.Engine.TransactionHandlers
|
||||
|
||||
// check if the fill currency and the order currency match the symbol currency
|
||||
var security = _algorithm.Securities[orderEvent.Symbol];
|
||||
// Bug in FXCM API flipping the currencies -- disabling for now. 5/17/16 RFB
|
||||
//if (fill.FillPriceCurrency != security.SymbolProperties.QuoteCurrency)
|
||||
//{
|
||||
// Log.Error(string.Format("Currency mismatch: Fill currency: {0}, Symbol currency: {1}", fill.FillPriceCurrency, security.SymbolProperties.QuoteCurrency));
|
||||
//}
|
||||
//if (order.PriceCurrency != security.SymbolProperties.QuoteCurrency)
|
||||
//{
|
||||
// Log.Error(string.Format("Currency mismatch: Order currency: {0}, Symbol currency: {1}", order.PriceCurrency, security.SymbolProperties.QuoteCurrency));
|
||||
//}
|
||||
|
||||
if (_algorithm.BrokerageModel.AccountType == AccountType.Cash
|
||||
&& order.Direction == OrderDirection.Buy
|
||||
&& CurrencyPairUtil.TryDecomposeCurrencyPair(orderEvent.Symbol, out var baseCurrency, out var quoteCurrency)
|
||||
&& orderEvent.OrderFee.Value.Currency == baseCurrency)
|
||||
{
|
||||
// fees are in the base currency, so we have to subtract them from the filled quantity
|
||||
// else the virtual position will bigger than the real size and we might no be able to liquidate
|
||||
orderEvent.FillQuantity -= orderEvent.OrderFee.Value.Amount;
|
||||
orderEvent.OrderFee = new ModifiedFillQuantityOrderFee(orderEvent.OrderFee.Value, quoteCurrency, security.SymbolProperties.ContractMultiplier);
|
||||
|
||||
if (!_loggedFeeAdjustmentWarning)
|
||||
{
|
||||
_loggedFeeAdjustmentWarning = true;
|
||||
const string message = "When buying currency pairs, using Cash account types, fees in base currency" +
|
||||
" will be deducted from the filled quantity so virtual positions reflect actual holdings.";
|
||||
Log.Trace($"BrokerageTransactionHandler.HandleOrderEvent(): {message}");
|
||||
_algorithm.Debug(message);
|
||||
}
|
||||
}
|
||||
|
||||
var multiplier = security.SymbolProperties.ContractMultiplier;
|
||||
var securityConversionRate = security.QuoteCurrency.ConversionRate;
|
||||
@@ -1183,10 +1174,14 @@ namespace QuantConnect.Lean.Engine.TransactionHandlers
|
||||
{
|
||||
if (_algorithm.Securities.TryGetValue(e.Symbol, out var security))
|
||||
{
|
||||
Log.Trace(
|
||||
"BrokerageTransactionHandler.HandleDelistingNotification(): clearing position for delisted holding: " +
|
||||
$"Symbol: {e.Symbol.Value}, " +
|
||||
$"Quantity: {security.Holdings.Quantity}");
|
||||
// only log always in live trading, in backtesting log if not 0 holdings
|
||||
if (_algorithm.LiveMode || security.Holdings.Quantity != 0)
|
||||
{
|
||||
Log.Trace(
|
||||
$"BrokerageTransactionHandler.HandleDelistingNotification(): UtcTime: {CurrentTimeUtc} clearing position for delisted holding: " +
|
||||
$"Symbol: {e.Symbol.Value}, " +
|
||||
$"Quantity: {security.Holdings.Quantity}");
|
||||
}
|
||||
|
||||
// Only submit an order if we have holdings
|
||||
var quantity = -security.Holdings.Quantity;
|
||||
@@ -1232,7 +1227,7 @@ namespace QuantConnect.Lean.Engine.TransactionHandlers
|
||||
if (_algorithm.LiveMode || security.Holdings.Quantity != 0)
|
||||
{
|
||||
Log.Trace(
|
||||
"BrokerageTransactionHandler.HandleOptionNotification(): clearing position for expired option holding: " +
|
||||
$"BrokerageTransactionHandler.HandleOptionNotification(): UtcTime: {CurrentTimeUtc} clearing position for expired option holding: " +
|
||||
$"Symbol: {e.Symbol.Value}, " +
|
||||
$"Holdings: {security.Holdings.Quantity}");
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<ServerGarbageCollection>true</ServerGarbageCollection>
|
||||
<Description>QuantConnect LEAN Engine: Launcher Project - Main startup executable for live and backtesting</Description>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
|
||||
@@ -152,6 +152,15 @@
|
||||
"tt-order-routing-port": "",
|
||||
"tt-log-fix-messages": false,
|
||||
|
||||
// Exante trading configuration
|
||||
// client-id, application-id, shared-key are required to access Exante REST API
|
||||
// Exante generates them at https://exante.eu/clientsarea/dashboard/ after adding an application
|
||||
"exante-client-id": "",
|
||||
"exante-application-id": "",
|
||||
"exante-shared-key": "",
|
||||
"exante-account-id": "", // Account-id is assigned after registration at Exante
|
||||
"exante-platform-type": "", // Environment of the application: live or demo
|
||||
|
||||
// Required to access data from Nasdaq
|
||||
// To get your access token go to https://data.nasdaq.com/account/profile
|
||||
"nasdaq-auth-token": "",
|
||||
@@ -451,6 +460,20 @@
|
||||
"transaction-handler": "QuantConnect.Lean.Engine.TransactionHandlers.BrokerageTransactionHandler",
|
||||
"history-provider": "BrokerageHistoryProvider"
|
||||
},
|
||||
|
||||
"live-exante": {
|
||||
"live-mode": true,
|
||||
|
||||
// real brokerage implementations require the BrokerageTransactionHandler
|
||||
"live-mode-brokerage": "QuantConnect.ExanteBrokerage.ExanteBrokerage",
|
||||
"data-queue-handler": [ "QuantConnect.ExanteBrokerage.ExanteBrokerage" ],
|
||||
"setup-handler": "QuantConnect.Lean.Engine.Setup.BrokerageSetupHandler",
|
||||
"result-handler": "QuantConnect.Lean.Engine.Results.LiveTradingResultHandler",
|
||||
"data-feed-handler": "QuantConnect.Lean.Engine.DataFeeds.LiveTradingDataFeed",
|
||||
"real-time-handler": "QuantConnect.Lean.Engine.RealTime.LiveTradingRealTimeHandler",
|
||||
"transaction-handler": "QuantConnect.Lean.Engine.TransactionHandlers.BrokerageTransactionHandler",
|
||||
"history-provider": "BrokerageHistoryProvider"
|
||||
},
|
||||
|
||||
// defines the 'live-ftxus' environment
|
||||
"live-ftxus": {
|
||||
|
||||
@@ -15,11 +15,15 @@
|
||||
|
||||
using System;
|
||||
using NodaTime;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Python.Runtime;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Util;
|
||||
using QuantConnect.Python;
|
||||
using QuantConnect.Algorithm;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Data.Market;
|
||||
using System.Collections.Generic;
|
||||
@@ -27,7 +31,6 @@ using QuantConnect.Lean.Engine.DataFeeds;
|
||||
using QuantConnect.Tests.Engine.DataFeeds;
|
||||
using QuantConnect.Data.Custom.AlphaStreams;
|
||||
using QuantConnect.Lean.Engine.HistoricalData;
|
||||
using QuantConnect.Securities;
|
||||
using HistoryRequest = QuantConnect.Data.HistoryRequest;
|
||||
|
||||
namespace QuantConnect.Tests.Algorithm
|
||||
@@ -262,6 +265,60 @@ namespace QuantConnect.Tests.Algorithm
|
||||
Assert.AreEqual(1, lastKnownPrices.Count(data => data.GetType() == typeof(QuoteBar)));
|
||||
}
|
||||
|
||||
[TestCase(Language.CSharp)]
|
||||
[TestCase(Language.Python)]
|
||||
public void GetLastKnownPricesCustomData(Language language)
|
||||
{
|
||||
var algorithm = GetAlgorithm(new DateTime(2013, 10, 8));
|
||||
|
||||
Symbol symbol;
|
||||
if (language == Language.CSharp)
|
||||
{
|
||||
symbol = algorithm.AddData<CustomData>("SPY").Symbol;
|
||||
}
|
||||
else
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
var customDataType = PythonEngine.ModuleFromString("testModule",
|
||||
@"
|
||||
from AlgorithmImports import *
|
||||
from QuantConnect.Tests import *
|
||||
|
||||
class Test(PythonData):
|
||||
def GetSource(self, config, date, isLiveMode):
|
||||
fileName = LeanData.GenerateZipFileName(Symbols.SPY, date, config.Resolution, config.TickType)
|
||||
source = f'{Globals.DataFolder}equity/usa/minute/spy/{fileName}'
|
||||
return SubscriptionDataSource(source, SubscriptionTransportMedium.LocalFile, FileFormat.Csv)
|
||||
|
||||
def Reader(self, config, line, date, isLiveMode):
|
||||
|
||||
data = line.split(',')
|
||||
|
||||
result = Test()
|
||||
result.DataType = MarketDataType.Base
|
||||
result.Symbol = config.Symbol
|
||||
result.Time = date + timedelta(milliseconds=int(data[0]))
|
||||
result.Value = 1
|
||||
|
||||
return result
|
||||
").GetAttr("Test");
|
||||
symbol = algorithm.AddData(customDataType, "SPY").Symbol;
|
||||
}
|
||||
}
|
||||
|
||||
var lastKnownPrices = algorithm.GetLastKnownPrices(symbol).ToList();
|
||||
Assert.AreEqual(1, lastKnownPrices.Count);
|
||||
if (language == Language.CSharp)
|
||||
{
|
||||
Assert.AreEqual(1, lastKnownPrices.Count(data => data.GetType() == typeof(CustomData)));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreEqual(1, lastKnownPrices.Count(data => data.GetType() == typeof(PythonData)));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetLastKnownPriceEquity()
|
||||
{
|
||||
@@ -428,5 +485,27 @@ namespace QuantConnect.Tests.Algorithm
|
||||
return Slices.Where(x => x.Time >= startTime && x.Time <= endTime).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public class CustomData : TradeBar
|
||||
{
|
||||
public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
|
||||
{
|
||||
var source = Path.Combine(Globals.DataFolder, "equity", "usa", config.Resolution.ToString().ToLower(),
|
||||
Symbols.SPY.Value.ToLowerInvariant(), LeanData.GenerateZipFileName(Symbols.SPY, date, config.Resolution, config.TickType));
|
||||
return new SubscriptionDataSource(source, SubscriptionTransportMedium.LocalFile, FileFormat.Csv);
|
||||
}
|
||||
public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
|
||||
{
|
||||
var baseData = base.Reader(new SubscriptionDataConfig(config, symbol: Symbols.SPY), line, date, isLiveMode);
|
||||
|
||||
return new CustomData
|
||||
{
|
||||
DataType = MarketDataType.Base,
|
||||
Symbol = config.Symbol,
|
||||
Time = baseData.EndTime,
|
||||
Value = baseData.Price
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
99
Tests/Brokerages/Binance/BinanceBrokerageAdditionalTests.cs
Normal file
99
Tests/Brokerages/Binance/BinanceBrokerageAdditionalTests.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 Moq;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Brokerages;
|
||||
using QuantConnect.Brokerages.Binance;
|
||||
using QuantConnect.Configuration;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Lean.Engine.DataFeeds;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Tests.Common.Securities;
|
||||
using System;
|
||||
using QuantConnect.Util;
|
||||
|
||||
namespace QuantConnect.Tests.Brokerages.Binance
|
||||
{
|
||||
[TestFixture, Explicit("This test requires a configured and testable Binance practice account")]
|
||||
public class BinanceBrokerageAdditionalTests
|
||||
{
|
||||
[Test]
|
||||
public void ParameterlessConstructorComposerUsage()
|
||||
{
|
||||
var brokerage = Composer.Instance.GetExportedValueByTypeName<IDataQueueHandler>("BinanceBrokerage");
|
||||
Assert.IsNotNull(brokerage);
|
||||
Assert.True(brokerage.IsConnected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConnectedIfNoAlgorithm()
|
||||
{
|
||||
using var brokerage = CreateBrokerage(null);
|
||||
Assert.True(brokerage.IsConnected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConnectedIfAlgorithmIsNotNullAndClientNotCreated()
|
||||
{
|
||||
using var brokerage = CreateBrokerage(Mock.Of<IAlgorithm>());
|
||||
Assert.True(brokerage.IsConnected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ConnectToUserDataStreamIfAlgorithmNotNullAndApiIsCreated()
|
||||
{
|
||||
var securities = new SecurityManager(new TimeKeeper(DateTime.UtcNow, TimeZones.NewYork));
|
||||
|
||||
var transactions = new SecurityTransactionManager(null, securities);
|
||||
transactions.SetOrderProcessor(new FakeOrderProcessor());
|
||||
|
||||
var algorithm = new Mock<IAlgorithm>();
|
||||
algorithm.Setup(a => a.Transactions).Returns(transactions);
|
||||
algorithm.Setup(a => a.BrokerageModel).Returns(new BinanceBrokerageModel());
|
||||
algorithm.Setup(a => a.Portfolio).Returns(new SecurityPortfolioManager(securities, transactions));
|
||||
|
||||
using var brokerage = CreateBrokerage(algorithm.Object);
|
||||
|
||||
Assert.True(brokerage.IsConnected);
|
||||
|
||||
var _ = brokerage.GetCashBalance();
|
||||
|
||||
Assert.True(brokerage.IsConnected);
|
||||
|
||||
brokerage.Disconnect();
|
||||
|
||||
Assert.False(brokerage.IsConnected);
|
||||
}
|
||||
|
||||
private static Brokerage CreateBrokerage(IAlgorithm algorithm)
|
||||
{
|
||||
var apiKey = Config.Get("binance-api-key");
|
||||
var apiSecret = Config.Get("binance-api-secret");
|
||||
var apiUrl = Config.Get("binance-api-url", "https://api.binance.com");
|
||||
var websocketUrl = Config.Get("binance-websocket-url", "wss://stream.binance.com:9443/ws");
|
||||
|
||||
return new BinanceBrokerage(
|
||||
apiKey,
|
||||
apiSecret,
|
||||
apiUrl,
|
||||
websocketUrl,
|
||||
algorithm,
|
||||
new AggregationManager(),
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Brokerages.Binance;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Logging;
|
||||
using QuantConnect.Util;
|
||||
using System.Threading;
|
||||
|
||||
namespace QuantConnect.Tests.Brokerages.Binance
|
||||
{
|
||||
[TestFixture]
|
||||
public partial class BinanceBrokerageTests
|
||||
{
|
||||
private static readonly Symbol XRP_USDT = Symbol.Create("XRPUSDT", SecurityType.Crypto, Market.FTX);
|
||||
|
||||
private static TestCaseData[] TestParameters
|
||||
{
|
||||
get
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
// valid parameters, for example
|
||||
new TestCaseData(XRP_USDT, Resolution.Tick, false),
|
||||
new TestCaseData(XRP_USDT, Resolution.Minute, false),
|
||||
new TestCaseData(XRP_USDT, Resolution.Second, false),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(TestParameters))]
|
||||
public void StreamsData(Symbol symbol, Resolution resolution, bool throwsException)
|
||||
{
|
||||
var cancelationToken = new CancellationTokenSource();
|
||||
var brokerage = (BinanceBrokerage)Brokerage;
|
||||
|
||||
SubscriptionDataConfig[] configs;
|
||||
if (resolution == Resolution.Tick)
|
||||
{
|
||||
var tradeConfig = new SubscriptionDataConfig(GetSubscriptionDataConfig<Tick>(symbol, resolution),
|
||||
tickType: TickType.Trade);
|
||||
var quoteConfig = new SubscriptionDataConfig(GetSubscriptionDataConfig<Tick>(symbol, resolution),
|
||||
tickType: TickType.Quote);
|
||||
configs = new[] { tradeConfig, quoteConfig };
|
||||
}
|
||||
else
|
||||
{
|
||||
configs = new[]
|
||||
{
|
||||
GetSubscriptionDataConfig<QuoteBar>(symbol, resolution),
|
||||
GetSubscriptionDataConfig<TradeBar>(symbol, resolution)
|
||||
};
|
||||
}
|
||||
|
||||
foreach (var config in configs)
|
||||
{
|
||||
ProcessFeed(brokerage.Subscribe(config, (s, e) =>
|
||||
{
|
||||
}),
|
||||
cancelationToken,
|
||||
(baseData) =>
|
||||
{
|
||||
if (baseData != null)
|
||||
{
|
||||
Log.Trace("{baseData}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Thread.Sleep(20000);
|
||||
|
||||
foreach (var config in configs)
|
||||
{
|
||||
brokerage.Unsubscribe(config);
|
||||
}
|
||||
|
||||
Thread.Sleep(20000);
|
||||
|
||||
cancelationToken.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ namespace QuantConnect.Tests.Brokerages.Binance
|
||||
[TestFixture, Explicit("This test requires a configured and testable Binance practice account")]
|
||||
public partial class BinanceBrokerageTests : BrokerageTests
|
||||
{
|
||||
private BinanceRestApiClient _binanceApi;
|
||||
private BinanceBaseRestApiClient _binanceApi;
|
||||
|
||||
/// <summary>
|
||||
/// Creates the brokerage under test and connects it
|
||||
@@ -53,6 +53,7 @@ namespace QuantConnect.Tests.Brokerages.Binance
|
||||
|
||||
var algorithm = new Mock<IAlgorithm>();
|
||||
algorithm.Setup(a => a.Transactions).Returns(transactions);
|
||||
algorithm.Setup(a => a.Securities).Returns(securities);
|
||||
algorithm.Setup(a => a.BrokerageModel).Returns(new BinanceBrokerageModel());
|
||||
algorithm.Setup(a => a.Portfolio).Returns(new SecurityPortfolioManager(securities, transactions));
|
||||
|
||||
@@ -61,7 +62,7 @@ namespace QuantConnect.Tests.Brokerages.Binance
|
||||
var apiUrl = Config.Get("binance-api-url", "https://api.binance.com");
|
||||
var websocketUrl = Config.Get("binance-websocket-url", "wss://stream.binance.com:9443/ws");
|
||||
|
||||
_binanceApi = new BinanceRestApiClient(
|
||||
_binanceApi = new BinanceSpotRestApiClient(
|
||||
new SymbolPropertiesDatabaseSymbolMapper(Market.Binance),
|
||||
algorithm.Object?.Portfolio,
|
||||
apiKey,
|
||||
|
||||
85
Tests/Brokerages/Binance/MarginAccountConverterTests.cs
Normal file
85
Tests/Brokerages/Binance/MarginAccountConverterTests.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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 Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Brokerages.Binance.Messages;
|
||||
|
||||
namespace QuantConnect.Tests.Brokerages.Binance
|
||||
{
|
||||
[TestFixture]
|
||||
public class MarginAccountConverterTests
|
||||
{
|
||||
[Test]
|
||||
public void DeserializeJson()
|
||||
{
|
||||
var json = @"{
|
||||
""borrowEnabled"": true,
|
||||
""marginLevel"": ""11.64405625"",
|
||||
""totalAssetOfBtc"": ""6.82728457"",
|
||||
""totalLiabilityOfBtc"": ""0.58633215"",
|
||||
""totalNetAssetOfBtc"": ""6.24095242"",
|
||||
""tradeEnabled"": true,
|
||||
""transferEnabled"": true,
|
||||
""userAssets"": [
|
||||
{
|
||||
""asset"": ""BTC"",
|
||||
""borrowed"": ""0.00000000"",
|
||||
""free"": ""0.00499500"",
|
||||
""interest"": ""0.00000000"",
|
||||
""locked"": ""0.00000000"",
|
||||
""netAsset"": ""0.00499500""
|
||||
},
|
||||
{
|
||||
""asset"": ""BNB"",
|
||||
""borrowed"": ""201.66666672"",
|
||||
""free"": ""2346.50000000"",
|
||||
""interest"": ""0.00000000"",
|
||||
""locked"": ""0.00000000"",
|
||||
""netAsset"": ""2144.83333328""
|
||||
},
|
||||
{
|
||||
""asset"": ""ETH"",
|
||||
""borrowed"": ""0.00000000"",
|
||||
""free"": ""0.00000000"",
|
||||
""interest"": ""0.00000000"",
|
||||
""locked"": ""0.00000000"",
|
||||
""netAsset"": ""0.00000000""
|
||||
},
|
||||
{
|
||||
""asset"": ""USDT"",
|
||||
""borrowed"": ""0.00000000"",
|
||||
""free"": ""0.00000000"",
|
||||
""interest"": ""0.00000000"",
|
||||
""locked"": ""0.00000000"",
|
||||
""netAsset"": ""0.00000000""
|
||||
}
|
||||
]
|
||||
}";
|
||||
|
||||
var balances = JsonConvert.DeserializeObject<AccountInformation>(json, new MarginAccountConverter()).Balances
|
||||
.Cast<MarginBalance>()
|
||||
.ToArray();
|
||||
|
||||
Assert.AreEqual(4, balances.Length);
|
||||
var bnb = balances.FirstOrDefault(a => a.Asset == "BNB");
|
||||
Assert.NotNull(bnb);
|
||||
Assert.AreEqual(201.66666672m, bnb.Borrowed);
|
||||
Assert.AreEqual(2144.83333328m, bnb.NetAsset);
|
||||
Assert.AreEqual(bnb.NetAsset, bnb.Amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
68
Tests/Brokerages/Binance/SpotAccountConverterTests.cs
Normal file
68
Tests/Brokerages/Binance/SpotAccountConverterTests.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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 Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Brokerages.Binance.Messages;
|
||||
|
||||
namespace QuantConnect.Tests.Brokerages.Binance
|
||||
{
|
||||
[TestFixture]
|
||||
public class SpotAccountConverterTests
|
||||
{
|
||||
[Test]
|
||||
public void DeserializeJson()
|
||||
{
|
||||
var json = @"{
|
||||
""makerCommission"": 15,
|
||||
""takerCommission"": 15,
|
||||
""buyerCommission"": 0,
|
||||
""sellerCommission"": 0,
|
||||
""canTrade"": true,
|
||||
""canWithdraw"": true,
|
||||
""canDeposit"": true,
|
||||
""updateTime"": 123456789,
|
||||
""accountType"": ""SPOT"",
|
||||
""balances"": [
|
||||
{
|
||||
""asset"": ""BTC"",
|
||||
""free"": ""4723846.89208129"",
|
||||
""locked"": ""0.00000000""
|
||||
},
|
||||
{
|
||||
""asset"": ""LTC"",
|
||||
""free"": ""4763368.68006011"",
|
||||
""locked"": ""1.00000000""
|
||||
}
|
||||
],
|
||||
""permissions"": [
|
||||
""SPOT""
|
||||
]
|
||||
}";
|
||||
|
||||
var balances = JsonConvert.DeserializeObject<AccountInformation>(json, new SpotAccountConverter()).Balances
|
||||
.Cast<SpotBalance>()
|
||||
.ToArray();
|
||||
|
||||
Assert.AreEqual(2, balances.Length);
|
||||
var bnb = balances.FirstOrDefault(a => a.Asset == "LTC");
|
||||
Assert.NotNull(bnb);
|
||||
Assert.AreEqual(4763368.68006011m, bnb.Free);
|
||||
Assert.AreEqual(1m, bnb.Locked);
|
||||
Assert.AreEqual(bnb.Free + bnb.Locked, bnb.Amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
157
Tests/Brokerages/Exante/ExanteFeeModelTests.cs
Normal file
157
Tests/Brokerages/Exante/ExanteFeeModelTests.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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 NUnit.Framework;
|
||||
using QuantConnect.Brokerages;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Orders.Fees;
|
||||
using QuantConnect.Securities;
|
||||
using System;
|
||||
|
||||
namespace QuantConnect.Tests.Brokerages.Exante
|
||||
{
|
||||
[TestFixture]
|
||||
public class ExanteFeeModelTests
|
||||
{
|
||||
private static decimal HighPrice => 1000m;
|
||||
private static decimal LowPrice => 100m;
|
||||
|
||||
private static decimal Quantity => 1m;
|
||||
private static Symbol Symbol => Symbols.SPY;
|
||||
|
||||
private static OrderSubmissionData OrderSubmissionData =>
|
||||
new(Security.BidPrice, Security.AskPrice, (Security.BidPrice + Security.AskPrice) / 2);
|
||||
|
||||
private static Security Security
|
||||
{
|
||||
get
|
||||
{
|
||||
var security = new Security(
|
||||
SecurityExchangeHours.AlwaysOpen(TimeZones.NewYork),
|
||||
new SubscriptionDataConfig(
|
||||
typeof(TradeBar),
|
||||
Symbol,
|
||||
Resolution.Minute,
|
||||
TimeZones.NewYork,
|
||||
TimeZones.NewYork,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
),
|
||||
new Cash("USD", 0, 1m),
|
||||
SymbolProperties.GetDefault("USD"),
|
||||
ErrorCurrencyConverter.Instance,
|
||||
RegisteredSecurityDataTypesProvider.Null,
|
||||
new SecurityCache()
|
||||
);
|
||||
security.SetMarketPrice(new Tick(DateTime.UtcNow, Symbol, LowPrice, HighPrice));
|
||||
return security;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public static void GetFeeModelTest()
|
||||
{
|
||||
var model = new ExanteBrokerageModel();
|
||||
Assert.IsInstanceOf<ExanteFeeModel>(model.GetFeeModel(Security));
|
||||
}
|
||||
|
||||
private static TestCaseData[] MakerOrders => new[]
|
||||
{
|
||||
new TestCaseData(new LimitOrderTestParameters(Symbol, HighPrice, LowPrice)),
|
||||
new TestCaseData(new LimitOrderTestParameters(Symbol, HighPrice, LowPrice)
|
||||
{ OrderSubmissionData = OrderSubmissionData }),
|
||||
new TestCaseData(new LimitOrderTestParameters(Symbol, HighPrice, LowPrice, new OrderProperties())),
|
||||
new TestCaseData(
|
||||
new LimitOrderTestParameters(Symbol, LowPrice, HighPrice,
|
||||
new OrderProperties()) { OrderSubmissionData = OrderSubmissionData }),
|
||||
new TestCaseData(new LimitOrderTestParameters(Symbol, HighPrice, LowPrice, new OrderProperties()))
|
||||
};
|
||||
|
||||
private static TestCaseData[] TakerOrders => new[]
|
||||
{
|
||||
new TestCaseData(new MarketOrderTestParameters(Symbol)),
|
||||
new TestCaseData(new MarketOrderTestParameters(Symbol, new OrderProperties())),
|
||||
new TestCaseData(new LimitOrderTestParameters(Symbol, LowPrice, HighPrice)
|
||||
{ OrderSubmissionData = OrderSubmissionData })
|
||||
};
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(MakerOrders))]
|
||||
public void ReturnShortOrderMakerFees(OrderTestParameters parameters)
|
||||
{
|
||||
IFeeModel feeModel = new ExanteFeeModel();
|
||||
|
||||
var order = parameters.CreateShortOrder(Quantity);
|
||||
var fee = feeModel.GetOrderFee(new OrderFeeParameters(Security, order));
|
||||
|
||||
Assert.AreEqual(
|
||||
ExanteFeeModel.MarketUsaRate * Math.Abs(Quantity),
|
||||
fee.Value.Amount
|
||||
);
|
||||
Assert.AreEqual(Currencies.USD, fee.Value.Currency);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(TakerOrders))]
|
||||
public void ReturnShortOrderTakerFees(OrderTestParameters parameters)
|
||||
{
|
||||
IFeeModel feeModel = new ExanteFeeModel();
|
||||
|
||||
var order = parameters.CreateShortOrder(Quantity);
|
||||
var fee = feeModel.GetOrderFee(new OrderFeeParameters(Security, order));
|
||||
|
||||
Assert.AreEqual(
|
||||
ExanteFeeModel.MarketUsaRate * Math.Abs(Quantity),
|
||||
fee.Value.Amount
|
||||
);
|
||||
Assert.AreEqual(Currencies.USD, fee.Value.Currency);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(MakerOrders))]
|
||||
public void ReturnLongOrderMakerFees(OrderTestParameters parameters)
|
||||
{
|
||||
IFeeModel feeModel = new ExanteFeeModel();
|
||||
|
||||
var order = parameters.CreateLongOrder(Quantity);
|
||||
var fee = feeModel.GetOrderFee(new OrderFeeParameters(Security, order));
|
||||
|
||||
Assert.AreEqual(
|
||||
ExanteFeeModel.MarketUsaRate * Math.Abs(Quantity),
|
||||
fee.Value.Amount
|
||||
);
|
||||
Assert.AreEqual(Currencies.USD, fee.Value.Currency);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(TakerOrders))]
|
||||
public void ReturnLongOrderTakerFees(OrderTestParameters parameters)
|
||||
{
|
||||
IFeeModel feeModel = new ExanteFeeModel();
|
||||
|
||||
var order = parameters.CreateLongOrder(Quantity);
|
||||
var fee = feeModel.GetOrderFee(new OrderFeeParameters(Security, order));
|
||||
|
||||
Assert.AreEqual(
|
||||
ExanteFeeModel.MarketUsaRate * Math.Abs(Quantity),
|
||||
fee.Value.Amount
|
||||
);
|
||||
Assert.AreEqual(Currencies.USD, fee.Value.Currency);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,6 +81,10 @@ namespace QuantConnect.Tests.Brokerages.InteractiveBrokers
|
||||
"ZS",
|
||||
"ZT",
|
||||
"ZW",
|
||||
"2YY",
|
||||
"5YY",
|
||||
"10Y",
|
||||
"30Y"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -88,21 +92,28 @@ namespace QuantConnect.Tests.Brokerages.InteractiveBrokers
|
||||
new[]
|
||||
{
|
||||
"6A",
|
||||
"M6A",
|
||||
"6B",
|
||||
"M6B",
|
||||
"6C",
|
||||
"M6C",
|
||||
"6E",
|
||||
"M6E",
|
||||
"6J",
|
||||
"M6J",
|
||||
"6L",
|
||||
"6M",
|
||||
"6N",
|
||||
"6R",
|
||||
"6S",
|
||||
"M6S",
|
||||
"6Z",
|
||||
//"ACD",
|
||||
//"AJY",
|
||||
//"ANE",
|
||||
"BIO",
|
||||
"BTC",
|
||||
"MicroBTC",
|
||||
"CB",
|
||||
//"CJY",
|
||||
//"CNH",
|
||||
@@ -115,6 +126,7 @@ namespace QuantConnect.Tests.Brokerages.InteractiveBrokers
|
||||
//"EI",
|
||||
"EMD",
|
||||
"ES",
|
||||
"MES",
|
||||
//"ESK",
|
||||
"GD",
|
||||
"GDK",
|
||||
@@ -128,7 +140,9 @@ namespace QuantConnect.Tests.Brokerages.InteractiveBrokers
|
||||
"LE",
|
||||
"NKD",
|
||||
"NQ",
|
||||
"MicroNQ",
|
||||
"RTY",
|
||||
"M2K"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -138,8 +152,10 @@ namespace QuantConnect.Tests.Brokerages.InteractiveBrokers
|
||||
//"AUP",
|
||||
//"EDP",
|
||||
"GC",
|
||||
"MGC",
|
||||
"HG",
|
||||
"SI",
|
||||
"SIL"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -232,6 +248,15 @@ namespace QuantConnect.Tests.Brokerages.InteractiveBrokers
|
||||
"PL",
|
||||
"RB",
|
||||
//"YO",
|
||||
"M1B",
|
||||
"M35",
|
||||
"M5F",
|
||||
"MAF",
|
||||
"MCL",
|
||||
"MEF",
|
||||
"PAM",
|
||||
"R5O",
|
||||
"S5O"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -28,9 +28,10 @@ namespace QuantConnect.Tests.Brokerages
|
||||
Symbol symbol,
|
||||
decimal highLimit,
|
||||
decimal lowLimit,
|
||||
IOrderProperties properties = null
|
||||
IOrderProperties properties = null,
|
||||
OrderSubmissionData orderSubmissionData = null
|
||||
)
|
||||
: base(symbol, properties)
|
||||
: base(symbol, properties, orderSubmissionData)
|
||||
{
|
||||
_highLimit = highLimit;
|
||||
_lowLimit = lowLimit;
|
||||
|
||||
@@ -24,8 +24,8 @@ namespace QuantConnect.Tests.Brokerages
|
||||
private readonly decimal _highLimit;
|
||||
private readonly decimal _lowLimit;
|
||||
|
||||
public LimitOrderTestParameters(Symbol symbol, decimal highLimit, decimal lowLimit, IOrderProperties properties = null)
|
||||
: base(symbol, properties)
|
||||
public LimitOrderTestParameters(Symbol symbol, decimal highLimit, decimal lowLimit, IOrderProperties properties = null, OrderSubmissionData orderSubmissionData = null)
|
||||
: base(symbol, properties, orderSubmissionData)
|
||||
{
|
||||
_highLimit = highLimit;
|
||||
_lowLimit = lowLimit;
|
||||
@@ -81,4 +81,4 @@ namespace QuantConnect.Tests.Brokerages
|
||||
|
||||
public override bool ModifyUntilFilled => false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ namespace QuantConnect.Tests.Brokerages
|
||||
{
|
||||
public class MarketOrderTestParameters : OrderTestParameters
|
||||
{
|
||||
public MarketOrderTestParameters(Symbol symbol, IOrderProperties properties = null)
|
||||
: base(symbol, properties)
|
||||
public MarketOrderTestParameters(Symbol symbol, IOrderProperties properties = null, OrderSubmissionData orderSubmissionData = null)
|
||||
: base(symbol, properties, orderSubmissionData)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -54,4 +54,4 @@ namespace QuantConnect.Tests.Brokerages
|
||||
|
||||
public override bool ExpectedCancellationResult => false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,11 +29,12 @@ namespace QuantConnect.Tests.Brokerages
|
||||
public IOrderProperties Properties { get; private set; }
|
||||
public OrderSubmissionData OrderSubmissionData { get; internal set; }
|
||||
|
||||
protected OrderTestParameters(Symbol symbol, IOrderProperties properties = null)
|
||||
protected OrderTestParameters(Symbol symbol, IOrderProperties properties = null, OrderSubmissionData orderSubmissionData = null)
|
||||
{
|
||||
Symbol = symbol;
|
||||
SecurityType = symbol.ID.SecurityType;
|
||||
Properties = properties;
|
||||
OrderSubmissionData = orderSubmissionData;
|
||||
}
|
||||
|
||||
public MarketOrder CreateLongMarketOrder(decimal quantity)
|
||||
@@ -76,9 +77,6 @@ namespace QuantConnect.Tests.Brokerages
|
||||
/// <summary>
|
||||
/// True to continue modifying the order until it is filled, false otherwise
|
||||
/// </summary>
|
||||
public virtual bool ModifyUntilFilled
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
public virtual bool ModifyUntilFilled => true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ namespace QuantConnect.Tests.Brokerages
|
||||
private readonly decimal _highLimit;
|
||||
private readonly decimal _lowLimit;
|
||||
|
||||
public StopLimitOrderTestParameters(Symbol symbol, decimal highLimit, decimal lowLimit, IOrderProperties properties = null)
|
||||
: base(symbol, properties)
|
||||
public StopLimitOrderTestParameters(Symbol symbol, decimal highLimit, decimal lowLimit, IOrderProperties properties = null, OrderSubmissionData orderSubmissionData = null)
|
||||
: base(symbol, properties, orderSubmissionData)
|
||||
{
|
||||
_highLimit = highLimit;
|
||||
_lowLimit = lowLimit;
|
||||
@@ -94,4 +94,4 @@ namespace QuantConnect.Tests.Brokerages
|
||||
|
||||
public override bool ModifyUntilFilled => false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ namespace QuantConnect.Tests.Brokerages
|
||||
private readonly decimal _highLimit;
|
||||
private readonly decimal _lowLimit;
|
||||
|
||||
public StopMarketOrderTestParameters(Symbol symbol, decimal highLimit, decimal lowLimit, IOrderProperties properties = null)
|
||||
: base(symbol, properties)
|
||||
public StopMarketOrderTestParameters(Symbol symbol, decimal highLimit, decimal lowLimit, IOrderProperties properties = null, OrderSubmissionData orderSubmissionData = null)
|
||||
: base(symbol, properties, orderSubmissionData)
|
||||
{
|
||||
_highLimit = highLimit;
|
||||
_lowLimit = lowLimit;
|
||||
@@ -80,4 +80,4 @@ namespace QuantConnect.Tests.Brokerages
|
||||
|
||||
public override bool ModifyUntilFilled => false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,5 +138,29 @@ namespace QuantConnect.Tests.Common.Brokerages
|
||||
Assert.AreEqual(false, _binanceBrokerageModel.CanSubmitOrder(security, order.Object, out var message));
|
||||
Assert.NotNull(message);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Returns1m_IfCashAccount()
|
||||
{
|
||||
Assert.AreEqual(1m, new BinanceBrokerageModel(AccountType.Cash).GetLeverage(_security));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Returns3m_IfMarginAccount()
|
||||
{
|
||||
Assert.AreEqual(3m, new BinanceBrokerageModel(AccountType.Margin).GetLeverage(_security));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ReturnsCashBuyinPowerModel_ForCashAccount()
|
||||
{
|
||||
Assert.IsInstanceOf<CashBuyingPowerModel>(new BinanceBrokerageModel(AccountType.Cash).GetBuyingPowerModel(_security));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ReturnsSecurityMarginModel_ForMarginAccount()
|
||||
{
|
||||
Assert.IsInstanceOf<SecurityMarginModel>(new BinanceBrokerageModel(AccountType.Margin).GetBuyingPowerModel(_security));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
57
Tests/Common/Brokerages/ExanteBrokerageModelTests.cs
Normal file
57
Tests/Common/Brokerages/ExanteBrokerageModelTests.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 Moq;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Brokerages;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Tests.Brokerages;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Tests.Common.Brokerages
|
||||
{
|
||||
[TestFixture, Parallelizable(ParallelScope.All)]
|
||||
public class ExanteBrokerageModelTests
|
||||
{
|
||||
private readonly ExanteBrokerageModel _exanteBrokerageModel = new();
|
||||
private readonly Symbol _btcusd = Symbol.Create("BTCUSD", SecurityType.Crypto, "empty");
|
||||
private Security _security;
|
||||
|
||||
[SetUp]
|
||||
public void Init()
|
||||
{
|
||||
_security = TestsHelpers.GetSecurity(symbol: _btcusd.Value, market: _btcusd.ID.Market,
|
||||
quoteCurrency: "EUR");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CannotSubmitMarketOrder_IfPriceNotInitialized()
|
||||
{
|
||||
var order = new Mock<MarketOrder>
|
||||
{
|
||||
Object =
|
||||
{
|
||||
Quantity = 1
|
||||
}
|
||||
};
|
||||
|
||||
var security =
|
||||
TestsHelpers.GetSecurity(symbol: _btcusd.Value, market: _btcusd.ID.Market, quoteCurrency: "EUR");
|
||||
|
||||
Assert.False(_exanteBrokerageModel.CanSubmitOrder(security, order.Object, out var message));
|
||||
Assert.NotNull(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,6 +64,7 @@ namespace QuantConnect.Tests.Common.Data.Auxiliary
|
||||
var dataProviderTest = new DefaultDataProviderTest();
|
||||
|
||||
fileProviderTest.Initialize(TestGlobals.MapFileProvider, dataProviderTest);
|
||||
fileProviderTest.CacheCleared.Reset();
|
||||
|
||||
fileProviderTest.Get(Symbols.AAPL);
|
||||
Assert.AreEqual(1, dataProviderTest.FetchCount);
|
||||
@@ -71,7 +72,7 @@ namespace QuantConnect.Tests.Common.Data.Auxiliary
|
||||
fileProviderTest.Get(Symbols.AAPL);
|
||||
Assert.AreEqual(1, dataProviderTest.FetchCount);
|
||||
|
||||
Thread.Sleep(1000);
|
||||
fileProviderTest.CacheCleared.WaitOne(TimeSpan.FromSeconds(2));
|
||||
|
||||
fileProviderTest.Get(Symbols.AAPL);
|
||||
Assert.AreEqual(2, dataProviderTest.FetchCount);
|
||||
@@ -83,6 +84,7 @@ namespace QuantConnect.Tests.Common.Data.Auxiliary
|
||||
private class LocalZipFactorFileProviderTest : LocalZipFactorFileProvider
|
||||
{
|
||||
public bool Enabled = true;
|
||||
public readonly ManualResetEvent CacheCleared = new (false);
|
||||
protected override TimeSpan CacheRefreshPeriod => TimeSpan.FromMilliseconds(500);
|
||||
|
||||
protected override void StartExpirationTask()
|
||||
@@ -90,6 +92,7 @@ namespace QuantConnect.Tests.Common.Data.Auxiliary
|
||||
if (Enabled)
|
||||
{
|
||||
base.StartExpirationTask();
|
||||
CacheCleared.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NodaTime;
|
||||
using NUnit.Framework;
|
||||
using Python.Runtime;
|
||||
using QuantConnect.Data;
|
||||
@@ -229,6 +230,58 @@ def Test(slice):
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PythonGetPythonCustomData()
|
||||
{
|
||||
using (Py.GIL())
|
||||
{
|
||||
dynamic testModule = PythonEngine.ModuleFromString("testModule",
|
||||
@"
|
||||
|
||||
from AlgorithmImports import *
|
||||
|
||||
class CustomDataTest(PythonData):
|
||||
def Reader(self, config, line, date, isLiveMode):
|
||||
result = CustomDataTest()
|
||||
result.Symbol = config.Symbol
|
||||
result.Value = 10
|
||||
return result
|
||||
def GetSource(config, date, isLiveMode):
|
||||
return None
|
||||
|
||||
class CustomDataTest2(PythonData):
|
||||
def Reader(self, config, line, date, isLiveMode):
|
||||
result = CustomDataTest2()
|
||||
result.Symbol = config.Symbol
|
||||
result.Value = 11
|
||||
return result
|
||||
def GetSource(config, date, isLiveMode):
|
||||
return None
|
||||
|
||||
def Test(slice):
|
||||
data = slice.Get(CustomDataTest)
|
||||
return data");
|
||||
var test = testModule.GetAttr("Test");
|
||||
|
||||
var type = Extensions.CreateType(testModule.GetAttr("CustomDataTest"));
|
||||
var customDataTest = new PythonData(testModule.GetAttr("CustomDataTest")());
|
||||
var config = new SubscriptionDataConfig(type, Symbols.SPY, Resolution.Daily, DateTimeZone.Utc,
|
||||
DateTimeZone.Utc, false, false, false, isCustom: true);
|
||||
var data1 = customDataTest.Reader(config, "something", DateTime.UtcNow, false);
|
||||
|
||||
var customDataTest2 = new PythonData(testModule.GetAttr("CustomDataTest2")());
|
||||
var config2 = new SubscriptionDataConfig(config, Extensions.CreateType(testModule.GetAttr("CustomDataTest2")));
|
||||
var data2 = customDataTest2.Reader(config2, "something2", DateTime.UtcNow, false);
|
||||
|
||||
var unlinkedDataSpy = new UnlinkedData { Symbol = Symbols.SPY, Time = DateTime.UtcNow, Value = 10 };
|
||||
var slice = new Slice(DateTime.UtcNow, new[] { unlinkedDataSpy, data2, data1 });
|
||||
|
||||
var data = test(new PythonSlice(slice));
|
||||
Assert.AreEqual(1, (int)data.Count);
|
||||
Assert.AreEqual(10, (int)data[Symbols.SPY].Value);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PythonEnumerationWorks()
|
||||
{
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* 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 Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Packets;
|
||||
|
||||
namespace QuantConnect.Tests.Common.Packets
|
||||
{
|
||||
[TestFixture, Parallelizable(ParallelScope.All)]
|
||||
public class BreakpointTests
|
||||
{
|
||||
[Test]
|
||||
public void SurvivesSerializationRoundTrip()
|
||||
{
|
||||
var breakpoints = new List<Breakpoint>
|
||||
{
|
||||
new Breakpoint{ FileName = "MichelAngelo", LineNumber = 1475},
|
||||
new Breakpoint{ FileName = "LeonardoDaVinci", LineNumber = 1452}
|
||||
};
|
||||
|
||||
var serialized = JsonConvert.SerializeObject(breakpoints);
|
||||
var deserialized = JsonConvert.DeserializeObject<List<Breakpoint>>(serialized);
|
||||
|
||||
Assert.AreEqual(deserialized.Count, 2);
|
||||
Assert.AreEqual(deserialized[0].FileName, "MichelAngelo");
|
||||
Assert.AreEqual(deserialized[0].LineNumber, 1475);
|
||||
Assert.AreEqual(deserialized[1].FileName, "LeonardoDaVinci");
|
||||
Assert.AreEqual(deserialized[1].LineNumber, 1452);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
@@ -152,6 +152,8 @@ namespace QuantConnect.Tests.Common.Securities.Futures
|
||||
[TestCase(QuantConnect.Securities.Futures.Currencies.EURCAD, NineSixteenCentralTime)]
|
||||
[TestCase(QuantConnect.Securities.Futures.Currencies.EURSEK, NineSixteenCentralTime)]
|
||||
[TestCase(QuantConnect.Securities.Futures.Currencies.JapaneseYenEmini, NineSixteenCentralTime)]
|
||||
[TestCase(QuantConnect.Securities.Futures.Currencies.MicroEUR, NineSixteenCentralTime)]
|
||||
[TestCase(QuantConnect.Securities.Futures.Currencies.MicroBTC, FourPMLondonTime)]
|
||||
public void CurrenciesExpiryDateFunction_WithDifferentDates_ShouldFollowContract(string symbol, string dayTime)
|
||||
{
|
||||
Assert.IsTrue(_data.ContainsKey(symbol), "Symbol " + symbol + " not present in Test Data");
|
||||
@@ -241,6 +243,7 @@ namespace QuantConnect.Tests.Common.Securities.Futures
|
||||
[TestCase(QuantConnect.Securities.Futures.Energies.NaturalGas, Zero)]
|
||||
[TestCase(QuantConnect.Securities.Futures.Energies.BrentCrude, Zero)]
|
||||
[TestCase(QuantConnect.Securities.Futures.Energies.LowSulfurGasoil, TwelveOclock)]
|
||||
[TestCase(QuantConnect.Securities.Futures.Energies.MicroCrudeOilWTI, Zero)]
|
||||
public void EnergiesExpiryDateFunction_WithDifferentDates_ShouldFollowContract(string symbol, string dayTime)
|
||||
{
|
||||
Assert.IsTrue(_data.ContainsKey(symbol), "Symbol " + symbol + " not present in Test Data");
|
||||
@@ -267,6 +270,7 @@ namespace QuantConnect.Tests.Common.Securities.Futures
|
||||
[TestCase(QuantConnect.Securities.Futures.Financials.FiveYearUSDMACSwap, TwoPMCentralTime)]
|
||||
[TestCase(QuantConnect.Securities.Futures.Financials.UltraUSTreasuryBond, TwelveOne)]
|
||||
[TestCase(QuantConnect.Securities.Futures.Financials.UltraTenYearUSTreasuryNote, Zero)]
|
||||
[TestCase(QuantConnect.Securities.Futures.Financials.MicroY10TreasuryNote, Zero)]
|
||||
public void FinancialsExpiryDateFunction_WithDifferentDates_ShouldFollowContract(string symbol, string dayTime)
|
||||
{
|
||||
Assert.IsTrue(_data.ContainsKey(symbol), "Symbol " + symbol + " not present in Test Data");
|
||||
@@ -303,6 +307,8 @@ namespace QuantConnect.Tests.Common.Securities.Futures
|
||||
[TestCase(QuantConnect.Securities.Futures.Indices.BankNifty, ThreeThirtyPM)]
|
||||
[TestCase(QuantConnect.Securities.Futures.Indices.BseSensex, ThreeThirtyPM)]
|
||||
[TestCase(QuantConnect.Securities.Futures.Indices.HangSeng, FourPM)]
|
||||
[TestCase(QuantConnect.Securities.Futures.Indices.MicroSP500EMini, NineThirtyEasternTime)]
|
||||
[TestCase(QuantConnect.Securities.Futures.Indices.MicroDow30EMini, NineThirtyEasternTime)]
|
||||
public void IndicesExpiryDateFunction_WithDifferentDates_ShouldFollowContract(string symbol, string dayTime)
|
||||
{
|
||||
Assert.IsTrue(_data.ContainsKey(symbol), "Symbol " + symbol + " not present in Test Data");
|
||||
@@ -369,6 +375,7 @@ namespace QuantConnect.Tests.Common.Securities.Futures
|
||||
[TestCase(QuantConnect.Securities.Futures.Metals.AluminiumEuropeanPremiumDutyPaidMetalBulletin, Zero)]
|
||||
[TestCase(QuantConnect.Securities.Futures.Metals.Copper, TwelvePMCentralTime)]
|
||||
[TestCase(QuantConnect.Securities.Futures.Metals.USMidwestDomesticHotRolledCoilSteelCRUIndex, Zero)]
|
||||
[TestCase(QuantConnect.Securities.Futures.Metals.MicroGold, Zero)]
|
||||
public void MetalsExpiryDateFunction_WithDifferentDates_ShouldFollowContract(string symbol, string dayTime)
|
||||
{
|
||||
Assert.IsTrue(_data.ContainsKey(symbol), "Symbol " + symbol + " not present in Test Data");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
@@ -301,7 +301,11 @@ namespace QuantConnect.Tests.Common.Securities.Futures
|
||||
}
|
||||
|
||||
[TestCase("06/01/2015 00:00:01", DayOfWeek.Friday, "30/01/2015 00:00:00")]
|
||||
[TestCase("06/07/2015 00:00:01", DayOfWeek.Thursday, "30/07/2015 00:00:00")]
|
||||
[TestCase("06/05/2016 00:00:01", DayOfWeek.Wednesday, "25/05/2016 00:00:00")]
|
||||
[TestCase("06/01/2016 00:00:01", DayOfWeek.Friday, "29/01/2016 00:00:00")]
|
||||
[TestCase("06/07/2016 00:00:01", DayOfWeek.Thursday, "28/07/2016 00:00:00")]
|
||||
[TestCase("06/05/2017 00:00:01", DayOfWeek.Wednesday, "31/05/2017 00:00:00")]
|
||||
public void Last_WeekDay_ShouldReturnCorrectDate(string contractDate, DayOfWeek dayOfWeek, string expectedOutput)
|
||||
{
|
||||
// Arrange
|
||||
|
||||
@@ -148,9 +148,9 @@ namespace QuantConnect.Tests.Common.Securities.Options.StrategyMatcher
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void None_CreatesOptionPosition_WithZeroQuantity()
|
||||
public void Empty_CreatesOptionPosition_WithZeroQuantity()
|
||||
{
|
||||
var none = OptionPosition.None(Symbols.SPY);
|
||||
var none = OptionPosition.Empty(Symbols.SPY);
|
||||
Assert.AreEqual(0, none.Quantity);
|
||||
Assert.AreEqual(Symbols.SPY, none.Symbol);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
@@ -68,7 +68,7 @@ namespace QuantConnect.Tests.Common.Storage
|
||||
Log.LogHandler = _logHandler;
|
||||
}
|
||||
|
||||
[TestCase(FileAccess.Read, false)]
|
||||
[TestCase(FileAccess.Read, true)]
|
||||
[TestCase(FileAccess.ReadWrite, false)]
|
||||
[TestCase(0, true)]
|
||||
[TestCase(FileAccess.Write, true)]
|
||||
@@ -83,7 +83,7 @@ namespace QuantConnect.Tests.Common.Storage
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Throws<KeyNotFoundException>(() => store.GetFilePath("Jose"));
|
||||
Assert.DoesNotThrow(() => store.GetFilePath("Jose"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,7 +250,6 @@ namespace QuantConnect.Tests.Common.Storage
|
||||
public void GetFilePathReturnsFileName(string key, string expectedRelativePath)
|
||||
{
|
||||
var expectedPath = Path.GetFullPath(expectedRelativePath).Replace("\\", "/");
|
||||
_store.SaveString(key, "data");
|
||||
Assert.AreEqual(expectedPath, _store.GetFilePath(key).Replace("\\", "/"));
|
||||
}
|
||||
|
||||
@@ -412,6 +411,55 @@ namespace QuantConnect.Tests.Common.Storage
|
||||
Assert.IsFalse(_store.SaveString("breaker", "gotem"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WriteFromExternalMethodAndSaveFromSource()
|
||||
{
|
||||
using (var store = new ObjectStore(new LocalObjectStore()))
|
||||
{
|
||||
store.Initialize("test", 0, 0, "", new Controls() { PersistenceIntervalSeconds = -1 });
|
||||
Assert.IsTrue(Directory.Exists("./LocalObjectStoreTests/test"));
|
||||
|
||||
var key = "Test";
|
||||
var content = "Example text";
|
||||
|
||||
var path = store.GetFilePath(key);
|
||||
|
||||
DummyMachineLearning(path, content);
|
||||
store.Save(key);
|
||||
|
||||
var storeContent = store.Read(key);
|
||||
Assert.AreEqual(content, storeContent);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetFilePathMethodWorksProperly()
|
||||
{
|
||||
using (var store = new ObjectStore(new LocalObjectStore()))
|
||||
{
|
||||
store.Initialize("test", 0, 0, "", new Controls() { PersistenceIntervalSeconds = -1 });
|
||||
Assert.IsTrue(Directory.Exists("./LocalObjectStoreTests/test"));
|
||||
|
||||
var key = "test";
|
||||
var path = store.GetFilePath(key);
|
||||
Assert.IsFalse(File.Exists(path));
|
||||
Assert.IsNull(store.Read(key));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TrySaveKeyWithNotFileAssociated()
|
||||
{
|
||||
using (var store = new ObjectStore(new LocalObjectStore()))
|
||||
{
|
||||
store.Initialize("test", 0, 0, "", new Controls() { PersistenceIntervalSeconds = -1 });
|
||||
Assert.IsTrue(Directory.Exists("./LocalObjectStoreTests/test"));
|
||||
|
||||
var key = "test";
|
||||
Assert.Throws<ArgumentException>(() => store.Save(key));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeletedObjectIsNotReloaded()
|
||||
{
|
||||
@@ -448,6 +496,20 @@ namespace QuantConnect.Tests.Common.Storage
|
||||
}
|
||||
}
|
||||
|
||||
private static void DummyMachineLearning(string outputFile, string content)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sw = new StreamWriter(outputFile);
|
||||
sw.Write(content);
|
||||
sw.Close();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public class TestSettings
|
||||
{
|
||||
public int EmaFastPeriod { get; set; }
|
||||
|
||||
@@ -42,6 +42,33 @@ namespace QuantConnect.Tests.Common.Util
|
||||
[TestFixture]
|
||||
public class ExtensionsTests
|
||||
{
|
||||
[TestCase("20220101", false, true, Resolution.Daily)]
|
||||
[TestCase("20220101", false, false, Resolution.Daily)]
|
||||
[TestCase("20220103 09:31", true, false, Resolution.Minute)]
|
||||
[TestCase("20220103 07:31", false, false, Resolution.Minute)]
|
||||
[TestCase("20220103 07:31", false, false, Resolution.Daily)]
|
||||
[TestCase("20220103 07:31", true, true, Resolution.Daily)]
|
||||
[TestCase("20220103 08:31", true, true, Resolution.Daily)]
|
||||
public void IsMarketOpenSecurity(string exchangeTime, bool expectedResult, bool extendedMarketHours, Resolution resolution)
|
||||
{
|
||||
var security = CreateSecurity(Symbols.SPY);
|
||||
var utcTime = Time.ParseDate(exchangeTime).ConvertToUtc(security.Exchange.TimeZone);
|
||||
security.SetLocalTimeKeeper(new LocalTimeKeeper(utcTime, security.Exchange.TimeZone));
|
||||
|
||||
Assert.AreEqual(expectedResult, security.IsMarketOpen(extendedMarketHours));
|
||||
}
|
||||
|
||||
[TestCase("20220101", false, true)]
|
||||
[TestCase("20220101", false, false)]
|
||||
[TestCase("20220103 09:31", true, false)]
|
||||
[TestCase("20220103 07:31", false, false)]
|
||||
[TestCase("20220103 08:31", true, true)]
|
||||
public void IsMarketOpenSymbol(string nyTime, bool expectedResult, bool extendedMarketHours)
|
||||
{
|
||||
var utcTime = Time.ParseDate(nyTime).ConvertToUtc(TimeZones.NewYork);
|
||||
Assert.AreEqual(expectedResult, Symbols.SPY.IsMarketOpen(utcTime, extendedMarketHours));
|
||||
}
|
||||
|
||||
[TestCase("CL XTN6UA1G9QKH")]
|
||||
[TestCase("ES VU1EHIDJYLMP")]
|
||||
[TestCase("ES VRJST036ZY0X")]
|
||||
@@ -1448,5 +1475,20 @@ actualDictionary.update({'IBM': 5})
|
||||
private class Derived2 : Derived1
|
||||
{
|
||||
}
|
||||
|
||||
private static Security CreateSecurity(Symbol symbol)
|
||||
{
|
||||
var entry = MarketHoursDatabase.FromDataFolder()
|
||||
.GetEntry(symbol.ID.Market, symbol, symbol.SecurityType);
|
||||
|
||||
return new Security(symbol,
|
||||
entry.ExchangeHours,
|
||||
new Cash(Currencies.USD, 0, 1),
|
||||
SymbolProperties.GetDefault(Currencies.USD),
|
||||
ErrorCurrencyConverter.Instance,
|
||||
RegisteredSecurityDataTypesProvider.Null,
|
||||
new SecurityCache()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using QuantConnect.Securities.Future;
|
||||
|
||||
namespace QuantConnect.Tests.Engine.DataFeeds.Enumerators
|
||||
{
|
||||
@@ -1696,7 +1697,8 @@ namespace QuantConnect.Tests.Engine.DataFeeds.Enumerators
|
||||
while (fillForwardEnumerator.MoveNext())
|
||||
{
|
||||
Assert.NotNull(fillForwardEnumerator.Current);
|
||||
Assert.GreaterOrEqual(fillForwardEnumerator.Current.Time, previous?.Time);
|
||||
// we don't care about 'Time' because lean only uses 'EndTime', in case some auxiliary data point comes in 'Time == EndTime'
|
||||
// but the enumerator output should always go increasing 'EndTime'
|
||||
Assert.GreaterOrEqual(fillForwardEnumerator.Current.EndTime, previous?.EndTime);
|
||||
Assert.AreEqual(
|
||||
fillForwardEnumerator.Current.DataType != MarketDataType.Auxiliary,
|
||||
@@ -1825,6 +1827,72 @@ namespace QuantConnect.Tests.Engine.DataFeeds.Enumerators
|
||||
fillForwardEnumerator.Dispose();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FillsForwardSymbolChangedDailyResolution()
|
||||
{
|
||||
var symbol = Symbols.Fut_SPY_Mar19_2016;
|
||||
var entry = MarketHoursDatabase.FromDataFolder().GetEntry(symbol.ID.Market, symbol, symbol.SecurityType);
|
||||
var reference = new DateTime(2014, 6, 5, 20, 0, 0);
|
||||
var dataResolution = Time.OneDay;
|
||||
|
||||
var data = new BaseData[]
|
||||
{
|
||||
new TradeBar {
|
||||
Time = reference,
|
||||
Value = 1,
|
||||
Period = dataResolution,
|
||||
Volume = 100,
|
||||
Symbol = symbol
|
||||
}, new TradeBar {
|
||||
Time = reference.AddDays(1),
|
||||
Value = 2,
|
||||
Period = dataResolution,
|
||||
Volume = 200,
|
||||
Symbol = symbol
|
||||
},
|
||||
new SymbolChangedEvent(symbol, reference.AddDays(3).Date, symbol, symbol),
|
||||
new TradeBar {
|
||||
Time = reference.AddDays(2),
|
||||
Value = 3,
|
||||
Period = dataResolution,
|
||||
Volume = 300,
|
||||
Symbol = symbol
|
||||
}}.ToList();
|
||||
|
||||
var enumerator = data.GetEnumerator();
|
||||
|
||||
var fillForwardEnumerator = new FillForwardEnumerator(enumerator,
|
||||
new FutureExchange(entry.ExchangeHours),
|
||||
Ref.Create(dataResolution),
|
||||
false,
|
||||
data.Last().EndTime,
|
||||
dataResolution,
|
||||
entry.DataTimeZone);
|
||||
|
||||
BaseData previous = null;
|
||||
var counter = 0;
|
||||
while (fillForwardEnumerator.MoveNext())
|
||||
{
|
||||
Assert.NotNull(fillForwardEnumerator.Current);
|
||||
// we don't care about 'Time' because lean only uses 'EndTime', in case some auxiliary data point comes in 'Time == EndTime'
|
||||
// but the enumerator output should always go increasing 'EndTime'
|
||||
if (previous != null)
|
||||
{
|
||||
Assert.GreaterOrEqual(fillForwardEnumerator.Current.EndTime, previous?.EndTime);
|
||||
}
|
||||
if (fillForwardEnumerator.Current.IsFillForward)
|
||||
{
|
||||
Assert.AreNotEqual(MarketDataType.Auxiliary, fillForwardEnumerator.Current.DataType);
|
||||
counter++;
|
||||
}
|
||||
previous = fillForwardEnumerator.Current;
|
||||
}
|
||||
|
||||
Assert.AreEqual((int)(data.Last().EndTime - data[1].EndTime).TotalDays - 1, counter);
|
||||
|
||||
fillForwardEnumerator.Dispose();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(ExchangeDaylightTimeSet), new object[] { 6, Resolution.Daily })]
|
||||
[TestCaseSource(nameof(ExchangeDaylightTimeSet), new object[] { 7, Resolution.Daily })]
|
||||
@@ -2056,7 +2124,8 @@ namespace QuantConnect.Tests.Engine.DataFeeds.Enumerators
|
||||
while (fillForwardEnumerator.MoveNext())
|
||||
{
|
||||
Assert.NotNull(fillForwardEnumerator.Current);
|
||||
Assert.GreaterOrEqual(fillForwardEnumerator.Current.Time, previous?.Time ?? DateTime.MinValue);
|
||||
// we don't care about .Time because lean only uses .EndTime
|
||||
// in case some auxiliary data point comes in it will respect endtime being ascendant but it's time == endtime
|
||||
Assert.GreaterOrEqual(fillForwardEnumerator.Current.EndTime, previous?.EndTime ?? DateTime.MinValue);
|
||||
Assert.AreEqual(
|
||||
fillForwardEnumerator.Current.DataType != MarketDataType.Auxiliary,
|
||||
|
||||
@@ -17,12 +17,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using NodaTime;
|
||||
using NUnit.Framework;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Auxiliary;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Lean.Engine.DataFeeds;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Util;
|
||||
|
||||
namespace QuantConnect.Tests.Engine.DataFeeds
|
||||
@@ -47,21 +50,20 @@ namespace QuantConnect.Tests.Engine.DataFeeds
|
||||
var start = new DateTime(2019, 12, 9);
|
||||
var end = new DateTime(2019, 12, 12);
|
||||
|
||||
var dataReader = new SubscriptionDataReader(
|
||||
new SubscriptionDataConfig(typeof(TradeBar),
|
||||
Symbols.SPY,
|
||||
dataResolution,
|
||||
TimeZones.NewYork,
|
||||
TimeZones.NewYork,
|
||||
false,
|
||||
false,
|
||||
false),
|
||||
start,
|
||||
end,
|
||||
var symbol = Symbols.SPY;
|
||||
var entry = MarketHoursDatabase.FromDataFolder().GetEntry(symbol.ID.Market, symbol, symbol.SecurityType);
|
||||
var config = new SubscriptionDataConfig(typeof(TradeBar),
|
||||
symbol,
|
||||
dataResolution,
|
||||
TimeZones.NewYork,
|
||||
TimeZones.NewYork,
|
||||
false,
|
||||
false,
|
||||
false);
|
||||
var dataReader = new SubscriptionDataReader(config,
|
||||
new HistoryRequest(config, entry.ExchangeHours, start, end),
|
||||
TestGlobals.MapFileProvider,
|
||||
TestGlobals.FactorFileProvider,
|
||||
LinqExtensions.Range(start, end, time => time + TimeSpan.FromDays(1)),
|
||||
false,
|
||||
new TestDataCacheProvider
|
||||
{ Data = data },
|
||||
TestGlobals.DataProvider
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
|
||||
<ServerGarbageCollection>true</ServerGarbageCollection>
|
||||
<Description>QuantConnect LEAN Engine: Tests Project - The test collection</Description>
|
||||
<NoWarn>CS0618</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace QuantConnect.Tests
|
||||
from type in typeof(BasicTemplateAlgorithm).Assembly.GetTypes()
|
||||
where typeof(IRegressionAlgorithmDefinition).IsAssignableFrom(type)
|
||||
where !type.IsAbstract // non-abstract
|
||||
where type.GetConstructor(new Type[0]) != null // has default ctor
|
||||
where type.GetConstructor(Array.Empty<Type>()) != null // has default ctor
|
||||
let instance = (IRegressionAlgorithmDefinition) Activator.CreateInstance(type)
|
||||
let status = nonDefaultStatuses.GetValueOrDefault(type.Name, AlgorithmStatus.Completed)
|
||||
where instance.CanRunLocally // open source has data to run this algorithm
|
||||
|
||||
Binary file not shown.
@@ -1,6 +1,10 @@
|
||||
Globex,Product Name,MultipleFactor,Info
|
||||
10Y,Micro 10-Year Yield Futures,0.1,
|
||||
1S,Propane Non-LDH Mont Belvieu (OPIS) BALMO Futures,1,
|
||||
22,Argus Propane Far East Index BALMO Futures,1,
|
||||
2YY,Micro 2-Year Yield Futures,0.1,
|
||||
30Y,Micro 30-Year Yield Futures,0.1,
|
||||
5YY,Micro 5-Year Yield Futures,0.1
|
||||
6A,Australian Dollar Futures,0.01,
|
||||
6B,British Pound Futures,0.01,
|
||||
6C,Canadian Dollar Futures,0.01,
|
||||
@@ -126,31 +130,49 @@ LIT,2-Year Eris Swap Futures,1,
|
||||
LIW,5-year Eris Swap Futures,1,
|
||||
LIY,10-year Eris Swap Futures,1,
|
||||
LT,Gulf Coast ULSD (Platts) Up-Down Futures,0.01,
|
||||
M1B,Micro Gasoil 0.1% Barges FOB ARA (Platts) Futures,1,
|
||||
M2K,Micro E-mini Russell 2000 Index Futures,1,
|
||||
M35,Micro European 3.5% Fuel Oil Cargoes FOB Med (Platts) Futures,0.1,
|
||||
M5F,Micro Coal (API 5) fob Newcastle (Argus/McCloskey) Futures,1,
|
||||
M6A,E-micro Australian Dollar/American Dollar Futures,100,
|
||||
M6B,E-micro British Pound/American Dollar Futures,100,
|
||||
M6C,Micro USD/CAD Futures,100,
|
||||
M6E,E-micro Euro/American Dollar Futures,100,
|
||||
M6J,Micro USD/JPY Futures,100,
|
||||
M6S,Micro USD/CHF Futures,1000,
|
||||
MAE,Mini Argus Propane Far East Index Futures,0.1,
|
||||
MAF,Micro Singapore Fuel Oil 380CST (Platts) Futures,1,
|
||||
MBT,Micro Bitcoin Futures,100,
|
||||
MCD,E-micro Canadian Dollar/American Dollar Futures,100,
|
||||
MCL,Micro WTI Crude Oil Futures,1,
|
||||
ME,Gulf Coast Jet (Platts) Up-Down Futures,0.01,
|
||||
MEF,Micro European 3.5% Fuel Oil Barges FOB Rdam (Platts) Futures,0.1,
|
||||
MEE,Mini European Naphtha (Platts) BALMO Futures,1,
|
||||
MEO,Mini Gasoline Euro-bob Oxy NWE Barges (Argus) Futures,0.1,
|
||||
MES,Micro E-mini Standard and Poor's 500 Stock Price Index Futures,1,
|
||||
MET,Micro Ether Futures,1,
|
||||
MFB,Gulf Coast HSFO (Platts) Futures,1,
|
||||
MFF,Coal (API4) FOB Richards Bay (ARGUS-McCloskey) Futures,1,
|
||||
MGB,Mini Gasoil 0.1 Barges FOB Rdam (Platts) vs. Low Sulphur Gasoil Futures,0.1,
|
||||
MGC,E-micro Gold Futures,10,
|
||||
MIR,E-micro Indian Rupee/USD Futures,100,
|
||||
MGT,Micro Gold TAS,1,
|
||||
MIB,BTIC on Micro Bitcoin Futures,1,
|
||||
MIR,E-micro Indian Rupee/USD Futures,1,
|
||||
MJN,Mini Japan C&F Naphtha (Platts) Futures,0.1,
|
||||
MJY,E-micro Japanese Yen/American Dollar Futures,100,
|
||||
MM,New York Harbor Residual Fuel 1.0% (Platts) Futures,1,
|
||||
MMF,Mini 3.5% Fuel Oil Cargoes FOB MED (Platts) Financial Futures,0.1,
|
||||
MNC,Mini European Naphtha CIF NWE (Platts) Futures,0.1,
|
||||
MNH,Micro USD/CNH Futures,100,
|
||||
MNQ,Micro E-mini Nasdaq-100 Index Futures,1,
|
||||
MPS,Mini European Propane CIF ARA (Argus) Futures,2,
|
||||
MRB,BTIC on Micro Ether Futures,1,
|
||||
MSF,E-micro Swiss Franc/American Dollar Futures,100,
|
||||
MSG,Mini Singapore Gasoil (Platts) Futures,0.1,
|
||||
MTB,Mini Singapore Fuel Oil 380 cst (Platts) BALMO Futures,3,
|
||||
MTF,Coal (API2) CIF ARA (ARGUS-McCloskey) Futures,1,
|
||||
MTS,Mini Singapore Fuel Oil 380 cst (Platts) Futures,0.1,
|
||||
MYM,Micro E-mini Dow Jones Industrial Average Index Futures,100,
|
||||
N1B,Singapore Mogas 92 Unleaded (Platts) Futures,4,
|
||||
N1U,10-Year USD MAC Swap Futures,5,
|
||||
NBB,Naphtha Cargoes CIF NWE (Platts) Crack Spread (1000mt) BALMO Futures,6,
|
||||
@@ -162,6 +184,7 @@ NOK,Norwegian Krone Futures,0.001,
|
||||
NOO,Naphtha Cargoes CIF NWE (Platts) Crack Spread (1000mt) Futures,0.01,
|
||||
NQ,E-mini Nasdaq-100 Futures,1,
|
||||
PA,Palladium Futures,1,
|
||||
PAM,Micro Palladium Futures,1,
|
||||
PJY,British Pound/Japanese Yen Futures,1,
|
||||
PL,Platinum Futures,10,
|
||||
PLN,Polish Zloty Futures,0.001,
|
||||
@@ -171,6 +194,7 @@ QG,E-mini Natural Gas Futures,0.1,
|
||||
QI,E-mini Silver Futures,0.01,
|
||||
QM,E-mini Crude Oil Futures,0.1,
|
||||
QO,E-mini Gold Futures,1,
|
||||
R5O,Micro European FOB Rdam Marine Fuel 0.5% Barges (Platts) Futures,1,
|
||||
RB,RBOB Gasoline Futures,0.01,
|
||||
RBB,RBOB Gasoline Brent Crack Spread Futures,0.1,
|
||||
RF,Euro/Swiss Franc Futures,0.01,
|
||||
@@ -185,6 +209,7 @@ RY,Euro/Japanese Yen Futures,1,
|
||||
SDA,S&P 500 Annual Dividend Index Futures,0.1,
|
||||
SE,Singapore Fuel Oil 380 cst (Platts) Futures,0.1,
|
||||
SEK,Swedish Krona Futures,0.001,
|
||||
S5O,Micro Singapore FOB Marine Fuel 0.5% (Platts) Futures,1,
|
||||
SI,Silver Futures,0.1,
|
||||
SIL,1000-oz. Silver Futures,0.1,
|
||||
SIR,Indian Rupee/USD Futures,1,
|
||||
|
||||
|
@@ -53,7 +53,6 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="NodaTime" Version="3.0.5" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.2.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\LICENSE">
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user