Skip to content

Commit

Permalink
Add Nikkei 225 index support (#8529)
Browse files Browse the repository at this point in the history
- Add Osaka exchange for nikkei index. Adding regression test
- Improve default market resolution
  • Loading branch information
Martin-Molinero authored Jan 14, 2025
1 parent 5accfd6 commit cda769d
Show file tree
Hide file tree
Showing 10 changed files with 338 additions and 57 deletions.
2 changes: 1 addition & 1 deletion Algorithm.CSharp/HSIFutureHourRegressionAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public override void Initialize()
SetTimeZone(TimeZones.HongKong);

UniverseSettings.Resolution = Resolution;
_index = AddIndex("HSI", Resolution, market: Market.HKFE).Symbol;
_index = AddIndex("HSI", Resolution).Symbol;
var future = AddFuture(Futures.Indices.HangSeng, Resolution);
future.SetFilter(TimeSpan.Zero, TimeSpan.FromDays(182));
_futureSymbol = future.Symbol;
Expand Down
122 changes: 122 additions & 0 deletions Algorithm.CSharp/NikkeiIndexRegressionAlgorithm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* 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.Indicators;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
using System.Collections.Generic;

namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Example algorithm using nikkei index
/// </summary>
public class NikkeiIndexRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private Security _security;
private ExponentialMovingAverage _emaSlow;
private ExponentialMovingAverage _emaFast;

/// <summary>
/// Initialize your algorithm and add desired assets.
/// </summary>
public override void Initialize()
{
SetStartDate(2021, 1, 14);
SetEndDate(2021, 1, 15);
SetCash(1000000);

_security = AddIndex("N225");

var symbol = _security.Symbol;
_emaSlow = EMA(symbol, 80);
_emaFast = EMA(symbol, 200);

Settings.DailyPreciseEndTime = true;
}

public override void OnEndOfAlgorithm()
{
if (!_emaSlow.IsReady || !_emaFast.IsReady)
{
throw new RegressionTestException("Indicators are not ready!");
}
if (!_security.HasData)
{
throw new RegressionTestException("Security does not have data!");
}
}

/// <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 virtual bool CanRunLocally { get; } = true;

/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public virtual List<Language> Languages { get; } = new() { Language.CSharp };

/// <summary>
/// Data Points count of all timeslices of algorithm
/// </summary>
public virtual long DataPoints => 443;

/// <summary>
/// Data Points count of the algorithm history
/// </summary>
public virtual int AlgorithmHistoryDataPoints => 0;

/// <summary>
/// Final status of the algorithm
/// </summary>
public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;

/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public virtual Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Orders", "0"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "0%"},
{"Drawdown", "0%"},
{"Expectancy", "0"},
{"Start Equity", "1000000"},
{"End Equity", "1000000"},
{"Net Profit", "0%"},
{"Sharpe Ratio", "0"},
{"Sortino 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", "$0.00"},
{"Estimated Strategy Capacity", "$0"},
{"Lowest Capacity Asset", ""},
{"Portfolio Turnover", "0%"},
{"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"}
};
}
}
84 changes: 37 additions & 47 deletions Algorithm/QCAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
using Python.Runtime;
using QuantConnect.Commands;
using Newtonsoft.Json;
using QuantConnect.Securities.Index;

namespace QuantConnect.Algorithm
{
Expand Down Expand Up @@ -1378,11 +1379,7 @@ public void SetBenchmark(SecurityType securityType, string symbol)
throw new InvalidOperationException("Algorithm.SetBenchmark(): Cannot change Benchmark after algorithm initialized.");
}

string market;
if (!BrokerageModel.DefaultMarkets.TryGetValue(securityType, out market))
{
market = Market.USA;
}
var market = GetMarket(null, symbol, securityType, defaultMarket: Market.USA);

var benchmarkSymbol = QuantConnect.Symbol.Create(symbol, securityType, market);
SetBenchmark(benchmarkSymbol);
Expand Down Expand Up @@ -1886,13 +1883,7 @@ public Security AddSecurity(SecurityType securityType, string ticker, Resolution

try
{
if (market == null)
{
if (!BrokerageModel.DefaultMarkets.TryGetValue(securityType, out market))
{
throw new KeyNotFoundException($"No default market set for security type: {securityType}");
}
}
market = GetMarket(market, ticker, securityType);

Symbol symbol;
if (!SymbolCache.TryGetSymbol(ticker, out symbol) ||
Expand Down Expand Up @@ -2068,13 +2059,7 @@ public Equity AddEquity(string ticker, Resolution? resolution = null, string mar
[DocumentationAttribute(AddingData)]
public Option AddOption(string underlying, Resolution? resolution = null, string market = null, bool fillForward = true, decimal leverage = Security.NullLeverage)
{
if (market == null)
{
if (!BrokerageModel.DefaultMarkets.TryGetValue(SecurityType.Option, out market))
{
throw new KeyNotFoundException($"No default market set for security type: {SecurityType.Option}");
}
}
market = GetMarket(market, underlying, SecurityType.Option);

var underlyingSymbol = QuantConnect.Symbol.Create(underlying, SecurityType.Equity, market);
return AddOption(underlyingSymbol, resolution, market, fillForward, leverage);
Expand Down Expand Up @@ -2117,13 +2102,7 @@ public Option AddOption(Symbol underlying, string targetOption, Resolution? reso
{
var optionType = QuantConnect.Symbol.GetOptionTypeFromUnderlying(underlying);

if (market == null)
{
if (!BrokerageModel.DefaultMarkets.TryGetValue(optionType, out market))
{
throw new KeyNotFoundException($"No default market set for security type: {optionType}");
}
}
market = GetMarket(market, targetOption, optionType);

Symbol canonicalSymbol;

Expand Down Expand Up @@ -2165,14 +2144,7 @@ public Future AddFuture(string ticker, Resolution? resolution = null, string mar
bool fillForward = true, decimal leverage = Security.NullLeverage, bool extendedMarketHours = false,
DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null, int contractDepthOffset = 0)
{
if (market == null)
{
if (!SymbolPropertiesDatabase.TryGetMarket(ticker, SecurityType.Future, out market)
&& !BrokerageModel.DefaultMarkets.TryGetValue(SecurityType.Future, out market))
{
throw new KeyNotFoundException($"No default market set for security type: {SecurityType.Future}");
}
}
market = GetMarket(market, ticker, SecurityType.Future);

Symbol canonicalSymbol;
var alias = "/" + ticker;
Expand Down Expand Up @@ -2301,13 +2273,8 @@ public IndexOption AddIndexOption(Symbol symbol, string targetOption, Resolution
[DocumentationAttribute(AddingData)]
public IndexOption AddIndexOption(string underlying, string targetOption, Resolution? resolution = null, string market = null, bool fillForward = true)
{
if (market == null && !BrokerageModel.DefaultMarkets.TryGetValue(SecurityType.Index, out market))
{
throw new KeyNotFoundException($"No default market set for underlying security type: {SecurityType.Index}");
}

return AddIndexOption(
QuantConnect.Symbol.Create(underlying, SecurityType.Index, market),
QuantConnect.Symbol.Create(underlying, SecurityType.Index, GetMarket(market, underlying, SecurityType.Index)),
targetOption, resolution, fillForward);
}

Expand Down Expand Up @@ -2946,13 +2913,7 @@ private T AddSecurity<T>(SecurityType securityType, string ticker, Resolution? r
DataMappingMode? mappingMode = null, DataNormalizationMode? normalizationMode = null)
where T : Security
{
if (market == null)
{
if (!BrokerageModel.DefaultMarkets.TryGetValue(securityType, out market))
{
throw new Exception("No default market set for security type: " + securityType);
}
}
market = GetMarket(market, ticker, securityType);

Symbol symbol;
if (!SymbolCache.TryGetSymbol(ticker, out symbol) ||
Expand Down Expand Up @@ -3489,6 +3450,35 @@ public CommandResultPacket RunCommand(CallbackCommand command)
return true;
}

/// <summary>
/// Helper method to get a market for a given security type and ticker
/// </summary>
private string GetMarket(string market, string ticker, SecurityType securityType, string defaultMarket = null)
{
if (string.IsNullOrEmpty(market))
{
if (securityType == SecurityType.Index && IndexSymbol.TryGetIndexMarket(ticker, out market))
{
return market;
}

if (securityType == SecurityType.Future && SymbolPropertiesDatabase.TryGetMarket(ticker, securityType, out market))
{
return market;
}

if (!BrokerageModel.DefaultMarkets.TryGetValue(securityType, out market))
{
if (string.IsNullOrEmpty(defaultMarket))
{
throw new KeyNotFoundException($"No default market set for security type: {securityType}");
}
return defaultMarket;
}
}
return market;
}

private string CommandLink(string typeName, object command)
{
var payload = new Dictionary<string, dynamic> { { "projectId", ProjectId }, { "command", command } };
Expand Down
7 changes: 6 additions & 1 deletion Common/Currencies.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ public static class Currencies
/// </summary>
public const string HKD = "HKD";

/// <summary>
/// JPY (Japanese yen) currency string
/// </summary>
public const string JPY = "JPY";

/// <summary>
/// Null currency used when a real one is not required
/// </summary>
Expand All @@ -81,7 +86,7 @@ public static class Currencies
{
{USD, "$"},
{GBP, "₤"},
{"JPY", "¥"},
{JPY, "¥"},
{EUR, "€"},
{"NZD", "$"},
{"AUD", "$"},
Expand Down
8 changes: 7 additions & 1 deletion Common/Market.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ public static class Market
Tuple.Create(Bybit, 37),
Tuple.Create(Coinbase, 38),
Tuple.Create(InteractiveBrokers, 39),
Tuple.Create(EUREX, 40)
Tuple.Create(EUREX, 40),
Tuple.Create(OSE, 41)
};

static Market()
Expand Down Expand Up @@ -169,6 +170,11 @@ static Market()
/// </summary>
public const string HKFE = "hkfe";

/// <summary>
/// Osaka Stock Exchange
/// </summary>
public const string OSE = "ose";

/// <summary>
/// London International Financial Futures and Options Exchange
/// </summary>
Expand Down
27 changes: 21 additions & 6 deletions Common/Securities/Index/IndexSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* limitations under the License.
*/

using System;
using System.Collections.Generic;

namespace QuantConnect.Securities.Index
Expand All @@ -22,27 +23,41 @@ namespace QuantConnect.Securities.Index
/// </summary>
public static class IndexSymbol
{
private static readonly Dictionary<string, string> _indexMarket = new Dictionary<string, string>
private static readonly Dictionary<string, string> _indexExchange = new(StringComparer.InvariantCultureIgnoreCase)
{
{ "SPX", Market.CBOE },
{ "NDX", "NASDAQ" },
{ "VIX", Market.CBOE },
{ "SPXW", Market.CBOE },
{ "NQX", "NASDAQ" },
{ "VIXW", Market.CBOE },
{ "HSI", Market.HKFE }
{ "VIXW", Market.CBOE }
};

private static readonly Dictionary<string, string> _indexMarket = new(StringComparer.InvariantCultureIgnoreCase)
{
{ "HSI", Market.HKFE },
{ "N225", Market.OSE }
};

/// <summary>
/// Gets the actual exchange the index lives on
/// </summary>
/// <returns>The market of the index</returns>
/// <remarks>Useful for live trading</remarks>
/// <returns>The exchange of the index</returns>
public static string GetIndexExchange(Symbol symbol)
{
string market;
return _indexMarket.TryGetValue(symbol.Value, out market)
return _indexExchange.TryGetValue(symbol.Value, out var market)
? market
: symbol.ID.Market;
}

/// <summary>
/// Gets the lean market for this index ticker
/// </summary>
/// <returns>The market of the index</returns>
public static bool TryGetIndexMarket(string ticker, out string market)
{
return _indexMarket.TryGetValue(ticker, out market);
}
}
}
Binary file added Data/index/ose/minute/n225/20210114_trade.zip
Binary file not shown.
Loading

0 comments on commit cda769d

Please sign in to comment.