Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18b99338fc | ||
|
|
f129ab1a09 | ||
|
|
f86926bf7a | ||
|
|
68c046fc7b | ||
|
|
3e52816f6e | ||
|
|
c598a8d260 | ||
|
|
e63bfc9127 | ||
|
|
e316f12394 | ||
|
|
306298a16f | ||
|
|
e8160f33d5 | ||
|
|
ce3cb8e1a3 | ||
|
|
2d644d7879 | ||
|
|
b80e274d4f | ||
|
|
9a355c9be5 | ||
|
|
303b95ab50 | ||
|
|
d826d267f4 | ||
|
|
eb55311052 | ||
|
|
27d18fa2e8 | ||
|
|
bb0c671e7c | ||
|
|
c8dc343c13 | ||
|
|
b6815d22de | ||
|
|
459f60603b | ||
|
|
1aaaa20c61 | ||
|
|
07b6572bf9 | ||
|
|
a675aca7e5 | ||
|
|
87db3fe379 | ||
|
|
74321d1727 | ||
|
|
9fd50a302e | ||
|
|
fc0b2f3fa4 | ||
|
|
c4a2d6eef4 | ||
|
|
c2b60e4e48 |
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 }}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm reproducing issue where underlying option contract would be removed with the first call
|
||||
/// too RemoveOptionContract
|
||||
/// </summary>
|
||||
public class AddTwoAndRemoveOneOptionContractRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _contract1;
|
||||
private Symbol _contract2;
|
||||
private bool _hasRemoved;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2014, 06, 06);
|
||||
SetEndDate(2014, 06, 06);
|
||||
|
||||
UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw;
|
||||
UniverseSettings.MinimumTimeInUniverse = TimeSpan.Zero;
|
||||
|
||||
var aapl = QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, Market.USA);
|
||||
|
||||
var contracts = OptionChainProvider.GetOptionContractList(aapl, Time)
|
||||
.OrderBy(symbol => symbol.ID.Symbol)
|
||||
.Where(optionContract => optionContract.ID.OptionRight == OptionRight.Call
|
||||
&& optionContract.ID.OptionStyle == OptionStyle.American)
|
||||
.Take(2)
|
||||
.ToList();
|
||||
|
||||
_contract1 = contracts[0];
|
||||
_contract2 = contracts[1];
|
||||
AddOptionContract(_contract1);
|
||||
AddOptionContract(_contract2);
|
||||
}
|
||||
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
if (slice.HasData)
|
||||
{
|
||||
if (!_hasRemoved)
|
||||
{
|
||||
RemoveOptionContract(_contract1);
|
||||
_hasRemoved = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var subscriptions =
|
||||
SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs("AAPL");
|
||||
if (subscriptions.Count == 0)
|
||||
{
|
||||
throw new Exception("No configuration for underlying was found!");
|
||||
}
|
||||
|
||||
if (!Portfolio.Invested)
|
||||
{
|
||||
Buy(_contract2, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (!_hasRemoved)
|
||||
{
|
||||
throw new Exception("Expect a single call to OnData where we removed the option and underlying");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
|
||||
/// </summary>
|
||||
public bool CanRunLocally { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public Language[] Languages { get; } = { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "2"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$2.00"},
|
||||
{"Estimated Strategy Capacity", "$230000.00"},
|
||||
{"Lowest Capacity Asset", "AAPL VXBK4QQIRLZA|AAPL R735QTJ8XC9X"},
|
||||
{"Fitness Score", "0"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Return Over Maximum Drawdown", "0"},
|
||||
{"Portfolio Turnover", "0"},
|
||||
{"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", "228194dcc6fd8689a67f383577ee2d85"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using QuantConnect.Brokerages;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
|
||||
@@ -33,6 +34,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
SetStartDate(2018, 04, 04); //Set Start Date
|
||||
SetEndDate(2018, 04, 04); //Set End Date
|
||||
SetBrokerageModel(BrokerageName.GDAX, AccountType.Cash);
|
||||
//Before setting any cash or adding a Security call SetAccountCurrency
|
||||
SetAccountCurrency("EUR");
|
||||
SetCash(100000); //Set Strategy Cash
|
||||
@@ -87,14 +89,14 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Total Fees", "$298.35"},
|
||||
{"Estimated Strategy Capacity", "$85000.00"},
|
||||
{"Lowest Capacity Asset", "BTCEUR XJ"},
|
||||
{"Fitness Score", "0.506"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "-14.148"},
|
||||
{"Return Over Maximum Drawdown", "-13.614"},
|
||||
{"Portfolio Turnover", "1.073"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
@@ -109,7 +111,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "18dc611407abec4ea47092e71f33f983"}
|
||||
{"OrderListHash", "2ba443899dcccc79dc0f04441f797bf9"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
90
Algorithm.CSharp/BinanceCashAccountFeeRegressionAlgorithm.cs
Normal file
90
Algorithm.CSharp/BinanceCashAccountFeeRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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 QuantConnect.Brokerages;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Binance cash account regression algorithm, reproduces issue https://github.com/QuantConnect/Lean/issues/6123
|
||||
/// </summary>
|
||||
public class BinanceCashAccountFeeRegressionAlgorithm : CryptoBaseCurrencyFeeRegressionAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// The target account type
|
||||
/// </summary>
|
||||
protected override AccountType AccountType { get; } = AccountType.Cash;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetAccountCurrency("USDT");
|
||||
SetStartDate(2018, 05, 02);
|
||||
SetEndDate(2018, 05, 03);
|
||||
BrokerageName = BrokerageName.Binance;
|
||||
Pair = "BTCUSDT";
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public override Dictionary<string, string> ExpectedStatistics => new()
|
||||
{
|
||||
{"Total Trades", "49"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$45.62"},
|
||||
{"Estimated Strategy Capacity", "$220000.00"},
|
||||
{"Lowest Capacity Asset", "BTCUSDT 18N"},
|
||||
{"Fitness Score", "0.208"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "26.189"},
|
||||
{"Portfolio Turnover", "0.208"},
|
||||
{"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", "USDT0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "USDT0"},
|
||||
{"Mean Population Estimated Insight Value", "USDT0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "7417649395922ff3791471b4f3b5c021"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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 QuantConnect.Brokerages;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Binance margin account regression algorithm, reproduces issue https://github.com/QuantConnect/Lean/issues/6123
|
||||
/// </summary>
|
||||
public class BinanceMarginAccountFeeRegressionAlgorithm : CryptoBaseCurrencyFeeRegressionAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// The target account type
|
||||
/// </summary>
|
||||
protected override AccountType AccountType { get; } = AccountType.Margin;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetAccountCurrency("USDT");
|
||||
SetStartDate(2018, 05, 02);
|
||||
SetEndDate(2018, 05, 03);
|
||||
BrokerageName = BrokerageName.Binance;
|
||||
Pair = "BTCUSDT";
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public override Dictionary<string, string> ExpectedStatistics => new()
|
||||
{
|
||||
{"Total Trades", "49"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$45.62"},
|
||||
{"Estimated Strategy Capacity", "$12000000.00"},
|
||||
{"Lowest Capacity Asset", "BTCUSDT 18N"},
|
||||
{"Fitness Score", "0.208"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "26.189"},
|
||||
{"Portfolio Turnover", "0.208"},
|
||||
{"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", "USDT0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "USDT0"},
|
||||
{"Mean Population Estimated Insight Value", "USDT0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "7417649395922ff3791471b4f3b5c021"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 QuantConnect.Brokerages;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Bitfinex cash account regression algorithm, reproduces issue https://github.com/QuantConnect/Lean/issues/6123
|
||||
/// </summary>
|
||||
public class BitfinexCashAccountFeeRegressionAlgorithm : CryptoBaseCurrencyFeeRegressionAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// The target account type
|
||||
/// </summary>
|
||||
protected override AccountType AccountType { get; } = AccountType.Cash;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2013, 10, 02);
|
||||
SetEndDate(2013, 10, 03);
|
||||
BrokerageName = BrokerageName.Bitfinex;
|
||||
Pair = "BTCUSD";
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public override Dictionary<string, string> ExpectedStatistics => new()
|
||||
{
|
||||
{"Total Trades", "49"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$1.13"},
|
||||
{"Estimated Strategy Capacity", "$2000.00"},
|
||||
{"Lowest Capacity Asset", "BTCUSD E3"},
|
||||
{"Fitness Score", "0.002"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
|
||||
{"Portfolio Turnover", "0.002"},
|
||||
{"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", "7f892f0c42d8826ff770ee602fe207a2"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 QuantConnect.Brokerages;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Bitfinex margin account regression algorithm, reproduces issue https://github.com/QuantConnect/Lean/issues/6123
|
||||
/// </summary>
|
||||
public class BitfinexMarginAccountFeeRegressionAlgorithm : CryptoBaseCurrencyFeeRegressionAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// The target account type
|
||||
/// </summary>
|
||||
protected override AccountType AccountType { get; } = AccountType.Margin;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2013, 10, 02);
|
||||
SetEndDate(2013, 10, 03);
|
||||
BrokerageName = BrokerageName.Bitfinex;
|
||||
Pair = "BTCUSD";
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public override Dictionary<string, string> ExpectedStatistics => new()
|
||||
{
|
||||
{"Total Trades", "49"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$1.13"},
|
||||
{"Estimated Strategy Capacity", "$640000.00"},
|
||||
{"Lowest Capacity Asset", "BTCUSD E3"},
|
||||
{"Fitness Score", "0.002"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
|
||||
{"Portfolio Turnover", "0.002"},
|
||||
{"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", "7f892f0c42d8826ff770ee602fe207a2"}
|
||||
};
|
||||
}
|
||||
}
|
||||
110
Algorithm.CSharp/CryptoBaseCurrencyFeeRegressionAlgorithm.cs
Normal file
110
Algorithm.CSharp/CryptoBaseCurrencyFeeRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using QuantConnect.Util;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Brokerages;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Base crypto account regression algorithm trading in and out
|
||||
/// </summary>
|
||||
public abstract class CryptoBaseCurrencyFeeRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _symbol;
|
||||
|
||||
/// <summary>
|
||||
/// The target account type
|
||||
/// </summary>
|
||||
protected abstract AccountType AccountType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The target brokerage model name
|
||||
/// </summary>
|
||||
protected BrokerageName BrokerageName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The pair to add and trade
|
||||
/// </summary>
|
||||
protected string Pair { get; set; }
|
||||
|
||||
/// <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()
|
||||
{
|
||||
SetBrokerageModel(BrokerageName, AccountType);
|
||||
_symbol = AddCrypto(Pair, Resolution.Hour).Symbol;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
|
||||
/// </summary>
|
||||
/// <param name="data">Slice object keyed by symbol containing the stock data</param>
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
if (!Portfolio.Invested)
|
||||
{
|
||||
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
|
||||
{
|
||||
Liquidate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
|
||||
/// </summary>
|
||||
public bool CanRunLocally { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public Language[] Languages { get; } = { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public abstract Dictionary<string, string> ExpectedStatistics { get; }
|
||||
}
|
||||
}
|
||||
@@ -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"},
|
||||
|
||||
120
Algorithm.CSharp/HistoryProviderManagerRegressionAlgorithm.cs
Normal file
120
Algorithm.CSharp/HistoryProviderManagerRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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 QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Test algorithm to verify the corret working of <see cref="HistoryProviderManager"/>
|
||||
/// </summary>
|
||||
public class HistoryProviderManagerRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private bool _onDataTriggered = new();
|
||||
|
||||
/// <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(2017, 12, 17);
|
||||
AddCrypto("BTCUSD");
|
||||
SetWarmup(1000000);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
|
||||
/// </summary>
|
||||
/// <param name="data">Slice object keyed by symbol containing the stock data</param>
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
_onDataTriggered = true;
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (IsWarmingUp)
|
||||
{
|
||||
throw new Exception("Warm up not complete");
|
||||
}
|
||||
if (!_onDataTriggered)
|
||||
{
|
||||
throw new Exception("No data received is OnData method");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
|
||||
/// </summary>
|
||||
public bool CanRunLocally { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public Language[] Languages { get; } = { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "0"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-0.56"},
|
||||
{"Tracking Error", "0.164"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Estimated Strategy Capacity", "$0"},
|
||||
{"Lowest Capacity Asset", ""},
|
||||
{"Fitness Score", "0"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
|
||||
{"Portfolio Turnover", "0"},
|
||||
{"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", "d41d8cd98f00b204e9800998ecf8427e"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using QuantConnect.Brokerages;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
|
||||
@@ -33,6 +34,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
SetStartDate(2018, 04, 04);
|
||||
SetEndDate(2018, 04, 04);
|
||||
SetBrokerageModel(BrokerageName.GDAX, AccountType.Cash);
|
||||
|
||||
// GDAX doesn't have LTCETH or ETHLTC, but they do have ETHUSD and LTCUSD to form a path between ETH and LTC
|
||||
SetAccountCurrency("ETH");
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using QuantConnect.Brokerages;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
|
||||
@@ -32,6 +33,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
SetStartDate(2018, 4, 5);
|
||||
SetEndDate(2018, 4, 5);
|
||||
SetBrokerageModel(BrokerageName.GDAX, AccountType.Cash);
|
||||
SetCash(10000);
|
||||
|
||||
SetWarmUp(TimeSpan.FromDays(1));
|
||||
@@ -100,14 +102,14 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Total Fees", "$29.84"},
|
||||
{"Estimated Strategy Capacity", "$410000.00"},
|
||||
{"Lowest Capacity Asset", "LTCUSD XJ"},
|
||||
{"Fitness Score", "0.499"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "-315.48"},
|
||||
{"Return Over Maximum Drawdown", "-189.336"},
|
||||
{"Portfolio Turnover", "0.999"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
@@ -122,7 +124,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "d38f5aec6a9c4dcf62de7b0dff26117f"}
|
||||
{"OrderListHash", "c764c98687300a2da250436baae2963c"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ class BasicSetAccountCurrencyAlgorithm(QCAlgorithm):
|
||||
|
||||
self.SetStartDate(2018, 4, 4) #Set Start Date
|
||||
self.SetEndDate(2018, 4, 4) #Set End Date
|
||||
self.SetBrokerageModel(BrokerageName.GDAX, AccountType.Cash);
|
||||
# Before setting any cash or adding a Security call SetAccountCurrency
|
||||
self.SetAccountCurrency("EUR")
|
||||
self.SetCash(100000) #Set Strategy Cash
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ class TwoLegCurrencyConversionRegressionAlgorithm(QCAlgorithm):
|
||||
def Initialize(self):
|
||||
self.SetStartDate(2018, 4, 4)
|
||||
self.SetEndDate(2018, 4, 4)
|
||||
|
||||
self.SetBrokerageModel(BrokerageName.GDAX, AccountType.Cash);
|
||||
# GDAX doesn't have LTCETH or ETHLTC, but they do have ETHUSD and LTCUSD to form a path between ETH and LTC
|
||||
self.SetAccountCurrency("ETH")
|
||||
self.SetCash("ETH", 100000)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1981,8 +1981,9 @@ namespace QuantConnect.Algorithm
|
||||
Universe universe;
|
||||
if (!UniverseManager.TryGetValue(universeSymbol, out universe))
|
||||
{
|
||||
var settings = new UniverseSettings(UniverseSettings) { DataNormalizationMode = DataNormalizationMode.Raw, Resolution = underlyingConfigs.GetHighestResolution() };
|
||||
universe = _pendingUniverseAdditions.FirstOrDefault(u => u.Configuration.Symbol == universeSymbol)
|
||||
?? AddUniverse(new OptionContractUniverse(new SubscriptionDataConfig(configs.First(), symbol: universeSymbol), UniverseSettings));
|
||||
?? AddUniverse(new OptionContractUniverse(new SubscriptionDataConfig(configs.First(), symbol: universeSymbol), settings));
|
||||
}
|
||||
|
||||
// update the universe
|
||||
|
||||
@@ -65,7 +65,9 @@ namespace QuantConnect.Algorithm.Selection
|
||||
_symbols.Remove(removedSymbol);
|
||||
|
||||
// the option has been removed! This can happen when the user manually removed the option contract we remove the underlying
|
||||
if (removedSymbol.SecurityType.IsOption())
|
||||
// but only if there isn't any other option selected using the same underlying!
|
||||
if (removedSymbol.SecurityType.IsOption()
|
||||
&& !_symbols.Any(symbol => symbol.SecurityType.IsOption() && symbol.Underlying == removedSymbol.Underlying))
|
||||
{
|
||||
Remove(removedSymbol.Underlying);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1,226 +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 QuantConnect.Brokerages.Binance.Messages;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Logging;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Orders.Fees;
|
||||
using QuantConnect.Securities;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using QuantConnect.Data;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance
|
||||
{
|
||||
public partial class BinanceBrokerage
|
||||
{
|
||||
private IDataAggregator _aggregator;
|
||||
|
||||
/// <summary>
|
||||
/// Locking object for the Ticks list in the data queue handler
|
||||
/// </summary>
|
||||
protected readonly object TickLocker = new object();
|
||||
|
||||
private void OnUserMessage(WebSocketMessage webSocketMessage)
|
||||
{
|
||||
var e = (WebSocketClientWrapper.TextMessage)webSocketMessage.Data;
|
||||
|
||||
try
|
||||
{
|
||||
if (Log.DebuggingEnabled)
|
||||
{
|
||||
Log.Debug($"BinanceBrokerage.OnUserMessage(): {e.Message}");
|
||||
}
|
||||
|
||||
var obj = JObject.Parse(e.Message);
|
||||
|
||||
var objError = obj["error"];
|
||||
if (objError != null)
|
||||
{
|
||||
var error = objError.ToObject<ErrorMessage>();
|
||||
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, error.Code, error.Message));
|
||||
return;
|
||||
}
|
||||
|
||||
var objData = obj;
|
||||
|
||||
var objEventType = objData["e"];
|
||||
if (objEventType != null)
|
||||
{
|
||||
var eventType = objEventType.ToObject<string>();
|
||||
|
||||
switch (eventType)
|
||||
{
|
||||
case "executionReport":
|
||||
var upd = objData.ToObject<Execution>();
|
||||
if (upd.ExecutionType.Equals("TRADE", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
OnFillOrder(upd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, -1, $"Parsing wss message failed. Data: {e.Message} Exception: {exception}"));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDataMessage(WebSocketMessage webSocketMessage)
|
||||
{
|
||||
var e = (WebSocketClientWrapper.TextMessage)webSocketMessage.Data;
|
||||
|
||||
try
|
||||
{
|
||||
var obj = JObject.Parse(e.Message);
|
||||
|
||||
var objError = obj["error"];
|
||||
if (objError != null)
|
||||
{
|
||||
var error = objError.ToObject<ErrorMessage>();
|
||||
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, error.Code, error.Message));
|
||||
return;
|
||||
}
|
||||
|
||||
var objData = obj;
|
||||
|
||||
var objEventType = objData["e"];
|
||||
if (objEventType != null)
|
||||
{
|
||||
var eventType = objEventType.ToObject<string>();
|
||||
|
||||
switch (eventType)
|
||||
{
|
||||
case "trade":
|
||||
var trade = objData.ToObject<Trade>();
|
||||
EmitTradeTick(
|
||||
_symbolMapper.GetLeanSymbol(trade.Symbol, SecurityType.Crypto, Market.Binance),
|
||||
Time.UnixMillisecondTimeStampToDateTime(trade.Time),
|
||||
trade.Price,
|
||||
trade.Quantity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (objData["u"] != null)
|
||||
{
|
||||
var quote = objData.ToObject<BestBidAskQuote>();
|
||||
EmitQuoteTick(
|
||||
_symbolMapper.GetLeanSymbol(quote.Symbol, SecurityType.Crypto, Market.Binance),
|
||||
quote.BestBidPrice,
|
||||
quote.BestBidSize,
|
||||
quote.BestAskPrice,
|
||||
quote.BestAskSize);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, -1, $"Parsing wss message failed. Data: {e.Message} Exception: {exception}"));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitQuoteTick(Symbol symbol, decimal bidPrice, decimal bidSize, decimal askPrice, decimal askSize)
|
||||
{
|
||||
var tick = new Tick
|
||||
{
|
||||
AskPrice = askPrice,
|
||||
BidPrice = bidPrice,
|
||||
Time = DateTime.UtcNow,
|
||||
Symbol = symbol,
|
||||
TickType = TickType.Quote,
|
||||
AskSize = askSize,
|
||||
BidSize = bidSize
|
||||
};
|
||||
tick.SetValue();
|
||||
|
||||
lock (TickLocker)
|
||||
{
|
||||
_aggregator.Update(tick);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitTradeTick(Symbol symbol, DateTime time, decimal price, decimal quantity)
|
||||
{
|
||||
var tick = new Tick
|
||||
{
|
||||
Symbol = symbol,
|
||||
Value = price,
|
||||
Quantity = Math.Abs(quantity),
|
||||
Time = time,
|
||||
TickType = TickType.Trade
|
||||
};
|
||||
|
||||
lock (TickLocker)
|
||||
{
|
||||
_aggregator.Update(tick);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFillOrder(Execution data)
|
||||
{
|
||||
try
|
||||
{
|
||||
var order = FindOrderByExternalId(data.OrderId);
|
||||
if (order == null)
|
||||
{
|
||||
// not our order, nothing else to do here
|
||||
return;
|
||||
}
|
||||
|
||||
var fillPrice = data.LastExecutedPrice;
|
||||
var fillQuantity = data.Direction == OrderDirection.Sell ? -data.LastExecutedQuantity : data.LastExecutedQuantity;
|
||||
var updTime = Time.UnixMillisecondTimeStampToDateTime(data.TransactionTime);
|
||||
var orderFee = new OrderFee(new CashAmount(data.Fee, data.FeeCurrency));
|
||||
var status = ConvertOrderStatus(data.OrderStatus);
|
||||
var orderEvent = new OrderEvent
|
||||
(
|
||||
order.Id, order.Symbol, updTime, status,
|
||||
data.Direction, fillPrice, fillQuantity,
|
||||
orderFee, $"Binance Order Event {data.Direction}"
|
||||
);
|
||||
|
||||
if (status == OrderStatus.Filled)
|
||||
{
|
||||
Orders.Order outOrder;
|
||||
CachedOrderIDs.TryRemove(order.Id, out outOrder);
|
||||
}
|
||||
|
||||
OnOrderEvent(orderEvent);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private Orders.Order FindOrderByExternalId(string brokerId)
|
||||
{
|
||||
var order = CachedOrderIDs
|
||||
.FirstOrDefault(o => o.Value.BrokerId.Contains(brokerId))
|
||||
.Value;
|
||||
if (order == null)
|
||||
{
|
||||
order = _algorithm.Transactions.GetOrderByBrokerageId(brokerId);
|
||||
}
|
||||
|
||||
return order;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +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 QuantConnect.Orders;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance
|
||||
{
|
||||
/// <summary>
|
||||
/// Binance utility methods
|
||||
/// </summary>
|
||||
public partial class BinanceBrokerage
|
||||
{
|
||||
private static OrderStatus ConvertOrderStatus(string raw)
|
||||
{
|
||||
switch (raw.LazyToUpper())
|
||||
{
|
||||
case "NEW":
|
||||
return OrderStatus.New;
|
||||
|
||||
case "PARTIALLY_FILLED":
|
||||
return OrderStatus.PartiallyFilled;
|
||||
|
||||
case "FILLED":
|
||||
return OrderStatus.Filled;
|
||||
|
||||
case "PENDING_CANCEL":
|
||||
return OrderStatus.CancelPending;
|
||||
|
||||
case "CANCELED":
|
||||
return OrderStatus.Canceled;
|
||||
|
||||
case "REJECTED":
|
||||
case "EXPIRED":
|
||||
return OrderStatus.Invalid;
|
||||
|
||||
default:
|
||||
return OrderStatus.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,596 +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 Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using QuantConnect.Configuration;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Logging;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Packets;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Util;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Timer = System.Timers.Timer;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance
|
||||
{
|
||||
/// <summary>
|
||||
/// Binance brokerage implementation
|
||||
/// </summary>
|
||||
[BrokerageFactory(typeof(BinanceBrokerageFactory))]
|
||||
public partial class BinanceBrokerage : BaseWebsocketsBrokerage, IDataQueueHandler
|
||||
{
|
||||
private IAlgorithm _algorithm;
|
||||
private readonly SymbolPropertiesDatabaseSymbolMapper _symbolMapper = new SymbolPropertiesDatabaseSymbolMapper(Market.Binance);
|
||||
|
||||
// Binance allows 5 messages per second, but we still get rate limited if we send a lot of messages at that rate
|
||||
// By sending 3 messages per second, evenly spaced out, we can keep sending messages without being limited
|
||||
private readonly RateGate _webSocketRateLimiter = new RateGate(1, TimeSpan.FromMilliseconds(330));
|
||||
|
||||
private long _lastRequestId;
|
||||
|
||||
private LiveNodePacket _job;
|
||||
private string _webSocketBaseUrl;
|
||||
private Timer _keepAliveTimer;
|
||||
private Timer _reconnectTimer;
|
||||
private BinanceRestApiClient _apiClient;
|
||||
private BrokerageConcurrentMessageHandler<WebSocketMessage> _messageHandler;
|
||||
|
||||
private const int MaximumSymbolsPerConnection = 512;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for brokerage
|
||||
/// </summary>
|
||||
public BinanceBrokerage() : base("Binance")
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for brokerage
|
||||
/// </summary>
|
||||
/// <param name="apiKey">api key</param>
|
||||
/// <param name="apiSecret">api secret</param>
|
||||
/// <param name="restApiUrl">The rest api url</param>
|
||||
/// <param name="webSocketBaseUrl">The web socket base url</param>
|
||||
/// <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>
|
||||
public BinanceBrokerage(string apiKey, string apiSecret, string restApiUrl, string webSocketBaseUrl, IAlgorithm algorithm, IDataAggregator aggregator, LiveNodePacket job)
|
||||
: base("Binance")
|
||||
{
|
||||
Initialize(
|
||||
wssUrl: webSocketBaseUrl,
|
||||
restApiUrl: restApiUrl,
|
||||
apiKey: apiKey,
|
||||
apiSecret: apiSecret,
|
||||
algorithm: algorithm,
|
||||
aggregator: aggregator,
|
||||
job: job
|
||||
);
|
||||
}
|
||||
|
||||
#region IBrokerage
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the websocket connection is connected or in the process of connecting
|
||||
/// </summary>
|
||||
public override bool IsConnected => WebSocket.IsOpen;
|
||||
|
||||
/// <summary>
|
||||
/// Creates wss connection
|
||||
/// </summary>
|
||||
public override void Connect()
|
||||
{
|
||||
if (IsConnected)
|
||||
return;
|
||||
|
||||
_apiClient.CreateListenKey();
|
||||
_reconnectTimer.Start();
|
||||
|
||||
WebSocket.Initialize($"{_webSocketBaseUrl}/{_apiClient.SessionId}");
|
||||
|
||||
base.Connect();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the websockets connection
|
||||
/// </summary>
|
||||
public override void Disconnect()
|
||||
{
|
||||
_reconnectTimer.Stop();
|
||||
|
||||
WebSocket?.Close();
|
||||
_apiClient.StopSession();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all open positions
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override List<Holding> GetAccountHoldings()
|
||||
{
|
||||
if (_algorithm.BrokerageModel.AccountType == AccountType.Cash)
|
||||
{
|
||||
return base.GetAccountHoldings(_job?.BrokerageData, _algorithm.Securities.Values);
|
||||
}
|
||||
return _apiClient.GetAccountHoldings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total account cash balance for specified account type
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override List<CashAmount> GetCashBalance()
|
||||
{
|
||||
var account = _apiClient.GetCashBalance();
|
||||
var balances = account.Balances?.Where(balance => balance.Amount > 0).ToList();
|
||||
if (balances == null || !balances.Any())
|
||||
return new List<CashAmount>();
|
||||
|
||||
return balances
|
||||
.Select(b => new CashAmount(b.Amount, b.Asset.LazyToUpper()))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all orders not yet closed
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override List<Order> GetOpenOrders()
|
||||
{
|
||||
var orders = _apiClient.GetOpenOrders();
|
||||
List<Order> list = new List<Order>();
|
||||
foreach (var item in orders)
|
||||
{
|
||||
Order order;
|
||||
switch (item.Type.LazyToUpper())
|
||||
{
|
||||
case "MARKET":
|
||||
order = new MarketOrder { Price = item.Price };
|
||||
break;
|
||||
|
||||
case "LIMIT":
|
||||
case "LIMIT_MAKER":
|
||||
order = new LimitOrder { LimitPrice = item.Price };
|
||||
break;
|
||||
|
||||
case "STOP_LOSS":
|
||||
case "TAKE_PROFIT":
|
||||
order = new StopMarketOrder { StopPrice = item.StopPrice, Price = item.Price };
|
||||
break;
|
||||
|
||||
case "STOP_LOSS_LIMIT":
|
||||
case "TAKE_PROFIT_LIMIT":
|
||||
order = new StopLimitOrder { StopPrice = item.StopPrice, LimitPrice = item.Price };
|
||||
break;
|
||||
|
||||
default:
|
||||
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, -1,
|
||||
"BinanceBrokerage.GetOpenOrders: Unsupported order type returned from brokerage: " + item.Type));
|
||||
continue;
|
||||
}
|
||||
|
||||
order.Quantity = item.Quantity;
|
||||
order.BrokerId = new List<string> { item.Id };
|
||||
order.Symbol = _symbolMapper.GetLeanSymbol(item.Symbol, SecurityType.Crypto, Market.Binance);
|
||||
order.Time = Time.UnixMillisecondTimeStampToDateTime(item.Time);
|
||||
order.Status = ConvertOrderStatus(item.Status);
|
||||
order.Price = item.Price;
|
||||
|
||||
if (order.Status.IsOpen())
|
||||
{
|
||||
var cached = CachedOrderIDs.Where(c => c.Value.BrokerId.Contains(order.BrokerId.First())).ToList();
|
||||
if (cached.Any())
|
||||
{
|
||||
CachedOrderIDs[cached.First().Key] = order;
|
||||
}
|
||||
}
|
||||
|
||||
list.Add(order);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Places a new order and assigns a new broker ID to the order
|
||||
/// </summary>
|
||||
/// <param name="order">The order to be placed</param>
|
||||
/// <returns>True if the request for a new order has been placed, false otherwise</returns>
|
||||
public override bool PlaceOrder(Order order)
|
||||
{
|
||||
var submitted = false;
|
||||
|
||||
_messageHandler.WithLockedStream(() =>
|
||||
{
|
||||
submitted = _apiClient.PlaceOrder(order);
|
||||
});
|
||||
|
||||
return submitted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the order with the same id
|
||||
/// </summary>
|
||||
/// <param name="order">The new order information</param>
|
||||
/// <returns>True if the request was made for the order to be updated, false otherwise</returns>
|
||||
public override bool UpdateOrder(Order order)
|
||||
{
|
||||
throw new NotSupportedException("BinanceBrokerage.UpdateOrder: Order update not supported. Please cancel and re-create.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the order with the specified ID
|
||||
/// </summary>
|
||||
/// <param name="order">The order to cancel</param>
|
||||
/// <returns>True if the request was submitted for cancellation, false otherwise</returns>
|
||||
public override bool CancelOrder(Order order)
|
||||
{
|
||||
var submitted = false;
|
||||
|
||||
_messageHandler.WithLockedStream(() =>
|
||||
{
|
||||
submitted = _apiClient.CancelOrder(order);
|
||||
});
|
||||
|
||||
return submitted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the history for the requested security
|
||||
/// </summary>
|
||||
/// <param name="request">The historical data request</param>
|
||||
/// <returns>An enumerable of bars covering the span specified in the request</returns>
|
||||
public override IEnumerable<BaseData> GetHistory(Data.HistoryRequest request)
|
||||
{
|
||||
if (request.Resolution == Resolution.Tick || request.Resolution == Resolution.Second)
|
||||
{
|
||||
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidResolution",
|
||||
$"{request.Resolution} resolution is not supported, no history returned"));
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (request.TickType != TickType.Trade)
|
||||
{
|
||||
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidTickType",
|
||||
$"{request.TickType} tick type not supported, no history returned"));
|
||||
yield break;
|
||||
}
|
||||
|
||||
var period = request.Resolution.ToTimeSpan();
|
||||
|
||||
foreach (var kline in _apiClient.GetHistory(request))
|
||||
{
|
||||
yield return new TradeBar()
|
||||
{
|
||||
Time = Time.UnixMillisecondTimeStampToDateTime(kline.OpenTime),
|
||||
Symbol = request.Symbol,
|
||||
Low = kline.Low,
|
||||
High = kline.High,
|
||||
Open = kline.Open,
|
||||
Close = kline.Close,
|
||||
Volume = kline.Volume,
|
||||
Value = kline.Close,
|
||||
DataType = MarketDataType.TradeBar,
|
||||
Period = period
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wss message handler
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
protected override void OnMessage(object sender, WebSocketMessage e)
|
||||
{
|
||||
_messageHandler.HandleNewMessage(e);
|
||||
}
|
||||
|
||||
#endregion IBrokerage
|
||||
|
||||
#region IDataQueueHandler
|
||||
|
||||
/// <summary>
|
||||
/// Sets the job we're subscribing for
|
||||
/// </summary>
|
||||
/// <param name="job">Job we're subscribing for</param>
|
||||
public void SetJob(LiveNodePacket job)
|
||||
{
|
||||
var webSocketBaseUrl = job.BrokerageData["binance-websocket-url"];
|
||||
var restApiUrl = job.BrokerageData["binance-api-url"];
|
||||
var apiKey = job.BrokerageData["binance-api-key"];
|
||||
var apiSecret = job.BrokerageData["binance-api-secret"];
|
||||
var aggregator = Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(
|
||||
Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"), forceTypeNameOnExisting: false);
|
||||
|
||||
Initialize(
|
||||
wssUrl: webSocketBaseUrl,
|
||||
restApiUrl: restApiUrl,
|
||||
apiKey: apiKey,
|
||||
apiSecret: apiSecret,
|
||||
algorithm: null,
|
||||
aggregator: aggregator,
|
||||
job: job
|
||||
);
|
||||
|
||||
if (!IsConnected)
|
||||
{
|
||||
Connect();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to the specified configuration
|
||||
/// </summary>
|
||||
/// <param name="dataConfig">defines the parameters to subscribe to a data feed</param>
|
||||
/// <param name="newDataAvailableHandler">handler to be fired on new data available</param>
|
||||
/// <returns>The new enumerator for this subscription request</returns>
|
||||
public IEnumerator<BaseData> Subscribe(SubscriptionDataConfig dataConfig, EventHandler newDataAvailableHandler)
|
||||
{
|
||||
if (!CanSubscribe(dataConfig.Symbol))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var enumerator = _aggregator.Add(dataConfig, newDataAvailableHandler);
|
||||
SubscriptionManager.Subscribe(dataConfig);
|
||||
|
||||
return enumerator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified configuration
|
||||
/// </summary>
|
||||
/// <param name="dataConfig">Subscription config to be removed</param>
|
||||
public void Unsubscribe(SubscriptionDataConfig dataConfig)
|
||||
{
|
||||
SubscriptionManager.Unsubscribe(dataConfig);
|
||||
_aggregator.Remove(dataConfig);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if this brokerage supports the specified symbol
|
||||
/// </summary>
|
||||
/// <param name="symbol">The symbol</param>
|
||||
/// <returns>returns true if brokerage supports the specified symbol; otherwise false</returns>
|
||||
private static bool CanSubscribe(Symbol symbol)
|
||||
{
|
||||
return !symbol.Value.Contains("UNIVERSE") &&
|
||||
symbol.SecurityType == SecurityType.Crypto &&
|
||||
symbol.ID.Market == Market.Binance;
|
||||
}
|
||||
|
||||
#endregion IDataQueueHandler
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
_keepAliveTimer.DisposeSafely();
|
||||
_reconnectTimer.DisposeSafely();
|
||||
_apiClient.DisposeSafely();
|
||||
_webSocketRateLimiter.DisposeSafely();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not used
|
||||
/// </summary>
|
||||
protected override bool Subscribe(IEnumerable<Symbol> symbols)
|
||||
{
|
||||
// NOP
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the instance of this class
|
||||
/// </summary>
|
||||
/// <param name="wssUrl">The web socket base url</param>
|
||||
/// <param name="restApiUrl">The rest api url</param>
|
||||
/// <param name="apiKey">api key</param>
|
||||
/// <param name="apiSecret">api secret</param>
|
||||
/// <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,
|
||||
IAlgorithm algorithm, IDataAggregator aggregator, LiveNodePacket job)
|
||||
{
|
||||
if (IsInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
base.Initialize(wssUrl, new WebSocketClientWrapper(), null, apiKey, apiSecret);
|
||||
_job = job;
|
||||
_algorithm = algorithm;
|
||||
_aggregator = aggregator;
|
||||
_webSocketBaseUrl = wssUrl;
|
||||
_messageHandler = new BrokerageConcurrentMessageHandler<WebSocketMessage>(OnUserMessage);
|
||||
|
||||
var maximumWebSocketConnections = Config.GetInt("binance-maximum-websocket-connections");
|
||||
var symbolWeights = maximumWebSocketConnections > 0 ? FetchSymbolWeights() : null;
|
||||
|
||||
var subscriptionManager = new BrokerageMultiWebSocketSubscriptionManager(
|
||||
wssUrl,
|
||||
MaximumSymbolsPerConnection,
|
||||
maximumWebSocketConnections,
|
||||
symbolWeights,
|
||||
() => new BinanceWebSocketWrapper(null),
|
||||
Subscribe,
|
||||
Unsubscribe,
|
||||
OnDataMessage,
|
||||
new TimeSpan(23, 45, 0));
|
||||
|
||||
SubscriptionManager = subscriptionManager;
|
||||
|
||||
_apiClient = new BinanceRestApiClient(_symbolMapper,
|
||||
algorithm?.Portfolio,
|
||||
apiKey,
|
||||
apiSecret,
|
||||
restApiUrl);
|
||||
|
||||
_apiClient.OrderSubmit += (s, e) => OnOrderSubmit(e);
|
||||
_apiClient.OrderStatusChanged += (s, e) => OnOrderEvent(e);
|
||||
_apiClient.Message += (s, e) => OnMessage(e);
|
||||
|
||||
// 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
|
||||
_keepAliveTimer = new Timer
|
||||
{
|
||||
// 30 minutes
|
||||
Interval = 30 * 60 * 1000
|
||||
};
|
||||
_keepAliveTimer.Elapsed += (s, e) => _apiClient.SessionKeepAlive();
|
||||
|
||||
WebSocket.Open += (s, e) => { _keepAliveTimer.Start(); };
|
||||
WebSocket.Closed += (s, e) => { _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
|
||||
_reconnectTimer = new Timer
|
||||
{
|
||||
// 23.5 hours
|
||||
Interval = 23.5 * 60 * 60 * 1000
|
||||
};
|
||||
_reconnectTimer.Elapsed += (s, e) =>
|
||||
{
|
||||
Log.Trace("Daily websocket restart: disconnect");
|
||||
Disconnect();
|
||||
|
||||
Log.Trace("Daily websocket restart: connect");
|
||||
Connect();
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to the requested symbol (using an individual streaming channel)
|
||||
/// </summary>
|
||||
/// <param name="webSocket">The websocket instance</param>
|
||||
/// <param name="symbol">The symbol to subscribe</param>
|
||||
private bool Subscribe(IWebSocket webSocket, Symbol symbol)
|
||||
{
|
||||
Send(webSocket,
|
||||
new
|
||||
{
|
||||
method = "SUBSCRIBE",
|
||||
@params = new[]
|
||||
{
|
||||
$"{symbol.Value.ToLowerInvariant()}@trade",
|
||||
$"{symbol.Value.ToLowerInvariant()}@bookTicker"
|
||||
},
|
||||
id = GetNextRequestId()
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends current subscription
|
||||
/// </summary>
|
||||
/// <param name="webSocket">The websocket instance</param>
|
||||
/// <param name="symbol">The symbol to unsubscribe</param>
|
||||
private bool Unsubscribe(IWebSocket webSocket, Symbol symbol)
|
||||
{
|
||||
Send(webSocket,
|
||||
new
|
||||
{
|
||||
method = "UNSUBSCRIBE",
|
||||
@params = new[]
|
||||
{
|
||||
$"{symbol.Value.ToLowerInvariant()}@trade",
|
||||
$"{symbol.Value.ToLowerInvariant()}@bookTicker"
|
||||
},
|
||||
id = GetNextRequestId()
|
||||
}
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Send(IWebSocket webSocket, object obj)
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(obj);
|
||||
|
||||
_webSocketRateLimiter.WaitToProceed();
|
||||
|
||||
Log.Trace("Send: " + json);
|
||||
|
||||
webSocket.Send(json);
|
||||
}
|
||||
|
||||
private long GetNextRequestId()
|
||||
{
|
||||
return Interlocked.Increment(ref _lastRequestId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event invocator for the OrderFilled event
|
||||
/// </summary>
|
||||
/// <param name="e">The OrderEvent</param>
|
||||
private void OnOrderSubmit(BinanceOrderSubmitEventArgs e)
|
||||
{
|
||||
var brokerId = e.BrokerId;
|
||||
var order = e.Order;
|
||||
if (CachedOrderIDs.ContainsKey(order.Id))
|
||||
{
|
||||
CachedOrderIDs[order.Id].BrokerId.Clear();
|
||||
CachedOrderIDs[order.Id].BrokerId.Add(brokerId);
|
||||
}
|
||||
else
|
||||
{
|
||||
order.BrokerId.Add(brokerId);
|
||||
CachedOrderIDs.TryAdd(order.Id, order);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the weights for each symbol (the weight value is the count of trades in the last 24 hours)
|
||||
/// </summary>
|
||||
private static Dictionary<Symbol, int> FetchSymbolWeights()
|
||||
{
|
||||
var dict = new Dictionary<Symbol, int>();
|
||||
|
||||
try
|
||||
{
|
||||
const string url = "https://api.binance.com/api/v3/ticker/24hr";
|
||||
var json = url.DownloadData();
|
||||
|
||||
foreach (var row in JArray.Parse(json))
|
||||
{
|
||||
var ticker = row["symbol"].ToObject<string>();
|
||||
var count = row["count"].ToObject<int>();
|
||||
|
||||
var symbol = Symbol.Create(ticker, SecurityType.Crypto, Market.Binance);
|
||||
|
||||
dict.Add(symbol, count);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.Error(exception);
|
||||
throw;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,98 +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;
|
||||
using System.Collections.Generic;
|
||||
using QuantConnect.Configuration;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Util;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory method to create binance Websockets brokerage
|
||||
/// </summary>
|
||||
public class BinanceBrokerageFactory : BrokerageFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory constructor
|
||||
/// </summary>
|
||||
public BinanceBrokerageFactory() : base(typeof(BinanceBrokerage))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not required
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// provides brokerage connection data
|
||||
/// </summary>
|
||||
public override Dictionary<string, string> BrokerageData => new Dictionary<string, string>
|
||||
{
|
||||
{ "binance-api-key", Config.Get("binance-api-key")},
|
||||
{ "binance-api-secret", Config.Get("binance-api-secret")},
|
||||
// paper trading available using https://testnet.binance.vision
|
||||
{ "binance-api-url", Config.Get("binance-api-url", "https://api.binance.com")},
|
||||
// paper trading available using wss://testnet.binance.vision/ws
|
||||
{ "binance-websocket-url", Config.Get("binance-websocket-url", "wss://stream.binance.com:9443/ws")},
|
||||
|
||||
// load holdings if available
|
||||
{ "live-holdings", Config.Get("live-holdings")},
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The brokerage model
|
||||
/// </summary>
|
||||
/// <param name="orderProvider">The order provider</param>
|
||||
public override IBrokerageModel GetBrokerageModel(IOrderProvider orderProvider) => new BinanceBrokerageModel();
|
||||
|
||||
/// <summary>
|
||||
/// Create the Brokerage instance
|
||||
/// </summary>
|
||||
/// <param name="job"></param>
|
||||
/// <param name="algorithm"></param>
|
||||
/// <returns></returns>
|
||||
public override IBrokerage CreateBrokerage(Packets.LiveNodePacket job, IAlgorithm algorithm)
|
||||
{
|
||||
var required = new[] { "binance-api-secret", "binance-api-key", "binance-api-url", "binance-websocket-url" };
|
||||
|
||||
foreach (var item in required)
|
||||
{
|
||||
if (string.IsNullOrEmpty(job.BrokerageData[item]))
|
||||
{
|
||||
throw new ArgumentException($"BinanceBrokerageFactory.CreateBrokerage: Missing {item} in config.json");
|
||||
}
|
||||
}
|
||||
|
||||
var brokerage = new BinanceBrokerage(
|
||||
job.BrokerageData["binance-api-key"],
|
||||
job.BrokerageData["binance-api-secret"],
|
||||
job.BrokerageData["binance-api-url"],
|
||||
job.BrokerageData["binance-websocket-url"],
|
||||
algorithm,
|
||||
Composer.Instance.GetExportedValueByTypeName<IDataAggregator>(Config.Get("data-aggregator", "QuantConnect.Lean.Engine.DataFeeds.AggregationManager"), forceTypeNameOnExisting: false),
|
||||
job);
|
||||
Composer.Instance.AddPart<IDataQueueHandler>(brokerage);
|
||||
|
||||
return brokerage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 QuantConnect.Orders;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a binance submit order event data
|
||||
/// </summary>
|
||||
public class BinanceOrderSubmitEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Order Event Constructor.
|
||||
/// </summary>
|
||||
/// <param name="brokerId">Binance order id returned from brokerage</param>
|
||||
/// <param name="order">Order for this order placement</param>
|
||||
public BinanceOrderSubmitEventArgs(string brokerId, Order order)
|
||||
{
|
||||
BrokerId = brokerId;
|
||||
Order = order;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Original brokerage id
|
||||
/// </summary>
|
||||
public string BrokerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The lean order
|
||||
/// </summary>
|
||||
public Order Order { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,606 +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 Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using QuantConnect.Logging;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Orders.Fees;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Util;
|
||||
using RestSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance
|
||||
{
|
||||
/// <summary>
|
||||
/// Binance REST API implementation
|
||||
/// </summary>
|
||||
public class BinanceRestApiClient : IDisposable
|
||||
{
|
||||
private const string UserDataStreamEndpoint = "/api/v3/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();
|
||||
|
||||
/// <summary>
|
||||
/// Event that fires each time an order is filled
|
||||
/// </summary>
|
||||
public event EventHandler<BinanceOrderSubmitEventArgs> OrderSubmit;
|
||||
|
||||
/// <summary>
|
||||
/// Event that fires each time an order is filled
|
||||
/// </summary>
|
||||
public event EventHandler<OrderEvent> OrderStatusChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Event that fires when an error is encountered in the brokerage
|
||||
/// </summary>
|
||||
public event EventHandler<BrokerageMessageEvent> Message;
|
||||
|
||||
/// <summary>
|
||||
/// Key Header
|
||||
/// </summary>
|
||||
public readonly string KeyHeader = "X-MBX-APIKEY";
|
||||
|
||||
/// <summary>
|
||||
/// The api secret
|
||||
/// </summary>
|
||||
protected string ApiSecret;
|
||||
|
||||
/// <summary>
|
||||
/// The api key
|
||||
/// </summary>
|
||||
protected string ApiKey;
|
||||
|
||||
/// <summary>
|
||||
/// Represents UserData Session listen key
|
||||
/// </summary>
|
||||
public string SessionId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BinanceRestApiClient"/> 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)
|
||||
{
|
||||
_symbolMapper = symbolMapper;
|
||||
_securityProvider = securityProvider;
|
||||
_restClient = new RestClient(restApiUrl);
|
||||
ApiKey = apiKey;
|
||||
ApiSecret = apiSecret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all open positions
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<Holding> GetAccountHoldings()
|
||||
{
|
||||
return new List<Holding>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total account cash balance for specified account type
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Messages.AccountInformation GetCashBalance()
|
||||
{
|
||||
var queryString = $"timestamp={GetNonce()}";
|
||||
var endpoint = $"/api/v3/account?{queryString}&signature={AuthenticationToken(queryString)}";
|
||||
var request = new RestRequest(endpoint, Method.GET);
|
||||
request.AddHeader(KeyHeader, ApiKey);
|
||||
|
||||
var response = ExecuteRestRequest(request);
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all orders not yet closed
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<Messages.OpenOrder> GetOpenOrders()
|
||||
{
|
||||
var queryString = $"timestamp={GetNonce()}";
|
||||
var endpoint = $"/api/v3/openOrders?{queryString}&signature={AuthenticationToken(queryString)}";
|
||||
var request = new RestRequest(endpoint, Method.GET);
|
||||
request.AddHeader(KeyHeader, ApiKey);
|
||||
|
||||
var response = ExecuteRestRequest(request);
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new Exception($"BinanceBrokerage.GetCashBalance: request failed: [{(int)response.StatusCode}] {response.StatusDescription}, Content: {response.Content}, ErrorMessage: {response.ErrorMessage}");
|
||||
}
|
||||
|
||||
return JsonConvert.DeserializeObject<Messages.OpenOrder[]>(response.Content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Places a new order and assigns a new broker ID to the order
|
||||
/// </summary>
|
||||
/// <param name="order">The order to be placed</param>
|
||||
/// <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) }
|
||||
};
|
||||
|
||||
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);
|
||||
request.AddHeader(KeyHeader, ApiKey);
|
||||
request.AddParameter(
|
||||
"application/x-www-form-urlencoded",
|
||||
Encoding.UTF8.GetBytes(body.ToQueryString()),
|
||||
ParameterType.RequestBody
|
||||
);
|
||||
|
||||
var response = ExecuteRestRequest(request);
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
var raw = JsonConvert.DeserializeObject<Messages.NewOrder>(response.Content);
|
||||
|
||||
if (string.IsNullOrEmpty(raw?.Id))
|
||||
{
|
||||
var errorMessage = $"Error parsing response from place order: {response.Content}";
|
||||
OnOrderEvent(new OrderEvent(
|
||||
order,
|
||||
DateTime.UtcNow,
|
||||
OrderFee.Zero,
|
||||
"Binance Order Event")
|
||||
{ Status = OrderStatus.Invalid, Message = errorMessage });
|
||||
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, (int)response.StatusCode, errorMessage));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
OnOrderSubmit(raw, order);
|
||||
return true;
|
||||
}
|
||||
|
||||
var message = $"Order failed, Order Id: {order.Id} timestamp: {order.Time} quantity: {order.Quantity} content: {response.Content}";
|
||||
OnOrderEvent(new OrderEvent(
|
||||
order,
|
||||
DateTime.UtcNow,
|
||||
OrderFee.Zero,
|
||||
"Binance Order Event")
|
||||
{ Status = OrderStatus.Invalid });
|
||||
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Warning, -1, message));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the order with the specified ID
|
||||
/// </summary>
|
||||
/// <param name="order">The order to cancel</param>
|
||||
/// <returns>True if the request was submitted for cancellation, false otherwise</returns>
|
||||
public bool CancelOrder(Order order)
|
||||
{
|
||||
var success = new List<bool>();
|
||||
IDictionary<string, object> body = new Dictionary<string, object>()
|
||||
{
|
||||
{ "symbol", _symbolMapper.GetBrokerageSymbol(order.Symbol) }
|
||||
};
|
||||
foreach (var id in order.BrokerId)
|
||||
{
|
||||
if (body.ContainsKey("signature"))
|
||||
{
|
||||
body.Remove("signature");
|
||||
}
|
||||
body["orderId"] = id;
|
||||
body["timestamp"] = GetNonce();
|
||||
body["signature"] = AuthenticationToken(body.ToQueryString());
|
||||
|
||||
var request = new RestRequest("/api/v3/order", Method.DELETE);
|
||||
request.AddHeader(KeyHeader, ApiKey);
|
||||
request.AddParameter(
|
||||
"application/x-www-form-urlencoded",
|
||||
Encoding.UTF8.GetBytes(body.ToQueryString()),
|
||||
ParameterType.RequestBody
|
||||
);
|
||||
|
||||
var response = ExecuteRestRequest(request);
|
||||
success.Add(response.StatusCode == HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
var canceled = false;
|
||||
if (success.All(a => a))
|
||||
{
|
||||
OnOrderEvent(new OrderEvent(order,
|
||||
DateTime.UtcNow,
|
||||
OrderFee.Zero,
|
||||
"Binance Order Event")
|
||||
{ Status = OrderStatus.Canceled });
|
||||
|
||||
canceled = true;
|
||||
}
|
||||
return canceled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the history for the requested security
|
||||
/// </summary>
|
||||
/// <param name="request">The historical data request</param>
|
||||
/// <returns>An enumerable of bars covering the span specified in the request</returns>
|
||||
public IEnumerable<Messages.Kline> GetHistory(Data.HistoryRequest request)
|
||||
{
|
||||
var resolution = ConvertResolution(request.Resolution);
|
||||
var resolutionInMs = (long)request.Resolution.ToTimeSpan().TotalMilliseconds;
|
||||
var symbol = _symbolMapper.GetBrokerageSymbol(request.Symbol);
|
||||
var startMs = (long)Time.DateTimeToUnixTimeStamp(request.StartTimeUtc) * 1000;
|
||||
var endMs = (long)Time.DateTimeToUnixTimeStamp(request.EndTimeUtc) * 1000;
|
||||
// we always use the real endpoint for history requests
|
||||
var endpoint = $"https://api.binance.com/api/v3/klines?symbol={symbol}&interval={resolution}&limit=1000";
|
||||
|
||||
while (endMs - startMs >= resolutionInMs)
|
||||
{
|
||||
var timeframe = $"&startTime={startMs}&endTime={endMs}";
|
||||
|
||||
var restRequest = new RestRequest(endpoint + timeframe, Method.GET);
|
||||
var response = ExecuteRestRequest(restRequest);
|
||||
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new Exception($"BinanceBrokerage.GetHistory: request failed: [{(int)response.StatusCode}] {response.StatusDescription}, Content: {response.Content}, ErrorMessage: {response.ErrorMessage}");
|
||||
}
|
||||
|
||||
var klines = JsonConvert.DeserializeObject<object[][]>(response.Content)
|
||||
.Select(entries => new Messages.Kline(entries))
|
||||
.ToList();
|
||||
|
||||
if (klines.Count > 0)
|
||||
{
|
||||
var lastValue = klines[klines.Count - 1];
|
||||
if (Log.DebuggingEnabled)
|
||||
{
|
||||
var windowStartTime = Time.UnixMillisecondTimeStampToDateTime(klines[0].OpenTime);
|
||||
var windowEndTime = Time.UnixMillisecondTimeStampToDateTime(lastValue.OpenTime + resolutionInMs);
|
||||
Log.Debug($"BinanceRestApiClient.GetHistory(): Received [{symbol}] data for timeperiod from {windowStartTime.ToStringInvariant()} to {windowEndTime.ToStringInvariant()}..");
|
||||
}
|
||||
startMs = lastValue.OpenTime + resolutionInMs;
|
||||
|
||||
foreach (var kline in klines)
|
||||
{
|
||||
yield return kline;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if there is no data just break
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check User Data stream listen key is alive
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool SessionKeepAlive()
|
||||
{
|
||||
if (string.IsNullOrEmpty(SessionId))
|
||||
{
|
||||
throw new Exception("BinanceBrokerage:UserStream. listenKey wasn't allocated or has been refused.");
|
||||
}
|
||||
|
||||
var ping = new RestRequest(UserDataStreamEndpoint, Method.PUT);
|
||||
ping.AddHeader(KeyHeader, ApiKey);
|
||||
ping.AddParameter(
|
||||
"application/x-www-form-urlencoded",
|
||||
Encoding.UTF8.GetBytes($"listenKey={SessionId}"),
|
||||
ParameterType.RequestBody
|
||||
);
|
||||
|
||||
var pong = ExecuteRestRequest(ping);
|
||||
return pong.StatusCode == HttpStatusCode.OK;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the session
|
||||
/// </summary>
|
||||
public void StopSession()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(SessionId))
|
||||
{
|
||||
var request = new RestRequest(UserDataStreamEndpoint, Method.DELETE);
|
||||
request.AddHeader(KeyHeader, ApiKey);
|
||||
request.AddParameter(
|
||||
"application/x-www-form-urlencoded",
|
||||
Encoding.UTF8.GetBytes($"listenKey={SessionId}"),
|
||||
ParameterType.RequestBody
|
||||
);
|
||||
ExecuteRestRequest(request);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides the current tickers price
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Messages.PriceTicker[] GetTickers()
|
||||
{
|
||||
const string endpoint = "/api/v3/ticker/price";
|
||||
var req = new RestRequest(endpoint, Method.GET);
|
||||
var response = ExecuteRestRequest(req);
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new Exception($"BinanceBrokerage.GetTick: request failed: [{(int)response.StatusCode}] {response.StatusDescription}, Content: {response.Content}, ErrorMessage: {response.ErrorMessage}");
|
||||
}
|
||||
|
||||
return JsonConvert.DeserializeObject<Messages.PriceTicker[]>(response.Content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start user data stream
|
||||
/// </summary>
|
||||
public void CreateListenKey()
|
||||
{
|
||||
var request = new RestRequest(UserDataStreamEndpoint, Method.POST);
|
||||
request.AddHeader(KeyHeader, ApiKey);
|
||||
|
||||
var response = ExecuteRestRequest(request);
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new Exception($"BinanceBrokerage.StartSession: request failed: [{(int)response.StatusCode}] {response.StatusDescription}, Content: {response.Content}, ErrorMessage: {response.ErrorMessage}");
|
||||
}
|
||||
|
||||
var content = JObject.Parse(response.Content);
|
||||
lock (_listenKeyLocker)
|
||||
{
|
||||
SessionId = content.Value<string>("listenKey");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_restRateLimiter.DisposeSafely();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If an IP address exceeds a certain number of requests per minute
|
||||
/// HTTP 429 return code is used when breaking a request rate limit.
|
||||
/// </summary>
|
||||
/// <param name="request"></param>
|
||||
/// <returns></returns>
|
||||
private IRestResponse ExecuteRestRequest(IRestRequest request)
|
||||
{
|
||||
const int maxAttempts = 10;
|
||||
var attempts = 0;
|
||||
IRestResponse response;
|
||||
|
||||
do
|
||||
{
|
||||
if (!_restRateLimiter.WaitToProceed(TimeSpan.Zero))
|
||||
{
|
||||
Log.Trace("Brokerage.OnMessage(): " + new BrokerageMessageEvent(BrokerageMessageType.Warning, "RateLimit",
|
||||
"The API request has been rate limited. To avoid this message, please reduce the frequency of API calls."));
|
||||
|
||||
_restRateLimiter.WaitToProceed();
|
||||
}
|
||||
|
||||
response = _restClient.Execute(request);
|
||||
// 429 status code: Too Many Requests
|
||||
} while (++attempts < maxAttempts && (int)response.StatusCode == 429);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private decimal GetTickerPrice(Order order)
|
||||
{
|
||||
var security = _securityProvider.GetSecurity(order.Symbol);
|
||||
var tickerPrice = order.Direction == OrderDirection.Buy ? security.AskPrice : security.BidPrice;
|
||||
if (tickerPrice == 0)
|
||||
{
|
||||
var brokerageSymbol = _symbolMapper.GetBrokerageSymbol(order.Symbol);
|
||||
var tickers = GetTickers();
|
||||
var ticker = tickers.FirstOrDefault(t => t.Symbol == brokerageSymbol);
|
||||
if (ticker == null)
|
||||
{
|
||||
throw new KeyNotFoundException($"BinanceBrokerage: Unable to resolve currency conversion pair: {order.Symbol}");
|
||||
}
|
||||
tickerPrice = ticker.Price;
|
||||
}
|
||||
return tickerPrice;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp in milliseconds
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private long GetNonce()
|
||||
{
|
||||
return (long)(Time.TimeStamp() * 1000);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a signature for signed endpoints
|
||||
/// </summary>
|
||||
/// <param name="payload">the body of the request</param>
|
||||
/// <returns>a token representing the request params</returns>
|
||||
private string AuthenticationToken(string payload)
|
||||
{
|
||||
using (HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(ApiSecret)))
|
||||
{
|
||||
return hmac.ComputeHash(Encoding.UTF8.GetBytes(payload)).ToHexString();
|
||||
}
|
||||
}
|
||||
|
||||
private static string ConvertOrderDirection(OrderDirection orderDirection)
|
||||
{
|
||||
if (orderDirection == OrderDirection.Buy || orderDirection == OrderDirection.Sell)
|
||||
{
|
||||
return orderDirection.ToString().LazyToUpper();
|
||||
}
|
||||
|
||||
throw new NotSupportedException($"BinanceBrokerage.ConvertOrderDirection: Unsupported order direction: {orderDirection}");
|
||||
}
|
||||
|
||||
|
||||
private readonly Dictionary<Resolution, string> _knownResolutions = new Dictionary<Resolution, string>()
|
||||
{
|
||||
{ Resolution.Minute, "1m" },
|
||||
{ Resolution.Hour, "1h" },
|
||||
{ Resolution.Daily, "1d" }
|
||||
};
|
||||
|
||||
private string ConvertResolution(Resolution resolution)
|
||||
{
|
||||
if (_knownResolutions.ContainsKey(resolution))
|
||||
{
|
||||
return _knownResolutions[resolution];
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"BinanceBrokerage.ConvertResolution: Unsupported resolution type: {resolution}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event invocator for the OrderFilled event
|
||||
/// </summary>
|
||||
/// <param name="newOrder">The brokerage order submit result</param>
|
||||
/// <param name="order">The lean order</param>
|
||||
private void OnOrderSubmit(Messages.NewOrder newOrder, Order order)
|
||||
{
|
||||
try
|
||||
{
|
||||
OrderSubmit?.Invoke(
|
||||
this,
|
||||
new BinanceOrderSubmitEventArgs(newOrder.Id, order));
|
||||
|
||||
// Generate submitted event
|
||||
OnOrderEvent(new OrderEvent(
|
||||
order,
|
||||
Time.UnixMillisecondTimeStampToDateTime(newOrder.TransactionTime),
|
||||
OrderFee.Zero,
|
||||
"Binance Order Event")
|
||||
{ Status = OrderStatus.Submitted }
|
||||
);
|
||||
Log.Trace($"Order submitted successfully - OrderId: {order.Id}");
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
Log.Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event invocator for the OrderFilled event
|
||||
/// </summary>
|
||||
/// <param name="e">The OrderEvent</param>
|
||||
private void OnOrderEvent(OrderEvent e)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.Debug("Brokerage.OnOrderEvent(): " + e);
|
||||
|
||||
OrderStatusChanged?.Invoke(this, e);
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
Log.Error(err);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event invocator for the Message event
|
||||
/// </summary>
|
||||
/// <param name="e">The error</param>
|
||||
protected virtual void OnMessage(BrokerageMessageEvent e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (e.Type == BrokerageMessageType.Error)
|
||||
{
|
||||
Log.Error("Brokerage.OnMessage(): " + e);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Trace("Brokerage.OnMessage(): " + e);
|
||||
}
|
||||
|
||||
Message?.Invoke(this, e);
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
Log.Error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +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;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper class for a Binance websocket connection
|
||||
/// </summary>
|
||||
public class BinanceWebSocketWrapper : WebSocketClientWrapper
|
||||
{
|
||||
/// <summary>
|
||||
/// The unique Id for the connection
|
||||
/// </summary>
|
||||
public string ConnectionId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The handler for the connection
|
||||
/// </summary>
|
||||
public IConnectionHandler ConnectionHandler { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BinanceWebSocketWrapper"/> class.
|
||||
/// </summary>
|
||||
public BinanceWebSocketWrapper(IConnectionHandler connectionHandler)
|
||||
{
|
||||
ConnectionId = Guid.NewGuid().ToString();
|
||||
ConnectionHandler = connectionHandler;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,209 +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 Newtonsoft.Json;
|
||||
using QuantConnect.Orders;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace QuantConnect.Brokerages.Binance.Messages
|
||||
{
|
||||
#pragma warning disable 1591
|
||||
|
||||
public class AccountInformation
|
||||
{
|
||||
public Balance[] 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 PriceTicker
|
||||
{
|
||||
public string Symbol { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
}
|
||||
|
||||
public class Order
|
||||
{
|
||||
[JsonProperty("orderId")]
|
||||
public string Id { get; set; }
|
||||
public string Symbol { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
public decimal StopPrice { get; set; }
|
||||
[JsonProperty("origQty")]
|
||||
public decimal OriginalAmount { get; set; }
|
||||
[JsonProperty("executedQty")]
|
||||
public decimal ExecutedAmount { get; set; }
|
||||
public string Status { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string Side { get; set; }
|
||||
|
||||
public decimal Quantity => string.Equals(Side, "buy", StringComparison.OrdinalIgnoreCase) ? OriginalAmount : -OriginalAmount;
|
||||
}
|
||||
|
||||
public class OpenOrder : Order
|
||||
{
|
||||
public long Time { get; set; }
|
||||
}
|
||||
|
||||
public class NewOrder : Order
|
||||
{
|
||||
[JsonProperty("transactTime")]
|
||||
public long TransactionTime { get; set; }
|
||||
}
|
||||
|
||||
public enum EventType
|
||||
{
|
||||
None,
|
||||
OrderBook,
|
||||
Trade,
|
||||
Execution
|
||||
}
|
||||
|
||||
public class ErrorMessage
|
||||
{
|
||||
[JsonProperty("code")]
|
||||
public int Code { get; set; }
|
||||
|
||||
[JsonProperty("msg")]
|
||||
public string Message { get; set; }
|
||||
}
|
||||
|
||||
public class BestBidAskQuote
|
||||
{
|
||||
[JsonProperty("u")]
|
||||
public long OrderBookUpdateId { get; set; }
|
||||
|
||||
[JsonProperty("s")]
|
||||
public string Symbol { get; set; }
|
||||
|
||||
[JsonProperty("b")]
|
||||
public decimal BestBidPrice { get; set; }
|
||||
|
||||
[JsonProperty("B")]
|
||||
public decimal BestBidSize { get; set; }
|
||||
|
||||
[JsonProperty("a")]
|
||||
public decimal BestAskPrice { get; set; }
|
||||
|
||||
[JsonProperty("A")]
|
||||
public decimal BestAskSize { get; set; }
|
||||
}
|
||||
|
||||
public class BaseMessage
|
||||
{
|
||||
public virtual EventType @Event { get; } = EventType.None;
|
||||
|
||||
[JsonProperty("e")]
|
||||
public string EventName { get; set; }
|
||||
|
||||
[JsonProperty("E")]
|
||||
public long Time { get; set; }
|
||||
|
||||
[JsonProperty("s")]
|
||||
public string Symbol { get; set; }
|
||||
}
|
||||
|
||||
public class Trade : BaseMessage
|
||||
{
|
||||
public override EventType @Event => EventType.Trade;
|
||||
|
||||
[JsonProperty("T")]
|
||||
public new long Time { get; set; }
|
||||
|
||||
[JsonProperty("p")]
|
||||
public decimal Price { get; private set; }
|
||||
|
||||
[JsonProperty("q")]
|
||||
public decimal Quantity { get; private set; }
|
||||
}
|
||||
|
||||
public class Execution : BaseMessage
|
||||
{
|
||||
public override EventType @Event => EventType.Execution;
|
||||
|
||||
[JsonProperty("i")]
|
||||
public string OrderId { get; set; }
|
||||
|
||||
[JsonProperty("t")]
|
||||
public string TradeId { get; set; }
|
||||
|
||||
[JsonProperty("I")]
|
||||
public string Ignore { get; set; }
|
||||
|
||||
[JsonProperty("x")]
|
||||
public string ExecutionType { get; private set; }
|
||||
|
||||
[JsonProperty("X")]
|
||||
public string OrderStatus { get; private set; }
|
||||
|
||||
[JsonProperty("T")]
|
||||
public long TransactionTime { get; set; }
|
||||
|
||||
[JsonProperty("L")]
|
||||
public decimal LastExecutedPrice { get; set; }
|
||||
|
||||
[JsonProperty("l")]
|
||||
public decimal LastExecutedQuantity { get; set; }
|
||||
|
||||
[JsonProperty("S")]
|
||||
public string Side { get; set; }
|
||||
|
||||
[JsonProperty("n")]
|
||||
public decimal Fee { get; set; }
|
||||
|
||||
[JsonProperty("N")]
|
||||
public string FeeCurrency { get; set; }
|
||||
|
||||
public OrderDirection Direction => Side.Equals("BUY", StringComparison.OrdinalIgnoreCase) ? OrderDirection.Buy : OrderDirection.Sell;
|
||||
}
|
||||
|
||||
public class Kline
|
||||
{
|
||||
public long OpenTime { get; }
|
||||
public decimal Open { get; }
|
||||
public decimal Close { get; }
|
||||
public decimal High { get; }
|
||||
public decimal Low { get; }
|
||||
public decimal Volume { get; }
|
||||
|
||||
public Kline() { }
|
||||
|
||||
public Kline(long msts, decimal close)
|
||||
{
|
||||
OpenTime = msts;
|
||||
Open = Close = High = Low = close;
|
||||
Volume = 0;
|
||||
}
|
||||
|
||||
public Kline(object[] entries)
|
||||
{
|
||||
OpenTime = Convert.ToInt64(entries[0], CultureInfo.InvariantCulture);
|
||||
Open = ((string)entries[1]).ToDecimal();
|
||||
High = ((string)entries[2]).ToDecimal();
|
||||
Low = ((string)entries[3]).ToDecimal();
|
||||
Close = ((string)entries[4]).ToDecimal();
|
||||
Volume = ((string)entries[5]).ToDecimal();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning restore 1591
|
||||
}
|
||||
@@ -24,7 +24,6 @@ using QuantConnect.Orders;
|
||||
using QuantConnect.Orders.Fees;
|
||||
using QuantConnect.Packets;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Securities.Crypto;
|
||||
using QuantConnect.Util;
|
||||
using RestSharp;
|
||||
using System;
|
||||
@@ -49,7 +48,6 @@ namespace QuantConnect.Brokerages.Bitfinex
|
||||
private IAlgorithm _algorithm;
|
||||
private readonly RateGate _restRateLimiter = new RateGate(10, TimeSpan.FromMinutes(1));
|
||||
private readonly ConcurrentDictionary<int, decimal> _fills = new ConcurrentDictionary<int, decimal>();
|
||||
private SymbolPropertiesDatabase _symbolPropertiesDatabase;
|
||||
private IDataAggregator _aggregator;
|
||||
|
||||
// map Bitfinex ClientOrderId -> LEAN order (only used for orders submitted in PlaceOrder, not for existing orders)
|
||||
@@ -188,7 +186,6 @@ namespace QuantConnect.Brokerages.Bitfinex
|
||||
TimeSpan.Zero,
|
||||
_connectionRateLimiter);
|
||||
|
||||
_symbolPropertiesDatabase = SymbolPropertiesDatabase.FromDataFolder();
|
||||
_algorithm = algorithm;
|
||||
_aggregator = aggregator;
|
||||
|
||||
@@ -473,27 +470,14 @@ namespace QuantConnect.Brokerages.Bitfinex
|
||||
: OrderStatus.PartiallyFilled;
|
||||
}
|
||||
|
||||
if (_algorithm.BrokerageModel.AccountType == AccountType.Cash &&
|
||||
order.Direction == OrderDirection.Buy)
|
||||
if (_algorithm.BrokerageModel.AccountType == AccountType.Cash && order.Direction == OrderDirection.Buy)
|
||||
{
|
||||
var symbolProperties = _symbolPropertiesDatabase.GetSymbolProperties(symbol.ID.Market,
|
||||
symbol,
|
||||
symbol.SecurityType,
|
||||
AccountBaseCurrency);
|
||||
Crypto.DecomposeCurrencyPair(symbol, symbolProperties, out var baseCurrency, out var _);
|
||||
|
||||
CurrencyPairUtil.DecomposeCurrencyPair(symbol, out var baseCurrency, out _);
|
||||
if (orderFee.Value.Currency != baseCurrency)
|
||||
{
|
||||
OnMessage(new BrokerageMessageEvent(BrokerageMessageType.Error, "UnexpectedFeeCurrency", $"Unexpected fee currency {orderFee.Value.Currency} for symbol {symbol}. OrderId {order.Id}. BrokerageOrderId {brokerId}. " +
|
||||
"This error can happen because your account is Margin type and Lean is configured to be Cash type or while using Cash type the Bitfinex account fee settings are set to 'Asset Trading Fee' and should be set to 'Currency Exchange Fee'."));
|
||||
}
|
||||
else
|
||||
{
|
||||
// fees are debited in the base currency, so we have to subtract them from the filled quantity
|
||||
fillQuantity -= orderFee.Value.Amount;
|
||||
|
||||
orderFee = new ModifiedFillQuantityOrderFee(orderFee.Value);
|
||||
}
|
||||
}
|
||||
|
||||
var orderEvent = new OrderEvent
|
||||
|
||||
@@ -23,7 +23,6 @@ using QuantConnect.Logging;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Packets;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Securities.Crypto;
|
||||
using QuantConnect.Util;
|
||||
using RestSharp;
|
||||
using System;
|
||||
@@ -308,16 +307,7 @@ namespace QuantConnect.Brokerages.Bitfinex
|
||||
foreach (var holding in GetAccountHoldings().Where(x => x.Symbol.SecurityType == SecurityType.Crypto))
|
||||
{
|
||||
var defaultQuoteCurrency = _algorithm.Portfolio.CashBook.AccountCurrency;
|
||||
|
||||
var symbolProperties = _symbolPropertiesDatabase.GetSymbolProperties(
|
||||
holding.Symbol.ID.Market,
|
||||
holding.Symbol,
|
||||
holding.Symbol.SecurityType,
|
||||
defaultQuoteCurrency);
|
||||
|
||||
string baseCurrency;
|
||||
string quoteCurrency;
|
||||
Crypto.DecomposeCurrencyPair(holding.Symbol, symbolProperties, out baseCurrency, out quoteCurrency);
|
||||
CurrencyPairUtil.DecomposeCurrencyPair(holding.Symbol, out var baseCurrency, out var quoteCurrency, defaultQuoteCurrency);
|
||||
|
||||
var baseQuantity = holding.Quantity;
|
||||
CashAmount baseCurrencyAmount;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
@@ -105,29 +105,29 @@ namespace QuantConnect.Brokerages.Tradier
|
||||
|
||||
var start = request.StartTimeUtc.ConvertTo(DateTimeZone.Utc, TimeZones.NewYork);
|
||||
var end = request.EndTimeUtc.ConvertTo(DateTimeZone.Utc, TimeZones.NewYork);
|
||||
|
||||
|
||||
var history = Enumerable.Empty<Slice>();
|
||||
|
||||
switch (request.Resolution)
|
||||
{
|
||||
case Resolution.Tick:
|
||||
history = GetHistoryTick(request.Symbol, start, end);
|
||||
history = GetHistoryTick(request, start, end);
|
||||
break;
|
||||
|
||||
case Resolution.Second:
|
||||
history = GetHistorySecond(request.Symbol, start, end);
|
||||
history = GetHistorySecond(request, start, end);
|
||||
break;
|
||||
|
||||
case Resolution.Minute:
|
||||
history = GetHistoryMinute(request.Symbol, start, end);
|
||||
history = GetHistoryMinute(request, start, end);
|
||||
break;
|
||||
|
||||
case Resolution.Hour:
|
||||
history = GetHistoryHour(request.Symbol, start, end);
|
||||
history = GetHistoryHour(request, start, end);
|
||||
break;
|
||||
|
||||
case Resolution.Daily:
|
||||
history = GetHistoryDaily(request.Symbol, start, end);
|
||||
history = GetHistoryDaily(request, start, end);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -174,8 +174,10 @@ namespace QuantConnect.Brokerages.Tradier
|
||||
ReaderErrorDetected?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private IEnumerable<Slice> GetHistoryTick(Symbol symbol, DateTime start, DateTime end)
|
||||
private IEnumerable<Slice> GetHistoryTick(HistoryRequest request, DateTime start, DateTime end)
|
||||
{
|
||||
var symbol = request.Symbol;
|
||||
var exchangeTz = request.ExchangeHours.TimeZone;
|
||||
var history = GetTimeSeries(symbol, start, end, TradierTimeSeriesIntervals.Tick);
|
||||
|
||||
if (history == null)
|
||||
@@ -192,11 +194,13 @@ namespace QuantConnect.Brokerages.Tradier
|
||||
TickType = TickType.Trade,
|
||||
Quantity = Convert.ToInt32(tick.Volume)
|
||||
})
|
||||
.Select(tradeBar => new Slice(tradeBar.EndTime, new[] { tradeBar }));
|
||||
.Select(tradeBar => new Slice(tradeBar.EndTime, new[] { tradeBar }, tradeBar.EndTime.ConvertToUtc(exchangeTz)));
|
||||
}
|
||||
|
||||
private IEnumerable<Slice> GetHistorySecond(Symbol symbol, DateTime start, DateTime end)
|
||||
private IEnumerable<Slice> GetHistorySecond(HistoryRequest request, DateTime start, DateTime end)
|
||||
{
|
||||
var symbol = request.Symbol;
|
||||
var exchangeTz = request.ExchangeHours.TimeZone;
|
||||
var history = GetTimeSeries(symbol, start, end, TradierTimeSeriesIntervals.Tick);
|
||||
|
||||
if (history == null)
|
||||
@@ -222,7 +226,7 @@ namespace QuantConnect.Brokerages.Tradier
|
||||
g.Last().LastPrice,
|
||||
g.Sum(t => t.Quantity),
|
||||
Time.OneSecond))
|
||||
.Select(tradeBar => new Slice(tradeBar.EndTime, new[] { tradeBar }))
|
||||
.Select(tradeBar => new Slice(tradeBar.EndTime, new[] { tradeBar }, tradeBar.EndTime.ConvertToUtc(exchangeTz)))
|
||||
.ToList();
|
||||
|
||||
DataPointCount += result.Count;
|
||||
@@ -230,8 +234,10 @@ namespace QuantConnect.Brokerages.Tradier
|
||||
return result;
|
||||
}
|
||||
|
||||
private IEnumerable<Slice> GetHistoryMinute(Symbol symbol, DateTime start, DateTime end)
|
||||
private IEnumerable<Slice> GetHistoryMinute(HistoryRequest request, DateTime start, DateTime end)
|
||||
{
|
||||
var symbol = request.Symbol;
|
||||
var exchangeTz = request.ExchangeHours.TimeZone;
|
||||
var history = GetTimeSeries(symbol, start, end, TradierTimeSeriesIntervals.OneMinute);
|
||||
|
||||
if (history == null)
|
||||
@@ -241,11 +247,13 @@ namespace QuantConnect.Brokerages.Tradier
|
||||
|
||||
return history
|
||||
.Select(bar => new TradeBar(bar.Time, symbol, bar.Open, bar.High, bar.Low, bar.Close, bar.Volume, Time.OneMinute))
|
||||
.Select(tradeBar => new Slice(tradeBar.EndTime, new[] { tradeBar }));
|
||||
.Select(tradeBar => new Slice(tradeBar.EndTime, new[] { tradeBar }, tradeBar.EndTime.ConvertToUtc(exchangeTz)));
|
||||
}
|
||||
|
||||
private IEnumerable<Slice> GetHistoryHour(Symbol symbol, DateTime start, DateTime end)
|
||||
private IEnumerable<Slice> GetHistoryHour(HistoryRequest request, DateTime start, DateTime end)
|
||||
{
|
||||
var symbol = request.Symbol;
|
||||
var exchangeTz = request.ExchangeHours.TimeZone;
|
||||
var history = GetTimeSeries(symbol, start, end, TradierTimeSeriesIntervals.FifteenMinutes);
|
||||
|
||||
if (history == null)
|
||||
@@ -264,7 +272,7 @@ namespace QuantConnect.Brokerages.Tradier
|
||||
g.Last().Close,
|
||||
g.Sum(t => t.Volume),
|
||||
Time.OneHour))
|
||||
.Select(tradeBar => new Slice(tradeBar.EndTime, new[] { tradeBar }))
|
||||
.Select(tradeBar => new Slice(tradeBar.EndTime, new[] { tradeBar }, tradeBar.EndTime.ConvertToUtc(exchangeTz)))
|
||||
.ToList();
|
||||
|
||||
DataPointCount += result.Count;
|
||||
@@ -272,15 +280,17 @@ namespace QuantConnect.Brokerages.Tradier
|
||||
return result;
|
||||
}
|
||||
|
||||
private IEnumerable<Slice> GetHistoryDaily(Symbol symbol, DateTime start, DateTime end)
|
||||
private IEnumerable<Slice> GetHistoryDaily(HistoryRequest request, DateTime start, DateTime end)
|
||||
{
|
||||
var symbol = request.Symbol;
|
||||
var exchangeTz = request.ExchangeHours.TimeZone;
|
||||
var history = GetHistoricalData(symbol, start, end);
|
||||
|
||||
DataPointCount += history.Count;
|
||||
|
||||
return history
|
||||
.Select(bar => new TradeBar(bar.Time, symbol, bar.Open, bar.High, bar.Low, bar.Close, bar.Volume, Time.OneDay))
|
||||
.Select(tradeBar => new Slice(tradeBar.EndTime, new[] { tradeBar }));
|
||||
.Select(tradeBar => new Slice(tradeBar.EndTime, new[] { tradeBar }, tradeBar.EndTime.ConvertToUtc(exchangeTz)));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -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 *
|
||||
|
||||
@@ -66,13 +66,25 @@ namespace QuantConnect.Api
|
||||
/// </returns>
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
var jObject = JObject.Load(reader);
|
||||
if (reader.TokenType == JsonToken.StartArray)
|
||||
{
|
||||
if (JArray.Load(reader).Count == 0)
|
||||
{
|
||||
return new ParameterSet(-1, new Dictionary<string, string>());
|
||||
}
|
||||
}
|
||||
else if (reader.TokenType == JsonToken.StartObject)
|
||||
{
|
||||
var jObject = JObject.Load(reader);
|
||||
|
||||
var value = jObject["parameterSet"] ?? jObject;
|
||||
var value = jObject["parameterSet"] ?? jObject;
|
||||
|
||||
var parameterSet = new ParameterSet(-1, value.ToObject<Dictionary<string, string>>());
|
||||
var parameterSet = new ParameterSet(-1, value.ToObject<Dictionary<string, string>>());
|
||||
|
||||
return parameterSet;
|
||||
return parameterSet;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Unexpected Tokentype {reader.TokenType}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,10 +29,17 @@ namespace QuantConnect.Brokerages
|
||||
/// </summary>
|
||||
public class BinanceBrokerageModel : DefaultBrokerageModel
|
||||
{
|
||||
private const decimal _defaultLeverage = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Market name
|
||||
/// </summary>
|
||||
protected virtual string MarketName => Market.Binance;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a map of the default markets to be used for each security type
|
||||
/// </summary>
|
||||
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets();
|
||||
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets(Market.Binance);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BinanceBrokerageModel"/> class
|
||||
@@ -41,22 +47,6 @@ 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
|
||||
/// </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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -66,8 +56,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>
|
||||
@@ -77,7 +71,7 @@ namespace QuantConnect.Brokerages
|
||||
/// <returns>The benchmark for this brokerage</returns>
|
||||
public override IBenchmark GetBenchmark(SecurityManager securities)
|
||||
{
|
||||
var symbol = Symbol.Create("BTCUSDC", SecurityType.Crypto, Market.Binance);
|
||||
var symbol = Symbol.Create("BTCUSDC", SecurityType.Crypto, MarketName);
|
||||
return SecurityBenchmark.CreateInstance(securities, symbol);
|
||||
}
|
||||
|
||||
@@ -187,10 +181,10 @@ namespace QuantConnect.Brokerages
|
||||
order.AbsoluteQuantity * price > security.SymbolProperties.MinimumOrderSize;
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<SecurityType, string> GetDefaultMarkets()
|
||||
protected static IReadOnlyDictionary<SecurityType, string> GetDefaultMarkets(string marketName)
|
||||
{
|
||||
var map = DefaultMarketMap.ToDictionary();
|
||||
map[SecurityType.Crypto] = Market.Binance;
|
||||
map[SecurityType.Crypto] = marketName;
|
||||
return map.ToReadOnlyDictionary();
|
||||
}
|
||||
}
|
||||
|
||||
60
Common/Brokerages/BinanceUSBrokerageModel.cs
Normal file
60
Common/Brokerages/BinanceUSBrokerageModel.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 QuantConnect.Securities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Brokerages
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides Binance.US specific properties
|
||||
/// </summary>
|
||||
public class BinanceUSBrokerageModel : BinanceBrokerageModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Market name
|
||||
/// </summary>
|
||||
protected override string MarketName => Market.BinanceUS;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a map of the default markets to be used for each security type
|
||||
/// </summary>
|
||||
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets(Market.BinanceUS);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BinanceBrokerageModel"/> class
|
||||
/// </summary>
|
||||
/// <param name="accountType">The type of account to be modeled, defaults to <see cref="AccountType.Cash"/></param>
|
||||
public BinanceUSBrokerageModel(AccountType accountType = AccountType.Cash) : base(accountType)
|
||||
{
|
||||
if (accountType == AccountType.Margin)
|
||||
{
|
||||
throw new ArgumentException("The Binance.US brokerage does not currently support Margin trading.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binance global leverage rule
|
||||
/// </summary>
|
||||
/// <param name="security"></param>
|
||||
/// <returns></returns>
|
||||
public override decimal GetLeverage(Security security)
|
||||
{
|
||||
// margin trading is not currently supported by Binance.US
|
||||
return 1m;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,20 +45,6 @@ namespace QuantConnect.Brokerages
|
||||
{
|
||||
}
|
||||
|
||||
/// <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.
|
||||
/// For margin trading, max leverage = 3.3
|
||||
/// </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 AccountType == AccountType.Cash
|
||||
? (IBuyingPowerModel)new CashBuyingPowerModel()
|
||||
: new SecurityMarginModel(_maxLeverage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bitfinex global leverage rule
|
||||
/// </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,16 @@ 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,
|
||||
|
||||
/// <summary>
|
||||
/// Transaction and submit/execution rules will use Binance.US models
|
||||
/// </summary>
|
||||
BinanceUS,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,33 +351,16 @@ namespace QuantConnect.Brokerages
|
||||
/// <returns>The buying power model for this brokerage/security</returns>
|
||||
public virtual IBuyingPowerModel GetBuyingPowerModel(Security security)
|
||||
{
|
||||
var leverage = GetLeverage(security);
|
||||
IBuyingPowerModel model;
|
||||
|
||||
switch (security.Type)
|
||||
return security.Type switch
|
||||
{
|
||||
case SecurityType.Crypto:
|
||||
model = new CashBuyingPowerModel();
|
||||
break;
|
||||
case SecurityType.Forex:
|
||||
case SecurityType.Cfd:
|
||||
model = new SecurityMarginModel(leverage, RequiredFreeBuyingPowerPercent);
|
||||
break;
|
||||
case SecurityType.Option:
|
||||
model = new OptionMarginModel(RequiredFreeBuyingPowerPercent);
|
||||
break;
|
||||
case SecurityType.FutureOption:
|
||||
model = new FuturesOptionsMarginModel(RequiredFreeBuyingPowerPercent, (Option)security);
|
||||
break;
|
||||
case SecurityType.Future:
|
||||
model = new FutureMarginModel(RequiredFreeBuyingPowerPercent, security);
|
||||
break;
|
||||
case SecurityType.Index:
|
||||
default:
|
||||
model = new SecurityMarginModel(leverage, RequiredFreeBuyingPowerPercent);
|
||||
break;
|
||||
}
|
||||
return model;
|
||||
SecurityType.Future => new FutureMarginModel(RequiredFreeBuyingPowerPercent, security),
|
||||
SecurityType.FutureOption => new FuturesOptionsMarginModel(RequiredFreeBuyingPowerPercent, (Option)security),
|
||||
SecurityType.IndexOption => new OptionMarginModel(RequiredFreeBuyingPowerPercent),
|
||||
SecurityType.Option => new OptionMarginModel(RequiredFreeBuyingPowerPercent),
|
||||
_ => AccountType == AccountType.Cash
|
||||
? new CashBuyingPowerModel()
|
||||
: new SecurityMarginModel(GetLeverage(security), RequiredFreeBuyingPowerPercent)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,20 +47,6 @@ namespace QuantConnect.Brokerages
|
||||
{
|
||||
}
|
||||
|
||||
/// <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.
|
||||
/// For margin trading, FTX supports up to 20x leverage, default is 3xs
|
||||
/// </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 AccountType == AccountType.Margin
|
||||
? new SecurityMarginModel(GetLeverage(security))
|
||||
: new CashBuyingPowerModel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the brokerage's leverage for the specified security
|
||||
/// </summary>
|
||||
|
||||
@@ -207,6 +207,9 @@ namespace QuantConnect.Brokerages
|
||||
case BrokerageName.Binance:
|
||||
return new BinanceBrokerageModel(accountType);
|
||||
|
||||
case BrokerageName.BinanceUS:
|
||||
return new BinanceUSBrokerageModel(accountType);
|
||||
|
||||
case BrokerageName.GDAX:
|
||||
return new GDAXBrokerageModel(accountType);
|
||||
|
||||
@@ -227,6 +230,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);
|
||||
|
||||
@@ -133,23 +133,6 @@ namespace QuantConnect.Brokerages
|
||||
return new KrakenFeeModel();
|
||||
}
|
||||
|
||||
/// <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.
|
||||
/// For margin trading, max leverage = 5
|
||||
/// </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)
|
||||
{
|
||||
if (AccountType == AccountType.Margin)
|
||||
{
|
||||
return new SecurityMarginModel(GetLeverage(security));
|
||||
}
|
||||
|
||||
return new CashBuyingPowerModel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kraken global leverage rule
|
||||
/// </summary>
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace QuantConnect.Brokerages
|
||||
typeof(GoodTilDateTimeInForce)
|
||||
};
|
||||
|
||||
private const decimal _maxLeverage = 7m;
|
||||
private const decimal _maxLeverage = 5m;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SamcoBrokerageModel"/> class
|
||||
@@ -141,20 +141,6 @@ namespace QuantConnect.Brokerages
|
||||
/// </summary>
|
||||
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets();
|
||||
|
||||
/// <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. For margin
|
||||
/// trading, max leverage = 7
|
||||
/// </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 AccountType == AccountType.Cash
|
||||
? (IBuyingPowerModel)new CashBuyingPowerModel()
|
||||
: new SecurityMarginModel(_maxLeverage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Samco global leverage rule
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
@@ -34,7 +34,7 @@ namespace QuantConnect.Brokerages
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DefaultBrokerageModel"/> class
|
||||
/// </summary>
|
||||
/// <param name="accountType">The type of account to be modelled, defaults to
|
||||
/// <param name="accountType">The type of account to be modeled, defaults to
|
||||
/// <see cref="QuantConnect.AccountType.Margin"/></param>
|
||||
public TradierBrokerageModel(AccountType accountType = AccountType.Margin)
|
||||
: base(accountType)
|
||||
@@ -102,7 +102,7 @@ namespace QuantConnect.Brokerages
|
||||
if (request.Quantity != null && request.Quantity != order.Quantity)
|
||||
{
|
||||
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "UpdateRejected",
|
||||
"Traider does not support updating order quantities."
|
||||
"Tradier does not support updating order quantities."
|
||||
);
|
||||
|
||||
return false;
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace QuantConnect.Brokerages
|
||||
typeof(GoodTilDateTimeInForce)
|
||||
};
|
||||
|
||||
private const decimal _maxLeverage = 7m;
|
||||
private const decimal _maxLeverage = 5m;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ZerodhaBrokerageModel"/> class
|
||||
@@ -136,22 +136,6 @@ namespace QuantConnect.Brokerages
|
||||
/// </summary>
|
||||
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets();
|
||||
|
||||
|
||||
|
||||
/// <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.
|
||||
/// For margin trading, max leverage = 7
|
||||
/// </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 AccountType == AccountType.Cash
|
||||
? (IBuyingPowerModel)new CashBuyingPowerModel()
|
||||
: new SecurityMarginModel(_maxLeverage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Zerodha global leverage rule
|
||||
/// </summary>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
@@ -29,23 +29,24 @@ namespace QuantConnect.Data
|
||||
/// </summary>
|
||||
public class Slice : ExtendedDictionary<dynamic>, IEnumerable<KeyValuePair<Symbol, BaseData>>
|
||||
{
|
||||
private readonly Ticks _ticks;
|
||||
private readonly TradeBars _bars;
|
||||
private readonly QuoteBars _quoteBars;
|
||||
private readonly OptionChains _optionChains;
|
||||
private readonly FuturesChains _futuresChains;
|
||||
private Ticks _ticks;
|
||||
private TradeBars _bars;
|
||||
private QuoteBars _quoteBars;
|
||||
private OptionChains _optionChains;
|
||||
private FuturesChains _futuresChains;
|
||||
|
||||
// aux data
|
||||
private readonly Splits _splits;
|
||||
private readonly Dividends _dividends;
|
||||
private readonly Delistings _delistings;
|
||||
private readonly SymbolChangedEvents _symbolChangedEvents;
|
||||
private Splits _splits;
|
||||
private Dividends _dividends;
|
||||
private Delistings _delistings;
|
||||
private SymbolChangedEvents _symbolChangedEvents;
|
||||
|
||||
// string -> data for non-tick data
|
||||
// string -> list{data} for tick data
|
||||
private readonly Lazy<DataDictionary<SymbolData>> _data;
|
||||
private Lazy<DataDictionary<SymbolData>> _data;
|
||||
// UnlinkedData -> DataDictonary<UnlinkedData>
|
||||
private Dictionary<Type, object> _dataByType;
|
||||
private List<BaseData> _rawDataList;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timestamp for this slice of data
|
||||
@@ -55,6 +56,14 @@ namespace QuantConnect.Data
|
||||
get; private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timestamp for this slice of data in UTC
|
||||
/// </summary>
|
||||
public DateTime UtcTime
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether or not this slice has data
|
||||
/// </summary>
|
||||
@@ -190,8 +199,9 @@ namespace QuantConnect.Data
|
||||
/// </summary>
|
||||
/// <param name="time">The timestamp for this slice of data</param>
|
||||
/// <param name="data">The raw data in this slice</param>
|
||||
public Slice(DateTime time, IEnumerable<BaseData> data)
|
||||
: this(time, data.ToList())
|
||||
/// <param name="utcTime">The timestamp for this slice of data in UTC</param>
|
||||
public Slice(DateTime time, IEnumerable<BaseData> data, DateTime utcTime)
|
||||
: this(time, data.ToList(), utcTime: utcTime)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -202,7 +212,8 @@ namespace QuantConnect.Data
|
||||
/// </summary>
|
||||
/// <param name="time">The timestamp for this slice of data</param>
|
||||
/// <param name="data">The raw data in this slice</param>
|
||||
public Slice(DateTime time, List<BaseData> data)
|
||||
/// <param name="utcTime">The timestamp for this slice of data in UTC</param>
|
||||
public Slice(DateTime time, List<BaseData> data, DateTime utcTime)
|
||||
: this(time, data, CreateCollection<TradeBars, TradeBar>(time, data),
|
||||
CreateCollection<QuoteBars, QuoteBar>(time, data),
|
||||
CreateTicksCollection(time, data),
|
||||
@@ -211,7 +222,8 @@ namespace QuantConnect.Data
|
||||
CreateCollection<Splits, Split>(time, data),
|
||||
CreateCollection<Dividends, Dividend>(time, data),
|
||||
CreateCollection<Delistings, Delisting>(time, data),
|
||||
CreateCollection<SymbolChangedEvents, SymbolChangedEvent>(time, data))
|
||||
CreateCollection<SymbolChangedEvents, SymbolChangedEvent>(time, data),
|
||||
utcTime: utcTime)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -223,7 +235,8 @@ namespace QuantConnect.Data
|
||||
protected Slice(Slice slice)
|
||||
{
|
||||
Time = slice.Time;
|
||||
|
||||
UtcTime = slice.UtcTime;
|
||||
_rawDataList = slice._rawDataList;
|
||||
_dataByType = slice._dataByType;
|
||||
|
||||
_data = slice._data;
|
||||
@@ -257,13 +270,15 @@ namespace QuantConnect.Data
|
||||
/// <param name="dividends">The dividends for this slice</param>
|
||||
/// <param name="delistings">The delistings for this slice</param>
|
||||
/// <param name="symbolChanges">The symbol changed events for this slice</param>
|
||||
/// <param name="utcTime">The timestamp for this slice of data in UTC</param>
|
||||
/// <param name="hasData">true if this slice contains data</param>
|
||||
public Slice(DateTime time, IEnumerable<BaseData> data, TradeBars tradeBars, QuoteBars quoteBars, Ticks ticks, OptionChains optionChains, FuturesChains futuresChains, Splits splits, Dividends dividends, Delistings delistings, SymbolChangedEvents symbolChanges, bool? hasData = null)
|
||||
public Slice(DateTime time, List<BaseData> data, TradeBars tradeBars, QuoteBars quoteBars, Ticks ticks, OptionChains optionChains, FuturesChains futuresChains, Splits splits, Dividends dividends, Delistings delistings, SymbolChangedEvents symbolChanges, DateTime utcTime, bool? hasData = null)
|
||||
{
|
||||
Time = time;
|
||||
|
||||
UtcTime = utcTime;
|
||||
_rawDataList = data;
|
||||
// market data
|
||||
_data = new Lazy<DataDictionary<SymbolData>>(() => CreateDynamicDataDictionary(data));
|
||||
_data = new Lazy<DataDictionary<SymbolData>>(() => CreateDynamicDataDictionary(_rawDataList));
|
||||
|
||||
HasData = hasData ?? _data.Value.Count > 0;
|
||||
|
||||
@@ -340,7 +355,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 +405,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 });
|
||||
}
|
||||
@@ -444,6 +472,65 @@ namespace QuantConnect.Data
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merge two slice with same Time
|
||||
/// </summary>
|
||||
/// <param name="inputSlice">slice instance</param>
|
||||
/// <remarks> Will change the input collection for re-use</remarks>
|
||||
public void MergeSlice(Slice inputSlice)
|
||||
{
|
||||
if (UtcTime != inputSlice.UtcTime)
|
||||
{
|
||||
throw new InvalidOperationException($"Slice with time {UtcTime} can't be merged with given slice with different {inputSlice.UtcTime}");
|
||||
}
|
||||
|
||||
_bars = (TradeBars)UpdateCollection(_bars, inputSlice.Bars);
|
||||
_quoteBars = (QuoteBars)UpdateCollection(_quoteBars, inputSlice.QuoteBars);
|
||||
_ticks = (Ticks)UpdateCollection(_ticks, inputSlice.Ticks);
|
||||
_optionChains = (OptionChains)UpdateCollection(_optionChains, inputSlice.OptionChains);
|
||||
_futuresChains = (FuturesChains)UpdateCollection(_futuresChains, inputSlice.FuturesChains);
|
||||
_splits = (Splits)UpdateCollection(_splits, inputSlice.Splits);
|
||||
_dividends = (Dividends)UpdateCollection(_dividends, inputSlice.Dividends);
|
||||
_delistings = (Delistings)UpdateCollection(_delistings, inputSlice.Delistings);
|
||||
_symbolChangedEvents = (SymbolChangedEvents)UpdateCollection(_symbolChangedEvents, inputSlice.SymbolChangedEvents);
|
||||
|
||||
if (inputSlice._rawDataList.Count != 0)
|
||||
{
|
||||
if (_rawDataList.Count == 0)
|
||||
{
|
||||
_rawDataList = inputSlice._rawDataList;
|
||||
_data = inputSlice._data;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Should keep this._rawDataList last so that selected data points are not overriden
|
||||
// while creating _data
|
||||
inputSlice._rawDataList.AddRange(_rawDataList);
|
||||
_rawDataList = inputSlice._rawDataList;
|
||||
_data = new Lazy<DataDictionary<SymbolData>>(() => CreateDynamicDataDictionary(_rawDataList));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static DataDictionary<T> UpdateCollection<T>(DataDictionary<T> baseCollection, DataDictionary<T> inputCollection)
|
||||
{
|
||||
if (baseCollection == null || baseCollection.Count == 0)
|
||||
{
|
||||
return inputCollection;
|
||||
}
|
||||
if (inputCollection?.Count > 0)
|
||||
{
|
||||
foreach (var kvp in inputCollection)
|
||||
{
|
||||
if (!baseCollection.ContainsKey(kvp.Key))
|
||||
{
|
||||
baseCollection.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return baseCollection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Produces the dynamic data dictionary from the input data
|
||||
/// </summary>
|
||||
@@ -653,15 +740,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,10 +174,18 @@ namespace QuantConnect
|
||||
/// Helper method to download a provided url as a string
|
||||
/// </summary>
|
||||
/// <param name="url">The url to download data from</param>
|
||||
public static string DownloadData(this string url)
|
||||
/// <param name="headers">Add custom headers for the request</param>
|
||||
public static string DownloadData(this string url, Dictionary<string, string> headers = null)
|
||||
{
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
if (headers != null)
|
||||
{
|
||||
foreach (var kvp in headers)
|
||||
{
|
||||
client.DefaultRequestHeaders.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
using (var response = client.GetAsync(url).Result)
|
||||
@@ -1634,6 +1642,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 +2904,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 +2921,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>
|
||||
|
||||
@@ -63,7 +63,8 @@ namespace QuantConnect
|
||||
|
||||
Tuple.Create(CFE, 33),
|
||||
Tuple.Create(FTX, 34),
|
||||
Tuple.Create(FTXUS, 35)
|
||||
Tuple.Create(FTXUS, 35),
|
||||
Tuple.Create(BinanceUS, 36)
|
||||
};
|
||||
|
||||
static Market()
|
||||
@@ -218,6 +219,10 @@ namespace QuantConnect
|
||||
/// </summary>
|
||||
public const string FTXUS = "ftxus";
|
||||
|
||||
/// <summary>
|
||||
/// Binance.US
|
||||
/// </summary>
|
||||
public const string BinanceUS = "binanceus";
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified market to the map of available markets with the specified identifier.
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using QuantConnect.Util;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Orders.Fees
|
||||
@@ -58,7 +59,8 @@ namespace QuantConnect.Orders.Fees
|
||||
var security = parameters.Security;
|
||||
var order = parameters.Order;
|
||||
|
||||
decimal fee = _takerFee;
|
||||
// apply fee factor, currently we do not model 30-day volume, so we use the first tier
|
||||
var fee = _takerFee;
|
||||
var props = order.Properties as BinanceOrderProperties;
|
||||
|
||||
if (order.Type == OrderType.Limit &&
|
||||
@@ -68,6 +70,13 @@ namespace QuantConnect.Orders.Fees
|
||||
fee = _makerFee;
|
||||
}
|
||||
|
||||
if (order.Direction == OrderDirection.Buy)
|
||||
{
|
||||
// fees taken in the received currency
|
||||
CurrencyPairUtil.DecomposeCurrencyPair(order.Symbol, out var baseCurrency, out _);
|
||||
return new OrderFee(new CashAmount(order.AbsoluteQuantity * fee, baseCurrency));
|
||||
}
|
||||
|
||||
// get order value in quote currency
|
||||
var unitPrice = order.Direction == OrderDirection.Buy ? security.AskPrice : security.BidPrice;
|
||||
if (order.Type == OrderType.Limit)
|
||||
@@ -78,7 +87,6 @@ namespace QuantConnect.Orders.Fees
|
||||
|
||||
unitPrice *= security.SymbolProperties.ContractMultiplier;
|
||||
|
||||
// apply fee factor, currently we do not model 30-day volume, so we use the first tier
|
||||
return new OrderFee(new CashAmount(
|
||||
unitPrice * order.AbsoluteQuantity * fee,
|
||||
security.QuoteCurrency.Symbol));
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using QuantConnect.Util;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Orders.Fees
|
||||
@@ -46,7 +47,8 @@ namespace QuantConnect.Orders.Fees
|
||||
{
|
||||
var order = parameters.Order;
|
||||
var security = parameters.Security;
|
||||
decimal fee = TakerFee;
|
||||
// apply fee factor, currently we do not model 30-day volume, so we use the first tier
|
||||
var fee = TakerFee;
|
||||
var props = order.Properties as BitfinexOrderProperties;
|
||||
|
||||
if (order.Type == OrderType.Limit &&
|
||||
@@ -57,6 +59,13 @@ namespace QuantConnect.Orders.Fees
|
||||
fee = MakerFee;
|
||||
}
|
||||
|
||||
if (order.Direction == OrderDirection.Buy)
|
||||
{
|
||||
// fees taken in the received currency
|
||||
CurrencyPairUtil.DecomposeCurrencyPair(order.Symbol, out var baseCurrency, out _);
|
||||
return new OrderFee(new CashAmount(order.AbsoluteQuantity * fee, baseCurrency));
|
||||
}
|
||||
|
||||
// get order value in quote currency
|
||||
var unitPrice = order.Direction == OrderDirection.Buy ? security.AskPrice : security.BidPrice;
|
||||
if (order.Type == OrderType.Limit)
|
||||
@@ -67,7 +76,6 @@ namespace QuantConnect.Orders.Fees
|
||||
|
||||
unitPrice *= security.SymbolProperties.ContractMultiplier;
|
||||
|
||||
// apply fee factor, currently we do not model 30-day volume, so we use the first tier
|
||||
return new OrderFee(new CashAmount(
|
||||
unitPrice * order.AbsoluteQuantity * fee,
|
||||
security.QuoteCurrency.Symbol));
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace QuantConnect.Orders.Fees
|
||||
/// <summary>
|
||||
/// Brokerage calculation Factor
|
||||
/// </summary>
|
||||
protected override decimal BrokerageMultiplier => 0.0003M;
|
||||
protected override decimal BrokerageMultiplier => 0.0002M;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum brokerage per order
|
||||
@@ -38,7 +38,7 @@ namespace QuantConnect.Orders.Fees
|
||||
/// <summary>
|
||||
/// Exchange Transaction Charge calculation Factor
|
||||
/// </summary>
|
||||
protected override decimal ExchangeTransactionChargeMultiplier => 0.0000325M;
|
||||
protected override decimal ExchangeTransactionChargeMultiplier => 0.0000345M;
|
||||
|
||||
/// <summary>
|
||||
/// State Tax calculation Factor
|
||||
@@ -48,11 +48,11 @@ namespace QuantConnect.Orders.Fees
|
||||
/// <summary>
|
||||
/// Sebi Charges calculation Factor
|
||||
/// </summary>
|
||||
protected override decimal SebiChargesMultiplier => 0.000002M;
|
||||
protected override decimal SebiChargesMultiplier => 0.000001M;
|
||||
|
||||
/// <summary>
|
||||
/// Stamp Charges calculation Factor
|
||||
/// </summary>
|
||||
protected override decimal StampChargesMultiplier => 0.00002M;
|
||||
protected override decimal StampChargesMultiplier => 0.00003M;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,14 +202,11 @@ namespace QuantConnect.Orders
|
||||
protected Order()
|
||||
{
|
||||
Time = new DateTime();
|
||||
Price = 0;
|
||||
PriceCurrency = string.Empty;
|
||||
Quantity = 0;
|
||||
Symbol = Symbol.Empty;
|
||||
Status = OrderStatus.None;
|
||||
Tag = "";
|
||||
Tag = string.Empty;
|
||||
BrokerId = new List<string>();
|
||||
ContingentId = 0;
|
||||
Properties = new OrderProperties();
|
||||
}
|
||||
|
||||
@@ -224,14 +221,12 @@ namespace QuantConnect.Orders
|
||||
protected Order(Symbol symbol, decimal quantity, DateTime time, string tag = "", IOrderProperties properties = null)
|
||||
{
|
||||
Time = time;
|
||||
Price = 0;
|
||||
PriceCurrency = string.Empty;
|
||||
Quantity = quantity;
|
||||
Symbol = symbol;
|
||||
Status = OrderStatus.None;
|
||||
Tag = tag;
|
||||
BrokerId = new List<string>();
|
||||
ContingentId = 0;
|
||||
Properties = properties ?? new OrderProperties();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ namespace QuantConnect.Securities.Future
|
||||
var marginRequirementsEntries = _dataProvider.ReadLines(file)
|
||||
.Where(x => !x.StartsWith("#") && !string.IsNullOrWhiteSpace(x))
|
||||
.Skip(1)
|
||||
.Select(FromCsvLine)
|
||||
.Select(MarginRequirementsEntry.Create)
|
||||
.OrderBy(x => x.Date)
|
||||
.ToArray();
|
||||
|
||||
@@ -261,87 +261,5 @@ namespace QuantConnect.Securities.Future
|
||||
return marginRequirementsEntries;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="MarginRequirementsEntry"/> from the specified csv line
|
||||
/// </summary>
|
||||
/// <param name="csvLine">The csv line to be parsed</param>
|
||||
/// <returns>A new <see cref="MarginRequirementsEntry"/> for the specified csv line</returns>
|
||||
private MarginRequirementsEntry FromCsvLine(string csvLine)
|
||||
{
|
||||
var line = csvLine.Split(',');
|
||||
|
||||
DateTime date;
|
||||
if (!DateTime.TryParseExact(line[0], DateFormat.EightCharacter, CultureInfo.InvariantCulture, DateTimeStyles.None, out date))
|
||||
{
|
||||
Log.Trace($"Couldn't parse date/time while reading future margin requirement file. Line: {csvLine}");
|
||||
}
|
||||
|
||||
decimal initialOvernight;
|
||||
if (!decimal.TryParse(line[1], out initialOvernight))
|
||||
{
|
||||
Log.Trace($"Couldn't parse Initial Overnight margin requirements while reading future margin requirement file. Line: {csvLine}");
|
||||
}
|
||||
|
||||
decimal maintenanceOvernight;
|
||||
if (!decimal.TryParse(line[2], out maintenanceOvernight))
|
||||
{
|
||||
Log.Trace($"Couldn't parse Maintenance Overnight margin requirements while reading future margin requirement file. Line: {csvLine}");
|
||||
}
|
||||
|
||||
// default value, if present in file we try to parse
|
||||
decimal initialIntraday = initialOvernight * 0.4m;
|
||||
if (line.Length >= 4
|
||||
&& !decimal.TryParse(line[3], out initialIntraday))
|
||||
{
|
||||
Log.Trace($"Couldn't parse Initial Intraday margin requirements while reading future margin requirement file. Line: {csvLine}");
|
||||
}
|
||||
|
||||
// default value, if present in file we try to parse
|
||||
decimal maintenanceIntraday = maintenanceOvernight * 0.4m;
|
||||
if (line.Length >= 5
|
||||
&& !decimal.TryParse(line[4], out maintenanceIntraday))
|
||||
{
|
||||
Log.Trace($"Couldn't parse Maintenance Intraday margin requirements while reading future margin requirement file. Line: {csvLine}");
|
||||
}
|
||||
|
||||
return new MarginRequirementsEntry
|
||||
{
|
||||
Date = date,
|
||||
InitialOvernight = initialOvernight,
|
||||
MaintenanceOvernight = maintenanceOvernight,
|
||||
InitialIntraday = initialIntraday,
|
||||
MaintenanceIntraday = maintenanceIntraday
|
||||
};
|
||||
}
|
||||
|
||||
// Private POCO class for modeling margin requirements at given date
|
||||
class MarginRequirementsEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Date of margin requirements change
|
||||
/// </summary>
|
||||
public DateTime Date;
|
||||
|
||||
/// <summary>
|
||||
/// Initial overnight margin for the contract effective from the date of change
|
||||
/// </summary>
|
||||
public decimal InitialOvernight;
|
||||
|
||||
/// <summary>
|
||||
/// Maintenance overnight margin for the contract effective from the date of change
|
||||
/// </summary>
|
||||
public decimal MaintenanceOvernight;
|
||||
|
||||
/// <summary>
|
||||
/// Initial intraday margin for the contract effective from the date of change
|
||||
/// </summary>
|
||||
public decimal InitialIntraday;
|
||||
|
||||
/// <summary>
|
||||
/// Maintenance intraday margin for the contract effective from the date of change
|
||||
/// </summary>
|
||||
public decimal MaintenanceIntraday;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
105
Common/Securities/Future/MarginRequirementsEntry.cs
Normal file
105
Common/Securities/Future/MarginRequirementsEntry.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.Globalization;
|
||||
using QuantConnect.Logging;
|
||||
|
||||
namespace QuantConnect.Securities.Future
|
||||
{
|
||||
/// <summary>
|
||||
/// POCO class for modeling margin requirements at given date
|
||||
/// </summary>
|
||||
public class MarginRequirementsEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Date of margin requirements change
|
||||
/// </summary>
|
||||
public DateTime Date { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Initial overnight margin for the contract effective from the date of change
|
||||
/// </summary>
|
||||
public decimal InitialOvernight { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Maintenance overnight margin for the contract effective from the date of change
|
||||
/// </summary>
|
||||
public decimal MaintenanceOvernight { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Initial intraday margin for the contract effective from the date of change
|
||||
/// </summary>
|
||||
public decimal InitialIntraday { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Maintenance intraday margin for the contract effective from the date of change
|
||||
/// </summary>
|
||||
public decimal MaintenanceIntraday { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="MarginRequirementsEntry"/> from the specified csv line
|
||||
/// </summary>
|
||||
/// <param name="csvLine">The csv line to be parsed</param>
|
||||
/// <returns>A new <see cref="MarginRequirementsEntry"/> for the specified csv line</returns>
|
||||
public static MarginRequirementsEntry Create(string csvLine)
|
||||
{
|
||||
var line = csvLine.Split(',');
|
||||
|
||||
DateTime date;
|
||||
if (!DateTime.TryParseExact(line[0], DateFormat.EightCharacter, CultureInfo.InvariantCulture, DateTimeStyles.None, out date))
|
||||
{
|
||||
Log.Trace($"Couldn't parse date/time while reading future margin requirement file. Line: {csvLine}");
|
||||
}
|
||||
|
||||
decimal initialOvernight;
|
||||
if (!decimal.TryParse(line[1], out initialOvernight))
|
||||
{
|
||||
Log.Trace($"Couldn't parse Initial Overnight margin requirements while reading future margin requirement file. Line: {csvLine}");
|
||||
}
|
||||
|
||||
decimal maintenanceOvernight;
|
||||
if (!decimal.TryParse(line[2], out maintenanceOvernight))
|
||||
{
|
||||
Log.Trace($"Couldn't parse Maintenance Overnight margin requirements while reading future margin requirement file. Line: {csvLine}");
|
||||
}
|
||||
|
||||
// default value, if present in file we try to parse
|
||||
decimal initialIntraday = initialOvernight * 0.4m;
|
||||
if (line.Length >= 4
|
||||
&& !decimal.TryParse(line[3], out initialIntraday))
|
||||
{
|
||||
Log.Trace($"Couldn't parse Initial Intraday margin requirements while reading future margin requirement file. Line: {csvLine}");
|
||||
}
|
||||
|
||||
// default value, if present in file we try to parse
|
||||
decimal maintenanceIntraday = maintenanceOvernight * 0.4m;
|
||||
if (line.Length >= 5
|
||||
&& !decimal.TryParse(line[4], out maintenanceIntraday))
|
||||
{
|
||||
Log.Trace($"Couldn't parse Maintenance Intraday margin requirements while reading future margin requirement file. Line: {csvLine}");
|
||||
}
|
||||
|
||||
return new MarginRequirementsEntry
|
||||
{
|
||||
Date = date,
|
||||
InitialOvernight = initialOvernight,
|
||||
MaintenanceOvernight = maintenanceOvernight,
|
||||
InitialIntraday = initialIntraday,
|
||||
MaintenanceIntraday = maintenanceIntraday
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
using System;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Securities.Cfd;
|
||||
using QuantConnect.Securities.Crypto;
|
||||
using QuantConnect.Securities.Forex;
|
||||
using QuantConnect.Securities.Crypto;
|
||||
|
||||
namespace QuantConnect.Util
|
||||
{
|
||||
@@ -29,28 +29,48 @@ namespace QuantConnect.Util
|
||||
private static readonly Lazy<SymbolPropertiesDatabase> SymbolPropertiesDatabase =
|
||||
new Lazy<SymbolPropertiesDatabase>(Securities.SymbolPropertiesDatabase.FromDataFolder);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to decomposes the specified currency pair into a base and quote currency provided as out parameters
|
||||
/// </summary>
|
||||
/// <param name="currencyPair">The input currency pair to be decomposed</param>
|
||||
/// <param name="baseCurrency">The output base currency</param>
|
||||
/// <param name="quoteCurrency">The output quote currency</param>
|
||||
/// <returns>True if was able to decompose the currency pair</returns>
|
||||
public static bool TryDecomposeCurrencyPair(Symbol currencyPair, out string baseCurrency, out string quoteCurrency)
|
||||
{
|
||||
baseCurrency = null;
|
||||
quoteCurrency = null;
|
||||
|
||||
if (!IsValidSecurityType(currencyPair?.SecurityType, throwException: false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
DecomposeCurrencyPair(currencyPair, out baseCurrency, out quoteCurrency);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decomposes the specified currency pair into a base and quote currency provided as out parameters
|
||||
/// </summary>
|
||||
/// <param name="currencyPair">The input currency pair to be decomposed</param>
|
||||
/// <param name="baseCurrency">The output base currency</param>
|
||||
/// <param name="quoteCurrency">The output quote currency</param>
|
||||
public static void DecomposeCurrencyPair(Symbol currencyPair, out string baseCurrency, out string quoteCurrency)
|
||||
/// <param name="defaultQuoteCurrency">Optionally can provide a default quote currency</param>
|
||||
public static void DecomposeCurrencyPair(Symbol currencyPair, out string baseCurrency, out string quoteCurrency, string defaultQuoteCurrency = Currencies.USD)
|
||||
{
|
||||
if (currencyPair == null)
|
||||
{
|
||||
throw new ArgumentException("Currency pair must not be null");
|
||||
}
|
||||
|
||||
IsValidSecurityType(currencyPair?.SecurityType, throwException: true);
|
||||
var securityType = currencyPair.SecurityType;
|
||||
|
||||
if (securityType != SecurityType.Forex &&
|
||||
securityType != SecurityType.Cfd &&
|
||||
securityType != SecurityType.Crypto)
|
||||
{
|
||||
throw new ArgumentException($"Unsupported security type: {securityType}");
|
||||
}
|
||||
|
||||
if (securityType == SecurityType.Forex)
|
||||
{
|
||||
Forex.DecomposeCurrencyPair(currencyPair.Value, out baseCurrency, out quoteCurrency);
|
||||
@@ -61,7 +81,7 @@ namespace QuantConnect.Util
|
||||
currencyPair.ID.Market,
|
||||
currencyPair,
|
||||
currencyPair.SecurityType,
|
||||
Currencies.USD);
|
||||
defaultQuoteCurrency);
|
||||
|
||||
if (securityType == SecurityType.Cfd)
|
||||
{
|
||||
@@ -184,5 +204,30 @@ namespace QuantConnect.Util
|
||||
|
||||
return Match.NoMatch;
|
||||
}
|
||||
|
||||
private static bool IsValidSecurityType(SecurityType? securityType, bool throwException)
|
||||
{
|
||||
if (securityType == null)
|
||||
{
|
||||
if (throwException)
|
||||
{
|
||||
throw new ArgumentException("Currency pair must not be null");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (securityType != SecurityType.Forex &&
|
||||
securityType != SecurityType.Cfd &&
|
||||
securityType != SecurityType.Crypto)
|
||||
{
|
||||
if (throwException)
|
||||
{
|
||||
throw new ArgumentException($"Unsupported security type: {securityType}");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
BIN
Data/crypto/binance/hour/btcusdt_trade.zip
Normal file
BIN
Data/crypto/binance/hour/btcusdt_trade.zip
Normal file
Binary file not shown.
4
Data/future/cbot/margins/10Y.csv
Normal file
4
Data/future/cbot/margins/10Y.csv
Normal file
@@ -0,0 +1,4 @@
|
||||
date,initial,maintenance
|
||||
20210816,193,175
|
||||
20211130,220,200
|
||||
20220204,264,240
|
||||
|
5
Data/future/cbot/margins/2YY.csv
Normal file
5
Data/future/cbot/margins/2YY.csv
Normal file
@@ -0,0 +1,5 @@
|
||||
date,initial,maintenance
|
||||
20210816,132,120
|
||||
20211112,143,130
|
||||
20211130,176,160
|
||||
20220204,209,190
|
||||
|
3
Data/future/cbot/margins/30Y.csv
Normal file
3
Data/future/cbot/margins/30Y.csv
Normal file
@@ -0,0 +1,3 @@
|
||||
date,initial,maintenance
|
||||
20210816,220,200
|
||||
20220204,264,240
|
||||
|
5
Data/future/cbot/margins/5YY.csv
Normal file
5
Data/future/cbot/margins/5YY.csv
Normal file
@@ -0,0 +1,5 @@
|
||||
date,initial,maintenance
|
||||
20210816,154,140
|
||||
20211112,182,165
|
||||
20211130,220,200
|
||||
20220204,264,240
|
||||
|
@@ -19,4 +19,7 @@ date,initial,maintenance
|
||||
20200316,363,330
|
||||
20200331,495,450
|
||||
20200424,550,500
|
||||
20200526,462,420
|
||||
20200526,462,420
|
||||
20200605,396,360
|
||||
20201211,341,310
|
||||
20211130,413,375
|
||||
|
@@ -6,4 +6,9 @@ date,initial,maintenance
|
||||
20190308,275,250
|
||||
20190503,242,220
|
||||
20190726,330,300
|
||||
20190823,385,350
|
||||
20190823,385,350
|
||||
20201030,440,400
|
||||
20210108,583,530
|
||||
20210114,660,600
|
||||
20210625,798,725
|
||||
20210709,880,800
|
||||
|
@@ -6,4 +6,15 @@ date,initial,maintenance
|
||||
20190308,374,340
|
||||
20190503,330,300
|
||||
20190726,363,330
|
||||
20200422,402,365
|
||||
20200422,402,365
|
||||
20200731,385,350
|
||||
20201002,429,390
|
||||
20201215,495,450
|
||||
20210108,550,500
|
||||
20210114,660,600
|
||||
20210625,726,660
|
||||
20210709,792,720
|
||||
20210730,825,750
|
||||
20210922,880,800
|
||||
20220107,913,830
|
||||
20220128,1045,950
|
||||
|
@@ -33,4 +33,11 @@ date,initial,maintenance
|
||||
20190815,3025,2750
|
||||
20200117,2365,2150
|
||||
20200318,3190,2900
|
||||
20200325,4785,4350
|
||||
20200325,4785,4350
|
||||
20200616,3850,3500
|
||||
20200903,3300,3000
|
||||
20201016,2970,2700
|
||||
20210129,3300,3000
|
||||
20210405,3850,3500
|
||||
20210423,4290,3900
|
||||
20210507,4730,4300
|
||||
|
@@ -22,4 +22,14 @@ date,initial,maintenance
|
||||
20200117,1073,975
|
||||
20200219,913,830
|
||||
20200303,1056,960
|
||||
20200312,1265,1150
|
||||
20200312,1265,1150
|
||||
20200721,1045,950
|
||||
20200925,825,750
|
||||
20201120,715,650
|
||||
20210108,649,590
|
||||
20210301,770,700
|
||||
20210319,935,850
|
||||
20210604,836,760
|
||||
20210917,770,700
|
||||
20211112,902,820
|
||||
20211130,1122,1020
|
||||
|
@@ -28,4 +28,18 @@ date,initial,maintenance
|
||||
20190314,1485,1350
|
||||
20190603,1595,1450
|
||||
20191113,1375,1250
|
||||
20200320,1595,1450
|
||||
20200320,1595,1450
|
||||
20200617,1375,1250
|
||||
20200724,1265,1150
|
||||
20200814,1155,1050
|
||||
20201002,1375,1250
|
||||
20201117,1485,1350
|
||||
20210104,1650,1500
|
||||
20210106,1760,1600
|
||||
20210114,1925,1750
|
||||
20210205,2035,1850
|
||||
20210426,2255,2050
|
||||
20210429,2475,2250
|
||||
20210507,2585,2350
|
||||
20210917,2338,2125
|
||||
20220127,2695,2450
|
||||
|
16
Data/future/cbot/margins/MYM.csv
Normal file
16
Data/future/cbot/margins/MYM.csv
Normal file
@@ -0,0 +1,16 @@
|
||||
date,initial,maintenance
|
||||
20190503,649,590
|
||||
20190523,605,550
|
||||
20191101,550,500
|
||||
20200109,605,550
|
||||
20200302,682,620
|
||||
20200304,748,680
|
||||
20200310,825,750
|
||||
20200312,908,825
|
||||
20200316,1067,970
|
||||
20200318,1320,1200
|
||||
20200526,1177,1070
|
||||
20200804,1045,950
|
||||
20201211,990,900
|
||||
20211210,935,850
|
||||
20220121,880,800
|
||||
|
@@ -24,4 +24,13 @@ date,initial,maintenance
|
||||
20200311,2970,2700
|
||||
20200313,3410,3100
|
||||
20200319,3905,3550
|
||||
20200526,3520,3200
|
||||
20200526,3520,3200
|
||||
20200626,3300,3000
|
||||
20200707,2970,2700
|
||||
20200721,2750,2500
|
||||
20201120,2530,2300
|
||||
20210108,2255,2050
|
||||
20210226,2420,2200
|
||||
20210319,2695,2450
|
||||
20210910,2420,2200
|
||||
20211130,2695,2450
|
||||
|
@@ -38,4 +38,10 @@ date,initial,maintenance
|
||||
20200318,13420,12200
|
||||
20200324,15400,14000
|
||||
20200428,13750,12500
|
||||
20200526,12650,11500
|
||||
20200526,12650,11500
|
||||
20200619,11660,10600
|
||||
20200721,11000,10000
|
||||
20201120,9570,8700
|
||||
20210108,8690,7900
|
||||
20210416,7920,7200
|
||||
20210527,7150,6500
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user