Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using System;
using System.Collections.Generic;
using QuantConnect.Interfaces;
using QuantConnect.Securities;

namespace QuantConnect.Algorithm.CSharp
{
Expand Down Expand Up @@ -54,7 +55,7 @@ public override void Initialize()
throw new RegressionTestException("Expected the SPY CFD market hours to be the same as the underlying equity market hours.");
}

if (!ReferenceEquals(spyCfd.SymbolProperties, equitySymbolProperties))
if (!SymbolPropertiesAreEquivalent(spyCfd.SymbolProperties, equitySymbolProperties))
{
throw new RegressionTestException("Expected the SPY CFD symbol properties to be the same as the underlying equity symbol properties.");
}
Expand All @@ -74,12 +75,25 @@ public override void Initialize()
throw new RegressionTestException("Expected the AUDUSD CFD market hours to be the same as the underlying forex market hours.");
}

if (!ReferenceEquals(audUsdCfd.SymbolProperties, audUsdForexSymbolProperties))
if (!SymbolPropertiesAreEquivalent(audUsdCfd.SymbolProperties, audUsdForexSymbolProperties))
{
throw new RegressionTestException("Expected the AUDUSD CFD symbol properties to be the same as the underlying forex symbol properties.");
}
}

private static bool SymbolPropertiesAreEquivalent(SymbolProperties a, SymbolProperties b)
{
return a.Description == b.Description &&
a.QuoteCurrency == b.QuoteCurrency &&
a.ContractMultiplier == b.ContractMultiplier &&
a.MinimumPriceVariation == b.MinimumPriceVariation &&
a.LotSize == b.LotSize &&
a.MarketTicker == b.MarketTicker &&
a.MinimumOrderSize == b.MinimumOrderSize &&
a.PriceMagnifier == b.PriceMagnifier &&
a.StrikeMultiplier == b.StrikeMultiplier;
}

/// <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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def initialize(self):
if json.JsonConvert.serialize_object(spy_cfd.exchange.hours) != json.JsonConvert.serialize_object(equity_market_hours_entry.exchange_hours):
raise AssertionError("Expected the SPY CFD market hours to be the same as the underlying equity market hours.")

if json.JsonConvert.serialize_object(spy_cfd.symbol_properties) != json.JsonConvert.serialize_object(equity_symbol_properties):
if not self.symbol_properties_are_equivalent(spy_cfd.symbol_properties, equity_symbol_properties):
raise AssertionError("Expected the SPY CFD symbol properties to be the same as the underlying equity symbol properties.")

# We can also do it for a specific ticker
Expand All @@ -66,5 +66,16 @@ def initialize(self):
if json.JsonConvert.serialize_object(aud_usd_cfd.exchange.hours) != json.JsonConvert.serialize_object(aud_usd_forex_market_hours_entry.exchange_hours):
raise AssertionError("Expected the AUDUSD CFD market hours to be the same as the underlying forex market hours.")

if json.JsonConvert.serialize_object(aud_usd_cfd.symbol_properties) != json.JsonConvert.serialize_object(aud_usd_forex_symbol_properties):
if not self.symbol_properties_are_equivalent(aud_usd_cfd.symbol_properties, aud_usd_forex_symbol_properties):
raise AssertionError("Expected the AUDUSD CFD symbol properties to be the same as the underlying forex symbol properties.")

def symbol_properties_are_equivalent(self, a, b):
return (a.description == b.description and
a.quote_currency == b.quote_currency and
a.contract_multiplier == b.contract_multiplier and
a.minimum_price_variation == b.minimum_price_variation and
a.lot_size == b.lot_size and
a.market_ticker == b.market_ticker and
a.minimum_order_size == b.minimum_order_size and
a.price_magnifier == b.price_magnifier and
a.strike_multiplier == b.strike_multiplier)
13 changes: 9 additions & 4 deletions Common/Securities/Cfd/Cfd.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ namespace QuantConnect.Securities.Cfd
/// <seealso cref="Security"/>
public class Cfd : Security
{
private readonly ContractSymbolProperties _symbolProperties;

/// <summary>
/// Constructor for the CFD security
/// </summary>
Expand All @@ -45,7 +47,7 @@ public Cfd(SecurityExchangeHours exchangeHours,
IRegisteredSecurityDataTypesProvider registeredTypes)
: base(config,
quoteCurrency,
symbolProperties,
new ContractSymbolProperties(symbolProperties),
new CfdExchange(exchangeHours),
new CfdCache(),
new SecurityPortfolioModel(),
Expand All @@ -63,6 +65,7 @@ public Cfd(SecurityExchangeHours exchangeHours,
)
{
Holdings = new CfdHolding(this, currencyConverter);
_symbolProperties = (ContractSymbolProperties)SymbolProperties;
}

/// <summary>
Expand All @@ -85,7 +88,7 @@ public Cfd(Symbol symbol,
SecurityCache securityCache)
: base(symbol,
quoteCurrency,
symbolProperties,
new ContractSymbolProperties(symbolProperties),
new CfdExchange(exchangeHours),
securityCache,
new SecurityPortfolioModel(),
Expand All @@ -103,14 +106,16 @@ public Cfd(Symbol symbol,
)
{
Holdings = new CfdHolding(this, currencyConverter);
_symbolProperties = (ContractSymbolProperties)SymbolProperties;
}

/// <summary>
/// Gets the contract multiplier for this CFD security
/// Gets or sets the contract multiplier for this CFD security
/// </summary>
public decimal ContractMultiplier
{
get { return SymbolProperties.ContractMultiplier; }
get { return _symbolProperties.ContractMultiplier; }
set { _symbolProperties.SetContractMultiplier(value); }
}

/// <summary>
Expand Down
65 changes: 65 additions & 0 deletions Common/Securities/ContractSymbolProperties.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* 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.
*/

namespace QuantConnect.Securities
{
/// <summary>
/// Represents common properties for contract-based securities such as options and CFDs
/// </summary>
public class ContractSymbolProperties : SymbolProperties
{
/// <summary>
/// The contract multiplier for the security.
/// </summary>
/// <remarks>
/// If manually set by a consumer, this value will be used instead of the
/// <see cref="SymbolProperties.ContractMultiplier"/> and also allows to make
/// sure it is not overridden when the symbol properties database gets updated.
/// </remarks>
private decimal? _contractMultiplier;

/// <summary>
/// The contract multiplier for the security
/// </summary>
public override decimal ContractMultiplier => _contractMultiplier ?? base.ContractMultiplier;

/// <summary>
/// Creates an instance of the <see cref="ContractSymbolProperties"/> class from a <see cref="SymbolProperties"/> instance
/// </summary>
public ContractSymbolProperties(SymbolProperties properties)
: base(properties)
{
}

/// <summary>
/// Creates an instance of the <see cref="ContractSymbolProperties"/> class
/// </summary>
public ContractSymbolProperties(string description, string quoteCurrency, decimal contractMultiplier,
decimal minimumPriceVariation, decimal lotSize, string marketTicker,
decimal? minimumOrderSize = null, decimal priceMagnifier = 1, decimal strikeMultiplier = 1)
: base(description, quoteCurrency, contractMultiplier, minimumPriceVariation, lotSize, marketTicker,
minimumOrderSize, priceMagnifier, strikeMultiplier)
{
}

/// <summary>
/// Sets a custom contract multiplier that persists through symbol properties database updates
/// </summary>
internal void SetContractMultiplier(decimal multiplier)
{
_contractMultiplier = multiplier;
}
}
}
22 changes: 1 addition & 21 deletions Common/Securities/Option/OptionSymbolProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,8 @@ namespace QuantConnect.Securities.Option
/// <summary>
/// Represents common properties for a specific option contract
/// </summary>
public class OptionSymbolProperties : SymbolProperties
public class OptionSymbolProperties : ContractSymbolProperties
{
/// <summary>
/// The contract multiplier for the security.
/// </summary>
/// <remarks>
/// If manually set by a consumer, this value will be used instead of the
/// <see cref="SymbolProperties.ContractMultiplier"/> and also allows to make
/// sure it is not overridden when the symbol properties database gets updated.
/// </remarks>
private decimal? _contractMultiplier;

/// <summary>
/// The contract multiplier for the security
/// </summary>
public override decimal ContractMultiplier => _contractMultiplier ?? base.ContractMultiplier;

/// <summary>
/// When the holder of an equity option exercises one contract, or when the writer of an equity option is assigned
/// an exercise notice on one contract, this unit of trade, usually 100 shares of the underlying security, changes hands.
Expand Down Expand Up @@ -65,10 +50,5 @@ internal void SetContractUnitOfTrade(int unitOfTrade)
{
ContractUnitOfTrade = unitOfTrade;
}

internal void SetContractMultiplier(decimal multiplier)
{
_contractMultiplier = multiplier;
}
}
}
21 changes: 16 additions & 5 deletions Tests/Common/Securities/Cfd/CfdTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
* limitations under the License.
*/

using System;
using NUnit.Framework;
using QuantConnect.Data;
using QuantConnect.Data.Market;
Expand All @@ -27,12 +26,24 @@ public class CfdTests
[Test]
public void ConstructorExtractsQuoteCurrency()
{
var symbol = Symbol.Create("DE30EUR", SecurityType.Cfd, Market.Oanda);
var config = new SubscriptionDataConfig(typeof(TradeBar), symbol, Resolution.Minute, TimeZones.Utc, TimeZones.NewYork, true, true, true);
var symbolProperties = new SymbolProperties("Dax German index", "EUR", 1, 1, 1, string.Empty);
var cfd = new QuantConnect.Securities.Cfd.Cfd(SecurityExchangeHours.AlwaysOpen(config.DataTimeZone), new Cash("EUR", 0, 0), config, symbolProperties, ErrorCurrencyConverter.Instance, RegisteredSecurityDataTypesProvider.Null);
var cfd = CreateCfd(contractMultiplier: 1m);
Assert.AreEqual("EUR", cfd.QuoteCurrency.Symbol);
}

[Test]
public void ContractMultiplierCanBeSetByUser()
{
var cfd = CreateCfd(contractMultiplier: 1m);
cfd.ContractMultiplier = 5m;
Assert.AreEqual(5m, cfd.ContractMultiplier);
}

private static QuantConnect.Securities.Cfd.Cfd CreateCfd(decimal contractMultiplier)
{
var symbol = Symbol.Create("DE30EUR", SecurityType.Cfd, Market.Oanda);
var config = new SubscriptionDataConfig(typeof(TradeBar), symbol, Resolution.Minute, TimeZones.Utc, TimeZones.NewYork, true, true, true);
var symbolProperties = new SymbolProperties("Dax German index", "EUR", contractMultiplier, 1, 1, string.Empty);
return new QuantConnect.Securities.Cfd.Cfd(SecurityExchangeHours.AlwaysOpen(config.DataTimeZone), new Cash("EUR", 0, 0), config, symbolProperties, ErrorCurrencyConverter.Instance, RegisteredSecurityDataTypesProvider.Null);
}
}
}
Loading